*** springcloud相關maven依賴介紹:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>*********</artifactId>
</dependency>
spring-cloud-starter-parent 具備spring-boot-starter-parent同樣功能並附加Spring Cloud的依賴
spring-cloud-starter-config 默認的配置服務依賴,快速自動引入服務的方式,端口8888
spring-cloud-config-server/client 用戶自定義配置服務的服務端/客戶端依賴
spring-cloud-starter-eureka-server 服務發現的Eureka Server依賴
spring-cloud-starter-eureka 服務發現的Eureka客戶端依賴
spring-cloud-starter-hystrix/zuul/feign/ribbon 斷路器(Hystrix),智能路有(Zuul),客戶端負載均衡(Ribbon)的依賴
angular-ui-router 頁面分發路由依賴
*** springcloud相關使用註解:
@EnableEurekaServer ->代表該啓動類是一個eureka server端,也就是註冊中心
@EnableEurekaClient ->代表該啓動類是一個eureka client端,也就是服務生產者/消費者
@EnableDiscoveryClient ->代表該服務是一個可被發現的服務提供者
@LoadBalanced -> 代表ribbon的負載均衡開啓
@RibbonClient -> 代表自定義ribbon的負載均衡規則機制
@HystrixCommand(fallbackMethod = "deptGetById_GET") -> 服務熔斷機制,並指定熔斷處理方法
@EnableCircuitBreaker -> 表示某客戶端開啓服務熔斷機制
***相關概念介紹 :
- restTempl調用:springcloud是基於rest風格進行調用的微服務框架,所以,我們可以通過搭建一個生產者工程,提供增刪改查的API接口,在消費者中,通過注入restTemplate的bean的方式,調用生產者啓動的API接口,來完成消費者與生產者之間的相互調用(並沒有使用到eurake註冊中心)restTemplate是一個spring提供的用於訪問客戶端模板工具類
- lombok工具的使用: 能夠對實體類自動生成get、set等方法,通過註解的方式完成編譯
目錄
3. actuator與註冊中心微服務信息完善(spring-boot-starter-actuator(健康監控)配置和使用)
5. 服務的發現Discovery(對外暴露微服務的信息,提供API接口)
一:微服務簡介
1. 什麼是微服務?
拆分系統,一個模塊一個微服務,之間通過HTTP的restful風格進行互相調用。
2. 微服務的優缺點?
優點:降低了系統之間的耦合、增加了系統的高併發可用、減少維護成本、增加了代碼的複用性。
缺點:增加了資源使用成本...
3. 微服務技術棧
一個分佈式的微服務架構,應該是由多種維度(技術)組合搭配而成的一個完整的架構,都有哪些維度組成?
服務開發技術 | spring、springboot、springMVC... |
服務配置與管理 | Netflix公司的Archaius、阿里的diamond... |
服務註冊與發現(z註冊中心) | Eureka、zookeeper、consul... |
服務調用(服務之間通訊方式) | restFul風格、RPC、GRPC |
服務熔斷器(容災處理) | Hystrix、Envoy等 |
負載均衡 | Ribbon、nginx等 |
服務接口調用(消費者調用生產者的方式) | Feign(基於ribbon封裝)等 |
消息隊列 | kafka、rabbitMQ、activeMQ等 |
服務配置中心 | SpringCloudConfig、Chef等 |
服務路由網關(通過配置路由訪問服務) | Zuul等 |
服務監控中心 | zabbix、nagios、metrics、spectator等 |
全鏈路追蹤(追蹤服務之間互相調用等情況) | zipkin、brave、dapper等 |
服務部署 | Docket、openStack、kubernetes |
事件消息總線 | Spring Cloud Bus |
數據流操作開發包 | Spring Cloud Stream |
springcloud微服務架構,是一整套的微服務解決方案,通過各個不同的維度功能技術,將一個有一個的springboot服務工程連接起來,互相調用,從而形成帶有集羣的分佈式服務系統。
二:eureka註冊中心
1. 創建註冊中心工程
1.1 引入pom文件
<!--eurekaServer註冊中心所需要的依賴jar包-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
1.2 創建application配置文件
1.2.1. 設置端口號
1.2.2. 設置eureka的服務地址以及是否發現該服務
1.2.3. 設置註冊中心地址
application.yml文件配置:
server:
port: 8001
eureka:
instance:
hostname: localhost
client:
register-with-eureka: false #false表示不想註冊中心註冊自己
fetch-registry: false #false表示自己就是註冊中心,不需要去檢索註冊中心上的服務
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eurekaServer/ #設置客戶端(消費者與生產者)進行註冊和查詢服務的地址
1.3 通過@EnableEurekaServer在啓動類中標誌該服務是一個註冊中心,完成啓動
2. eureka註冊中心集羣設置
設置多個eureka集羣,使服務架構變的更加高可用,能夠更好的適應高併發的場景,本例設置三個註冊中心集羣8001/8002/8003
設置方法過程:
2.1 創建多個eureka子工程,並copy啓動類和配置文件。主要修改:
在8001端口的eurekayml文件中:添加其他集羣註冊中心地址
eureka.client.service-url.defaultZone:
http://localhost:8002/eureka/,http://localhost:8003/eureka/
在8002端口的eurekayml文件中:添加其他集羣註冊中心地址
eureka.client.service-url.defaultZone:
http://localhost:8001/eureka/,http://localhost:8003/eureka/
在8003端口的eurekayml文件中:添加其他集羣註冊中心地址
eureka.client.service-url.defaultZone:
http://localhost:8001/eureka/,http://localhost:8002/eureka/
2.2 客戶端訪問eureka時。同樣defaultZone註冊中心地址設置爲多個:
eureka.client.service-url.defaultZone:
http://localhost:8001/eureka/,http://localhost:8002/eureka/,http://localhost:8003/eureka/
2.3 註冊中心集羣設置完成,此時發佈一個服務後,三個註冊中心均會被註冊進去。
3. eureka與zookeeper註冊中心的區別
3.1 兩者之間最大的區別:eureka是AP(可用性,分區容錯性)、zookeeper是CP(數據強一致性,分區容錯性)
3.2 分佈式架構的設計都圍繞着一個原則:C(一致性)A(可用性)P(分區容錯性)
三:eurekaClient:(服務註冊與發現)
本文將創建一個7001/7002/7003端口的服務提供項目爲客戶端提供服務,三個端口均爲相同項目,設置爲不同端口意爲將該服務設置爲集羣。
1. 引入主要pom文件
<!--表示該服務是eureka client的客戶端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
2. 創建application配置文件
2.1 設置端口;
2.2 申明註冊中心地址
2.3 業務所需配置(mybatis/jdbc等信息)
2.4 同樣的配置創建三個項目,設置不同端口號,設置不同的eureka.instance.instance-id
server:
port: 7001
#mybatis的配置文件地址、實體類、映射文件mapper路徑
mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml
type-aliases-package: com.gugui.PO
mapper-locations: classpath:mybatis/mapper/**/*.xml
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/spring_cloud?characterEncoding=UTF-8
username: root
password: root
driver-class-name: org.gjt.mm.mysql.Driver
type: com.alibaba.druid.pool.DruidDataSource
dbcp:
dbcp2:
min-idle: 5
max-total: 5
initial-size: 5
max-wait-millis: 2000
application:
name: producer-dept #服務名
eureka:
client:
service-url:
defaultZone: http://www:8001/eureka/
#註冊到eureka上時,該服務顯示的別名
instance:
instance-id: produck-7001
3. actuator與註冊中心微服務信息完善(spring-boot-starter-actuator(健康監控)配置和使用)
本步驟設置該服務在註冊中心上的別名以及點擊該服務別名後,顯示服務提供者的IP以及端口等詳細信息,有助於在同一個註冊中心,註冊了N個服務和集羣,方便區分方便定位
在只設置了instance-id時,註冊中心截圖如下:
3.1 打開在註冊中心顯示服務本身IP及端口
eureka.client.instance.prefer-ip-address=true。此時點擊服務名,即可顯示IP
3.2 如何點擊服務名,進入詳情頁?
在不設置時,點擊服務名進去會報錯。此時就需要(健康監控)配置和使用相關內容了
添加jar包:
<!--spring-boot-starter-actuator(健康監控)配置和使用-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
在client微服務的yml文件中,添加info信息:
#註冊中心頁面訪問時,點擊微服務連接後進行顯示的信息
info:
appName: produck-7001
companyName: kangce
buildArtifactId: $project.artifactId$
version: $project.version$
其中$project.artifactId$可以通過maven中pom文件設置build規則設置讀取
此時,點擊註冊中心的鏈接後,會以json形式顯示這些信息
4. eureka的自我保護機制
如果服務在更換 名稱或者長時間沒有訪問時,註冊中心頁面就會出現如此報錯,不用擔心,並不是真正的報錯了,而是eureka的自我保護機制生效了。並不會影響使用。
Eureka Server 在運行期間會去統計心跳失敗比例在 15 分鐘之內是否低於 85%,如果低於 85%,Eureka Server 會將這些實例保護起來,
讓這些實例不會過期,但是在保護期內如果服務剛好這個服務提供者非正常下線了,此時服務消費者就會拿到一個無效的服務實例,此時會調用失敗,
對於這個問題需要服務消費者端要有一些容錯機制,如重試,斷路器等。
我們在單機測試的時候很容易滿足心跳失敗比例在 15 分鐘之內低於 85%,這個時候就會觸發 Eureka 的保護機制,
一旦開啓了保護機制,則服務註冊中心維護的服務實例就不是那麼準確了,此時我們可以使用eureka.server.enable-self-preservation=false(在註冊中心服務eureka-server的yml文件中)來關閉保護機制,這樣可以確保註冊中心中不可用的實例被及時的剔除(不推薦)。
當eureka在檢測服務時,如果超過了心跳檢測下限,就會啓動自我保護機制,不會強制下線連接失敗或者超時的服務,設計原則就是:寧可保留錯誤的服務註冊信息,也不盲目註銷任何可能健康的服務實例。
5. 服務的發現Discovery(對外暴露微服務的信息,提供API接口)
主要使用工具類:DiscoveryClient(org.springframework.cloud.client.discovery.DiscoveryClient)
作用:作用在服務提供者中,表示該 服務可以被發現,調用者可以獲取該服務的任何詳細信息
用法:
5.1 在需要 被發現的服務中,創建服務發現接口,通過DiscoveryClient來將服務的信息進行接口返回:
@RequestMapping(value = "/dept/discovery", method = RequestMethod.GET)
public Object discovery() {
// 獲取註冊中心中所有的服務名
List<String> allList = discoveryClient.getServices();
//獲取服務名稱是PRODUCER_7001的所有微服務集合
List<ServiceInstance> list = discoveryClient.getInstances("PRODUCER_7001");
for (ServiceInstance ser : list) {
System.out.println(ser.getHost() + " " + ser.getServiceId() + " " + ser.getPort() + " " + ser.getUri());
}
return this.discoveryClient;
}
5.2 接口寫好之後,在需要被發現的服務啓動類中,添加註解:@EnableDiscoveryClient
5.3 訪問該服務的該地址,即可訪問該服務的詳細信息
介紹完springcloud的註冊中心創建以及服務提供者的創建之後,就需要進行服務調用了。
兩種服務調用的方式:ribbon與feign
①:ribbon是通過ribbon+restTemplate的方式完成服務的調用,可通過註冊中心直接使用【服務名】+【接口地址】進行訪問微服務,類似於面向服務編程,缺點是如果一個服務被多出調用,則我們還需要將這種方式進行進一步自定義封裝,
②:而feign則直接設置成了面向接口的方式進行微服務的訪問,解決了這一問題。③:ribbon:ribbon+restTempalte
feign:接口+註解
四:ribbon負載均衡
源碼地址:https://github.com/Netflix/ribbon
1. 負載均衡LB(Load Balance)簡介:
ribbon是基於Netflix Ribbon 實現的一套【客戶端】的【負載均衡工具】
1.1 通過一定的負載均衡算法(簡單輪詢等),來完成服務的調用;
1.2 負載均衡的方式:
集中式LB:硬件的方式,在客戶端和服務端之間,通過硬件按照一定的策略完成轉發,從而達到負載均衡: 客戶端 -> 硬件(F5等) -> 服務端
進程內LB:將負載均衡邏輯算法集成在消費方,有消費方自主決定調用什麼服務。ribbon就是屬於進程內LB。
2. ribbon初步配置:
2.1 修改pom文件添加依賴:
<!--表示該服務是eureka client的客戶端-->
<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>
<!--ribbon的依賴包-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
2.2 修改yml文件,在消費端連接eureka註冊中心集羣,不再簡單的通過restTemplate直接轉發訪問服務提供者了,而是通過eureka通過負載均衡完成訪問。
eureka:
client:
register-with-eureka: false #不向註冊中心註冊自己
fetch-registry: true #但是需要從註冊中心讀取查詢服務
service-url:
defaultZone: http://eureka.8001.com:8001/eureka/, http://eureka.8001.com:8001/eureka/, http://eureka.8001.com:8001/eureka/
2.3 restTemplate創建Bean時添加註解@LoadBalanced,表示通過負載均衡的方式進行調用
2.4 啓動類通過@EnableEurekaClient表明,該服務是一個客戶端(消費者)
2.5 調用方式:由原來的通過直接明確的IP地址通過restTemplate的方式進行調用,修改爲通過微服務的服務名進行調用的方式,從而達到消費端通過註冊中心調用提供端
// public static final String URL_PRX = "http://localhost:7001";
public static final String URL_PRX = "http://producer-7001"; //通過服務名,來完成服務的調用
2.6 啓動eureka集羣,啓動服務提供者,啓動客戶端,進行調用測試
3. ribbon的負載均衡調用
3.1 當提供者部署了集羣之後,ribbon如果沒有特別設置,會按照輪詢的方式進行負載均衡依次調用。
3.2 提供者部署多個服務到註冊中心之後,消費端會調用ribbon的負載均衡算法,進行輪詢訪問服務。算法調用機制如下:
4. ribbon負載均衡的服務調用機制(自帶七種方式)
ribbon在沒有設置的情況下,默認調用輪詢方式進行調用服務,可以通過註冊bean的方式進行配置ribbon調用服務所使用的機制,設置如下:
在服務消費端中:調用ribbon時聲明機制即可
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
/**
*ribbon負載均衡調用方式配置
* 默認是RoundRobinRule
* 需要什麼方式就返回什麼對象即可
* ribbon提供了七中調用方式(當前版本)
*/
@Bean
public IRule getIRule() {
return new RandomRule(); //設置ribbon的負載均衡機制爲隨機調用服務
}
5. ribbon的自定義負載均衡機制設置
5.1 在消費者端啓動類中調用註解:@RibbonClient(name = "PRODUCER-DEPT", configuration = MyRule.class)
表示對於服務名稱爲PRODUCER-DEPT的微服務,負載均衡調用方式遵循MyRule類中規定的方式
注意:該註解使用在啓動類中,並且該註解所定義的自動以機制類,不能放在ComponentScan所掃描的當前包以及子包下,否則我們自定義的配置類就會被所有的ribbon共享,就不能達到對某一個服務進行特殊配置的目的了。
5.2 myRule.java(簡易版)
@Configuration
public class MyRule {
@Bean
public IRule getIRule() {
return new RoundRobinRule(); //設置ribbon的負載均衡機制爲輪詢調用服務
}
}
此處配置的IRule返回對象,不會與restTemplate公共ribbon設置的IRule所返回的類型對象所衝入,因爲公共配置的(也就是能被@ComponentScan所掃描的)bean,是應用於全局的ribbon適用於所有的微服務,本處配置的bean,僅僅只針對RibbonClient註解所指定的服務,進行負載均衡配置
5.3 自定義ribbon負載均衡機制
我們可以自己寫ribbon的負載均衡算法,只需要按照RandomRule類中的方式實現AbstractLoadBalancerRule這個抽象類中的方法,完成自己的算法邏輯,然後再啓動類中指定服務的規則類,實例化時,返回自己定義的實體即可。
步驟如下:
5.3.1 創建MyStyleRule.java 根據git源碼中的 RandomRule類進行修改的,邏輯爲隨即調用,每次調用五遍
/**
* ribbon負載均衡自定義規則
* 規則:隨機調用,並且每次隨機服務被重複調用5次
* 規則可以自定義算法邏輯進行設置
* @Date 2019/9/6 13:44
**/
public class MyStyleRule extends AbstractLoadBalancerRule {
private int index = 0;
private int times = 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) {
return null;
}
/*int index = chooseRandomInt(serverCount);
server = upList.get(index);*/
if(times < 5) {
server = upList.get(index);
times++;
}else {
times = 0;
index = chooseRandomInt(serverCount);
server = upList.get(index);
times++;
}
if (server == null) {
Thread.yield();
continue;
}
if (server.isAlive()) {
return (server);
}
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 iClientConfig) {
}
}
5.3.2 在實例化IRule接口時,返回自己創建的規則類
@Bean
public IRule getIRule() {
/**
* 設置ribbon的負載均衡機制爲輪詢調用服務
*/
return new MyStyleRule();
}
此時,即可完成我們的自定義配置類