簡單的springcloud(採用的版本爲:Finchley.RELEASE springboot版本爲:2.0.3.RELEASE):
1.創建註冊中心Eureka-server
1.1:需要的pom座標:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
1.2:在啓動類上加註解:
@EnableEurekaServer :標識是一個註冊中心
1.3:添加配置文件 application.yml/application.properties
server:
port: 8761 #註冊中心端口號
eureka:
instance:
hostname: localhost
client:
#聲明自己是一個服務
registerWithEureka: false
fetchRegistry: false
serviceUrl: #註冊中心地址
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
注:註冊中心完成
2.創建客戶端(消費者,一個簡單的客戶端):
2.1:需要的pom座標:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2.2:啓動類添加註解:
@EnableEurekaClient (注:這個註解可加可不加不影響) 聲明這是一個客戶端(消費着)
2.3:修改配置文件:
server:
port: 8771 #客戶端的端口號
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/ #指定註冊中心的地址
spring:
application:
name: product-client #客戶端的名稱,必選項,註冊中心需要用到,註冊中心使用的就是此名稱
3.客戶端間的調用:
3.1:選擇feign和ribbon feign中已經集成好了ribbon. 所以選擇feign
3.1.1:導入feign對應的pom座標
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
3.1.2:如果是ribbon的話,對應的pom座標
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
3.1.2.1:還需要在啓動類中注入一個Bean,用於調用其他服務並開啓負載均衡策略
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
3.1.2.2:調用方式:
a.注入RestTemplate類。
b.使用RestTemplate調用對應的方法。
注:底層可查看@LoadBalanced。實際使用的是LoadBalancerClient這個類。內部通過這個去註冊中心查找到對應的服務的所有節點,然後根據對應的負載均衡策略進行選擇節點。然後返回給最外層,也就是選擇好某個節點後進行調用,默認的負載均衡策略是輪詢策略。可以配置負載均衡策略。配置完成後使用feign同樣生效
#自定義負載均衡策略
product-client: #服務名稱
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule 隨機策略可查看IRule類的子類。
注:ribbon的默認超時時間爲: 60秒
設置ribbon的超時和重試設置
ribbon:
ReadTimeout: 3000
ConnectTimeout: 3000
MaxAutoRetries: 1 #同一臺實例最大重試次數,不包括首次調用
MaxAutoRetriesNextServer: 1 #重試負載均衡其他的實例最大重試次數,不包括首次調用
OkToRetryOnAllOperations: false #是否所有操作都重試 慎用
根據上面的參數計算重試的次數:MaxAutoRetries+MaxAutoRetriesNextServer+(MaxAutoRetries *MaxAutoRetriesNextServer) 即重試3次 則一共產生4次調用
如果在重試期間,時間超過了hystrix的超時時間,便會立即執行熔斷,fallback。所以要根據上面配置的參數計算hystrix的超時時間,使得在重試期間不能達到hystrix的超時時間,不然重試機制就會沒有意義
hystrix超時時間的計算: (1 + MaxAutoRetries + MaxAutoRetriesNextServer) * ReadTimeout 即按照以上的配置 hystrix的超時時間應該配置爲 (1+1+1)*3=9秒
Hystrix的超時計算規則應該是 Hystrix的超時時間=Ribbon的重試次數(包含首次)*(ribbon.ReadTimeout+ribbon.ConnectTimeout),如果以你的配置爲例Hystrix的超時配置應該是=4*(3000+3000)=24000,即24秒。
當ribbon超時後且hystrix沒有超時,便會採取重試機制。當OkToRetryOnAllOperations設置爲false時,只會對get請求進行重試。如果設置爲true,便會對所有的請求進行重試,如果是put或post等寫操作,如果服務器接口沒做冪等性,會產生不好的結果,所以OkToRetryOnAllOperations慎用。
如果不配置ribbon的重試次數,默認會重試一次
注意:
默認情況下,GET方式請求無論是連接異常還是讀取異常,都會進行重試
非GET方式請求,只有連接異常時,纔會進行重試
3.1.3:使用feign調用接口(基於接口實現feign);
a.創建一個接口類,在接口類上加註解@FeignClient然後值有name 和 fallback name:是需要調用的服務的名稱
fallback:失敗的回調(熔斷處理,相當於家裏的用電,當用電到達某一閾值,電路就會自動跳閘,從而保護整個電路。),失敗的一個降級處理。寫的是一個類。每一個方法對應一個方法,此方法參數類型和返回值必須和接口的一樣。如果使用feign調用的接口失敗了,則會進入這個方法,可以在內部進行一些操作並且返回一些虛擬數據,保證後續服務不會崩潰(解決雪崩效應)。
b.使用feign調用接口的時候,如果有參數 基本類型傳遞使用@RequestParm 如果是對象的話,要使用@RequestBody傳遞。
注:使用Feign調用接口分兩層,ribbon的調用和hystrix的調用,所以ribbon的超時時間和Hystrix的超時時間的結合就是Feign的超時時間。一般情況下都是ribbon的超時時間(<)hystrix的超時時間(因爲涉及到ribbon的重試機制因爲ribbon的重試機制和Feign的重試機制有衝突,所以源碼中默認關閉Feign的重試機制。
hystrix的默認超時時間是一秒,配置了hystrix的超時時間必須還要配置ribbon的超時時間,否則會有問題。
在使用 Ribbon 時,只需要配置 Hystrix 的超時時間就可以生效,不需要額外配置 Ribbon 的超時時間
記錄一個異常,就是第一次啓動完成後,調用服務的時候總是在第一次出現超時的情況。這是因爲懶加載的特性,在第一次用到的時候纔去加載一些東西。而加載這些東西需要耗時,可能耗時的時候就已經出發了熔斷/降級處理。
解決辦法有兩種 1.設置超時時間:全局設置超時時間爲60秒
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 60000
# 設置ribbon超時時間
ribbon:
ReadTimeout: 20000
ConnectTimeout: 20000
2. 配置立即加載。不過還需要配置所有的客戶端服務
ribbon:
eager-load:
enabled: true
clients: distribution, material-product, outer-data #客戶端服務
3.2:引入Hystrix(斷路器,熔斷)
a.引入所需要的pom座標文件
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
b.啓動類開啓斷路器,加註解//@EnableHystrix
@EnableCircuitBreaker 這兩個都可以,上邊的註解裏包含了下邊的註解。標識啓用斷路器功能。
c.配置文件配置斷路器。需要配置表示feign開啓hystrix 和 hystrix的超時時間配置。hystrix的默認超時時間時1秒。
feign:
#開啓斷路器
hystrix:
enabled: true
#修改調用超時時間
client:
config:
default:
connectTimeout: 3000
readTimeout: 2000
#修改hystrix的調用超時時間配置
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 4000
d.使用:
1. 在方法上加註解@HystrixCommand key有fallbackMethod 表示出現異常時要熔斷的方法。
ps:訪問該接口時報錯了或者超時了,會進入fallbackMethod設置的對應的方法內部。進行一些記錄操作返回一些給用戶的提示等操作。
注:1.feign/ribbon的超時時間一定要大於hystrix的超時時間,因爲只要訪問到接口,feign/ribbon 和 hystrix 的計時器就會同步啓動,去計算。如果沒有到達feign/ribbon的超時時間,但是到達了hystrix的超時時間,儘管沒有報錯,但是還是會進入的hystrix的熔斷方法。
2.設置的fallbackMethod的方法的請求參數和返回結果要和@HystrixCommand標記的接口的一致,否則會出問題。
3.3:引入hystrix儀表盤 hystrix-dashboard。一個健康檢查。
hystrix dashboard 儀表盤解釋:健康檢查監控hystrix的各項指標信息,檢查接口調用的成功與失敗記錄等信息,如果失敗率超過50%,會開啓熔斷,後續請求將不會請求接口。
a:引入pom文件:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<!-- 此依賴是打開 Actuator 作用 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
b.啓動類加註解:
@EnableHystrixDashboard
c.配置文件增加endpoint,開啓所有訪問權限,放行了所有端點,(這樣貌似不安全,默認 Actuator 只暴露了2個端點,heath 和 info)
management:
endpoints:
web:
exposure:
include: "*"
注:這個是用來暴露 Actuator 的所有端點的,這一點很重要,不配置你的 Hystrix Dashboard 會出現 Unable to connect to Command Metric Stream 的問題
d.訪問入口
http://localhost:8781/hystrix
Hystrix Dashboard輸入: http://localhost:8781/actuator/hystrix.stream
3.4:引入鏈路追蹤:seluth,其主要作用是做一個日誌的埋點,出現錯誤時可以根據日誌的埋點id找到對應的信息/報錯位置。Sleuth可以與日誌框架Logback、SLF4J輕鬆地集成,
a.引入pom文件
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
b.配置文件:
spring:
#配置採樣百分比,開發環境可以設置爲1,表示全部,生產就用默認(比如請求100次我記錄百分之多少/多少次。1代表全部,根據實際情況調節)
sleuth:
sampler:
probability: 1
b. 集成可直接使用,但是需要加log否則控制檯不會輸出日誌,本示例使用的是log4j
private final Logger log = LoggerFactory.getLogger(getClass());
log.info("查詢商品");
c.輸出的格式
2020-03-26 16:32:08.117 INFO [product-client,1371c3a83b9b13a5,6ac4a8cc3fbbfa12,true] 1812 --- [nio-8772-exec-2] t.o.p.controller.ProductController: findProductById
2020-03-26 16:32:08.117 INFO [product-client,1371c3a83b9b13a5,6ac4a8cc3fbbfa12,true] 1812 --- [nio-8772-exec-2] t.o.product.service.ProductServiceImpl: 查詢商品
解釋:[]中第一個標識服務的名稱,第二個標識本次請求的id唯一標識(一個請求分配的ID號,用來標識一條請求鏈路。),第三個標識一個工作的基本單元,一個請求可以又多個步驟,而第三個標識每一個步驟。第四個表示 是否要將該信息輸出到類似Zipkin這樣的聚合器進行收集和展示。
3.5:引入zipkin,上邊所提到的聚合器,這裏用於手機每次請求鏈路追蹤所產生的記錄,都在該組件內存在。
a.引入pom文件:
<!--其中已經包含了鏈路追蹤seluth-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
b.配置文件:
spring:
#zipkin服務所在地址
zipkin:
base-url: http://www.oyygke.top:9411/ #該地址表示zipkin所在地址
c.使用:
集成好sleuth和zipkin之後,進行發送請求,然後訪問上邊配置的zipkin的地址,就會看到請求記錄。點擊可查看詳情--整個請求的鏈路是怎麼走的。
3.6:引入網關zuul
a.引入pom文件
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
b.添加註解,開啓網關
@EnableZuulProxy
c.作用
使用網關進行過濾和請求轉發的作用,經過網關轉發到某服務。
d.配置文件
zuul:
routes:
#相當於oyygke映射到order-client
order-client: /oyygke/order/**
product-client: /oyygke/product/**
#忽略product-client,不經過網關
# ignored-services: product-client
#只使用一種方式進行訪問,過濾以client結尾的服務
ignored-patterns: /*-client/**
#處理http請求頭爲空的問題
sensitive-headers:
ribbon: #添加請求超時設置,如果不配置,默認第一次訪問會進入到熔斷的超時處理機制。因爲可能還沒有加載到,第一次加載需要時間,所以需要添加超時時間設置。
ReadTimeout: 6000
ConnectTimeout: 6000
e.使用過濾
i.新建類繼承ZuulFilter,實現其中的方法。相當於一個配置類,記得添加@component。還可以進行限流處理,
//令牌桶 每秒產生多少個令牌 這個需要進行壓測確定可以有多少個令牌
//令牌需要配置到配置文件中更改 多少個網管就是 總數/網關數量
//guava谷歌的框架 限流
private static final RateLimiter RATE_LIMITER = RateLimiter.create(100);
工具類:HttpStatus.TOO_MANY_REQUESTS.value() //表示請求過多。
示例:過濾token,表示 如果訪問該接口沒有token則過濾掉直接返回對應的錯誤提示信息。
@Component
public class LoginFilter extends ZuulFilter {
/**
* 過濾器的類型 前置還是後置
* @return
*/
@Override
public String filterType() {
return PRE_TYPE; //表示請求接口前執行
}
/**
* 過濾器的一個級別 越小越先執行
* @return
*/
@Override
public int filterOrder() {
return 4;
}
/**
* 是否進行過濾
* @return
*/
@Override
public boolean shouldFilter() {
//全局的上下文對象
RequestContext context = RequestContext.getCurrentContext();
HttpServletRequest request = context.getRequest();
//如果請求路徑包含order,則需要進行過濾驗證
if(request.getRequestURI().contains("/order/")){
return true;
}
return false;
}
/**
* 業務邏輯
* @return
* @throws ZuulException
*/
@Override
public Object run() throws ZuulException {
RequestContext context = RequestContext.getCurrentContext();
HttpServletRequest request = context.getRequest();
String token = request.getHeader("token");
if(StringUtils.isBlank(token)){
token = request.getParameter("token");
}
if(StringUtils.isBlank(token)){
context.setSendZuulResponse(false);
context.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
}
return null;
}
}
3.7:配置中心 config-server/config-client
a.先搭建配置中心服務,config-server 配置中心可以使用 git、碼雲等
b.product-client-dev.yml 配置中心的文件名採用 服務名-後綴命名。dev/test等
c.pom文件引入:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
d.@EnableConfigServer 表示這是一個配置服務中心
e.配置文件
spring:
application:
name: config-server #服務名稱
cloud:
config:
server:
git:
uri: https://gitee.com/xc-rong/spring-cloud.git #碼雲的項目地址
username: xxx #碼雲賬號
password: xxx #碼雲密碼
timeout: 5
default-label: master #使用哪個分支的
f.修改原有服務爲配置中心的客戶端 config-client
i.引入pom文件
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
ii.修改原有的application.yml爲bootstrap.yml /bootstrap.properties
iii.修改配置文件
spring:
application:
name: order-client #服務名稱
cloud:
config:
discovery:
enabled: true #開啓通過服務訪問config-server的功能
service-id: CONFIG-SERVER #配置中心的服務名
#後綴 一個區分 指定環境
profile: dev
#分支的區分,指定分支
label: master
iiii. 將一些配置文件的內容放到填寫的碼雲/git倉庫上。項目啓動的時候會從倉庫拉去配置文件。
注:到此配置完成,可以啓動項目進行查看日誌
g.配置消息總線,用於動態拉取配置,(表示 在碼雲/git倉庫更改配置文件之後,動態拉取不需要重啓項目,但是線下可以這樣,線上的話不建議這樣,因爲沒辦法觀察是否拉取到了最新的配置文件)
i:引入pom文件
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
ii.配置文件添加mq
spring:
application:
name: product-client
rabbitmq:
host: www.oyygke.top
port: 5672
username: guest
password: guest
ii.添加註解,在需要動態更新配置的地方添加註解 @RefreshScope
iii.至此代碼中的內容添加完畢。動態更新的話需要手動的訪問一個地址進行拉取最新配置
http://localhost:2009/refresh #表示每個服務的ip+端口號+refresh 進行刷新,每次只刷新一個服務,多個服務需要調用多次,而且該接口只支持post請求,get請求不支持。
注:必須要有這個pom
<!--開啓監控功能-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
4.完成以後可能會出現的小問題。
註冊中心每個服務的地址可能不是ip+端口號 可能是一串字母+端口號 這樣放到線上是有問題的,需要在配置文件中在加下配置,使註冊中心的地址是ip+端口號
解決辦法:
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
instance-id: ${spring.cloud.client.ip-address}:${server.port} #表示每個服務的ip示自定義id,ip+端口號格式
prefer-ip-address: true #將IP註冊到Eureka Server上