在上一篇文章,講了服務的注冊和發現。在微服務架構中,業務都會被拆分成一個獨立的服務,服務與服務的通訊是基於http restful的。Spring cloud有兩種服務調用方式,一種是ribbon+restTemplate,另一種是feign。在這一篇文章首先講解下基於ribbon+rest。
一、ribbon簡介
Ribbon is a client side load balancer which gives you a lot of control over the behaviour of HTTP and TCP clients. Feign already uses Ribbon, so if you are using @FeignClient then this section also applies.
—–摘自官網
ribbon是一個負載均衡客戶端,可以很好的控制htt和tcp的一些行為。Feign默認集成了ribbon。
ribbon 已經默認實現了這些配置bean:
-
IClientConfig ribbonClientConfig: DefaultClientConfigImpl
-
IRule ribbonRule: ZoneAvoidanceRule
-
IPing ribbonPing: NoOpPing
-
ServerList ribbonServerList: ConfigurationBasedServerList
-
ServerListFilter ribbonServerListFilter: ZonePreferenceServerListFilter
-
ILoadBalancer ribbonLoadBalancer: ZoneAwareLoadBalancer
二、准備工作
這一篇文章基於上一篇文章的工程,啟動eureka-server 工程;啟動eureka-client工程,它的端口為8882;將service-hi的配置文件的端口改為8883,並啟動,這時你會發現:eureka-client在eureka-server注冊了2個實例,我將端口又換成8886,這樣就往eureka-server中注冊了三個實例,這就相當於一個小的集群。如下圖可以看到圖標顯示為3,說明同時啟動了三個實例。

如何在idea下啟動多個實例,請參照這篇文章: https://blog.csdn.net/forezp/article/details/76408139
訪問localhost:8881如圖所示: 如何一個工程啟動多個實例,請看這篇文章:https://blog.csdn.net/forezp/article/details/76408139

