SpringCloud學習系列之二 ----- 服務消費者(Feign)和負載均衡(Ribbon)
本篇主要介紹的是SpringCloud中的服務消費者(Feign)和負載均衡(Ribbon)功能的實現以及使用Feign結合Ribbon實現負載均衡。
SpringCloud Feign
Feign 介紹
Feign是一個聲明式的Web Service客戶端,它使得編寫Web Serivce客戶端變得更加簡單。我們只需要使用Feign來創建一個接口並用註解來配置它既可完成。它具備可插拔的註解支持,包括Feign註解和JAX-RS註解。Feign也支持可插拔的編碼器和×××。Spring Cloud為Feign增加了對Spring MVC註解的支持,還整合了Ribbon和Eureka來提供均衡負載的HTTP客戶端實現。
開發準備
開發環境
- JDK:1.8
- SpringBoot:2.1.1.RELEASE
- SpringCloud:Finchley
註:不一定非要用上述的版本,可以根據情況進行相應的調整。需要註意的是SpringBoot2.x以後,jdk的版本必須是1.8以上!
確認了開發環境之後,我們再來添加相關的pom依賴。
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> </dependencies>
註: 基於SpringBoot1.x以上SpringCloud是Dalston版本的eureka 依賴是 <artifactId>spring-cloud-starter-eureka</artifactId>
,feign 依賴是 <artifactId>spring-cloud-starter-feign</artifactId>
,少了個 netflix 。SpringCloud的版本命名方式是通過倫敦的地方來命名的,版本順序是根據首字母的順序來的。
SpringCloud Feign 示例
服務端
首先建立一個springcloud-feign-eureka
服務的工程,用於做註冊中心。配置和之前的基本一樣。application.properties
添加如下的配置:
配置信息:
spring.application.name=springcloud-feign-eureka-server
server.port=8001
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.client.serviceUrl.defaultZone=http://localhost:8001/eureka/
配置說明:
- spring.application.name: 這個是指定服務名稱。
- server.port:服務指定的端口。
- eureka.client.register-with-eureka:表示是否將自己註冊到Eureka Server,默認是true。
- eureka.client.fetch-registry:表示是否從Eureka Server獲取註冊信息,默認為true。
- eureka.client.serviceUrl.defaultZone: 這個是設置與Eureka Server交互的地址,客戶端的查詢服務和註冊服務都需要依賴這個地址。
完成配置信息的添加後,我們再來看代碼如何實現。
在服務端這邊只需要在SpringBoot啟動類添加@EnableEurekaServer
註解就可以了,該註解表示此服務是一個服務註冊中心服務。
代碼示例:
@EnableEurekaServer
@SpringBootApplication
public class FeignEurekaApplication {
public static void main(String[] args) {
SpringApplication.run(FeignEurekaApplication.class, args);
System.out.println("feign註冊中心服務啟動...");
}
}
客戶端
這裏我們定義兩個消費者,springcloud-feign-consumer
和springcloud-feign-consumer2
,一個使用feign做轉發,另一個為一個普通的項目。 添加如上的依賴之後,在application.properties
添加如下的配置:
consumer 配置信息:
spring.application.name=springcloud-feign-consumer
server.port=9002
eureka.client.serviceUrl.defaultZone=http://localhost:8001/eureka/
consumer2 配置信息:
spring.application.name=springcloud-feign-consumer2
server.port=9003
eureka.client.serviceUrl.defaultZone=http://localhost:8001/eureka/
配置說明:
- spring.application.name: 這個是客戶端的服務名稱。如果有多個服務使用同一個名稱但是訪問地址不同,結合ribbon 使用,則可以實現負載均衡功能。
- server.port:服務指定的端口。
- eureka.client.serviceUrl.defaultZone: 註冊中心服務端的地址。
springcloud-feign-consumer
springcloud-feign-consumer
服務因為要實現fegin的功能,因此需要在啟動類上添加@EnableFeignClients
該註解,使用該註解表示啟用feign進行遠程調用。
啟動類代碼示例:
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class FeignConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(FeignConsumerApplication.class, args);
System.out.println("feign第一個消費者服務啟動...");
}
}
需要定義轉發的服務,這裏使用@FeignClient
註解來實現,該註解表示需要轉發服務的名稱,該名稱在application.properties
中進行配置。
這裏我們把請求轉發到第二個服務springcloud-feign-consumer2
。
轉發類代碼示例:
@FeignClient(name= "springcloud-feign-consumer2")
public interface HelloRemote {
@RequestMapping(value = "/hello")
public String hello(@RequestParam(value = "name") String name);
}
我們還需要提供一個入口供外部調用,然後調用上述的的方法進行轉發。
控制層代碼示例:
@RestController
public class ConsumerController {
@Autowired
HelloRemote helloRemote;
@RequestMapping("/hello/{name}")
public String index(@PathVariable("name") String name) {
System.out.println("接受到請求參數:"+name+",進行轉發到其他服務");
return helloRemote.hello(name);
}
}
springcloud-feign-consumer2
這個服務的代碼就比較簡單了,只是一個普通的服務,提供一個接口然後打印信息即可。
啟動類代碼示例:
@SpringBootApplication
@EnableDiscoveryClient
public class FeignConsumerApplication2 {
public static void main(String[] args) {
SpringApplication.run(FeignConsumerApplication2.class, args);
System.out.println("feign第二個消費者服務啟動...");
}
}
控制層代碼示例:
@RestController
public class ConsumerController {
@RequestMapping("/hello")
public String index(@RequestParam String name) {
return name+",Hello World";
}
}
功能測試
完成如上的工程開發之後,我們依次啟動服務端和客戶端的三個程序,然後在瀏覽器界面輸入:http://localhost:8001/
,即可查看註冊中心的信息。
在瀏覽器輸入:
http://localhost:9003/hello?name=pancm
返回:
pancm,Hello World
說明可以直接訪問第二個消費服務。
然後再輸入:
http://localhost:9002/hello/pancm
通過第一個消費服務調用第二個服務。
控制臺打印:
接受到請求參數:pancm,進行轉發到其他服務
界面返回結果:
pancm,Hello World
示例圖:
出現以上結果說明客戶端已經成功的通過feign調用了遠程服務hello,並且將結果返回到了瀏覽器。
SpringCloud Ribbon
Ribbon 介紹
Ribbon是Netflix發布的開源項目,主要功能是提供客戶端的軟件負載均衡算法,將Netflix的中間層服務連接在一起。Ribbon客戶端組件提供一系列完善的配置項如連接超時,重試等。簡單的說,就是在配置文件中列出Load Balancer後面所有的機器,Ribbon會自動的幫助你基於某種規則(如簡單輪詢,隨即連接等)去連接這些機器。我們也很容易使用Ribbon實現自定義的負載均衡算法。簡單地說,Ribbon是一個客戶端負載均衡器。
開發準備
開發環境
- JDK:1.8
- SpringBoot:2.1.1.RELEASE
- SpringCloud:Finchley
註:不一定非要用上述的版本,可以根據情況進行相應的調整。需要註意的是SpringBoot2.x以後,jdk的版本必須是1.8以上!
確認了開發環境之後,我們再來添加相關的pom依賴。
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
註: 基於SpringBoot1.x以上SpringCloud是Dalston版本的eureka 依賴是 <artifactId>spring-cloud-starter-eureka</artifactId>
,feign 依賴是 <artifactId>spring-cloud-starter-ribbon</artifactId>
,少了個 netflix 。SpringCloud的版本命名方式是通過倫敦的地方來命名的,版本順序是根據首字母的順序來的。
SpringCloud Ribbon 示例
服務端
首先建立一個springcloud-ribbon-eureka
服務的工程,用於做註冊中心。配置和代碼之前的基本一樣,除了端口和打印信息,這裏就不在說明了。
客戶端
這裏我們定義三個服務,一個服務使用Ribbon做負載均衡,另外兩個做普通的服務,服務名稱依次為springcloud-ribbon-consumer
、springcloud-ribbon-consumer2
和springcloud-ribbon-consumer3
。 添加如上的依賴之後,在application.properties
添加如下的配置:
consumer 配置信息:
spring.application.name=springcloud-ribbon-consumer
server.port=9006
eureka.client.serviceUrl.defaultZone=http://localhost:8003/eureka/
consumer2 配置信息:
spring.application.name=springcloud-ribbon-consumer2
server.port=9007
eureka.client.serviceUrl.defaultZone=http://localhost:8003/eureka/
consumer3 配置信息:
這裏的服務名稱和另一個服務的名稱保持一致才能實現負載均衡功能。
spring.application.name=springcloud-ribbon-consumer2
server.port=9008
eureka.client.serviceUrl.defaultZone=http://localhost:8003/eureka/
配置說明:
- spring.application.name: 這個是客戶端的服務名稱。如果有多個服務使用同一個名稱但是訪問地址不同,結合ribbon 使用,則可以實現負載均衡功能。
- server.port:服務指定的端口。
- eureka.client.serviceUrl.defaultZone: 註冊中心服務端的地址。
springcloud-ribbon-consumer
使用Ribbon實現負載均衡,只需要在啟動類中實例化RestTemplate
,通過@LoadBalanced
註解開啟均衡負載能力.。
啟動類代碼示例:
@SpringBootApplication
@EnableDiscoveryClient
public class RibbonConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(RibbonConsumerApplication.class, args);
System.out.println("ribbon第一個消費者服務啟動...");
}
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
需要定義轉發的服務,這裏使用RestTemplate
來進行調用,調用另外一個服務的名稱。
轉發類代碼示例:
@RestController
public class ConsumerController {
@Autowired
RestTemplate restTemplate;
@RequestMapping("/hello")
public String hello() {
//進行遠程調用
return restTemplate.getForObject("http://springcloud-ribbon-consumer2/hello/?name=xuwujing", String.class);
}
}
springcloud-ribbon-consumer2
和springcloud-ribbon-consumer3
代碼基本和fegin中的springcloud-feign-consumer
一樣,因此這裏就不在貼代碼了。
功能測試
完成如上的工程開發之後,我們依次啟動服務端和客戶端的四個程序,然後在瀏覽器界面輸入:http://localhost:8003/
,即可查看註冊中心的信息。
在瀏覽器輸入:
http://localhost:9006//hello
然後進行重復訪問,返回如下結果:
xuwujing,Hello World!
xuwujing,Hello World! 這是另一個服務!
xuwujing,Hello World!
xuwujing,Hello World! 這是另一個服務!
xuwujing,Hello World!
xuwujing,Hello World! 這是另一個服務!
說明已經實現了負載均衡功能了。
我們從上述的結果中發現了一點,這個調用貌似是有規律的,兩個服務進行來回調用。那麽根據這個我們發現了,負載均衡是由策略的。上述的這個示例的策略就是其中的一個RoundRobinRule輪詢策略。
這裏就順便說下Ribbon的策略,Ribbon一共有7中策略,默認使用的策略是輪詢。策略說明如下表格:
策略名 | 策略聲明 | 策略描述 | 實現說明 |
---|---|---|---|
BestAvailableRule | public class BestAvailableRule extends ClientConfigEnabledRoundRobinRule | 選擇一個最小的並發請求的server | 逐個考察Server,如果Server被tripped了,則忽略,在選擇其中ActiveRequestsCount最小的server |
AvailabilityFilteringRule | public class AvailabilityFilteringRule extends PredicateBasedRule | 過濾掉那些因為一直連接失敗的被標記為circuit tripped的後端server,並過濾掉那些高並發的的後端server(active connections 超過配置的閾值) | 使用一個AvailabilityPredicate來包含過濾server的邏輯,其實就就是檢查status裏記錄的各個server的運行狀態 |
WeightedResponseTimeRule | public class WeightedResponseTimeRule extends RoundRobinRule | 根據響應時間分配一個weight,響應時間越長,weight越小,被選中的可能性越低。 | 一個後臺線程定期的從status裏面讀取評價響應時間,為每個server計算一個weight。Weight的計算也比較簡單responsetime 減去每個server自己平均的responsetime是server的權重。當剛開始運行,沒有形成status時,使用roubine策略選擇server。 |
RetryRule | public class RetryRule extends AbstractLoadBalancerRule | 對選定的負載均衡策略機上重試機制。 | 在一個配置時間段內當選擇server不成功,則一直嘗試使用subRule的方式選擇一個可用的server |
RoundRobinRule | public class RoundRobinRule extends AbstractLoadBalancerRule | roundRobin方式輪詢選擇server | 輪詢index,選擇index對應位置的server |
RandomRule | public class RandomRule extends AbstractLoadBalancerRule | 隨機選擇一個server | 在index上隨機,選擇index對應位置的server |
ZoneAvoidanceRule | public class ZoneAvoidanceRule extends PredicateBasedRule | 復合判斷server所在區域的性能和server的可用性選擇server | 使用ZoneAvoidancePredicate和AvailabilityPredicate來判斷是否選擇某個server,前一個判斷判定一個zone的運行性能是否可用,剔除不可用的zone(的所有server),AvailabilityPredicate用於過濾掉連接數過多的Server。 |
這裏也順便說明如何指定一個策略使用方法。
最簡單的方式
在application.properties
添加如下配置:
springcloud-ribbon-consumer2.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
該配置的意思是為springcloud-ribbon-consumer2
服務指定隨機策略。
另一種方式
新建一個類,通過@Bean
註解指定策略。
@Configuration
public class RibbonConfiguration{
@Bean
public IRule ribbonRule(){
return new RandomRule();
}
}
然後再通過@RibbonClien
註解指定服務。
@RibbonClient(name="springcloud-ribbon-consumer2", configuration=RibbonConfiguration.class)
public class HelloRibbon{
}
註: 如果有的服務沒有在Eureka進行註冊,可以使用ribbon.listOfServers
方式在配置文件中來指定服務。
例如:
springcloud-ribbon-consumer2.ribbon.listOfServers:localhost:9007,localhost:9008
添加好了該配置之後,我們重啟springcloud-ribbon-consumer
服務,然後依舊重復訪問 http://localhost:9006//hello
該地址,
訪問的結果如下:
xuwujing,Hello World!
xuwujing,Hello World! 這是另一個服務!
xuwujing,Hello World! 這是另一個服務!
xuwujing,Hello World!
xuwujing,Hello World!
xuwujing,Hello World!
可以看到已經成功實現了隨機訪問的策略!
SpringCloud Fegin結合Ribbon實現負載均衡
Fegin包含了Ribbon,可以直接實現負載均衡功能。這裏我們就在Ribbon的項目稍微進行改造下實現該功能。
首先在pom文件添加Fegin的依賴包。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
然後在springcloud-ribbon-consumer
項目的啟動類上添加@EnableFeignClients
註解,啟用feign進行遠程調用。
添加完成之後,新建一個類,實現feign遠程調用。
代碼如下:
@FeignClient(name= "springcloud-ribbon-consumer2")
public interface HelloRemote {
@RequestMapping(value = "/hello")
public String hello(@RequestParam(value = "name") String name);
}
最後在提供一個新的接口供外部調用。這裏就直接在之前的代碼上新加一個接口了。
代碼如下:
@RestController
public class ConsumerController {
@Autowired
RestTemplate restTemplate;
@RequestMapping("/hello")
public String hello() {
return restTemplate.getForObject("http://springcloud-ribbon-consumer2/hello/?name=xuwujing", String.class);
}
@Autowired
HelloRemote helloRemote;
@RequestMapping("/hello/{name}")
public String index(@PathVariable("name") String name) {
System.out.println("接受到請求參數:"+name+",進行轉發到其他服務!");
return helloRemote.hello(name);
}
}
添加完之後,重啟springcloud-ribbon-consumer
服務,然後依舊重復訪問 http://localhost:9006//hello/pancm
該地址,
訪問的結果如下:
pancm,Hello World!
pancm,Hello World! 這是另一個服務!
pancm,Hello World!
pancm,Hello World! 這是另一個服務!
pancm,Hello World!
pancm,Hello World! 這是另一個服務!
示例圖:
其他
項目地址
基於SpringBoot1.x、SpringCloud的Dalston版本開發的地址:https://github.com/xuwujing/springcloud-study
基於SpringBoot1.x、SpringCloud 的Dalston版本開發的地址: https://github.com/xuwujing/springcloud-study-old
如果感覺項目不錯,希望能給個star,謝謝!
音樂推薦
一首非常棒的純音樂 NEXT TO YOU ,強烈推薦!
<iframe frameborder="no" border="0" marginwidth="0" marginheight="0" width=330 height=86 src="//music.163.com/outchain/player?type=2&id=29816800&auto=0&height=66"></iframe>
原創不易,如果感覺不錯,希望給個推薦!您的支持是我寫作的最大動力!
版權聲明:
作者:虛無境
博客園出處:http://www.cnblogs.com/xuwujing
CSDN出處:http://blog.csdn.net/qazwsxpcm
個人博客出處:http://www.panchengming.com
SpringCloud學習系列之二 ----- 服務消費者(Feign)和負載均衡(Ribbon)