視訊來源:狂神說Java https://www.bilibili.com/video/BV1jJ411S7xr
代碼已上傳碼雲 :https://gitee.com/ning_fei_fei/springcloud-config.git
1、微服務
什麼是微服務
是一種架構思想;從技術角度了解就是将傳統的一站式應用,根據業務拆分成一個一個的服務,徹底地去耦合,每一個微服務提供單個業務功能的服務,一個服務做一件事情,從技術角度看就是一種小而獨立的處理過程,類似程序的概念,能夠自行單獨啟動或銷毀,擁有自己獨立的資料庫。
微服務強調的是服務的大小,它關注的是某一個點,是具體解決某一個問題/提供落地對應服務的一個服務應用。
微服務架構
微服務架構是一種架構模式,它體長将單一應用程式劃分成一組小的服務,服務之間互相協調,互相配合,為使用者提供最終價值。每個服務運作在其獨立的程序中,服務與服務之間采用輕量級的通信機制**(如HTTP)互相協作,每個服務都圍繞着具體的業務進行建構,并且能夠被獨立的部署到生産環境中,另外,應盡量避免統一的,集中式的服務管理機制,對具體的一個服務而言,應根據業務上下文,選擇合适的語言、工具(如Maven)**對其進行建構。
微服務優缺點
優點
- 單一職責原則;
- 每個服務足夠内聚,足夠小,代碼容易了解,這樣能聚焦一個指定的業務功能或業務需求;
- 開發簡單,開發效率高,一個服務可能就是專一的隻幹一件事;
- 微服務能夠被小團隊單獨開發,這個團隊隻需2-5個開發人員組成;
- 微服務是松耦合的,是有功能意義的服務,無論是在開發階段或部署階段都是獨立的;
- 微服務能使用不同的語言開發;
- 易于和第三方內建,微服務允許容易且靈活的方式內建自動部署,通過持續內建工具,如jenkins,Hudson,bamboo;
- 微服務易于被一個開發人員了解,修改和維護,這樣小團隊能夠更關注自己的工作成果,無需通過合作才能展現價值;
- 微服務允許利用和融合最新技術;
- 微服務隻是業務邏輯的代碼,不會和HTML,CSS,或其他的界面混合;
- 每個微服務都有自己的存儲能力,可以有自己的資料庫,也可以有統一的資料庫;
缺點
- 開發人員要處理分布式系統的複雜性;
- 多服務運維難度,随着服務的增加,運維的壓力也在增大;
- 系統部署依賴問題;
- 服務間通信成本問題;
- 資料一緻性問題;
- 系統內建測試問題;
- 性能和監控問題;
2、架構使用springcloud
spring cloud 和dubbo對比
- 注冊中心不同,dubbo是zookeeper,springcloud是eureka
- dubbo是基于RPC異步通信方式,springcloud是http的REST方式
- 服務的監控不同,一個是dubbo-monitor,一個是springboot-admin
- dubbo更多的服務需要借助其他的技術,springcloud則自己具備,例如網關,服務配置等
springcloud元件
- Netflix-eureka,注冊中心,服務的注冊與發現
- Netflix-hystrix,服務熔斷和降級
- 負載均衡,Netflix-ribbon和feign
- 網關,Netflix-zuul
- spring cloud config,分布式配置
spring boot和spring cloud的關系
- Spring Boot專注于開發單個個體微服務;
- Spring Cloud是關注全局的微服務協調整理治理架構,它将Spring Boot開發的一個個單體微服務,整合并管理起來,為各個微服務之間提供:配置管理、服務發現、斷路器、路由、為代理、事件總棧、全局鎖、決策競選、分布式會話等等內建服務;
- Spring Boot可以離開Spring Cloud獨立使用,開發項目,但Spring Cloud離不開Spring Boot,屬于依賴關系;
- Spring Boot專注于快速、友善的開發單個個體微服務,SpringCloud關注全局的服務治理架構;
版本選擇
Spring Cloud沒有采用數字編号的方式命名版本号,而是采用了倫敦地鐵站的名稱,同時根據字母表的順序來對應版本時間順序,比如最早的Realse版本:Angel,第二個Realse版本:Brixton,然後是Camden、Dalston、Edgware;spring cloud和spring boot的版本的選擇也是有要求的 檢視版本對的對應關系(https://spring.io/projects/spring-cloud#overview
或者
3、spring cloud服務配置和搭建
3.1 版本選擇
版本選擇
spring boot 選擇2.2.4,對應可以選擇Hoxton.SR12
3.2 建立父項目
建立一個maven項目(名稱springcloud),用于管理版本依賴,src可以删除;
父項目管理版本依賴使用
所有的版本都放在這裡,子項目隻用導入依賴不需要使用版本
導入spring cloud依賴
<!-- springcloud依賴 版本要和springboot的版本對應,可在官網檢視-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR12</version>
<type>pom</type>
<scope>import</scope>
</dependency>
熱部署,沒有特殊說明所有springboot子項目都導入
<!-- spring-boot-devtools 熱部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<version>2.2.4.RELEASE</version>
</dependency>
之後所有帶版本的依賴都放在在父項目中
所有的父依賴:
<properties>
<mybatis.version>2.2.0</mybatis.version>
<mybatis-plus-boot-starter.version>3.0-RELEASE</mybatis-plus-boot-starter.version>
<fastjson.version>1.2.75</fastjson.version>
</properties>
<!--springcloud 父依賴管理-->
<dependencyManagement>
<dependencies>
<!-- springcloud依賴 版本要和springboot的版本對應,可在官網檢視-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR12</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- eureka-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- zuul -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- eureka 用戶端client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- ribbon 負載均衡loadbalance-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- feign 實作負載均衡-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- spring-boot springboot也可以以parent依賴的方式導入(<parent></parent>)-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<version>2.2.4.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus-boot-starter.version}</version>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<!-- logback-core -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!-- spring-boot-devtools 熱部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<version>2.2.4.RELEASE</version>
</dependency>
</dependencies>
</dependencyManagement>
3.3 Eureka
注冊中心eureka
服務的注冊和發現,服務提供者直接将服務注冊到注冊中心,消費者到服務注冊中心取,這個和dubbo+zookeeper一樣,但是,spring cloud的服務注冊是将自己整個全部注冊到了注冊中心,而dubbo+zookeeper則是注冊哪裡的服務,掃描哪裡的,通過注解;
Eureka采用了C-S的架構設計,EurekaServer作為服務注冊功能的伺服器,他是服務注冊中心。
而系統中的其他微服務,使用Eureka的用戶端連接配接到EurekaServer并維持心跳連接配接。這樣系統的維護人員就可以通過EurekaServer來監控系統中各個微服務是否正常運作,Springcloud 的一些其他子產品 (比如Zuul) 就可以通過EurekaServer來發現系統中的其他微服務,并執行相關的邏輯。服務的存活up
建立maven項目springcloud-eureka(也可以直接建立boot項目),導入依賴web,Eureka
<!-- eureka-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
resources下面建立
application.yml
,之後不再解釋,沒有特殊情況yml都放在resources下面,配置eureka
server:
port: 7001
#eureka 服務端(注冊中心)配置
eureka:
instance:
hostname: eureka7001.com
client:
register-with-eureka: false #是否向注冊中心注冊自己
fetch-registry: false #自己不是注冊中心,true表示是自己不是注冊中心,false表示是注冊中心
service-url: # 注冊中心頁面位址
# 單個注冊中心
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
server:
enable-self-preservation: false #關閉自我保護機制,一般開啟;測試的時候很煩,暫時關閉
建立啟動類,開啟注冊中心服務
@SpringBootApplication
@EnableEurekaServer //開啟eurekaserver,服務端
public class springcloudeurekaApplication {
public static void main(String[] args) {
SpringApplication.run(springcloudeurekaApplication.class,args);
}
}
ok ,啟動服務,通路
eureka7001.com:7001
搭建model
建立maven項目springcloud-model來存放所有的model和util以及接口;
導入依賴:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
依照資料庫表建立model,這裡我用的springboot+mybatis-plus
由于配置了自動裝配,是以需要依賴
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<!--model不需要jdbc連接配接配置,将依賴中的自動配置排除掉,不讓消費者會啟動報錯-->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</exclusion>
</exclusions>
</dependency>
ok ,model 整理好啦。
搭建服務提供者
導入依賴
<!--eureka 用戶端client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<!--完善監控資訊,配置中info的資訊-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
配置檔案配置:
server:
port: 8001
spring:
application:
name: springcloud-provide
#eureka 用戶端client配置,注冊服務到注冊中心
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
instance:
instance-id: springcloud-provide8001 #修改預設的描述資訊
prefer-ip-address: true # 顯示服務的ip位址
# 設定微服務的資訊,在注冊中心點選Status對應的服務資訊檢視(看到的是個json,沒什麼用,太可不必)
info:
app.auhor: ning
app.name: provide-server
company.name: hello-cloud
company.phone: 123456789
啟動類加上注解
檢視注冊到注冊中心服務的資訊,啟動類加上注解
擷取資訊
/**
* DiscoveryClient 可以用來擷取一些配置的資訊,得到具體的微服務!
*/
@Autowired
private DiscoveryClient client;
/**
* 擷取一些注冊進來的微服務的資訊~,
*
* @return
*/
@GetMapping("/dept/discovery")
public Object discovery() {
// 擷取微服務清單的清單
List<String> services = client.getServices();
System.out.println("discovery=>services:" + services);
// 得到一個具體的微服務資訊,通過具體的微服務id,applicaioinName;
List<ServiceInstance> instances = client.getInstances("SPRINGCLOUD-PROVIDER");
for (ServiceInstance instance : instances) {
System.out.println(
instance.getHost() + "\t" + // 主機名稱
instance.getPort() + "\t" + // 端口号
instance.getUri() + "\t" + // uri
instance.getServiceId() // 服務id
);
}
return this.client;
}
啟動項目
通路7001注冊中心,服務提供者注冊進來了。。。
由于測試的時候不友善,是以關掉了自動保護機制,一般開啟,就是上面的一串紅字;如果服務挂掉,up會顯示為down,但是它依舊在注冊中心注冊着。
EureKa自我保護機制
- 預設情況下,當eureka server在一定時間内沒有收到執行個體的心跳,便會把該執行個體從系統資料庫中删除(預設是90秒),但是,如果短時間内丢失大量的執行個體心跳,便會觸發eureka server的自我保護機制,比如在開發測試時,需要頻繁地重新開機微服務執行個體,但是我們很少會把eureka server一起重新開機(因為在開發過程中不會修改eureka注冊中心),當一分鐘内收到的心跳數大量減少時,會觸發該保護機制。可以在eureka管理界面看到Renews threshold和Renews(last min),當後者(最後一分鐘收到的心跳數)小于前者(心跳門檻值)的時候,觸發保護機制,會出現紅色的警告:
從警告中可以看到,eureka認為雖然收不到執行個體的心跳,但它認為執行個體還是健康的,eureka會保護這些執行個體,不會把它們從系統資料庫中删掉。EMERGENCY!EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT.RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEGING EXPIRED JUST TO BE SAFE.
- 該保護機制的目的是避免網絡連接配接故障,在發生網絡故障時,微服務和注冊中心之間無法正常通信,但服務本身是健康的,不應該登出該服務,如果eureka因網絡故障而把微服務誤删了,那即使網絡恢複了,該微服務也不會重新注冊到eureka server了,因為隻有在微服務啟動的時候才會發起注冊請求,後面隻會發送心跳和服務清單請求,這樣的話,該執行個體雖然是運作着,但永遠不會被其它服務所感覺。是以,eureka server在短時間内丢失過多的用戶端心跳時,會進入自我保護模式,該模式下,eureka會保護系統資料庫中的資訊,不在登出任何微服務,當網絡故障恢複後,eureka會自動退出保護模式。自我保護模式可以讓叢集更加健壯。
- 但是我們在開發測試階段,需要頻繁地重新開機釋出,如果觸發了保護機制,則舊的服務執行個體沒有被删除,這時請求有可能跑到舊的執行個體中,而該執行個體已經關閉了,這就導緻請求錯誤,影響開發測試。是以,在開發測試階段,我們可以把自我保護模式關閉,隻需在eureka server配置檔案中加上如下配置即可:
【不推薦關閉自我保護機制】eureka.server.enable-self-preservation=false
Eureka叢集
就是多個注冊中心互相有關聯
在建立兩個eureka項目,端口号為7002和7003,配置依賴和之前的一緻,可以直接複制;
這裡修改了一下本機的ip名稱,來模拟一下多個ip:
hosts檔案的位置:
C:\Windows\System32\drivers\etc
右鍵notepad打開,在最後添加:
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
127.0.0.1 eureka7003.com
儲存一下,如果不能添加,右鍵,屬性,把隻讀權限關掉,安全中勾選寫入,儲存;我的是這麼操作的,一般這個檔案不要亂改;
建立成功之後端口号7001修改配置檔案:
#多個注冊中心,叢集;就是幾個注冊中心互相建立關聯關系;本機關注其他注冊中心即可
defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
一個注冊中心
defaultZone
配置本機的注冊中心位址,叢集的話就是注冊中心互相有聯系,本機關注其他注冊中心即可,7001關注7002和7003,7002關注7001和7003,7003關注7001和7002
修改7002:
server:
port: 7002
#eureka 服務端(注冊中心)配置
eureka:
instance:
hostname: eureka7002.com
client:
register-with-eureka: false #是否向注冊中心注冊自己
fetch-registry: false #自己不是注冊中心,true表示是自己不是注冊中心,false表示是注冊中心
service-url: # 注冊中心頁面位址
# 單個注冊中心
# defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
#叢集
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7003/eureka/
server:
enable-self-preservation: false #關閉自我保護機制,一般開啟;測試的時候很煩,暫時關閉
修改7003:
server:
port: 7003
#eureka 服務端(注冊中心)配置
eureka:
instance:
hostname: eureka7003.com
client:
register-with-eureka: false #是否向注冊中心注冊自己
fetch-registry: false #自己不是注冊中心,true表示是自己不是注冊中心,false表示是注冊中心
service-url: # 注冊中心頁面位址;
# 單個注冊中心
# defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
#叢集
defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7001.com:7001/eureka/
server:
enable-self-preservation: false #關閉自我保護機制,一般開啟;測試的時候很煩,暫時關閉
全部啟動:
通路7002和7003也是基本一緻的。
服務提供者注冊到叢集環境中
隻需要修改配置檔案,把它注冊到所有的注冊中心裡面:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
有熱部署,等一下,重新整理頁面,發現三個注冊中心都注冊進去了。。。
如果provide配置的注冊中心沒有全部啟動,那麼provide啟動時會異常,但是不關鍵,隻是連接配接不上到配置的沒有啟動的注冊中心。
Ribbon 用戶端的負載均衡
用戶端的負載均衡,provide的項目再來兩個,provide02和provide03,用于實作負載均衡,配置的服務端名稱要一緻,依賴一樣;用戶端通路服務端的通路位址和端口可以用注冊中心application的名稱來替代,例如:
http://localhost:8001/systemUserController/getSystemUserList
可以替換為:
http://SPRINGCLOUD-PROVIDE/systemUserController/getSystemUserList
是以,不論這個服務放在那裡,通路的路徑都是一樣的,實作一個入口的效果。
配置用戶端的負載均衡,springboot隻需要一個注解
springcloud是http的rest方式,是以配置一下rest的模闆
RestTemplate
,開啟它的負載均衡
建立用戶端consumer
依賴導入eureka用戶端依賴
<!-- ribbon 負載均衡loadbalance-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<!--eureka 用戶端client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
配置檔案:
server:
port: 80 # 80端口不需要使用端口通路
#eureka配置
eureka:
client:
register-with-eureka: false #不向注冊中心注冊自己
service-url: #到哪個注冊中心取服務
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
開啟eureka用戶端:
配置配置類:
@Configuration
public class MyResquestConfig {
@Bean
@LoadBalanced //Ribbon 實作負載均衡,通過Restful;預設政策輪詢
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
為了更加顯示效果,将資料庫複制兩份,每個provide連各自的資料庫,資料庫表一樣;
寫個controller使用
RestTemplate
調用服務端接口:
@RestController
public class SystemUserConsumerController {
@Autowired
RestTemplate restTemplate;
//該類中請求的字首都一緻,定義一下
// http://localhost:8001/systemUserController/get
//SPRINGCLOUD-PROVIDE 服務的名稱,注冊中心Application對應的值
public static final String url_prefix= "http://SPRINGCLOUD-PROVIDE/systemUserController";
@RequestMapping("/systemUserConsumerController/getList")
public String getList(){
return restTemplate.getForObject(url_prefix+"/getSystemUserList",String.class);
}
@GetMapping("/addUser")
public Boolean addUser(){
SystemUser systemUser = new SystemUser();
systemUser.setUserAccount("ning").setPassword("123456").setDescribtion("hellocloud");
return restTemplate.postForObject(url_prefix+"/addSystemUser",systemUser,Boolean.class);
}
}
通路:
http://localhost/systemUserConsumerController/getList
數值代表是哪個資料庫,用戶端通路,發現是01,02,03,01,02,03…
可以看出實作了負載均衡,而且是輪詢的模式,Ribbon預設的是輪詢模式;
也可以自定義負載均衡的模式:
建立自己的類,繼承
AbstractLoadBalancerRule
,然後編寫自定義的模式,使用配置類注入到bean中,最後啟動類的注解
@RibbonClient
加上屬性configuration指定這個配置類;
//自定義模式類
public class MyRandomRule extends AbstractLoadBalancerRule{
/**
* 每個服務通路5次則換下一個服務(總共3個服務)
* <p>
* total=0,預設=0,如果=5,指向下一個服務節點
* index=0,預設=0,如果total=5,index+1
*/
private int total = 0;//被調用的次數
private int currentIndex = 0;//目前是誰在提供服務
//@edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE")
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 = chooseRandomInt(serverCount);//生成區間随機數
//server = upList.get(index);//從或活着的服務中,随機擷取一個
//=====================自定義代碼=========================
if (total < 5) {
server = upList.get(currentIndex);
total++;
} else {
total = 0;
currentIndex++;
if (currentIndex > upList.size()) {
currentIndex = 0;
}
server = upList.get(currentIndex);//從活着的服務中,擷取指定的服務來進行操作
}
//======================================================
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;
}
protected int chooseRandomInt(int serverCount) {
return ThreadLocalRandom.current().nextInt(serverCount);
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
// TODO Auto-generated method stub
}
}
配置類:
@Configuration
public class MyRule {
@Bean
public IRule myRule(){
return new MyRandomRule();//預設是輪詢RandomRule,現在自定義為自己的
}
}
開啟負載均衡和指定配置類(在啟動類上加注解):
注意,配置類
MyRule
不在主應用程式上下文的
@ComponentScan
中,否則将由所有
@RibbonClients
共享。如果您使用
@ComponentScan
(或
@SpringBootApplication
),則需要采取措施避免包含(例如将其放在一個單獨的,不重疊的包中,或者指定要在
@ComponentScan
),一般直接放在啟動類的上一級。
Feign 服務端的負載均衡
服務端提供接口,用戶端直接注入調用方法。
這裡我把接口放在了model裡面,導入依賴:
<!-- feign 實作負載均衡,定義接口-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
接口
interface
@Component
//value值是注冊中心Application的值
@FeignClient(value = "SPRINGCLOUD-PROVIDE")
public interface SystemUserService {
@GetMapping(value = "/systemUserController/getSystemUserList")
public String getSystemUserList();
@PostMapping(value = "/systemUserController/addSystemUser")
public Boolean addSystemUser(@RequestBody SystemUser systemUser);
@GetMapping(value = "/systemUserController/removeSystemUser/{id}")
public Boolean removeSystemUser(@PathVariable("id") Long id);
}
consumer用戶端
直接導入model的依賴和feign的依賴(和上面一樣),啟動類上加上feign的注解和服務的包的掃描(掃描model):
@EnableFeignClients(basePackages = {"com.ning"})
@ComponentScan("com.ning")
建立配置類:
@Configuration
public class MyResquestConfig {
@Bean
@LoadBalanced //Ribbon 實作負載均衡,通過Restful;預設政策輪詢
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
建立controller,注入接口,直接調用提供者提供的服務
@RestController
public class SystemUserConsumerController {
@Autowired
private SystemUserService systemUserService = null; //隻是一個接口,沒有實作類,為null就行
/**
* feign調用
* @return
*/
@GetMapping("/getList")
public String getList(){
return systemUserService.getSystemUserList();
}
}
Hystrix 服務熔斷和服務降級
Hystrix是一個應用于處理分布式系統的延遲和容錯的開源庫,在分布式系統裡,許多依賴不可避免的會調用失敗,比如逾時,異常等,Hystrix 能夠保證在一個依賴出問題的情況下,不會導緻整個體系服務失敗,避免級聯故障,以提高分布式系統的彈性。
熔斷可以說是發生錯誤了整個應用還可以繼續往下面走,傳回一個發生錯誤的狀态或者發生錯誤時執行其他程式後傳回的結果,使用注解
@HystrixCommand
,其中注解
fallbackMethod
屬性指向發生錯誤後執行的程式。
服務熔斷
服務的提供者provide進行操作,服務熔斷
導入依賴:
<!--導入Hystrix依賴-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
啟動類加上注解
@EnableCircuitBreaker
開啟熔斷服務
controller類中寫一個方法作為出錯的時候執行的方法,這個方法根據自己的業務邏輯來定,在需要熔斷的方法上加上注解
@HystrixCommand
并指定
fallbackMethod
屬性為寫的方法:
@RestController
@RequestMapping("/systemUserController")
public class SystemUserController {
@Autowired
SystemUserService systemUserService;
@HystrixCommand(fallbackMethod = "error")
@GetMapping(value = "/getSystemUserList")
public String getSystemUserList(){
return JSONObject.toJSONString(systemUserService.list(new QueryWrapper<SystemUser>()));
}
public String error(){
return "hello_hystrix";
}
}
可以寫個bug在getSystemUserList()中,調用的時候就會傳回error的傳回值;
服務降級
服務降級是指 當伺服器壓力劇增的情況下,根據實際業務情況及流量,對一些服務和頁面有政策的不處理,或換種簡單的方式處理,進而釋放伺服器資源以保證核心業務正常運作或高效運作。說白了,就是盡可能的把系統資源讓給優先級高的服務。
資源有限,而請求是無限的。如果在并發高峰期,不做服務降級處理,一方面肯定會影響整體服務的性能,嚴重的話可能會導緻當機某些重要的服務不可用。是以,一般在高峰期,為了保證核心功能服務的可用性,都要對某些服務降級處理。比如當雙11活動時,把交易無關的服務統統降級,如檢視螞蟻深林,檢視曆史訂單等等。
服務降級主要用于什麼場景呢?當整個微服務架構整體的負載超出了預設的上限門檻值或即将到來的流量預計将會超過預設的門檻值時,為了保證重要或基本的服務能正常運作,可以将一些 不重要 或 不緊急 的服務或任務進行服務的 延遲使用 或 暫停使用。
服務接口我放在了model中的service下,直接導入依賴,依賴是一樣的,hystrix的,在接口上加上注解:
//value值是注冊中心Application的值 , fallbackFactory指定服務降級配置類
@FeignClient(value = "SPRINGCLOUD-PROVIDE",fallbackFactory = SystemUserServiceFallBack.class)
建立hystrix的fallback類:
@Component //扔給spring管理
public class SystemUserServiceFallBack implements FallbackFactory {
public Object create(Throwable throwable) {
return new SystemUserService() {
public String getSystemUserList() {
return "服務暫停了-------";
}
public Boolean addSystemUser(SystemUser systemUser) {
return null;
}
public Boolean removeSystemUser(Long id) {
return null;
}
};
}
}
消費者consumer側操作
直接配置檔案配置開啟:
# 開啟降級feign.hystrix
feign:
hystrix:
enabled: true
hystrixDashboard流監控
直接建立項目springcloud-consumer-hystrix-dashboard:
pom依賴:
<!-- hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- hystrix-dashboard 監控頁面 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
配置檔案:
server:
port: 9001
hystrix:
dashboard:
proxy-stream-allow-list: "*" #表明監控所有的通路流
啟動類:
@SpringBootApplication
@EnableHystrixDashboard //開啟dashboard監控
public class ConsumerHystrixDashboardApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerHystrixDashboardApplication.class,args);
}
}
啟動通路:
http://localhost:9001/hystrix
豪豬(hystrix)來咯
上面寫的蠻清楚的,預設的叢集、自定義叢集以及單一的應用怎麼寫位址,delay監控間隔時間預設的2000就可以,名稱自己随便可以定義,例如:demo
monitor stream:監控流,是以要給服務的提供者provide配置一個servlet,讓它可以被監控:
依賴:
<!--完善監控資訊,配置中info的資訊-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
servlet:
@Bean //添加一個servlet,監控服務,使用hystrix-dashboard
public ServletRegistrationBean hystrixMetricsStreamServlet(){
ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
registrationBean.addUrlMappings("/actuator/hystrix.stream");
return registrationBean;
}
填寫要監控的項目位址端口:
http://localhost:8001//actuator/hystrix.stream
進來啦:
通路8001項目的接口就可以看到變化。
對應的意義
注入的提供者越多,這個頁面的這樣的圖越多,對每個應用的監控也一目了然;
zuul 網關
Zull包含了對請求的路由(用來跳轉的)和過濾兩個最主要功能:
其中路由功能負責将外部請求轉發到具體的微服務執行個體上,是實作外部通路統一入口的基礎,而過濾器功能則負責對請求的處理過程進行幹預,是實作請求校驗,服務聚合等功能的基礎。Zuul和Eureka進行整合,将Zuul自身注冊為Eureka服務治理下的應用,同時從Eureka中獲得其他服務的消息,也即以後的通路微服務都是通過Zuul跳轉後獲得。
依賴:
<!-- zuul -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
使用的是父依賴中的依賴,使用的是父依賴的版本;這裡注冊中心的依賴也是需要的,和上面的一樣,省略。
配置zuul:
zuul:
routes:
ning.serviceId: springcloud-provide #之前的通路路徑
ning.path: /ning/** #通路路徑
# prefix: /hello #設定通路字首
ignored-services: '*' #忽略所有的通路
既然注冊到注冊中心,肯定需要配置一下注冊中心和自己的名稱、端口,和eureka一樣,省略。
建立啟動類,使用注解
@EnableZuulProxy
開啟網關的服務,啟動項目,到注冊中心看一下:
使用
http://localhost:9527/springcloud-provide/systemUserController/getSystemUserList
通路一下,發現找不到了,配置生效了:
通路下配置的位址
http://localhost:9527/ning/systemUserController/getSystemUserList
springcloud-config 分布式配置
微服務意味着要将單體應用中的業務拆分成一個個子服務,每個服務的粒度相對較小,是以系統中會出現大量的服務,由于每個服務都需要必要的配置資訊才能運作,是以一套集中式的,動态的配置管理設施是必不可少的。
對每個yml配置檔案集中進行管理,則是springcloud-config。
建立一個服務端用于從配置庫中讀取配置資訊,用戶端都連接配接這個服務端,來擷取各自的配置資訊:
spring cloud config 分布式配置中心與GitHub整合
配置gitee
由于GitHub網頁登入和打開太慢,這裡使用gitee;
安裝git環境,建立gitee賬号,登入進去直接到最下面找到:
都可以,輸入指令來生成公鑰,
直接Windows+r輸入cmd ,執行
ssh-keygen -t rsa -C "[email protected]"
,
[email protected]
這是個郵箱位址,什麼郵箱都可以,注意==自己的郵箱!!!!==然後三次回車。OK,到c盤使用者下面的.ssh檔案夾裡郵件打開:
然後複制出公鑰:
OK,可以建立倉庫,并且拉取和推送東西了,這個省略了。
建立讀取配置的服務端
建立項目springcloud-config-server
導入依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- spring-cloud-config-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
順便導入熱部署依賴,每個項目都導入熱部署的依賴,這裡省略!
配置檔案:
server:
port: 8888
spring:
application:
name: springcloud-config-server
cloud:
config:
server:
git:
uri: https://gitee.com/ning_fei_fei/springcloud-config.git # Https
search-paths: springcloud-config-yml/ #掃描git這個包下面的所有檔案
一定要注意是uri,如果建立了檔案夾來放檔案,要掃描檔案夾,
search-paths
,多個值用
,
隔開就OK,這個可以檢視官方文檔。
建立啟動類,使用注解
@EnableConfigServer
開啟服務
@SpringBootApplication
@EnableConfigServer //開啟服務配置
public class SpringCloudConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudConfigServerApplication.class,args);
}
}
建立一個用戶端,當然也可以直接用其他的項目操作,注冊中心,zuul,provide等等,除了讀取配置的服務端,都是可以的,單獨建立一個是為了簡單的配置一下,就可以明了的看到;
依賴:
<!-- config-client 用戶端依賴 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
其他的依賴根據項目的類型添加自己的依賴。
建立兩個配置檔案,一個
application.yml
,另一個
bootstrap.yml
;bootstrap裡面放連接配接讀取服務端配置資訊:
#系統級餓配置檔案
spring:
cloud:
config:
name: application #從git上讀取的資源檔案名稱,不需要字尾
label: master #從git哪個分支擷取
profile: dev #讀取哪個配置
uri: http://localhost:8888 #擷取資源配置檔案的服務端通路路徑
application裡面隻用指定一下自己的用戶端的名稱,畢竟是微服務,各自的服務都要有一個自己的名字:
spring:
application:
name: config-client
啟動類正常寫,springboot項目嘛,不多說。
當然,gitee上還是需要這個配置檔案的:
檔案内容:
spring:
profiles:
active: dev
---
server:
port: 9999
spring:
profiles: dev
application:
name: springcloud-config-dev
---
spring:
profiles: test
application:
name: springcloud-config-test
啟動用戶端,啟動之前要保證之前的項目處于啟動狀态,讀取配置的服務端;
可以看到用戶端連接配接服務端遠端讀取配置檔案了,讀取到了gitee上面給他配置的端口号9999,tomcat預設端口号8080,如果沒有斷區到,端口号是8080。自己可以把gitee的配置檔案修改一下,嘗試嘗試。
其他的微服務和這個用戶端一樣,同樣的操作流程和配置,這裡就不一一替換了,自己可以嘗試操作;