在上一篇文章中,我們建立了一個服務注冊(或服務發現)服務,一個可樂服務,一個使用者服務,并且後兩個服務都注冊到了服務注冊服務上。
在真實環境中我們的微服務架構通常要承載幾十萬,幾百萬,幾千萬甚至更進階别的流量考驗,面對這種高并發場景,服務往往都是分布式部署。那麼問題來了,我們如何讓伺服器叢集的每個節點處理的請求數量相當呢,不至于一台伺服器快要撐爆了,一台伺服器無人問津,如何讓每台伺服器負載均衡呢?
在單體架構中,我們一般會到nginx來做負載均衡,正向代理,反向代理等等。那麼在Spring Cloud微服務架構中如何實作均衡負載呢?接下來我們有請ribbon登場。ningx是伺服器端的負載均衡元件,ribbon是用戶端的負載均衡元件。
源碼托管位址:https://github.com/cddofficial/SpringCloudRepo
目錄
1 ribbon介紹
1.1 簡介
1.2 原理
1.2.1 Eureka使用Ribbon時的架構
1.2.2 工作流程
2 實戰
2.1 RestTemplate應用執行個體
2.1.1 測試使用者微服務
2.1.2. 修改可樂微服務
2.1.3 測試
2.2 Ribbon應用
2.2.1 複制得到eureka-client-coke-ribbon微服務
2.2.2 啟動使用者服務eureka-client-user多個節點
2.2.3 處理eureka-client-coke-ribbon微服務
2.2.4 測試
1 ribbon介紹
1.1 簡介
ribbon也是Netflix公司的,英文直譯為絲帶,準确的是指 蝴蝶結。大家立馬能夠想到蝴蝶結兩邊是對稱的,這不正好有點均衡負載的意思在裡面嗎,是以把ribbon翻譯為蝴蝶結更好些。
Ribbon是Netflix釋出的雲中間層服務開源項目,其主要功能是提供用戶端側負載均衡算法。Ribbon用戶端元件提供一系列完善的配置項如連接配接逾時,重試等。簡單的說,Ribbon是一個用戶端負載均衡器,我們可以在配置檔案中列出Load Balancer後面所有的機器,Ribbon會自動的幫助你基于某種規則(如簡單輪詢,随機連接配接等)去連接配接這些機器,我們也很容易使用Ribbon實作自定義的負載均衡算法。
1.2 原理
1.2.1 Eureka使用Ribbon時的架構
架構圖如下:
在上圖中,假如:服務提供者是我們的使用者服務,有多個節點。服務的消費者是我們的可樂服務。
有3個使用者服務節點,1個可樂服務節點注冊到服務注冊服務;有一個請求過來時,可樂服務要向使用者服務發送請求。
可樂服務先從服務注冊服務擷取到可以請求的使用者服務節點,再根據負載均衡規則命中到其中一個使用者服務節點。
這時候請求處理了,3個使用者服務節點處理的請求數也差不多達到均衡。
1.2.2 工作流程
第一步先選擇 Eureka Server, 它優先選擇在同一個Zone且負載較少的Server;
第二步再根據使用者指定的政策,在從Server取到的服務注冊清單中選擇一個位址。其中Ribbon提供了多種政策,例如輪詢round robin、随機Random、根據響應時間權重等。
預設政策為輪詢方式。
2 實戰
下面的實戰在上一篇文章所編寫代碼的基礎上進行,如有問題可檢視上篇文章eureka實戰。
2.1 RestTemplate應用執行個體
在學習用ribbon負載均衡前,我們先要會微服務間的通信,即,可樂微服務向使用者微服務發送請求。在這裡可樂微服務就是服務消費者,使用者服務就是服務提供者。、
那麼如何實作微服務間的通信呢?我們可以采用HttpClient等一些其他技術,但是太過複雜。在這裡我們使用RestTemplate,是Spring提供的用于通路Rest服務的用戶端,RestTemplate提供了多種便捷通路遠端Http服務的方法,能夠大大提高用戶端的編寫效率。
2.1.1 測試使用者微服務
啟動服務eureka-client-user,啟動成功。在浏覽器中輸入:http://localhost:9002/user/2,響應頁面如下:
說明使用者微服務正常,如果有問題,一定要先解決。
2.1.2. 修改可樂微服務
修改eureka-client-coke微服務的代碼。
1. 在啟動類裡注冊RestTemplate的bean
代碼如下:
package com.cdd.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableEurekaClient //eureka client 注解
public class EurekaClientCokeApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaClientCokeApplication.class, args);
}
// 向spring容器中中注冊RestTemplate
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
2 建立User類
在啟動類所在包下建立entity包,entity包下建立一個User類,屬性和使用者微服務中的User類相同,代碼如下:
package com.cdd.demo.entity;
public class User {
private Long id;
private String name;
private Short age;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Short getAge() {
return age;
}
public void setAge(Short age) {
this.age = age;
}
}
3.建立CokeController類
在啟動類所在包下建立controller包,controller包下建立一個CokeController類,代碼如下:
package com.cdd.demo.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import com.cdd.demo.entity.User;
@RestController
public class CokeController {
// 注入RestTemplate
@Autowired
private RestTemplate restTemplate;
// 通過RestTemplate向使用者微服務發送請求
@GetMapping("/coke/{id}")
public User findById(@PathVariable Long id) {
return this.restTemplate.getForObject("http://localhost:9002/user/" + id, User.class);
}
}
4. 啟動可樂微服務eureka-client-coke(确定使用者微服務eureka-client-user也要在啟動狀态),啟動成功。
2.1.3 測試
在浏覽器中輸入:http://localhost:9001/coke/2,響應頁面如下:
說明我們通過RestTemplate進行微服務間的通信成功了。
2.2 Ribbon應用
上面的示例,是确定要通路那個使用者服務節點,可以看到CokeController類中restTemplate.getForObject方法的請求位址(主機+端口)是寫死的。那麼現在如果這種請求特别多呢,有多個使用者微服務節點,那麼我們怎麼用ribbon來實作負載均衡呢?請往下看。
從這裡開始,每學習一個新技術點,都要在一個新微服務上去實作。保證對應的微服務例子對應的技術點,這些代碼我會上傳的github上。到時候可以随時下載下傳下拉看。
2.2.1 複制得到eureka-client-coke-ribbon微服務
複制eureka-client-coke服務重命名為eureka-client-coke-ribbon,導入到工作IDE工具STS中(至于如何複制這裡不再贅述,請看eureka——實戰中的 “2.4.1 建立可樂微服務” 的 “複制得到eureka-client-coke“)。導入到STS中後,如下圖:
大家這裡有問題的話一定嘗試解決或者要問我,很簡單的,不能被這個步驟擋住了前進了腳步。
2.2.2 啟動使用者服務eureka-client-user多個節點
這個時候其他微服務全部關閉,先啟動服務注冊服務eureka-server-service-discover。
1. 修改eureka-client-user微服務的執行個體化id
在application.yml中,如下:
2.修改eureka-client-user微服務的啟動端口為8001
修改檔案為application.yml,修改後代碼如下:
server:
port: 8001 #修改端口為8001
spring:
application:
name: eureka-client-user #應用程式名稱
jpa:
generate-ddl: false
show-sql: true
hibernate:
ddl-auto: none
datasource:
platform: h2
schema: classpath:schema.sql #一般是建立表結構的sql
data: classpath:data.sql #一般是處理表資料的sql
logging:
level:
root: INFO
org.hibernate: INFO
com.cdd: DEBUG
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
instance-id: ${spring.application.name}:${spring.application.instance_id:${server.port}} #執行個體id
修改完成之後,啟動eureka-client-user微服務。從控制台我們可以看到這個8001端口的使用者服務節點啟動時間為:下午5:50:57
3. 再次修改eureka-client-user微服務的啟動端口為8002
修改檔案為application.yml,修改後代碼如下:
server:
port: 8002 #修改端口為8002
spring:
application:
name: eureka-client-user #應用程式名稱
jpa:
generate-ddl: false
show-sql: true
hibernate:
ddl-auto: none
datasource:
platform: h2
schema: classpath:schema.sql #一般是建立表結構的sql
data: classpath:data.sql #一般是處理表資料的sql
logging:
level:
root: INFO
org.hibernate: INFO
com.cdd: DEBUG
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
instance-id: ${spring.application.name}:${spring.application.instance_id:${server.port}} #執行個體id
修改完成之後,再啟動一次eureka-client-user微服務。從控制台我們可以看到這個8002端口的使用者服務節點啟動時間為:下午5:52:50
4. 檢視服務注冊清單
在浏覽器中輸入:http://localhost:8761/ 響應頁面如下:
此時我們的使用者微服務eureka-client-user已經有2個節點注冊打服務注冊服務了。
2.2.3 處理eureka-client-coke-ribbon微服務
1.添加ribbon依賴
eureka-client-coke-ribbon項目中需要ribbon依賴,首先我們先搜尋下已有的依賴中是否包含ribbon依賴,
(1)如果有我們就不用添加了,如下圖:
(2)如果沒有這個依賴,就要我們自己手動添加了,如下:
我們這裡是不用再手動添加ribbon依賴的,在spring-cloud-starter-netflix-eureka-client依賴中包含着。
2.修改啟動類
啟動類名稱改為:EurekaClientCokeRibbonApplication,并且給啟動類的restTemplate方法添加@LoadBalanced注解,
修改後代碼如下:
package com.cdd.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableEurekaClient //eureka client 注解
public class EurekaClientCokeRibbonApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaClientCokeRibbonApplication.class, args);
}
@Bean // 向spring容器中中注冊RestTemplate
@LoadBalanced // 負載均衡注解
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
3.修改CokeController類,修改後代碼如下:
package com.cdd.demo.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import com.cdd.demo.entity.User;
@RestController
public class CokeController {
// 注入RestTemplate
@Autowired
private RestTemplate restTemplate;
// 通過RestTemplate向使用者微服務發送請求
@GetMapping("/coke/{id}")
public User findById(@PathVariable Long id) {
// 方法getForObject目前請求路徑參數已經換成了使用者微服務的 serviceId,
return this.restTemplate.getForObject("http://eureka-client-user/user/" + id, User.class);
}
}
補充:使用者微服務serviceId的來源
來自服務注冊清單(localhost:8761/頁面),如下圖:
4. 啟動微服務eureka-client-coke-ribbon,啟動成功後,檢視服務注冊清單,如下圖:
說明該微服務已經注冊上來了。
2.2.4 測試
清空控制台是以微服務的日志。
在浏覽器中輸入:http://localhost:9001/coke/2,去請求可樂微服務,可樂微服務又回去請求使用者微服務,不過使用者微服務有兩個節點,端口分别為8001和8002,響應頁面如下:
再次清空所有執行個體的控制台日志,連續點選重新整理http://localhost:9001/coke/2頁面4次,接下來看控制台日志。
啟動時間為下午5:50:57啟動的端口為8001的使用者微服務執行個體,執行了2次(看列印sql條數),
啟動時間為下午5:52:50啟動的端口為8002的使用者微服務執行個體,也執行了2次,
從上面日志可以看出,可樂服務發送了4次請求,使用者微服務的2個執行個體分别執行處理了2次,說明已經實作負載均衡了。