一. 為什么使用spring cloud alibaba
很多人可能會問,有了spring cloud這個微服務的框架,為什么又要使用spring cloud alibaba這個框架了?最重要的原因在於spring cloud中的幾乎所有的組件都使用Netflix公司的產品,然后在其基礎上做了一層封裝。然而Netflix的服務發現組件Eureka已經停止更新,我們公司在使用的時候就發現過其一個細小的Bug;而其他的眾多組件預計會在明年(即2020年)停止維護。所以急需其他的一些替代產品,也就是spring cloud alibaba,目前正處於蓬勃發展的態式。
二. 注冊中心Nacos
nacos是阿里巴巴研發的一個集注冊中心與配置中心於一體的管理平台,使用其他非常的簡單。下載地址為:
https://github.com/alibaba/nacos/releases,nacos的主頁如下圖所示:
其中默認的登錄名和密碼是:nacos/nacos
2.1 更改用戶名和密碼
A. 分別執行conf目錄下的nacos-mysql.sql兩個腳本文件,生成的數據表如下:
B. 重新配置密碼
新建一個springboot的項目,引入如下的依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
生成密碼的Java代碼:
// 密碼加密處理
public static void main(String[] args) {
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
// 生成的密碼為:$2a$10$04MGTL.cJNZPpR3rFt/I2.43F.V75NH.5wdK.jngaO9Mc91mfonAO
System.out.println(bCryptPasswordEncoder.encode("admin"));
}
執行如下sql命令:
insert into users values('nacos','$2a$10$04MGTL.cJNZPpR3rFt/I2.43F.V75NH.5wdK.jngaO9Mc91mfonAO', 1);
insert into roles values('nacos', 'ROLE_ADMIN')
C. 配置數據庫的連接
在conf目錄下的application.properties目錄下加入如下內容:
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://mysql:3306/cloud-alibaba?useSSL=false&serverTimezone=UTC&serverTimezone=Asia/Shanghai
db.user=root
db.password=
2.2 nacos集群配置
A. 將conf目錄下的cluster.conf.example拷貝一份重命名為cluster.conf,在文件中加入所有集群節點的ip和端口號,文件內容如下:
127.0.0.1:8848
127.0.0.1:9948
B. 修改windows啟動文件 startup.cmd
的配置,修改內容如下:
set MODE="standalone" #默認的配置
set MODE="cluster" #修改后的內容
注:如果是Linux環境不用作任何的修改。
C.啟動兩個nacos,界面中出現如下的內容,表示集群配置成功
三. 服務提供方
pom.xml依賴配置
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.9.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
配置
spring
啟動類
服務頁面結果
四. 服務消費方
pom.xml依賴配置
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.9.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
配置
server
啟動類配置
調用服務提供方
五. Ribbon負載均衡
Spring Cloud Ribbon是基於Netflix Ribbon實現的一套客戶端負載均衡的工具。其主要功能是提供客戶端的負載均衡算法,並提供了完善的配置項如連接超時,重試等。簡單的說,就是配置文件中列出Load Balancer后面所有的機器,Ribbon會自動的基於某種規則(如簡單輪詢,隨機連接等)去連接這些機器,當然我們也可以使用Ribbon自定義負載均衡算法。
5.1 實現負載均衡
Ribbon只是一個客戶端的負載均衡器工具,實現起來非常的簡單,我們只需要在注入RestTemplate的bean上加上@LoadBalanced就可以了。如下:
5.2 服務消費方調用
// 直接寫上服務名即可
List<String> resultList = restTemplate.getForObject("http://alibaba-provider/user", List.class);
5.3 負載均衡策略
Ribbon提供了一個很重要的接口叫做IRule,其中定義了很多的負載均衡策略,默認的是輪詢的方式,以下是Ribbon的負載均衡策略:
類名 | 描述 |
---|---|
RoundRobbinRule | 輪詢 |
RandomRule | 隨機挑選 |
RetryRule | 按照輪詢的方式去調用服務,如果其中某個服務不可用,但是還是會嘗試幾次,如果嘗試過幾次都沒有成功,那么就不在調用該服務,會輪詢調用其他的可用服務。 |
AvailabilityFilteringRule | 會先過濾掉因為多次訪問不可達和並發超過閾值的服務,然后輪詢調用其他的服務 |
WeightedResponseTimeRule | 根據平均響應時間計算權重,響應越快權重越大,越容易被選中。服務剛重啟的時候,還未統計出權重會按照輪詢的方式;當統計信息足夠的時候,就會按照權重信息訪問 |
ZoneAvoidanceRule | 判斷server所在的區域性能和可用性選擇服務器 |
BestAvailableRule | 會過濾掉多次訪問都不可達的服務,然后選擇並發量最小的服務進行調用,默認方式 |
改變Ribbon的負責均衡策略:
5.4 自定義負載均衡策略
我們自定義的負載均衡策略需要繼承AbstractLoadBalancerRule這個類,然后重寫choose方法,然后將其注入到容器中,如下所示:
public class Customize_Rule extends AbstractLoadBalancerRule {
private static Logger logger = LoggerFactory.getLogger(Customize_Rule.class);
private int currentIndex = 0; //當前調用的索引
private int num = 1; //次數
private int limit = 5;
/**
* 初始化工作
* @param iClientConfig
*/
將其注入到容器中,方式如下:
六. Feign負載均衡
feign是基於Ribbon的另外一個負載均衡的客戶端框架,只需要在接口上定義要調用的服務名即可,使用起來非常的簡單。
pom.xml依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
啟動類配置
需要在啟動類上加上@EnableFeignClients這個注解
服務接口配置
@FeignClient(name="alibaba-provider")
public interface UserService {
@RequestMapping("/user")
public List<String> getUsers();
}
七. 熔斷與服務降級
分布式系統中一個微服務需要依賴於很多的其他的服務,那么服務就會不可避免的失敗。例如A服務依賴於B、C、D等很多的服務,當B服務不可用的時候,會一直阻塞或者異常,更不會去調用C服務和D服務。同時假設有其他的服務也依賴於B服務,也會碰到同樣的問題,這就及有可能導致雪崩效應。
如下案例:一個用戶通過通過web容器訪問應用,他要先后調用A、H、I、P四個模塊,一切看着都很美好。
由於某些原因,導致I服務不可用,與此同時我們沒有快速處理,會導致該用戶一直處於阻塞狀態。
當其他用戶做同樣的請求,也會面臨着同樣的問題,tomcat支持的最大並發數是有限的,資源都是有限的,將整個服務器拖垮都是有可能的。
Sentinel是一個用於分布式系統的延遲和容錯的開源庫,在分布式系統中,許多依賴會不可避免的調用失敗,例如超時,異常等,Hystrix能保證在一個依賴出現問題的情況下,不會導致整體服務失敗,避免級聯故障,以提高分布式系統的彈性。
斷路器本身是一種開關裝置,當某個服務單元發生故障后,通過斷路器的故障監控(類似於保險絲),向調用者返回符合預期的,可處理的備選響應,而不是長時間的等待或者拋出無法處理的異常,這樣就保證了服務調用的線程不會被長時間,不必要的占用,從而避免故障在分布式系統中的蔓延,乃至雪崩。
Sentinel在網絡依賴服務出現高延遲或者失敗時,為系統提供保護和控制;可以進行快速失敗,縮短延遲等待時間;提供失敗回退(Fallback)和相對優雅的服務降級機制;提供有效的服務容錯監控、報警和運維控制手段。
下載地址:https://github.com/alibaba/Sentinel/releases
7.1 依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
7.2 配置
通常情況下我們只需要在服務提供方實現熔斷或者服務降級即可,但是如果要相對服務消費方是實現限流,在服務的提供方和消費方都需要加如下配置。
spring
7.3 sentinel的控制面板
在瀏覽器輸入localhost:8080,使用sentinel/sentinel來訪問sentinel的控制面板:
接下來對sentinel的控制面板一一講解:
實時監控
用於查看接口調用的QPS(Query Per Second)以及平均響應時間。
簇點鏈路
查看當前追蹤的所有的訪問接口,可以添加流量規則
、降級規則
、熱點規則
、授權規則
。
流量規則
資源名:是需要控制的鏈路的名字,例如/student/all等
針對來源: 默認為default表示所有,也可以針對特定的服務進行設置。
閾值類型: 是指如何進行限制,可以是QPS,也可以是線程。
單機閾值: 是控制QPS或者線程的數量。
流量模式: 直接表示只是針對指定資源進行限制;關聯是指當被關聯的資源達到閾值時候,指定資源被限制訪問;鏈路是更加細粒度的控制,控制指定資源對鏈路的限制。
流控效果: 快速失敗是指,當無法訪問的時候立即給用戶一個錯誤響應;Warm Up(預熱)是指經過指定的時間后才達到指定的閾值(sentinel內有值為 coldFactor
為 3,即請求 QPS 從 threshold / 3
開始,經預熱時長逐漸升至設定的 QPS 閾值,參考地址:https://github.com/alibaba/Sentinel/wiki/%E9%99%90%E6%B5%81---%E5%86%B7%E5%90%AF%E5%8A%A8);排隊等待只是勻速的通過(每秒指定的QPS),其他的請求進行排隊,但是並不會一直排下去,超過指定的時間就會失敗,閾值類型必須設置為QPS,不能為線程。
降級規則
資源名: 要實現降級的資源。
降級策略:
1) RT(平均響應時間)
如果在一秒鍾之內進入的請求的平均響應時間小於1ms,那么在未來5s鍾之內所有的請求都會熔斷降級。
2) 異常比例
如果在一秒鍾之內的請求數異常比例大於指定的數據,那么在未來的時間窗口內會一直熔斷降級。統計單位為s.
3) 異常數
如果在一分鍾之內,異常數量大於指定的值,那么在指定的時間窗口內請求一直會熔斷降級,注意時間窗口的值一般設置要大於60,因為設置如果小於60,可能會一直處於熔斷狀態。
熱點規則
熱點規則是針對具體的請求參數進行設置,例如如下的方法:
資源名: 是@SentinelResource中設置的值
參數索引: 對那個參數進行QPS限制,通過索引來指定。
單機閾值:指定在統計時長內的閾值。
統計窗口時長: 統計的QPS的時間。
系統規則
LOAD: 僅對 Linux/Unix-like 機器生效,參考值一般是 CPU cores * 2.5
RT: 當單台機器上所有入口流量的平均 RT 達到閾值即觸發系統保護,單位是毫秒。
線程數: 當單台機器上所有入口流量的並發線程數達到閾值即觸發系統保護。
入口QPS: 當單台機器上所有入口流量的 QPS 達到閾值即觸發系統保護。
授權規則
授權規則是指可以將特定的訪問應用加入黑名單或者白名單,但是必須在訪問的時候攜帶應用的名稱。代碼實現部分如下:
加上了來源解析后,在往后的訪問中必須要攜帶origin參數,在sentinel的dashboard中可以作如下配置:
集群流控
是否集群: 是否采用集群
均攤閾值: 就是每個集群節點每秒的QPS.
集群閾值模式: 單機均攤是集群中每個節點每秒的QPS, 總體閾值是整個集群每秒的QPS.
集群流控
7.4 @SentinelResource
@SentinelResource是sentinel中非常重要的注解,提供了簡單易用的功能。其中blockHandler注解是限流的處理方法,fallback是服務降級的處理方法。
八. Feign與Sentinel的整合
配置
feign
8.1 服務降級后的處理
可以在@FeignClient中配置fallback,來指定服務降級后給用戶返回的什么樣的數據,fallback的值為Class類型的對象,該類必須要實現該對應的接口。
UserServiceFallback的實現如下:
8.2 服務降級的異常處理
對於fallback來講,並不能追蹤到異常信息,在實際的業務處理過程中,我們往往需要記錄異常的信息,那么就要使用fallbackFactory屬性來實現。
九. Sentinel的持久化
通過接入 Sentinel Dashboard 后,在頁面上操作來更新規則,都無法避免一個問題,那就是服務重新后,規則就丟失了,因為默認情況下規則是保存在內存中的。sentinel中持久化的方式有兩種,pull模式和push模式。
pull模式是指站在第三方持久化系統(redis, nacos)的角度,他們去到sentinel中定時去拉去配置信息,可能會造成數據的不一致性。
push模式是站在sentinel的角度,將其配置信息主動推送給第三方持久化系統,sentinel官方也推薦在線上使用該模式。
9.1 sentinel-dashboard改造
A. 將sentinel的源碼clone到本地
B. 進入到sentinel-dashboard目錄下,修改pom.xml文件
<!-- 修改之前的內容 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<scope>test</scope>
</dependency>
<!-- 修改之后的內容 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
C. 修改 src\main\webapp\resources\app\scripts\directives\sidebar\sidebar.html 文件
未修改之前的內容
<!-- <li ui-sref-active="active" ng-if="entry.appType==0"> -->
<!-- <a ui-sref="dashboard.flow({app: entry.app})"> -->
<!-- <i class="glyphicon glyphicon-filter"></i> 流控規則 V1</a> -->
<!-- </li> -->
<li ui-sref-active="active" ng-if="!entry.isGateway">
<a ui-sref="dashboard.flowV1({app: entry.app})">
<i class="glyphicon glyphicon-filter"></i> 流控規則
</a>
</li>
修改之后的內容
<li ui-sref-active="active" ng-if="entry.appType==0">
<a ui-sref="dashboard.flow({app: entry.app})">
<i class="glyphicon glyphicon-filter"></i> 流控規則
</a>
</li>
<!-- <li ui-sref-active="active" ng-if="!entry.isGateway">-->
<!-- <a ui-sref="dashboard.flow({app: entry.app})">-->
<!-- <i class="glyphicon glyphicon-filter"></i> 流控規則
</a> -->
<!-- </li>-->
D. 將 src\test\java\com\alibaba\csp\sentinel\dashboard\rule\nacos
目錄下的四個Java文件拷貝到src\main\java\com\alibaba\csp\sentinel\dashboard\rule
目錄下
E. 修改 src\main\webapp\resources\app\scripts\controllers\identity.js
文件,修改內容如下:
F. 重新打包生成Jar包,進入到sentinel目錄下(注:不是sentinel-dashboard目錄),執行如下命令:
mvn clean
mvn install -DskipTests
G. 進入到sentinel-dashboard/target目錄下,執行如下內容:
java -jar sentinel-dashboard.jar
9.2 配置
spring
9.3 測試
在sentinel-dashboard控制面板添加一個流量控制規則