微服務之間的呼叫(Ribbon與Feign)
概述
在前面的文章中,我們講了使用Eureka
作為服務註冊中心,在服務啟動後,各個微服務會將自己註冊到Eureka server
。那麼服務之間是如何呼叫?又是如何進行負載均衡的呢?本文講講服務之間呼叫及負載均衡Ribbon。
目前,在Spring cloud 中服務之間通過restful方式呼叫有兩種方式
- restTemplate+Ribbon
- feign
從實踐上看,採用feign的方式更優雅(feign內部也使用了ribbon做負載均衡)。
本文使用如下的專案例項來分別演示這兩種寫法。
- hello
服務,讀取資料庫,返回資訊
- world
服務,返回資訊
- helloworld
服務,呼叫了hello
服務和world
服務(restTemplate+Ribbon方式)
- helloworldfeign
服務,呼叫了hello
服務和world
服務(feign方式)
本文專案程式碼:springcloud-demo
如何理解客戶端Ribbon
zuul也有負載均衡的功能,它是針對外部請求做負載,那客戶端ribbon的負載均衡又是怎麼一回事?
客戶端ribbon的負載均衡,解決的是服務發起方(在Eureka註冊的服務)對被呼叫的服務的負載,比如我們查詢商品服務要呼叫顯示庫存和商品明細服務,通過商品服務的介面將兩個服務組合,可以減少外部應用的請求,比如手機App發起一次請求即可,可以節省網路頻寬,也更省電。
ribbon是對服務之間呼叫做負載,是服務之間的負載均衡,zuul是可以對外部請求做負載均衡。
hello服務與world服務
hello服務
Git專案程式碼:hello
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<!--connect the db-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>1.5.4.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.31</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>1.5.6.RELEASE</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
啟動類HelloApplication:
package com.example.hello;
import com.example.hello.model.Info;
import com.example.hello.repository.InfoRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.Bean;
@EnableDiscoveryClient
@SpringBootApplication
public class HelloApplication {
private static final Logger log= LoggerFactory.getLogger(HelloApplication.class);
public static final String KEY="Database";
public static final String VALUE="MYSQL FROM BILLJIANG";
public static void main(String[] args) {
SpringApplication.run(HelloApplication.class, args);
}
@Bean
public CommandLineRunner initDatabase(InfoRepository repository){
return (args) -> {
repository.save(new Info(KEY,VALUE));
};
}
}
入口類HelloController:
package com.example.hello.controller;
import com.example.hello.HelloApplication;
import com.example.hello.model.HelloMessage;
import com.example.hello.model.Info;
import com.example.hello.repository.InfoRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class HelloController {
@Autowired
private DiscoveryClient discoveryClient;
@Autowired
private InfoRepository infoRepository;
@GetMapping("/")
public String home(){
return "hello";
}
@GetMapping("/message")
public HelloMessage getMessage() {
HelloMessage helloMessage = new HelloMessage();
helloMessage.setName(getLocalInstanceInfo());
helloMessage.setMessage(getInfoFromDatabase());
return helloMessage;
}
private String getLocalInstanceInfo() {
ServiceInstance serviceInstance = discoveryClient.getLocalServiceInstance();
return serviceInstance.getServiceId() + ":" + serviceInstance.getHost() + ":" + serviceInstance.getPort();
}
private String getInfoFromDatabase() {
List<Info> infoList = infoRepository.findByName(HelloApplication.KEY);
for (Info info : infoList) {
return info.toString();
}
return "(no database info)";
}
}
配置檔案application.yml:
#隨機埠
server:
port: 0
#服務註冊中心
eureka:
client:
service-url:
default-zone: http://localhost:8761/eureka
instance:
instance-id: ${spring.application.name}:${spring.application.instance_id:${random.value}}
#資料來源
spring:
application:
name: hello
datasource:
url: jdbc:mysql://127.0.0.1:3306/${{MYSQL_DATABASE}:foodb}
username: ${{MYSQL_USERNAME}:root}
password: ${{MYSQL_PASSWORD}:billjiang}
testWhileIdle: true
validationQuery: SELECT 1
jpa:
show-sql: true
hibernate:
ddl-auto: update
naming-strategy: org.hibernate.cfg.ImprovedNamingStrategy
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL5Dialect
zipkin:
base-url: http://127.0.0.1:9411
其他類略,請參照原始碼:
world
world服務與hello服務類似,不過沒有連線資料庫
專案程式碼:world
helloworld呼叫hello服務和world服務
採用restTemplate+Ribbon呼叫服務。
專案程式碼:helloworld
啟動類HelloworldApplication:
配置restTemplate的Bean
package com.example.helloworld;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableDiscoveryClient
public class HelloworldApplication {
public static void main(String[] args) {
SpringApplication.run(HelloworldApplication.class, args);
}
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
}
入口類HelloworldController:
package com.example.helloworld.controller;
import com.example.helloworld.model.HelloMessage;
import com.example.helloworld.model.HelloworldMessage;
import com.example.helloworld.model.WorldMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
* @author billjiang 475572229@qq.com
* @create 17-8-22
*/
@RestController
public class HelloworldController {
private static final Logger log = LoggerFactory.getLogger(HelloworldController.class);
private static final String HELLO_SERVICE_NAME = "hello";
private static final String WORLD_SERVICE_NAME = "world";
@Autowired
private RestTemplate restTemplate;
@GetMapping("/")
public String home() {
return "hello world";
}
@GetMapping("/message")
public HelloworldMessage getMessage() {
HelloMessage hello = getMessageFromHelloService();
WorldMessage world = getMessageFromWorldService();
HelloworldMessage helloworld = new HelloworldMessage();
helloworld.setHello(hello);
helloworld.setWord(world);
log.debug("Result helloworld message:{}", helloworld);
return helloworld;
}
private HelloMessage getMessageFromHelloService() {
HelloMessage hello = restTemplate.getForObject("http://hello/message", HelloMessage.class);
log.debug("From hello service : {}.", hello);
return hello;
}
private WorldMessage getMessageFromWorldService() {
WorldMessage world = restTemplate.getForObject("http://world/message", WorldMessage.class);
log.debug("From world service : {}.", world);
return world;
}
}
測試:
helloworldfeign呼叫hello服務和world服務
feign方式呼叫服務,專案程式碼參考:helloworldfeign
pom.xml引入feignx
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
配置啟動類註解:
package com.example.helloworldfeign;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class HelloworldFeignApplication {
public static void main(String[] args) {
SpringApplication.run(HelloworldFeignApplication.class, args);
}
}
被呼叫介面HelloService
package com.example.helloworldfeign.service;
import com.example.helloworldfeign.model.HelloMessage;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
/**
* @author billjiang 475572229@qq.com
* @create 17-8-23
*/
@FeignClient(value="hello")
public interface HelloService {
@GetMapping("/message")
HelloMessage hello();
}
被呼叫介面WorldService
package com.example.helloworldfeign.service;
import com.example.helloworldfeign.model.WorldMessage;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
/**
* @author billjiang 475572229@qq.com
* @create 17-8-23
*/
@FeignClient(value="world")
public interface WorldService {
@GetMapping("/message")
WorldMessage world();
}
入口類HelloworldController:
package com.example.helloworldfeign.controller;
import com.example.helloworldfeign.model.HelloMessage;
import com.example.helloworldfeign.model.HelloworldMessage;
import com.example.helloworldfeign.model.WorldMessage;
import com.example.helloworldfeign.service.HelloService;
import com.example.helloworldfeign.service.WorldService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author billjiang 475572229@qq.com
* @create 17-8-23
*/
@RestController
public class HelloworldController {
private static final Logger log = LoggerFactory.getLogger(HelloworldController.class);
private static final String HELLO_SERVICE_NAME = "hello";
private static final String WORLD_SERVICE_NAME = "world";
@Autowired
private HelloService helloService;
@Autowired
private WorldService worldService;
@GetMapping("/")
public String home() {
return "hello world";
}
@GetMapping("/message")
public HelloworldMessage getMessage() {
HelloMessage hello = helloService.hello();
WorldMessage world = worldService.world();
HelloworldMessage helloworld = new HelloworldMessage();
helloworld.setHello(hello);
helloworld.setWord(world);
log.debug("Result helloworld message:{}", helloworld);
return helloworld;
}
}
為了更好的地檢視效果,hello服務和world服務可以啟動多個例項,並把呼叫例項的ip和埠輸出來。
相關文章
- Eureka微服務之間呼叫-feign微服務
- SpringCloud之服務提供與呼叫(Ribbon,Feign)SpringGCCloud
- 微服務呼叫元件 Feign微服務元件
- 微服務互相呼叫-Feign微服務
- Eureka的微服務之間呼叫微服務
- 微服務之間的相互呼叫微服務
- [jaeger] 四、微服務之呼叫鏈(Feign+SpringCloud)微服務SpringGCCloud
- SpringBoot+Eureka註冊中心+Feign進行微服務之間呼叫Spring Boot微服務
- Spring Cloud之微服務之間相互呼叫、如何讓一個微服務呼叫另外一個微服務SpringCloud微服務
- 微服務之間的呼叫方式哪種最佳?微服務
- 微服務架構 | 4.2 基於 Feign 與 OpenFeign 的服務介面呼叫微服務架構
- 微服務~Eureka實現的服務註冊與發現及服務之間的呼叫微服務
- Spring Cloud微服務-基於Eureka的feign呼叫(1)SpringCloud微服務
- 微服務通訊之feign的配置隔離微服務
- 難住了,微服務之間的呼叫方式哪種更優?微服務
- 微服務通訊之ribbon實現原理微服務
- 還在用Feign?推薦一款微服務間呼叫神器,跟SpringCloud絕配!微服務SpringGCCloud
- 微服務通訊之feign整合負載均衡微服務負載
- 微服務間的呼叫和應用內呼叫有啥區別微服務
- 微服務通訊之feign的註冊、發現過程微服務
- SpringCloud系列之使用Feign進行服務呼叫SpringGCCloud
- SpringCloud之使用Feign跨服務呼叫最佳方式SpringGCCloud
- 微服務之Eureka(二)服務中心互相註冊-Ribbon的結合使用微服務
- 微服務之間的協作方式微服務
- ②SpringCloud 實戰:引入Feign元件,發起服務間呼叫SpringGCCloud元件
- SpringCloud分散式微服務b2b2c電子商務docker-feign-hystrix-ribbon(八)SpringGCCloud分散式微服務Docker
- 微服務架構之「 呼叫鏈監控 」微服務架構
- 微服務之間如何共享DTO?微服務
- 微服務負載均衡器 Ribbon微服務負載
- 微服務間的方法呼叫和應用內方法呼叫有啥區別微服務
- 微服務的服務間通訊與服務治理微服務
- .NET Core微服務開發服務間呼叫篇-GRPC微服務RPC
- eureka實現服務之間的呼叫
- Spring Cloud中如何保證各個微服務之間呼叫的安全性SpringCloud微服務
- Blazor+Dapr+K8s微服務之服務呼叫BlazorK8S微服務
- 跟我學SpringCloud | 第三篇:服務的提供與Feign呼叫SpringGCCloud
- nacos、ribbon和feign的簡明教程
- Spring Cloud中如何保證各個微服務之間呼叫的安全性(下篇)SpringCloud微服務