序言
鑑於代碼的自行改造和版本升級,隨意更換依賴版本可能導致項目報錯,因此在正文之前附上版本對應表.
SpringBoot和SpringCloud的版本需要對應,不然會因爲jar包版本不兼容導致一系列奇怪的問題。因爲官方不會保證SpringBoot和SpringCloud不同版本的兼容性。
大版本對應
SpringCloud版本 | SpringBoot版本 |
---|---|
Hoxton | 2.2.x |
Greenwich | 2.1.x |
Finchley | 2.0.x |
Edgware | 1.5.x |
Dalston | 1.5.x |
參考:https://spring.io/projects/spring-cloud
其他
spring-cloud-dependencies 版本列表可查看:https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-dependencies
spring-boot-starter-parent 版本列表可查看:https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-parent
本文正文中的代碼均基於以下版本:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>1.5.9.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
附上尚硅周陽2018Spring Cloud第一期教程:【侵刪】
鏈接:https://pan.baidu.com/s/1jDuYfzmYL2YSJV4RBSoGyg
提取碼:zzgs
鑑於目前企業實際情況,自己重做了Spring Boot 2.1.6 + Spring Cloud Greenwich.SR3版本的代碼,期間坑真的是很多,大部分都是版本升級導致的依賴名稱變更等,感興趣的朋友可以自己嘗試重構,再拿我的代碼做參考:
鏈接:https://pan.baidu.com/s/1sIDEqymSMFjdlIl5XAxWIQ
提取碼:zfur
SpringCloud
一、微服務概述
馬丁福勒微服務論文: https://martinfowler.com/articles/microservices.html
1.1.什麼是微服務
- 微服務化的核心就是將傳統的一站式應用,根據業務拆分成一個一個的服務,徹底地去耦合,每一個微服務提供單個業務功能的服務,一個服務做一件事;從技術角度看就是一種小而獨立的處理過程,類似進程概念,能夠自行單獨啓動或銷燬,並且可以擁有自己獨立的數據庫。
1.2.微服務與微服務架構
- 微服務是一種架構模式或者一種架構風格,提倡將單一應用程序劃分成一組小的服務獨立部署,服務之間相互配合、相互協調,每個服務運行於自己的進程中;
- 服務與服務間採用輕量級通訊,如HTTP的RESTful API等;
- 避免統一的、集中式的服務管理機制。
補充:
要弄清楚什麼是RESTful API,首先要弄清楚什麼是REST。
REST – REpresentational State Transfer,英語的直譯就是“表現層狀態轉移”。如果看這個概念,估計沒幾個人能明白是什麼意思。那下面就讓我來用一句人話解釋一下什麼是RESTful:
URL定位資源,用HTTP動詞(GET,POST,PUT,DELETE等)描述操作。
Resource:資源,即數據。
Representational:某種表現形式,比如用JSON,XML,JPEG等;
State Transfer:狀態變化。通過HTTP動詞實現。
所以RESTful API就是REST風格的API。 那麼在什麼場景下使用RESTful API呢?在當今的互聯網應用的前端展示媒介很豐富。有手機、有平板電腦還有PC以及其他的展示媒介。那麼這些前端接收到的用戶請求統一由一個後臺來處理並返回給不同的前端肯定是最科學和最經濟的方式,RESTful API就是一套協議來規範多種形式的前端和同一個後臺的交互方式。
1.3.微服務的優缺點
1.3.1.優點
- 每個服務足夠內聚,足夠小,比較容易聚焦【代碼量少耦合低,開發人員能夠快速理解邏輯並上手修改代碼】;
- 開發簡單且效率高,一個服務只做一件事情;
- 開發團隊小,一般2-5人足以(當然按實際爲準);
- 微服務是鬆耦合的,無論開發還是部署都可以獨立完成;
- 微服務能用不同的語言開發;
- 易於和第三方集成,微服務允許容易且靈活的自動集成部署(持續集成工具有Jenkins,Hudson,bamboo等);
- 微服務易於被開發人員理解,修改和維護,這樣可以使小團隊更加關注自己的工作成果,而無需一定要通過合作才能體現價值;
- 微服務允許你融合最新的技術;
- 微服務只是業務邏輯的代碼,不會和HTML,CSS或其他界面組件混合;
- 每個微服務都可以有自己的存儲能力,數據庫可自有也可以統一,十分靈活。
1.3.2.缺點
- 開發人員要處理分佈式系統的複雜性
- 多服務運維難度,隨着服務的增加,運維的壓力也會增大
- 系統部署依賴
- 服務間通訊的成本
- 數據的一致性
- 系統集成測試
- 性能監控的難度
1.4.微服務的技術棧
微服務技術棧:多種技術的集合體
我們討論一個分佈式的微服務架構時,需要考慮哪些維度/有哪些落地技術?
微服務條目 | 落地技術 |
---|---|
服務開發 | SpringBoot,Spring,SpringMVC |
服務配置與管理 | Netflix公司的Archaius、阿里的Diamond等 |
服務註冊與發現 | Eureka、Consul、Zookeeper等 |
服務調用 | Rest、RPC、gRPC |
服務熔斷器 | Hystrix、Envoy等 |
負載均衡 | Ribbon、Nginx等 |
服務接口調用(客戶端調用服務的簡化工具) | Feign等 |
消息隊列 | Kafka、RabbitMQ、ActiveMQ等 |
服務配置中心管理 | Spring Cloud Config、Chef等 |
服務路由(API網關) | Zuul等 |
服務監控 | Zabbix、Nagios、Metrics、Specatator等 |
全鏈路追蹤 | Zipkin、Brave、Dapper等 |
服務部署 | Docker、OpenStack、Kubernetes等 |
數據流操作開發包 | Spring Cloud Stream(封裝與Redis,Rabbit,Kafka等發送接收消息) |
事件消息總線 | Spring Cloud Bus |
1.5.爲什麼選SpringCloud作爲微服務架構
1.5.1.選型依據
- 整體解決方案和框架的成熟度
- 社區熱度
- 可維護性
- 學習曲線
1.5.2.當前各大IT公司的微服務架構
- 阿里Dubbo/HSF(“好舒服”框架)
- 京東JSF
- 新浪Motan
- 噹噹DubboX
1.5.3.各微服務的框架對比
功能點/服務框架 | Netflix/SpringCloud | Motan(新浪) | gRPC | Thrift(Facebook) | Dubbo(阿里)/DubboX(噹噹) |
---|---|---|---|---|---|
功能定位 | 完整的微服務架構 | RPC框架,但整合了ZK或Consul,實現集羣環境的基本服務註冊/發現 | RPC框架 | RPC框架 | 服務框架 |
支持REST | 是,Ribbon支持多種可插拔的序列化選擇 | 否 | 否 | 否 | 否 |
支持RPC | 否 | 是 | 是 | 是 | 是 |
支持多語言 | 是(REST形式) | 否 | 是 | 是 | 否 |
服務註冊/發現 | 是(Eureka) Eureka服務註冊表,Karyon服務端框架支持服務自注冊和健康檢查 | 是(zookeeper/consul) | 否 | 否 | 是 |
負載均衡 | 是(服務端zuul+客戶端Ribbon) zuul-服務,動態路由 雲端負載均衡 Eureka(針對中間層服務器) | 是(客戶端) | 否 | 否 | 是(客戶端) |
配置服務 | Netflix Archaius SpringCloud Config Server集中配置 | 是(zookeeper提供) | 否 | 否 | 否 |
服務調用鏈監控 | 是(zuul) Zuul提供邊緣服務,API網關 | 否 | 否 | 否 | 否 |
高可用/容錯 | 是(服務端Hystrix+客戶端Ribbon) | 是(客戶端) | 否 | 否 | 是(客戶端) |
典型應用案例 | Netflix | Sina | |||
社區活躍度 | 高 | 一般 | 高 | 一般 | 2017年8月才重啓 |
學習難度 | 中等 | 一般 | 高 | 一般 | 低 |
文檔豐富度 | 高 | 一般 | 一般 | 一般 | 高 |
其他 | Spring Cloud Bus爲我們應用程序帶來了更多管理端點 | 支持降級 | Netflix內部在開發集成gRPC | IDL定義 | 實踐公司比較多 |
1.5.4.補充內容
-
Dubbo和Dubbox
Dubbo源於阿里的淘寶網開源的分佈式的服務架構,致力於提供高性能和透明化的RPC遠程服務調用方案,是SOA(Service-Oriented Architecture,面向服務的架構)服務化治理方案的核心框架。淘寶網將其開源之後,得到了很多的拓展和支持(比較出名的有:噹噹網的擴展版本dubbox,京東的擴展版本jd-hydra等),2012年之後Dubbo不再維護,17年8月份阿里重啓維護。正是這段時間讓SpringCloud異軍突起。 樑飛團隊被打散之後,內部開始使用HSF。Dubbox是一個開源的RPC(Remote ProcedureCall Protocol)遠程調用框架,是由噹噹對阿里的Dubbo的升級,可以被視爲Dubbo的增強版,基本架構沒有變動,升級spring2.x到spring3.x,支持RESTful風格的調用調試方式,豐富了序列化的方式,提高了序列化的性能。Dubbox(即Dubbo eXtensions)是噹噹網Fork基於dubbo2.x的升級版本,兼容原有的dubbo。其中升級了zookeeper和spring版本,並且支持RESTfull風格的遠程調用。【2018年3月噹噹被收購,對於後續社區維護不看好】
以下引自知乎:(如今樑飛已經掌管天貓)
阿里巴巴爲什麼主推HSF?比Dubbo有哪些優勢?
“真實原因是 dubbo 是阿里 b2b 團隊編寫的框架,hsf 是淘寶團隊編寫的框架。淘寶在業務複雜度以及規模上當然完爆 b2b,所以內部 pk 必然是 hsf 獲勝。”
-
RPC vs REST
服務提供方與調用方接口依賴方式太強:我們爲每個微服務定義了各自的service抽象接口,並通過持續集成發佈到私有倉庫中,調用方應用對微服務提供的抽象接口存在強依賴關係,因此不論開發、測試、集成環境都需要嚴格的管理版本依賴,纔不會出現服務方與調用方的不一致導致應用無法編譯成功等一系列問題,以及這也會直接影響本地開發的環境要求,往往一個依賴很多服務的上層應用,每天都要更新很多代碼並install之後才能進行後續的開發。若沒有嚴格的版本管理制度或開發一些自動化工具,這樣的依賴關係會成爲開發團隊的一大噩夢。而REST接口相比RPC更爲輕量化,服務提供方和調用方的依賴只是依靠一紙契約,不存在代碼級別的強依賴,當然REST接口也有痛點,因爲接口定義過輕,很容易導致接口文檔與實際實現不一致導致服務集成時的問題,但是該問題很好解決,只需要通過每個服務整合Swagger,讓每個服務的代碼與文檔一體化,就能解決。所以在分佈式環境下,REST方式的服務依賴要比RPC方式的依賴更爲靈活。服務對平臺敏感,難以簡單複用:通常我們在提供對外服務時,都會以REST的方式提供出去,這樣可以實現跨平臺的特點,任何一個語言的調用方都可以根據接口定義來實現。那麼在Dubbo中我們要提供REST接口時,不得不實現一層代理,用來將RPC接口轉換成REST接口進行對外發布。若我們每個服務本身就以REST接口方式存在,當要對外提供服務時,主要在API網關中配置映射關係和權限控制就可實現服務的複用了。
二、Spring Cloud入門概述
- Spring的三大模塊:Spring Boot(構建),Spring Cloud(協調),Spring Cloud Data Flow(連接)
2.1.Spring Cloud是什麼
- 分佈式系統的簡化版(官方介紹)
- Spring Cloud基於Spring Boot提供了一整套微服務的解決方案,包括服務註冊與發現,配置中心,全鏈路監控,服務網關,負載均衡,熔斷器等組件,除了基於Netflix的開源組件做高度抽象封裝之外,還有一些選型中立的開源組件
- Spring Cloud利用Spring Boot的開發便利性巧妙地簡化了分佈式系統的基礎設施開發,Spring Cloud爲開發人員提供了快速構建分佈式系統的一些工具,包括配置管理、服務發現、斷路器、路由、微代理、事件總線,全局所、決策精選、分佈式會話等等,他們都可以用Spring Boot的開發風格做到一鍵啓動和部署。
- 一句話概括:Spring Cloud是分佈式微服務架構下的一站式解決方案,是各個微服務架構落地技術的集合體,俗稱微服務全家桶
2.2.Spring Cloud和Spring Boot的關係
Spring Boot:專注於快速方便的開發單個個體微服務(關注微觀)
Spring Cloud:關注全局的微服務協調治理框架,將Spring Boot開發的一個個單體微服務組合並管理起來(關注宏觀)
- Spring Boot可以離開Spring Cloud獨立使用,但是Spring Cloud不可以離開Spring Boot,屬於依賴關係
2.3.Dubbo是怎麼到Spring Cloud的?哪些優缺點去技術選型
2.3.1.目前成熟的互聯網架構(分佈式+服務治理Dubbo)
2.3.2.對比
Dubbo | Spring | |
---|---|---|
服務註冊中心 | Zookeeper | Spring Cloud Netfilx Eureka |
服務調用方式 | RPC | REST API |
服務監控 | Dubbo-monitor | Spring Boot Admin |
斷路器 | 不完善 | Spring Cloud Netflix Hystrix |
服務網關 | 無 | Spring Cloud Netflix Zuul |
分佈式配置 | 無 | Spring Cloud Config |
服務跟蹤 | 無 | Spring Cloud Sleuth |
消息總線 | 無 | Spring Cloud Bus |
數據流 | 無 | Spring Cloud Stream |
批量任務 | 無 | Spring Cloud Task |
最大區別:
- Spring Cloud拋棄了RPC通訊,採用基於HTTP的REST方式。Spring Cloud犧牲了服務調用的性能,但是同時也避免了原生RPC帶來的問題。REST比RPC更爲靈活,不存在代碼級別的強依賴,在強調快速演化的微服務環境下,顯然更合適。
- 一句話:Dubbo像組裝,Spring Cloud像一體
- 社區的支持與力度:Dubbo曾經停運了5年,雖然重啓了,但是對於技術發展的新需求,還是需要開發者自行去拓展,對於中小型公司,顯然顯得比較費時費力,也不一定有強大的實力去修改源碼
2.3.3.總結
- 解決的問題域不一樣:Dubbo的定位是一款RPC框架,Spring Cloud的目標是微服務架構下的一站式解決方案
2.4.Spring Cloud的參考資料
Spring Cloud的中文參考資料: https://springcloud.cc/spring-cloud-netflix.html
Spring Cloud的中文API: https://springcloud.cc/spring-cloud-dalston.html
Spring Cloud中國社區: http://springcloud.cn/
Spring Cloud中文網: https://springcloud.cc/
三、構建Spring Cloud工程
概述:Spring Cloud工程由一個父工程和若干個Module組成
應該遵循的條件:約定 > 配置 > 編碼
3.1.RestTemplate類
3.1.1.介紹
RestTemplate是Spring提供的用於訪問Rest服務的客戶端模板工具集,提供了多種遠程訪問Http的方法
RestTemplate的API : https://docs.spring.io/spring/docs/5.2.2.RELEASE/javadoc-api/
3.1.2.意義
在一些不涉及實現方法的模塊中(消費者),只需要調用其他服務暴露出的接口即可滿足的需求,使用RestTemplate類中的方法可以發出需要的HTTP請求並得到返回結果。(類似Ajax)
3.1.3.RestTemplate用法
RestTemplate restTemplate = new RestTemplate();
//url:請求地址
//requestMap:請求參數
//type.class:HTTP響應轉換成的對象類型
restTemplate.getForObject(url,type.class);
restTemplate.postForObject(url,requestMap,type.class);
3.2.構建父工程
- 創建一個Maven父工程並命名GAV座標
<groupId>com.chengsw</groupId>
<artifactId>microservicecloud</artifactId>
<version>0.0.1-SNAPSHOT</version>
- 打包方式爲POM
- 在pom.xml中定義各依賴的版本號(若Module中pom.xml的依賴沒有指定版本號,則會根據父工程的版本號加入依賴)
- 加入通用的依賴和插件
3.3.構建Module
- 在父工程下新建Maven的Module,打包方式爲jar
- 一般來時GAV中GV隨父工程,自己定義A即可
- 在該Module下pom.xml中加入其它需要的依賴
- 正常開發即可
- 完成後先clean一下Maven項目,然後再install提供給其它模塊調用
3.3.1.添加其它Module的依賴方法
- 直接引用其GAV即可
<dependencies>
<dependency>
<groupId>com.lzl</groupId>
<artifactId>microservice-api</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
3.3.2.配置該module下的yml
- 微服務需要獨立的端口
- 微服務最重要的是取名字!!!!一定要給微服務配置一個名字!這個名字就是這個微服務對外暴露的名字!
- 配置該模塊下的其它相關配置(如本例配置了mybatis)
server:
port: 8001
mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml
type-aliases-package: com.XXX.entity
mapper-locations:
- classpath:mybatis/mapper/**/*.xml
spring:
application:
name: microservicecloud-dept #爲這個服務取名,非常重要!!!!!
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: org.gjt.mm.mysql.Driver
url: jdbc:mysql://localhost:3306/cloudDB01
username: root
password: 123456
dbcp2:
min-idle: 5 #最小連接數
initial-size: 5 #初始化連接數
max-total: 10 #最大連接數
max-wait-millis: 200 #等待連接最長的超時時間
3.3.3.編寫主啓動類
- 記得主啓動類放在根包下,com.xxx.xxx
package com.XXX;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Provider8001_APP {
public static void main(String[] args) {
SpringApplication.run(Provider8001_APP.class,args);
}
}
SpringCloud添加組件的基本套路
- 新增這個組件的maven座標GAV
- 在啓動類上面標註啓動該組件(一般來說是@EnableXXXXX)
- 編寫業務邏輯代碼
四、Eureka服務註冊與發現
4.1.Eureka介紹及原理
4.1.1.理解
Eureka就像一個物業管理公司,其他微服務就像小區的住戶,每個住戶入住時都要向物業管理公司註冊,並定時向物業公司交管理費
4.1.2.介紹
- Eureka是一個基於REST的服務,用於定位服務,以實現雲端中間層服務發現和故障轉移。
- Eureka主管服務註冊與發現,在微服務中,以後了這兩者,只需要使用服務的標識符(就是那個在每個服務的yml文件中取得服務名稱),就可以訪問到服務,不需要修改服務調用的配置文件
- Eureka遵循AP原則(高可用,分區容錯性),因爲使用了自我保護機制所以保證了高可用
4.1.3.原理
- Eureka使用的是C-S結構(客戶端-服務端)
- 兩大組件:Eureka Server(提供註冊服務)、 Eureka Client(JAVA客戶端,負責發送心跳)
- 系統中的其他微服務使用Eureka客戶端連接到Eureka服務端維持心跳連接(即註冊)。SpringCloud的其他模塊可以通過Eureka Server 來發現系統中的微服務並加以調用
4.2.Eureka服務註冊中心構建
4.2.1.加入服務端依賴
<dependency>
<!--Eureka-Server服務端-->
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
4.2.2.配置yml
- 理解:物業公司肯定不向自己註冊自己,並肯定知道自己在哪,不用參加檢索
server:
port: 7001
eureka:
instance:
hostname: localhost
client:
register-with-eureka: false #false表示不向註冊中心註冊自己
fetch-registry: false #false表示自己就是註冊中心,職責是維護實例,不參加檢索
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #設置eureka server的交互地址,即對外暴露的地址
4.2.3.添加啓動類
- 注意:要在類前加@EnableEurekaServer標註
package com.XXX;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class Eureka7001_APP {
public static void main(String[] args) {
SpringApplication.run(Eureka7001_APP.class,args);
}
}
4.2.4.驗證是否構建成功
先啓動EurekaServer主程序,訪問該服務地址即可
4.3.向Eureka註冊中心註冊微服務
4.3.1.增加依賴
在要註冊的微服務的pom.xml文件中增加依賴
<!--將微服務provider側註冊進Eureka-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
4.3.2.修改yml
- 在application.yml中增加以內容,將客戶端註冊到服務列表內
- 理解:小區用戶要找到物業管理處的地址進行註冊
# 將微服務客戶端註冊進Eureka服務列表中
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka # 就是Eureka註冊中心地址
4.3.3.主啓動類增加註解
- 增加@EnableEurekaClient註解
@SpringBootApplication //本服務啓動後,會自動註冊進Eureka服務中
@EnableEurekaClient
public class Provider8001_APP {
public static void main(String[] args) {
SpringApplication.run(Provider8001_APP.class,args);
}
}
4.4.actuator與微服務註冊完善
4.4.1.主機名稱與服務名稱的修改
- 修改服務名稱,在yml中eureka節點下添加如下內容
eureka:
instance:
instance-id: dept8001 #修改別名
prefer-ip-address: true #顯示IP地址
4.4.2.info內容的詳細信息修改
作用
在查看Eureka時點擊進入某個微服務的info時,能給查看者一些必要的信息,可以幫助查看者快速的瞭解該微服務,開發中十分有意義。
修改方法
- 當前工程添加依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- 總的父工程的build節點下添加如下內容
<build>
<finalName>microservicecloud</finalName>
<resources>
<resource>
<!--允許掃描該路徑下的資源文件-->
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<delimiters>
<!--指定動態獲取以$標誌開頭結尾的信息-->
<delimit>$</delimit>
</delimiters>
</configuration>
</plugin>
</plugins>
</build>
- 在當前工程 的application.yml文件添加回顯信息
info:
author: XXX
build-version: $project.version$
4.5.Eureka的自我保護
4.5.1.介紹
Eureka的自我保護機制主要是爲了網絡異常時保持高可用設計的,當在Eureka中註冊的微服務超過設定是時間內(默認90秒)沒有向Eureka服務端發送心跳,該微服務會進入自我保護模式。在自我保護模式中,Eureka會保護服務註冊表中的信息,不會註銷任何服務實例,直至收到的心跳數恢復至閾值以上,該微服務退出自我保護模式。
4.5.2.理解
好死不如賴活:Eureka的設計哲學是寧可保留錯誤的服務信息,也不盲目註銷可能健康的服務。所以異常的服務不會被註銷,而是進入了自我保護模式。
4.5.3.自我保護模式的開關
在Eureka Server模塊下的yml文件中添加配置信息即可,true表示打開自我保護模式;false表示關閉自我保護模式(不推薦)
eureka:
server:
enable-self-preservation: false #禁用自我保護
4.6.Eureka的服務發現
4.6.1.介紹
系統中的微服務可以通過Eureka的服務發現去獲得在Eureka中註冊的服務的信息,這是一個對外暴露的接口。
4.6.2.使用方法(provider中)
- 注入DiscoveryClient 對象(spring包下的),在controller方法中獲取
@Autowired
private DiscoveryClient discoveryClient;
@ResponseBody
@GetMapping("/provider/discovery")
public Object discovery(){
List<String> list = discoveryClient.getServices();
System.out.println(list);
List<ServiceInstance> insList = discoveryClient.getInstances("MICROSERVICECLOUD-DEPT");
for (ServiceInstance si:insList) {
System.out.println(si.getHost() +"," + si.getServiceId() +"," +si.getPort() +"," +si.getUri() +"," +si.getMetadata());
}
return this.discoveryClient;
}
- 在主啓動類中加入@EnableDiscoveryClient註解
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
public class Provider8001_APP {
public static void main(String[] args) {
SpringApplication.run(Provider8001_APP.class,args);
}
}
4.6.3.使用方法(consumer中)
在controller方法中使用restTemplate對象調用provider中暴露的URL 並獲得返回對象即可
private static final String URL_PREFIX = "http://localhost:8001";
@GetMapping("/discovery")
public Object discovery() {
return restTemplate.getForObject(URL_PREFIX+"/provider/discovery",Object.class);
}
4.7.Eureka的集羣配置
基本原理
上圖是來自eureka的官方架構圖,這是基於集羣配置的eureka;
- 處於不同節點的eureka通過Replicate進行數據同步
- Application Service爲服務提供者
- Application Client爲服務消費者
- Make Remote Call完成一次服務調用
服務啓動後向Eureka註冊,Eureka Server會將註冊信息向其他Eureka Server進行同步,當服務消費者要調用服務提供者,則向服務註冊中心獲取服務提供者地址,然後會將服務提供者地址緩存在本地,下次再調用時,則直接從本地緩存中取,完成一次調用。
當服務註冊中心Eureka Server檢測到服務提供者因爲宕機、網絡原因不可用時,則在服務註冊中心將服務置爲DOWN狀態,並把當前服務提供者狀態向訂閱者發佈,訂閱過的服務消費者更新本地緩存。
服務提供者在啓動後,週期性(默認30秒)向Eureka Server發送心跳,以證明當前服務是可用狀態。Eureka Server在一定的時間(默認90秒)未收到客戶端的心跳,則認爲服務宕機,註銷該實例。
4.7.1.集羣
集羣就是在不同的機器上配置相同的服務來構建要一個大的運算整體
4.7.2.實現集羣
- 新建N個Eureka Server模塊
- 每個模塊的pom.xml中加入與單個Eureka Server相同的依賴
- 每個模塊加入主程序(記得加@EnableEurekaServer註解)
- 修改hosts文件(C:\Windows\System32\drivers\etc)
單機版的Eureka可以設置localhost默認是127.0.0.1,但是搭建集羣后不可能N臺機器都用localhost,N個王富貴怎麼區分?因此需要修改hosts文件做映射。
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
127.0.0.1 eureka7003.com
- 修改Eureka Server模塊的application.yml文件,加入集羣,主要修改兩個地方:
- hostname:修改爲hosts文件中映射的地址
- service-url下的defaultZone節點:填入集羣中另外的server服務端的地址
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com #hostname爲hosts文件中映射的地址
client:
register-with-eureka: false #false表示不向註冊中心註冊自己
fetch-registry: false #false表示自己就是註冊中心,職責是維護實例,不參加檢索
service-url:
#defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ #設置eureka server的交互地址
defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ #其他兩個服務端的地址
- 修改Eureka Client模塊的application.yml文件,使其向集羣註冊服務
- service-url下的defaultZone節點:填入集羣中需要向其註冊server服務端的地址
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka
- 訪問地址
http://eureka7001.com:7001
http://eureka7002.com:7002
http://eureka7003.com:7003
- 注:defaultZone中/eureka 後綴是必須的,如果刪除,Server類不會報錯,但是Client註冊時會報404錯誤
4.8.Eureka與Zookeeper對比
4.8.1.CAP設計原則不同
Eureka遵守AP,Zookeeper遵守CP(C:強一致性,A:高可用,P:分區容錯性,三者只能選其二,高併發下P必選)
4.8.2.網絡波動下兩者的處理對比
Zookeeper | Eureka |
---|---|
當網絡出現故障時,剩餘zk集羣會發起投票選舉新的leader,但是此過程會持續30~120s,此過程對於高併發來說十分漫長,會導致整個註冊服務的癱瘓,這是不可容忍的 | 在15分鐘內85%的節點都沒有心跳,則註冊中心 會認爲客戶端與之出現了網絡故障,則會進入自動保護模式。1.Eureka不會移除沒有收到心跳的服務;2.新的服務仍能在服務端註冊,但是暫時不會被同步到其他節點上直到網絡穩定 |
4.8.3.結論
Eureka可以很好的應對網絡故障導致部分節點失去連接的情況,而不會像zookeeper那樣導致整個註冊服務系統的癱瘓。
4.8.4.補充
RDBMS(MySQL/Oracle/sqlServer) ——> ACID
NOSQL(Redis/mongoDB) ——> CAP
ACID:原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)、持久性(Durability)
CAP:一致性(Consistency)、可用性(Availability)、分區容錯性(Partition tolerance)
CAP原則又稱CAP定理,指的是在一個分佈式系統中,一致性(Consistency)、可用性(Availability)、分區容錯性(Partition tolerance)。**CAP 原則指的是,這三個要素最多隻能同時實現兩點,不可能三者兼顧。**由於分區容錯性P在是分佈式系統中是必須要保證的,因此我們只能在A和C之間進行權衡。
五、Ribbon負載均衡
Spring Cloud Ribbon是基於Netflix Ribbon實現的一套客戶端 負載均衡工具。
Ribbon是Netflix發佈的開源項目,主要功能是提供客戶端的軟件負載均衡算法,將Netflix的中間層服務連接在一起。Ribbon客戶端組件提供一系列完善的配置項如連接超時、重試等。簡單的說,就是在配置文件中列出Load Balancer(簡稱LB)後面所有的機器,Ribbon會自動的幫助你基於某種規則(如簡單輪詢、隨機連接等)去連接這些機器。我們也很容易使用Ribbon實現自定義的負載均衡算法。
Ribbon源碼: https://github.com/Netflix/Ribbon
5.1.負載均衡
-
英文名稱:Load Balance,微服務或分佈式集羣中常用的一種應用
-
簡單來說負載均衡就是將用戶的請求ping平攤的分配到多個任務上,從而是系統達到HA(高可用)
-
兩種負載均衡:
- 集中式LB:偏硬件,服務的消費方和提供方之間使用獨立的LB設施,由該設施負責把訪問請求以某種策略轉發至服務的提供方。
- 進程內LB:偏軟件, 將LB邏輯集成到消費方,消費方從服務註冊中心指導哪些地址可用,再自己選擇一個合適的服務器。
5.2.Ribbon初步配置
- ==Ribbon是客戶端負載均衡工具!!!Ribbon是客戶端負載均衡工具!!!Ribbon是客戶端負載均衡工具!!!==所以應該配置在客戶端
- 加入依賴,因爲Riboon需要依賴Eureka運行,所以要同時加入Eureka依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
- 對實現類加入@LoadBalanced註解
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
- 在application.yml文件中配置向註冊中心註冊,如果是作爲消費者模塊不提供服務,不應該註冊自己
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
register-with-eureka: false #作爲消費者不提供服務,不應該註冊自己
- 主啓動類中加入@EnableEurekaClient註解
@SpringBootApplication
@EnableEurekaClient
public class Consumer80_APP {
public static void main(String[] args) {
SpringApplication.run(Consumer80_APP.class,args);
}
}
- 以上步驟1~4完成後即可在controller中直接通過服務名訪問系統中的微服務,服務名作爲URI
Ribbon和Eureka整合後Consumer可以直接調用服務而不用再關心地址和端口號
private static final String URL_PREFIX = "http://MICROSERVICECLOUD-DEPT/";
5.3.Ribbon負載均衡實現
架構示意圖:
實現方法
目標:構建provider集羣后consumer通過負載均衡輪詢調用在Eureka中註冊的服務
- 構建集羣,新開兩個provider模塊,將原provider的代碼部分和pom.xml中依賴照搬到新的provider中
<!--修改對應的端口號-->
<artifactId>microservicecloud-provider-dept-8002</artifactId>
- 將原provider中application.yml文件照搬到新provider,並修改端口號,若新的provider使用自己的數據庫,則修改數據庫信息(其他配置也一樣,如修改別名)
- 集羣中服務名稱必須一致!!!
spring:
application:
name: microservicecloud-dept #同一集羣下必須使用同一服務名!!!!!
- 啓動服務,進行測試
總結
Ribbon其實就是一個軟負載均衡的客戶端組件,可以和其他需要請求的客戶端結合使用。
5.4.Ribbon核心組件IRule
IRule:根據特定算法從服務列表中選取一個需要訪問的服務
5.4.1.七大方法
IRule是一個接口,七大方法是其自帶的落地實現類
- RoundRobinRule:輪詢(默認方法)
- RandomRule:隨機
- AvailabilityFilteringRule:先過濾掉由於多次訪問故障而處於斷路器跳閘狀態的服務,還有併發的連接數量超過閾值的服務,然後對剩餘的服務進行輪詢
- WeightedResponseTimeRule:根據平均響應時間計算服務的權重。統計信息不足時會按照輪詢,統計信息足夠會按照響應的時間選擇服務
- RetryRule:正常時按照輪詢選擇服務,若過程中有服務出現故障,在輪詢一定次數後依然故障,則會跳過故障的服務繼續輪詢。
- BestAvailableRule:先過濾掉由於多次訪問故障而處於斷路器跳閘狀態的服務,然後選擇一個併發量最小的服務
- ZoneAvoidanceRule:默認規則,符合判斷server所在的區域的性能和server的可用性選擇服務
5.4.2.切換規則方法
只需在配置類中配置一個返回具體方法的bean即可
@Bean
public IRule MyRule(){
return new RandomRule();
}
RetryRule測試,在Eureka集羣啓動完成,provider集羣啓動完成後,啓動consumer服務,ConfigBean已改爲RetryRule()
先測試,發現是provider服務輪詢機制,然後人爲的停掉8002服務,再次測試,結果如下:
5.5.自定義Ribbon負載均衡算法
5.5.1.配置及包位置
- 自定義的Ribbon算法類不能放在主啓動類所在的包及子報下(確切來說是不能放在@ComponentScan註解的包及子包下),否則會被全局應用到Ribbon服務中。應該把自定義算法類放在另外新建的包下,且這個類應該是爲配置類。(其實與普通切換負載均衡規則類似,只不過是位置不同而已,普通的可以放在主啓動類所在的包,自定義的要放在外面的包下)
- 主啓動類添加@RibbonClient(name = “微服務名”,configuration = XXX.class)註解指定需要用到負載均衡的微服務名及自定義算法的class對象。
- 在啓動類配置@RibbonClient優先級>ConfigBean配置類中加@Bean自定義規則>什麼都不寫(默認輪詢)
例如簡單的:
@SpringBootApplication
@EnableEurekaClient
//在啓動該微服務的時候就能去加載我們的自定義Ribbon配置類,從而使配置生效
@RibbonClient(name="MICROSERVICECLOUD-DEPT",configuration=MySelfRule.class)
public class DeptConsumer80_App
{
public static void main(String[] args)
{
SpringApplication.run(DeptConsumer80_App.class, args);
}
}
5.5.2.通過修改源代碼獲得自定義算法
目標:每個服務調用3次後再隨機
package com.atguigu.myrule;
import java.util.List;
import java.util.Random;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
public class MyRandomRule extends AbstractLoadBalancerRule {
private int total = 0; // 總共被調用的次數,目前要求每臺被調用3次
private int currentIndex = 0; // 當前提供服務的機器號
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
}
Server server = null;
while (server == null) {
if (Thread.interrupted()) {
return null;
}
List<Server> upList = lb.getReachableServers();
List<Server> allList = lb.getAllServers();
int serverCount = allList.size();
if (serverCount == 0) {
/*
* No servers. End regardless of pass, because subsequent passes only get more
* restrictive.
*/
return null;
}
// int index = rand.nextInt(serverCount);// java.util.Random().nextInt(3);
// server = upList.get(index);
if (currentIndex < upList.size()) {
if (total < 2) {
server = upList.get(currentIndex);
total++;
} else {
server = upList.get(currentIndex);
currentIndex++;
total = 0;
}
} else {
Random random = new Random();
int index = random.nextInt(serverCount);
server = upList.get(index);
}
if (server == null) {
/*
* The only time this should happen is if the server list were somehow trimmed.
* This is a transient condition. Retry after yielding.
*/
Thread.yield();
continue;
}
if (server.isAlive()) {
return (server);
}
// Shouldn't actually happen.. but must be transient or a bug.
server = null;
Thread.yield();
}
return server;
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
}
六、Feign負載均衡
Feign是一個聲明式WebService客戶端,使用方法時定義一個接口並在上面添加註解即可。Feign支持可拔插式的編碼器和解碼器。Spring Cloud對Feign進行了封裝,使其支持SpringMVC和HttpMessageConverters。Feign可以與Eureka和Ribbon組合使用以支持負載均衡。
Feign源碼 : https://github.com/OpenFeign/Feign
6.1.使用案例
- 新建Feign模塊,加入依賴(其實跟80消費端差不多,主要是多了Feign依賴)
<dependencies>
<dependency><!-- 自己定義的api -->
<groupId>com.atguigu.springcloud</groupId>
<artifactId>microservicecloud-api</artifactId>
<version>${project.version}</version>
</dependency>
<!--feign依賴-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<!-- Ribbon相關 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 修改後立即生效,熱部署 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
- 因爲Feign開發其實是面向接口編程,所以Feign接口可以放在api模塊中供各模塊使用,所以要在microservicecloud-api模塊中添加Feign依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
- 在api中編寫接口,接口上添加@FeignClient註解,並通過value指定作用的微服務名
// microservicecloud-api模塊中
@FeignClient(value = "MICROSERVICECLOUD-DEPT")
public interface DeptClientService {
@PostMapping("/dept")
public boolean addDept(Dept dept);
@GetMapping("/dept")
public List<Dept> findAll();
@GetMapping("/dept/{id}")
public Dept findById(@PathVariable("id")Integer id);
}
- 在Feign模塊中編寫Controller,並注入FeignClient接口,直接調用service接口中的方法即可(因爲聲明Feign接口時已經指定過微服務,所以訪問時會正確地找到微服務)
@RestController
@RequestMapping("/consumer")
public class ConsumerDeptController {
@Autowired
private DeptClientService service;
@PostMapping("/dept")
public boolean addDept(Dept dept){
return service.addDept(dept);
}
@GetMapping("/dept")
public List<Dept> findAll(){
return service.findAll();
}
@GetMapping("/dept/{id}")
public Dept findById(@PathVariable("id")Integer id){
return service.findById(id);
}
}
- 修改Feign模塊的主啓動類,加入@EnableFeignClients註解和@ComponentScan註解(主要是掃描api中聲明的接口)
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages = {"com.XXX"})
@ComponentScan("com.XXX")
public class Consumer80Feign_APP {
public static void main(String[] args) {
SpringApplication.run(Consumer80Feign_APP.class,args);
}
}
- 啓動後訪問,即會按照輪詢的方式調用provider集羣
6.2.總結
- Feign通過接口方法調用REST服務,在Eureka中查找對應的服務
- Feign集成了Ribbon技術,所以也支持負載均衡(輪詢)
七、Hystrix斷路器
7.1.分佈式系統面臨的問題
複雜分佈式體系結構中的應用程序有數十個依賴關係,每個依賴關係在某些時候將不可避免地失敗。
扇出
多個微服務互相調用的時候,如果A調用B、C,而B、C又繼續調用其他微服務,這就是扇出(像一把扇子一樣慢慢打開)。
服務雪崩
- 扇出過程中,如果某一個環節的服務出現故障或連接超時,就會導致前面的服務佔用越來越多的資源,進而引起系統崩潰,就是“雪崩效應”。
- 對於高流量的應用來說,單一的後端依賴會導致服務器所有的資源都在幾秒鐘內飽和。比失敗更糟糕的是,這些應用程序還可能導致服務之間的延遲增加,備份隊列,線程和其他系統資源緊張,導致整個系統發生更多的級聯故障。這些都表示需要對故障和延遲進行隔離和管理,單個依賴關係的失敗,不能影響整個應用程序或系統。
7.2.Hystrix介紹
Hystrix源碼: https://github.com/Netflix/Hystrix
- Hystrix是一個用於處理分佈式系統延遲和容錯的開源庫。分佈式系統中,依賴避免不了調用失敗,比如超時,異常等。Hystrix能保證在出現問題的時候,不會導致整體服務失敗,避免級聯故障,以提高分佈式系統的彈性。
- Hystrix類似一個“斷路器”,本身是一種開關裝置,當某個服務單元發生故障之後,通過斷路器的故障監控(類似熔斷保險絲),向調用方返回一個符合預期的、可處理的備選響應(FallBack),而不是長時間的等待或者拋出調用方無法處理的異常,這樣就保證了服務調用方的線程不會被長時間、不必要地佔用,從而避免了故障在分佈式系統中的蔓延,乃至雪崩。
7.2.1.服務熔斷
- 熔斷機制的註解是@HystrixCommand
- 熔斷機制是應對雪崩效應的一種鏈路保護機制,一般存在於服務端
- 當扇出鏈路的某個服務出現故障或響應超時,會進行服務降級,進而熔斷該節點的服務調用,快速返回“錯誤”的相應信息。
- Hystrix的熔斷存在閾值,缺省是5秒內20次調用失敗就會觸發
熔斷案例
- 構建一個新的provider module:microservicecloud-provider-dept-hystrix-8001(複製8001)
- pom.xml加入hystrix依賴(一定要配合Eureka)
<!-- hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<!-- 將微服務provider側註冊進eureka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
- application.yml中配置端口和Eureka信息(必配)和其他框架的配置信息(可選,如mybatis)
- 編寫具體業務邏輯:
Controller類中,在需要配置Fallback的方法上加入@HystrixCommand(fallbackMethod = “XXX”)註解,XXX爲FallBack方法名,本例中作爲測試所以拋出了異常。
@ResponseBody
@GetMapping("/dept/{id}")
@HystrixCommand(fallbackMethod = "nullDeptFallBack")
public Dept findById(@PathVariable("id")Integer id) {
Dept dept = deptService.findById(id);
if (null == dept){
throw new RuntimeException("返回值爲空!");
}
return dept;
}
public Dept nullDeptFallBack(@PathVariable("id")Integer id) {
System.out.println(111);
return new Dept().setId(id).setDeptName("nullName").setDbSource("nullDB");
}
- 主啓動類中加入@EnableCircuitBreaker註解
@SpringBootApplication
@EnableEurekaClient //本服務啓動後會自動註冊進eureka服務中
@EnableDiscoveryClient //服務發現
@EnableCircuitBreaker//對hystrixR熔斷機制的支持
public class DeptProvider8001_Hystrix_App
{
public static void main(String[] args)
{
SpringApplication.run(DeptProvider8001_Hystrix_App.class, args);
}
}
- 開啓服務,測試。
7.2.2.解耦與降級處理
降級
- 當系統整體資源快不夠的時候,忍痛將部分服務暫時關閉,帶渡過難關後,再重新開啓。
- 降級處理時在客戶端完成的,與服務端沒有關係
- 理解:所謂降級,一般是從整體負荷考慮,當某個服務熔斷之後,服務器將不再被調用,此時客戶端可以自己準備一個本地的FallBack回調,返回一個缺省值。這樣做雖然服務水平下降,但好歹可用,比直接掛掉好。
爲什麼要解耦
如果按照上面的熔斷案例來做的話,Controller下的每個方法,都要給其編寫一個FallBack方法,當方法慢慢變多,就會造成代碼膨脹,一個是增加編寫的工作量,另外一個也會增大維護的難度,代碼的耦合度也會高,是十分不合理的,所以要將其解耦。
解耦思路
因爲服務端的是通過實現接口訪問服務端的,如果在父接口上實現了FallBack方法,通過這樣一種方式去維護起來就能實現解耦,也順便完成了降級的機制。
解耦&降級案例
- 在api模塊中新建實現了FallbackFactory接口的類,其中泛型T就是我們需要維護其FallBack的接口方法,並實現其create方法,在create方法中返回實現了T的對象,使用匿名內部類實現T。注意:這個類一定要加@Component註解!!
import com.XXX.entity.Dept;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class DeptClientServiceFallBackFactory implements FallbackFactory<DeptClientService> {
public DeptClientService create(Throwable throwable) {
return new DeptClientService() {
public boolean addDept(Dept dept) {
return false;
}
public List<Dept> findAll() {
return null;
}
public Dept findById(Integer id) {
return new Dept().setId(id).setDeptName("服務器跪了,").setDbSource("遲點來吧");
}
};
}
}
- 修改步驟1中傳入的泛型T接口,添加@FeignClient(fallbackFactory = T.class)註解
@FeignClient(value = "MICROSERVICECLOUD-DEPT",fallbackFactory = DeptClientServiceFallBackFactory.class)
public interface DeptClientService {
@PostMapping("/dept")
public boolean addDept(Dept dept);
@GetMapping("/dept")
public List<Dept> findAll();
@GetMapping("/dept/{id}")
public Dept findById(@PathVariable("id")Integer id);
}
- 修改microservicecloud-consumer-dept-feign模塊的application.xml文件,開啓hystrix
feign:
hystrix:
enabled: true
- 開啓服務並測試
7.2.3.HystrixDashboard服務監控
介紹:SpringCloud對Hystrix Dashboard進行了整合,可以對通過Hystrix發起的請求進行準實時統計,並以報表和圖形的形式展示給用戶(包括每秒執行多少次請求成功和失敗等)。
配置案例
- 新建HystrixDashboard模塊,pom.xml文件加入HystrixDashboard依賴,其他依賴包括Feign,Ribbon,Eureka(可參考80模塊的依賴)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>
- application.yml文件中配置端口(如9001)
- provider類中確認要加入actuator依賴(此爲監控依賴)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- 編寫主啓動類,上標@EnableHystrixDashboard標註
@SpringBootApplication
@EnableHystrixDashboard
public class DashBoard9001_APP {
public static void main(String[] args) {
SpringApplication.run(DashBoard9001_APP.class,args);
}
}
- 啓動服務進行測試,訪問地址:http://localhost:9001/hystrix,(根據配置端口號),看到刺蝟表示已經配置好了
如何監控
- 在dashboard界面中,填入需要監控的服務地址和端口號加上\hystrix.stream和監測間隔即可進入監控。如http://localhost:8001/hystrix.stream
- 在使用Spring Boot 2.0.x + Spring Cloud Finchley.x或者2.1.x + Greenwich.x版本,需要更改路徑/actuator/hystrix.stream,並更改代碼
【具體可以參考https://www.cnblogs.com/x1mercy/p/9291348.html】
監控分析
- 七色:進入監控界面後會有其中顏色的數字,其含義可以對應右上角相同顏色的單詞表示的狀態,其值代表該狀態下觸發的次數
- 一圈:圈的大小代表該服務的流量,圈越大流量越大
通過顏色的變化代表了實例的健康程度,它的健康度從綠色<黃色<橙色<紅色遞減。
通過大小代表實例的請求流量變化,流量越大該實心圓就越大。所以通過該實心圓的展示,就可以在大量的實例中快速的發現故障實例和高壓力實例。 - 一線:代表監控間隔中,服務被訪問的頻率的折線圖
- 通過觀察這些就可以在大量的實例中找出故障實例和高壓實例進行修復和維護。
八、Zuul路由網關
8.1.概述
-
代理:Zuul提供外部的請求轉發到具體的微服務實例中的服務
-
路由:Zuul可以對外部訪問實現統一的入口
-
過濾:Zuul可以對外部訪問進行干預,如請求校驗、服務聚合等
-
Zuul需要配合Eureka使用,需要在Eureka中註冊並獲得其他微服務的信息。Zuul服務最終還是會註冊進Eureka
-
理解:Zuul就像大樓的保安,可以請他找人(代理),找的人在外面叫什麼名字(路由),准不准你進樓(過濾)。因爲保安屬於物業公司,所以保安要在物業公司註冊,所獲得的信息也來源於物業公司(與Eureka的關係)。
Zuul的源碼: http://github.com/Netflix/zuul
8.2.基本配置
- 構建新的Zuul模塊並在pom.xml中加入依賴(Zuul和Eureka必須同時加入)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
- 新建application.yml文件並配置(一定要向Eureka註冊,因爲Zuul本身也是一個微服務)
server:
port: 9527
spring:
application:
name: microservicecloud-zuul #爲這個服務取名,非常重要!!!!!
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
instance:
instance-id: zuul.com
prefer-ip-address: true
- 修改hosts文件(非必須,不過能更好看出效果)
127.0.0.0 zuul.com
- 創建主啓動類,並加入@EnableZuulProxy註解
@SpringBootApplication
@EnableZuulProxy
public class Zuul9527_APP {
public static void main(String[] args) {
SpringApplication.run(Zuul9527_APP.class,args);
}
}
-
啓動測試,訪問規則:步驟3中指定映射+端口號+微服務名稱+訪問路徑。
例子:http://zuul.com:9527/microservicecloud-dept/dept
8.3.路由訪問映射規則
服務名映射和統一公共前綴
當不想暴露真實的服務名時,可以對服務名進行映射,只需在application.yml中配置即可,具體作用看註釋
# 隱藏真實微服務名稱
zuul:
# ignored-services: microservicecloud-dept 隱藏單個微服務名稱
prefix: /chengsw # 統一訪問路徑前綴
ignored-services: "*" # 隱藏所有微服務
routes: # 映射:mydept.serviceId映射到mydept.path,不做隱藏,則訪問兩個地址都能成功,不安全
mydept.serviceId: microservicecloud-dept
mydept.path: /mydept/**
注:因爲Zuul是針對外部訪問管理的,所以配置了隱藏的服務,在系統中其他模塊進行服務名訪問時依然可以正常運行的,這點可以通過打開consumer模塊進行驗證!
因爲做了映射之後,新舊地址還是都可以訪問,因此可以設置隱藏服務名,用於屏蔽原來的請求路徑。
九、Spring Cloud Config 分佈式配置中心
分佈式系統面臨的配置問題:微服務意味着將單體應用拆分成一個個子服務,這些服務都是要相應的配置信息才能運行,隨着系統內微服務數量越來越多,配置信息也不斷地增多,所以一套集中式的、動態的配置管理設施是必不可少的。
9.1.Spring Cloud Config概述
- Spring Cloud Config是一個提供外部集中式配置管理的設施,配置服務器爲各種不同的微服務應用提供了一箇中心化的外部配置
- Spring Cloud Config分爲客戶端和服務端兩部分
- 服務端:分佈式配置中心,是一個獨立的微服務,用來連接併爲客戶端提供配置信息,加密/解密信息等訪問接口
- 客戶端:通過指定的配置中心獲取配置資源,cloud推薦用git來存儲配置信息
- Spring Cloud Config解決的問題:
- 集中管理配置文件
- 不同環境不同配置,動態化的配置更新
- 運行期間動態調整配置,不再需要在每個服務部署的機器上編寫配置文件,讓服務中心統一爲服務拉取配置文件
- 當配置發生變動時,服務不需要重啓即可感知配置變化並應用
- 將配置信息以REST接口形式暴露
9.2.Spring Cloud Config服務端與Github通訊
目標:將配置文件部署在github,Config服務端從github獲取配置
9.2.1.案例
- 新建ConfigServer模塊並配置pom.xml
<!-- springCloud Config -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<!-- 避免Config的Git插件報錯:org/eclipse/jgit/api/TransportConfigCallback -->
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
<version>4.10.0.201712302008-r</version>
</dependency>
- 建立遠程倉庫,並上傳配置文件。yml文件編碼UTF-8!如下例
spring:
profiles:
active:
- dev
---
spring:
profiles: dev
application:
name: microservicecloud-config-XXX-dev
---
spring:
profiles: test
application:
name: microservicecloud-config-XXX-test
- 在application.xml文件中配置github地址
server:
port: 3344
spring:
application:
#爲這個服務取名,非常重要!!!!!
name: microservicecloud-config
cloud:
config:
server:
git:
# uri填github上倉庫地址
uri: https://github.com/XXXX/SpringCloud_Configuration.git
- 編寫主啓動類,加入@EnableConfigServer註解
@SpringBootApplication
@EnableConfigServer
public class ConfigServer3344_APP {
public static void main(String[] args) {
SpringApplication.run(ConfigServer3344_APP.class,args);
}
}
- 啓動服務並嘗試訪問配置文件,有以下五種訪問配置規則
- {application}:配置文件的文件名
- {profile}:讀取的環境
- {lable}:分支
/{application}/{profile}[/{lable}]
/{application}-{profile}.yml
/{lable}/{application}-{profile}.yml
/{application}-{profile}.properties
/{lable}/{application}-{profile}.properties
可用例子(返回格式可能不大相同,但返回值相同):
- http://config3344.com:3344/application-test.yml
- http://config3344.com:3344/master/application-dev.yml
- http://config3344.com:3344/application-test.yml/master
不可用例子:
- 沒有該環境,返回空值:http://config3344.com:3344/application-test11.yml/master
- 沒有配置文件,犯回錯誤頁面:http://config3344.com:3344/lkjliiusdfsddsfl.yml
9.3.bootstrap.yml介紹
- bootstrap.yml比application.yml具有更高的優先級。
- bootstrap.yml是系統級的資源配置項,application.yml是用戶級的資源配置項。
- Spring Cloud會創建"BootStrap Context"作爲"ApplicationContext"的父上下文。初始化的時候BootStrap Context負責從外部源加載配置屬性並解析。這兩個上下文共享一個"Environment",BootStrap 具有更高優先級,他們不會被本地配置覆蓋。
9.4.客戶端的配置與測試
介紹:客戶端主要是在加載時通過config server服務端獲得github配置倉庫的地址,進而通過目標配置文件的文件名獲取相應的配置,最後將取得的配置對自身資源進行賦值並提供訪問
9.4.1.實現過程
1.創建遠程配置yml文件並上傳到github上。如下測試案例因爲需要進行測試,所以配置了兩個profiles方便切換並觀察
spring:
profiles:
active:
- dev
---
server:
port: 8201
spring:
profiles: dev
application:
name: microservicecloud-config-client-dev
eureka:
client:
service-url:
defaultZone: http://eureka-dev.com:7001/eureka/
---
server:
port: 8202
spring:
profiles: test
application:
name: microservicecloud-config-client-test
eureka:
client:
service-url:
defaultZone: http://eureka-dev.com:7001/eureka/
- 本地創建config client模塊,並配置好pom.xml,以下本組件是必選依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
- 編寫bootstrap.yml配置文件,這個步驟比較關鍵,主要是根據此處的配置信息去尋找config server以獲得github倉庫地址和配置中的目標配置文件文件名
spring:
cloud:
config:
name: application_config #需要從github上讀取的資源名稱,注意沒有yml後綴名
profile: test #本次訪問的配置項
label: master
uri: http://config3344.com:3344 #本微服務啓動後先去找3344號服務,通過Spring Cloud Config獲取GitHub的服務地址
- application.yml文件在本module中其實是可寫可不寫的,爲了習慣需要,還是給他寫了個名字
spring:
application:
name: microservicecloud_config
- 修改host文件增加映射,和3344一樣
- 編寫主啓動類,沒什麼特別的,最基本的主啓動類
- 編寫controller,此步驟也比較關鍵,主要是利用@Value註解賦值,若寫錯了bootstrap.yml中的配置文件名稱而沒有獲取到配置,啓動時這裏會拋出異常。@Value中註解的參數即是目標配置文件中的參數值,使用El表達式獲取
@RestController
public class RestController {
@Value("${server.port}")
private String port;
@Value("${eureka.client.service-url.defaultZone}")
private String eurekaZone;
@Value("${spring.application.name}")
private String name;
@GetMapping("/config")
@Override
public String toString() {
return "RestController{" +
"port='" + port + '\'' +
", eurekaZone='" + eurekaZone + '\'' +
", name='" + name + '\'' +
'}';
}
}
- 先啓動Config server服務端服務,然後再啓用本Config客戶端服務,根據profiles的值訪問對應的端口即可。如本例選擇的是test,則訪問端口爲:http://config3355.com:8202/config。(config3355.com爲hosts文件中配置了的映射)
9.5.Spring Cloud的配置實戰
介紹:其實前面client的配置案例都是幫助理解這個組件爲主,並沒有很大的實際意義。。。。。。這節的案例中是配置一個Provider,一個Eureka,他們的配置統一在github上獲取,實現統一配置分佈式管理和多環境變更,這個才比較有實戰意義。
9.5.1.實現過程
- 先寫好provider和Eureka的配置yml文件,這兩個文件和平常配置沒什麼不同,因爲這裏主要是說config,所以就沒有配置集羣,上傳yml到github
Eureka配置文件示例:
spring:
profiles:
active:
- dev
---
spring:
profiles: dev
application:
name: microservicecloud-eureka-client-dev
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com #hostname爲hosts文件中映射的地址
client:
register-with-eureka: false #false表示不向註冊中心註冊自己
fetch-registry: false #false表示自己就是註冊中心,職責是維護實例,不參加檢索
service-url:
defaultZone: http://eureka7001.com:7001/eureka/ #設置eureka server的交互地址
---
spring:
profiles: test
application:
name: microservicecloud-eureka-client-dev
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com #hostname爲hosts文件中映射的地址
client:
register-with-eureka: false #false表示不向註冊中心註冊自己
fetch-registry: false #false表示自己就是註冊中心,職責是維護實例,不參加檢索
service-url:
defaultZone: http://eureka7001.com:7001/eureka/ #設置eureka server的交互地址
Provider配置文件示例:
spring:
profiles:
active:
- dev
---
server:
port: 8001
mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml
type-aliases-package: com.XXX.entity
mapper-locations:
- classpath:mybatis/mapper/**/*.xml
spring:
profiles: dev
application:
name: microservicecloud-dept #爲這個服務取名,非常重要!!!!!
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: org.gjt.mm.mysql.Driver
url: jdbc:mysql://192.168.88.246:3306/cloudDB01
username: root
password: 123456
dbcp2:
min-idle: 5 #最小連接數
initial-size: 5 #初始化連接數
max-total: 10 #最大連接數
max-wait-millis: 200 #等待連接最長的超時時間
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
instance:
instance-id: dept8001
prefer-ip-address: true
---
server:
port: 8001
mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml
type-aliases-package: com.XXX.entity
mapper-locations:
- classpath:mybatis/mapper/**/*.xml
spring:
profiles: test
application:
name: microservicecloud-dept #爲這個服務取名,非常重要!!!!!
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: org.gjt.mm.mysql.Driver
url: jdbc:mysql://192.168.88.246:3306/cloudDB02
username: root
password: 123456
dbcp2:
min-idle: 5 #最小連接數
initial-size: 5 #初始化連接數
max-total: 10 #最大連接數
max-wait-millis: 200 #等待連接最長的超時時間
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
instance:
instance-id: dept8001
prefer-ip-address: true
- 新開eureka和provide的模塊並在pom.xml中添加依賴,其他必要依賴和之前的案例一樣,但是config的依賴一定要添加上
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
- 兩個模塊都要編寫bootstrap.yml文件,和上面的案例一樣
spring:
cloud:
config:
name: application_config #需要從github上讀取的資源名稱,注意沒有yml後綴名
profile: test #本次訪問的配置項
label: master
uri: http://config3344.com:3344 #本微服務啓動後先去找3344號服務,通過SpringCloudConfig獲取GitHub的服務地址
- (可選)兩個模塊中編寫application.yml文件,可以配置一下服務名
spring:
application:
name: microservicecloud_config
- 兩個模塊的主啓動類,Eureka的正常加EurekaServer註解,Provider加EurekaClient註解,不詳述
- 編寫Provider模塊的業務代碼
- 啓動測試,因爲這兩個模塊都要通過3344ConfigServer爲其在github上獲取配置,所以要先啓動3344模塊,然後再一次啓動eureka和provider模塊,進行測試即可。
十、Spring Cloud內容技術梳理與架構
- 整套開發技術棧以SpringCloud爲主,單個微服務模塊以SpringMVC+SpringBoot+MyBatis組合進行開發
- 前端層,頁面H5+thymeleaf/樣式CSS3+Bootstrap/前端框架JQuery+Node/Vue等
- 負載層,前端訪問通過Http或Https協議到達服務端的LB,可以是F5等硬件做負載均衡,還可以自行部署LVS+Keepalived等(前期量小可以直接使用Nginx)
- 網關層,請求通過LB後,會到達整個微服務體系的網關層Zuul(Gateway),內嵌Ribbon做客戶端負載均衡,Hystrix做熔斷降級等
- 服務註冊,採用Eureka來做服務治理,Zuul會從Eureka集羣獲取已發佈的微服務訪問地址,然後根據配置把請求代理到相應的微服務去
- docker容器,所有的微服務模塊都部署在Docker容器裏面,而且前後端的服務完全分開,各自獨立部署後前端微服務調用後端微服務,後端微服務之間會有相互調用
- 服務調用,微服務模塊間調用都採用標準的Http/Https+REST+JSON的方式,調用技術採用Feign+HttpClient+Ribbon+Hystrix
- 統一配置,每個微服務模塊會跟Eureka集羣、配置中心(Spring Cloud Config)等進行交互
- 第3方框架,每個微服務模塊根據實現的需要,通常還需要使用一些第三發框架,比如常見的有:緩存服務(Redis)、圖片服務(FastDFS)、搜索服務(ElasticSearch)、安全管理(Shiro)等等
- Mysql數據庫,可以按照微服務模塊進行拆分,統一訪問公共庫或者單獨自己庫,可以單獨構建MySQL集羣或者分庫分表MyCat等。
十一、Spring Cloud微服務架構體系知識擴展
微服務架構體系知識展望,包含但不限於
- SpringCloud Stream:數據流操作開發包
- SpringCloud Turbine:是聚合服務器發送事件流數據的一個工具,用來監控集羣下hystrix的metrics情況。
- SpringCloud Task:提供雲端計劃任務管理、任務調度。
- SpringCloud Sleuth:日誌收集工具包實現了一種分佈式追蹤解決方案,封裝了Dapper和log-based追蹤以及Zipkin和HTrace操作,
- SpringCloud Security:基於spring security的安全工具包,爲應用程序添加安全控制
- 服務部署:Kubernetes , OpenStack
- 全鏈路追蹤:Zipkin,brave
- 服務監控:zabbix
- SpringCloud CLI:基於 Spring Boot CLI,可以讓你以命令行方式快速建立雲組件。
- 全局控制:選舉leader、全局鎖、全局唯一id
- 安全鑑權: auth2、 openId connect
- 自動化構建與部署: gitlab + jenkins + docker
- 服務監控和告警(Spring Boot Admin)
- ……