前言
"微服務”一詞源於 Martin Fowler的名為 Microservices的,博文,可以在他的官方部落格上找到http:/ /martinfowler . com/articles/microservices.html簡單地說,微服務是系統架構上的一種設計風格,它的主旨是將一個原本獨立的系統拆分成多個小型服務,這些小型服務都在各自獨立的程式中執行,服務之間通過基於HTTP的 RESTfuL AP進行通訊協作。常見微服務框架:Spring的spring cloud、阿里dubbo、華為ServiceComb、騰訊Tars、Facebook thrift、新浪微博Motan。本章節我們先從瞭解組成完整系統的各個元件開始,下章節將利用這些元件,搭建出一個完善的分散式系統。
Spring Cloud
這就不用多說了,官網有詳細的介紹。
Spring Cloud Alibaba
Spring Cloud ɵɹibaba 致力於提供微服務開發的一站式解決方案。此專案包含開發分散式應用微服務的必需元件,方便開發者通過 Spring Cloud 程式設計模型輕鬆使用這些元件來開發分散式應用服務
主要元件
Sentinel:把流量作為切入點,從流量控制、熔斷降級、系統負載保護等多個維度保護服務的穩定性。
Nacos:一個更易於構建雲原生應用的動態服務發現、配置管理和服務管理平臺。
RocketMQ:一款開源的分散式訊息系統,基於高可用分散式叢集技術,提供低延時的、高可靠的訊息釋出與訂閱服務。
Dubbo:Apache Dubbo™ 是一款高效能 Java RPC 框架。
Seata:阿里巴巴開源產品,一個易於使用的高效能微服務分散式事務解決方案。
服務註冊與發現
Eureka:官方宣佈2.x不再開源(閉源),之前的版本已經停止更新;也就說Eureka將來更多的技術提升已經沒有了。所以,如果希望註冊中心有更多強大功能的話,還需要另闢蹊徑 。
Zookeeper:在企業級Zookeeper註冊中心與 Dubbo組合比較多一些,kafka使用的也是,隨著Eureka的停更,我們可以通過spring-cloud-starter-zookeeper-discovery這個啟動器,將Zookeeper做為springcloud的註冊中心。
Consul:go語言開發的,也是一個優秀的服務註冊框架,使用量也比較多。
Nacos:來自於SpringCloudɵɹibaba,在企業中經過了百萬級註冊考驗的,不但可以完美替換Eureka,還能做其他元件的替換,所以,Naocs也強烈建議使用。
介紹下Nacos用作註冊中心
Nacos簡介
Nacos(Dynamic Naming and Configur ation Service) 是阿里巴巴2018年7月開源的專案,致力於發現、配置和管理微服務。
Nacos安裝
單節點
--下載映象
docker pull nacos/nacos-server:1.3.1
--啟動容器
docker run --name nacos --env MODE=standalone --privileged=true -p 8848:8848 --restart=always -d dc833dc45d8f
訪問:
http://127.0.0.1:8848/nacos 賬號密碼都是nacos
叢集
安裝前提
64 bit OS Linux/Unix/Mac,推薦使用Linux系統。
叢集需要依賴mysql,單機可不必
3個或3個以上Nacos節點才能構成叢集
搭建Nacos高可用叢集步驟:
1、需要去Nacos官網clone Nacos叢集專案nacos-docker
2、nacos-docker是使用的Docker Compose對容器進行編排,所以首先需要安裝Docker Compose詳細資訊可參照Nacos官網:https:/ /nacos.io/zh-cn/docs/quick-start-docker.html
1)安裝Docker Compose
什麼是Docker Compose
Compose專案是Docker官方的開源專案,負責實現對Docker容器叢集的快速編排。
#在Linux下下載(下載到/usr/local/bin)
curl -L https://github.com/docker/compose/releases/download/1.25.0/run.sh > /usr/local/bin/docker-compose
# 設定檔案可執行許可權
chmod +x /usr/local/bin/docker-compose
# 檢視版本資訊
docker-compose --version
2)克隆Nacos-docker專案
#切換到自定義目錄
cd /usr/local/nacos
#開始clone
git clone https://github.com/nacos-group/nacos-docker.git
3)執行nacos-docker指令碼
#執行編排命令
docker-compose -f /usr/local/nacos/nacos-docker/example/cluster-hostname.yaml up
上面的編排命令主要下載mysql映象和nacos映象,自動完成映象下載/容器啟動
Pulling from nacos/nacos-mysql,版本是5. 7(執行初始化指令碼)
Pulling nacos3 (nacos/nacos-server:latest)最新版本
4)停止、啟動
#啟動
docker-compose -f /usr/local/nacos/nacos-docker/example/cluster-hostname.yaml start
#停止
docker-compose -f /usr/local/nacos/nacos-docker/example/cluster-hostname.yaml stop
5)訪問Nacos
http://192.168.1.1:8848/nacos
http://192.168.1.1:8849/nacos
http://192.168.1.1:8850/nacos
Nacos快速入門
配置服務提供者
服務提供者可以通過 Nacos 的服務註冊發現功能將其服務註冊到 Nacos server 上。
新增nacos依賴
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>${latest.version}</version>
</dependency>
新增配置
server.port=8070
spring.application.name=nacos-demo
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
啟動類
@SpringBootApplication
@EnableDiscoveryClient
public class NacosProviderApplication {
public static void main(String[] args) {
SpringApplication.run(NacosProviderApplication.class, args);
}
@RestController
class EchoController {
@RequestMapping(value = "/echo/{string}", method = RequestMethod.GET)
public String echo(@PathVariable String string) {
return "Hello Nacos Discovery " + string;
}
}
}
啟動後,控制檯:
說明註冊成功,後臺檢視該服務:
下面我們用spring cloud 整合naocs實現服務呼叫
配置服務消費者
新增配置
server.port=8080
spring.application.name=service-consumer
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
新增啟動類:服務消費者使用 @LoadBalanced RestTemplate 實現服務呼叫
@SpringBootApplication
@EnableDiscoveryClient
public class NacosConsumerApplication {
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(NacosConsumerApplication.class, args);
}
@RestController
public class TestController {
private final RestTemplate restTemplate;
@Autowired
public TestController(RestTemplate restTemplate) {this.restTemplate = restTemplate;}
@RequestMapping(value = "/echo/{str}", method = RequestMethod.GET)
public String echo(@PathVariable String str) {
return restTemplate.getForObject("http://service-provider/echo/" + str, String.class);
}
}
}
測試
啟動 ProviderApplication
和 ConsumerApplication
,呼叫 http://localhost:8080/echo/2018
,返回內容為 Hello Nacos Discovery 2018
。
分散式配置中心解決方案與應用
目前市面上用的比較多的配置中心有(時間順序)
Disconf:2014年7月百度開源的配置管理中心,同樣具備配置的管理能力,不過目前已經不維護了,最近的一次提交是4-5年前了。
Spring Cloud Config:2014年9月開源,Spring Cloud 生態元件,可以和Spring Cloud體系無縫整合。
Apollo:2016年5月,攜程開源的配置管理中心,具備規範的許可權、流程治理等特性。
Nacos:2018年6月,阿里開源的配置中心,也可以做DNS和RPC的服務發現
介紹下Nacos用作分散式配置中心
啟動了 Nacos server 後,您就可以參考以下示例程式碼,為您的 Spring Cloud 應用啟動 Nacos 配置管理服務了。完整示例程式碼請參考:nacos-spring-cloud-config-example
- 新增依賴:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>${latest.version}</version>
</dependency>
注意:版本 2.1.x.RELEASE 對應的是 Spring Boot 2.1.x 版本。版本 2.0.x.RELEASE 對應的是 Spring Boot 2.0.x 版本,版本 1.5.x.RELEASE 對應的是 Spring Boot 1.5.x 版本。
更多版本對應關係參考:版本說明 Wiki
- 在
bootstrap.properties
中配置 Nacos server 的地址和應用名
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.application.name=example
說明:之所以需要配置 spring.application.name
,是因為它是構成 Nacos 配置管理 dataId
欄位的一部分。
在 Nacos Spring Cloud 中,dataId
的完整格式如下:
${prefix}-${spring.profiles.active}.${file-extension}
prefix
預設為spring.application.name
的值,也可以通過配置項spring.cloud.nacos.config.prefix
來配置。spring.profiles.active
即為當前環境對應的 profile,詳情可以參考 Spring Boot文件。 注意:當spring.profiles.active
為空時,對應的連線符-
也將不存在,dataId 的拼接格式變成${prefix}.${file-extension}
file-exetension
為配置內容的資料格式,可以通過配置項spring.cloud.nacos.config.file-extension
來配置。目前只支援properties
和yaml
型別。
- 通過 Spring Cloud 原生註解
@RefreshScope
實現配置自動更新:
@RestController
@RequestMapping("/config")
@RefreshScope
public class ConfigController {
@Value("${useLocalCache:false}")
private boolean useLocalCache;
@RequestMapping("/get")
public boolean get() {
return useLocalCache;
}
}
- 首先通過呼叫 Nacos Open API 向 Nacos Server 釋出配置:dataId 為
example.properties
,內容為useLocalCache=true
curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=example.properties&group=DEFAULT_GROUP&content=useLocalCache=true"
- 執行
NacosConfigApplication
,呼叫curl http://localhost:8080/config/get
,返回內容是true
。 - 再次呼叫 Nacos Open API 向 Nacos server 釋出配置:dataId 為
example.properties
,內容為useLocalCache=false
curl -X POST "http://127.0.0.1:8848/nacos/v1/cs/configs?dataId=example.properties&group=DEFAULT_GROUP&content=useLocalCache=false"
- 再次訪問
http://localhost:8080/config/get
,此時返回內容為false
,說明程式中的useLocalCache
值已經被動態更新了。
分散式服務呼叫
RPC概述
RPC 的主要功能目標是讓構建分散式計算(應用)更容易,在提供強大的遠端呼叫能力時不損失本地呼叫的語義簡潔性。為實現該目標,RPC 框架需提供一種透明呼叫機制,讓使用者不必顯式的區分本地呼叫和遠端呼叫。
RPC的優點:分散式設計、部署靈活、解耦服務、擴充套件性強。
RPC框架
Dubbo:國內最早開源的 RPC 框架,由阿里巴巴公司開發並於 2011 年末對外開源,僅支援 Java 語言。
Motan:微博內部使用的 RPC 框架,於 2016 年對外開源,僅支援 Java 語言。
Tars:騰訊內部使用的 RPC 框架,於 2017 年對外開源,僅支援 C++ 語言。
Spring Cloud:國外 Pivotal 公司 2014 年對外開源的 RPC 框架,提供了豐富的生態元件。
gRPC:Google 於 2015 年對外開源的跨語言 RPC 框架,支援多種語言。
Thrift:最初是由 Facebook 開發的內部系統跨語言的 RPC 框架,2007 年貢獻給了 Apache 基金,成為
Apache:開源專案之一,支援多種語言。
RPC框架優點
RPC框架一般使用長連結,不必每次通訊都要3次握手,減少網路開銷。
RPC框架一般都有註冊中心,有豐富的監控管理髮布、下線介面、動態擴充套件等,對呼叫方來說是無感知、統一化的操作協議私密,安全性較高
RPC 協議更簡單內容更小,效率更高,服務化架構、服務化治理,RPC框架是一個強力的支撐。
RPC框架應用:使用Spring Cloud Alibaba 整合Dubbo實現
由於 Dubbo Spring Cloud 構建在原生的 Spring Cloud 之上, 其服務治理方面的能力可認為是 Spring Cloud Plus,不僅完全覆蓋 Spring Cloud 原生特性,而且提供更為穩定和成熟的實現,特性比對如下表所示:
Dubbo 作為 Spring Cloud 服務呼叫
預設情況,Spring Cloud Open Feign 以及@LoadBalanced`RestTemplate 作為 Spring Cloud 的兩種服務呼叫方式。 Dubbo Spring Cloud 為其提供了第三種選擇,即 Dubbo 服務將作為 Spring Cloud 服務呼叫的同等公民出現,應用可通過 Apache Dubbo 註解@Service 和@Reference 暴露和引用 Dubbo 服務,實現服務間多種協議的通訊。同時,也可以利用 Dubbo 泛化介面輕鬆實現服務閘道器。
快速上手
按照傳統的 Dubbo 開發模式,在構建服務提供者之前,第一個步驟是為服務提供者和服務消費者定義 Dubbo 服務介面。
為了確保契約的一致性,推薦的做法是將 Dubbo 服務介面打包在第二方或者第三方的 artifact(jar)中,該 artifact 甚至無需新增任何依賴。
對於服務提供方而言,不僅通過依賴 artifact 的形式引入 Dubbo 服務介面,而且需要將其實現。對應的服務消費端,同樣地需要依賴該 artifact,並以介面呼叫的方式執行遠端方法。接下來的步驟則是建立 artifact。
建立服務API
建立一個api模組,專門寫各種介面的:
/**
* @author 原
* @date 2020/12/8
* @since 1.0
**/
public interface TestService {
String getMsg();
}
建立服務提供者
匯入依賴
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>
<!-- api介面的依賴包-->
<dependency>
<groupId>com.dubbo.demo</groupId>
<artifactId>dubbo-demo-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
編寫配置
dubbo:
scan:
# dubbo 服務掃描基準包
base-packages: org.springframework.cloud.alibaba.dubbo.bootstrap
protocol:
# dubbo 協議
name: dubbo
# dubbo 協議埠( -1 表示自增埠,從 20880 開始)
port: -1
spring:
cloud:
nacos:
# Nacos 服務發現與註冊配置
discovery:
server-addr: 127.0.0.1:8848實現
/**
* @author 原
* @date 2021/1/28
* @since 1.0
**/
@Service//dubbo的service註解
public class TestServiceImpl implements TestService {
@Override
public String getMsg() {
return "123123";
}
}
啟動類
/**
* @author 原
* @date 2021/1/28
* @since 1.0
**/
@SpringBootApplication
@EnableDiscoveryClient
public class DubboProviderApplication {
public static void main(String[] args) {
SpringApplication.run(DubboProviderApplication.class,args);
}
}
建立服務消費者
除了api的實現類 其他複用提供者的程式碼
編寫測試類
/**
* @author 原
* @date 2021/1/28
* @since 1.0
**/
@RestController
public class TestController {
@Reference
TestService testService;
@GetMapping("/dubbo/test")
public String getMsg(){
return testService.getMsg();
}
}
訪問:
返回111
服務流量管理
為什麼要流控降級
流量是非常隨機性的、不可預測的。前一秒可能還風平浪靜,後一秒可能就出現流量洪峰了(例如雙十一零點的場景)。然而我們系統的容量總是有限的,如果突然而來的流量超過了系統的承受能力,就可能會導致請求處理不過來,堆積的請求處理緩慢,CPU/Load飆高,最後導致系統崩潰。因此,我們需要針對這種突發的流量來進行限制,在儘可能處理請求的同時來保障服務不被打垮,這就是流量控制。
一個服務常常會呼叫別的模組,可能是另外的一個遠端服務、資料庫,或者第三方API 等。例如,支付的時候,可能需要遠端呼叫銀聯提供的 API;查詢某個商品的價格,可能需要進行資料庫查詢。然而,這個被依賴服務的穩定性是不能保證的。如果依賴的服務出現了不穩定的情況,請求的響應時間變長,那麼呼叫服務的方法的響應時間也會變長,執行緒會產生堆積,最終可能耗盡業務自身的執行緒池,服務本身也變得不可用。
現代微服務架構都是分散式的,由非常多的服務組成。不同服務之間相互呼叫,組成複雜的呼叫鏈路。以上的問題在鏈路呼叫中會產生放大的效果。複雜鏈路上的某一環不穩定,就可能會層層級聯, 最終導致整個鏈路都不可用。 因此我們需要對不穩定的弱依賴服務進行熔斷降級,暫時切斷不穩定呼叫,避免區域性不穩定因素導致整體的雪崩。
關於容錯元件的停更/升級/替換
服務降級:
Hystrix:官網不極力推薦,但是中國企業中還在大規模使用,對於限流和熔斷降級雖然在1 .5版本官方還支援(版本穩定),
但現在官方已經開始推薦大家使用Resilience4j
Resilience4J:官網推薦使用,但是國內很少用這個。
Sentienl:來自於Spring Cloud Alibaba,在中國企業替換Hystrix的元件,國內強烈建議使用
這裡就主要介紹下Sentinel。
Sentinel介紹
Sentinel是阿里開源的專案,提供了流量控制、熔斷降級、系統負載保護等多個維度來保障服務之間的穩定性。
Sentinel的流控操作起來非常簡單,在控制檯進行配置即可看見效,所見即所得
Sentinel 具有以下特徵:
- 豐富的應用場景:Sentinel 承接了阿里巴巴近 10 年的雙十一大促流量的核心場景,例如秒殺(即突發流量控制在系統容量可以承受的範圍)、訊息削峰填谷、叢集流量控制、實時熔斷下游不可用應用等。
- 完備的實時監控:Sentinel 同時提供實時的監控功能。您可以在控制檯中看到接入應用的單臺機器秒級資料,甚至 500 臺以下規模的叢集的彙總執行情況。
- 廣泛的開源生態:Sentinel 提供開箱即用的與其它開源框架/庫的整合模組,例如與 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相應的依賴並進行簡單的配置即可快速地接入 Sentinel。
- 完善的 SPI 擴充套件點:Sentinel 提供簡單易用、完善的 SPI 擴充套件介面。您可以通過實現擴充套件介面來快速地定製邏輯。例如定製規則管理、適配動態資料來源等。
官網
https://github.com/alibaba/Sentinel
中文
https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D
https://sentinelguard.io/zh-cn/docs/introduction.html
Sentinel 的使用可以分為兩個部分:
核心庫(Java 客戶端):不依賴任何框架/庫,能夠執行於 Java 7 及以上的版本的執行時環境,同時對Dubbo / Spring Cloud 等框架也有較好的支援。
控制檯(Dashboard):控制檯主要負責管理推送規則、監控、叢集限流分配管理、機器發現等
使用場景
在服務提供方(Service Provider)的場景下,我們需要保護服務提供方自身不被流量洪峰打垮。 這時候通常根據服務提供方的服務能力進行流量控制, 或針對特定的服務呼叫方進行限制。我們可以結合前期壓測評估核心口的承受能力,配置 QPS 模式的限流,當每秒的請求量超過設定的閾值時,會自動拒絕多餘的請求。
為了避免呼叫其他服務時被不穩定的服務拖垮自身,我們需要在服務呼叫端(Service Consumer)對不穩定服務依賴進行隔離和熔斷。手段包括訊號量隔離、異常比例降級、RT 降級等多種手段。
當系統長期處於低水位的情況下, 流量突然增加時, 直接把系統拉昇到高水位可能瞬間把系統壓垮。這時候我們可以藉助 Sentinel 的 WarmUp 流控模式控制通過的流量緩慢增加,在一定時間內逐漸增加到閾值上限,而不是在一瞬間全部放行。這樣可以給冷系統一個預熱的時間,避免冷系統被壓垮。
利用 Sentinel 的勻速排隊模式進行“削峰填谷”, 把請求突刺均攤到一段時間內, 讓系統負載保持在請求處理水位之內,同時儘可能地處理更多請求。
利用 Sentinel 的閘道器流控特性,在閘道器入口處進行流量防護,或限制 API 的呼叫頻率。
Sentinel安裝
1、下載jar包https://github.com/alibaba/Sentinel/releases
2、啟動
java -Dserver.port=8787 -Dcsp.sentinel.dashboard.server=127.0.0.1:8787 -Dproject.name=sentinel-dashboard -jar /home/sentinel/sentinel-dashboard-1.8.0.jar
3、訪問
http://127.0.0.1:8787/#/login
初始賬號密碼sentinel/sentinel
可以看到sentinel是自己本身的監控
sentinel快速入門
1、匯入依賴
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.8.0</version>
</dependency>
2、測試類
public class TestService {
public static void main(String[] args) {
initFlowRules();
while (true) {
Entry entry = null;
try {
entry = SphU.entry("HelloWorld");
/*您的業務邏輯 - 開始*/
System.out.println("hello world");
/*您的業務邏輯 - 結束*/
} catch (BlockException e1) {
/*流控邏輯處理 - 開始*/
System.out.println("block!");
/*流控邏輯處理 - 結束*/
} finally {
if (entry != null) {
entry.exit();
}
}
}
}
//設定流量控制規則 設定當QPS達到20時 會限制流量(丟擲異常,可以執行處理)
private static void initFlowRules(){
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("HelloWorld");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// Set limit QPS to 20.
rule.setCount(20);
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
}
執行結果:
可以看到,這個程式每秒穩定輸出 "hello world" 20 次,和規則中預先設定的閾值是一樣的。 block表示被阻止的請求。
官方使用文件:https://github.com/alibaba/Sentinel/wiki/如何使用
Sentinel整合SpringCloud實現服務限流/熔斷
匯入依賴
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
</dependencies>
配置
server.port=8082
spring.application.name=sentinel-demo
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
spring.cloud.sentinel.transport.dashboard=127.0.0.1:8787
//在需要流控的方法加上@SentinelResource
@RestController
public class TestController {
@GetMapping("/sentinel")
@SentinelResource
public String getMsg(){
return "11";
}
}
啟動應用,訪問http://127.0.0.1:8082/sentinel後去sentinel後臺
下面我們來配一條最簡單的流控規則。針對 sentinel_spring_web_context /sentinel 這個服務呼叫配置限流規則(需要有過訪問量才能看到)。我們配一條 QPS 為 1的流控規則,這代表針對該服務方法的呼叫每秒鐘不能超過 1 次,超出會直接拒絕。
現在快速訪問:http://localhost:8082/sentinel
檢視實時監控頁面:
其他功能的使用,大家可以參考官方文件自行摸索。
如何選擇流控降級元件
以下是 Sent inel 與其它fault-tolerance 元件的對比:
分散式事務
待續...