SpringCloud學習系列之二 ----- 服務消費者(Feign)和負載均衡(Ribbon)

虛無境發表於2019-01-15

前言

本篇主要介紹的是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-consumerspringcloud-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-consumerspringcloud-ribbon-consumer2springcloud-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-consumer2springcloud-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版本開發的地址:github.com/xuwujing/sp…

基於SpringBoot1.x、SpringCloud 的Dalston版本開發的地址: github.com/xuwujing/sp…

如果感覺專案不錯,希望能給個star,謝謝!

音樂推薦

一首非常棒的純音樂 NEXT TO YOU ,強烈推薦!

原創不易,如果感覺不錯,希望給個推薦!您的支援是我寫作的最大動力! 版權宣告: 作者:虛無境 部落格園出處:www.cnblogs.com/xuwujing CSDN出處:blog.csdn.net/qazwsxpcm     個人部落格出處:www.panchengming.com

相關文章