三、建一個服務消費者
重新建一個Maven工程,其中pom如下:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 5 <modelVersion>4.0.0</modelVersion> 6 7 <groupId>com.liumy</groupId> 8 <artifactId>ribbon-f-2</artifactId> 9 <version>1.0-SNAPSHOT</version> 10 <packaging>pom</packaging> 11 12 <parent> 13 <groupId>org.springframework.boot</groupId> 14 <artifactId>spring-boot-starter-parent</artifactId> 15 <version>2.0.3.RELEASE</version> 16 <relativePath/> 17 </parent> 18 19 <modules> 20 <module>service-ribbon</module> 21 </modules> 22 23 <properties> 24 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 25 <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> 26 <java.version>1.8</java.version> 27 <spring-cloud.version>Finchley.RELEASE</spring-cloud.version> 28 </properties> 29 30 <dependencies> 31 <dependency> 32 <groupId>org.springframework.boot</groupId> 33 <artifactId>spring-boot-starter-test</artifactId> 34 <scope>test</scope> 35 </dependency> 36 </dependencies> 37 38 <dependencyManagement> 39 <dependencies> 40 <dependency> 41 <groupId>org.springframework.cloud</groupId> 42 <artifactId>spring-cloud-dependencies</artifactId> 43 <version>${spring-cloud.version}</version> 44 <type>pom</type> 45 <scope>import</scope> 46 </dependency> 47 </dependencies> 48 </dependencyManagement> 49 50 <build> 51 <plugins> 52 <plugin> 53 <groupId>org.springframework.boot</groupId> 54 <artifactId>spring-boot-maven-plugin</artifactId> 55 </plugin> 56 </plugins> 57 </build> 58 59 </project>
再重新建一個spring-boot工程,取名為:service-ribbon; 在它的pom.xml繼承了父pom文件,並引入了以下依賴:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.liumy</groupId> <artifactId>ribbon-f-2</artifactId> <version>1.0-SNAPSHOT</version> </parent> <groupId>com.liumy</groupId> <artifactId>service-ribbon</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>service-ribbon</name> <description>Demo project for Spring Boot</description> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency> </dependencies> </project>
在工程的配置文件指定服務的注冊中心地址為http://localhost:8881/eureka/,程序名稱為 service-ribbon,程序端口為8884。配置文件application.yml如下:
1 eureka: 2 client: 3 serviceUrl: 4 defaultZone: http://localhost:8881/eureka/ 5 server: 6 port: 8884 7 spring: 8 application: 9 name: service-ribbon
在工程的啟動類中,通過@EnableDiscoveryClient向服務中心注冊;並且向程序的ioc注入一個bean: restTemplate;並通過@LoadBalanced注解表明這個restRemplate開啟負載均衡的功能。
1 package com.liumy.serviceribbon; 2 3 import org.springframework.boot.SpringApplication; 4 import org.springframework.boot.autoconfigure.SpringBootApplication; 5 import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 6 import org.springframework.cloud.client.loadbalancer.LoadBalanced; 7 import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 8 import org.springframework.context.annotation.Bean; 9 import org.springframework.web.client.RestTemplate; 10 11 @EnableEurekaClient 12 @SpringBootApplication 13 @EnableDiscoveryClient//通過@EnableDiscoveryClient向服務中心注冊 14 public class ServiceRibbonApplication { 15 16 public static void main(String[] args) { 17 SpringApplication.run(ServiceRibbonApplication.class, args); 18 } 19 20 @Bean//向程序的ioc注入一個bean 21 @LoadBalanced//通過@LoadBalanced注解表明這個restRemplate開啟負載均衡的功能。 22 RestTemplate restTemplate(){ 23 return new RestTemplate(); 24 } 25 }
寫一個測試類HelloService,通過之前注入ioc容器的restTemplate來消費eureka-client服務的“/hello”接口,在這里我們直接用的程序名替代了具體的url地址,在ribbon中它會根據服務名來選擇具體的服務實例,根據服務實例在請求的時候會用具體的url替換掉服務名,代碼如下:
1 package com.liumy.serviceribbon; 2 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.stereotype.Service; 5 import org.springframework.web.client.RestTemplate; 6 7 /** 8 * @ClassName HelloService 9 * @Descroption TODO ribbon測試service 10 * @Author Yubaba 11 * @Date 2019/10/20 15:11 12 **/ 13 @Service 14 public class HelloService { 15 16 @Autowired 17 RestTemplate restTemplate; 18 19 public String HelloService(String name){ 20 return restTemplate.getForObject("http://EUREKA-CLIENT/hello?name="+name,String.class); 21 } 22 }
寫一個controller,在controller中用調用HelloService 的方法,代碼如下:
1 package com.liumy.serviceribbon; 2 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.web.bind.annotation.GetMapping; 5 import org.springframework.web.bind.annotation.RequestParam; 6 import org.springframework.web.bind.annotation.RestController; 7 8 /** 9 * @ClassName HelloController 10 * @Descroption TODO ribbon測試controller 11 * @Author Yubaba 12 * @Date 2019/10/20 15:15 13 **/ 14 @RestController 15 public class HelloController { 16 17 @Autowired 18 HelloService helloService; 19 20 @GetMapping("helloController") 21 public String helloController(@RequestParam("name")String name){ 22 return helloService.HelloService(name); 23 } 24 25 }
在瀏覽器上多次訪問http://localhost:8884/helloController?name=測試,瀏覽器交替顯示:



這說明當我們通過調用restTemplate.getForObject(“http://EUREKA-CLIENT/hello?name=”+name,String.class)方法時,已經做了負載均衡,訪問了不同的端口的服務實例。
四、此時的架構

(圖例來源於網絡,為了方便理解)
- 一個服務注冊中心,eureka server,端口為8881
- service-hi工程跑了三個實例,端口分別為8882,8883,8886,分別向服務注冊中心注冊
- sercvice-ribbon端口為8884,向服務注冊中心注冊
- 當sercvice-ribbon通過restTemplate調用service-hi的hi接口時,因為用ribbon進行了負載均衡,會輪流的調用eureka-client:8882、8883和8886 三個端口的hello接口
五、參考資料
本文參考了以下:
http://blog.csdn.net/forezp/article/details/69788938
http://cloud.spring.io/spring-cloud-static/Finchley.RELEASE/single/spring-cloud.html
