有關(guān)Spring Cloud入門知識和配置方式的博客很多。這里就不仿照他們一篇一個模塊的寫了。直接上學(xué)習(xí)和總結(jié)的代碼。
talk is cheap show you the code
github 地址:https://github.com/xiaemperor/springcloud
最近有朋友發(fā)我郵件說注冊中心mvn之后的war包不能在tomcat中用。注冊中心實際生產(chǎn)使用時,請直接下載官方war包進行部署。http://mvnrepository.com/artifact/com.netflix.eureka/eureka-server
Spring Cloud實際應(yīng)用中,最核心的部分是hystrix斷路器,feign聲明式調(diào)用和zuul網(wǎng)關(guān)。其他模塊較簡單,也常會有其他技術(shù)來替代。
準(zhǔn)備工作:啟動注冊中心
先拋坑:所有程序啟動順序請遵循:1.注冊中心。2.服務(wù)提供端。3.服務(wù)消費端。否則可能有請求不通的情況。
注冊中心為高可用模式 代碼位置:
eureka-a
eureka-b
同時啟動a和b 。默認使用application-dev.yml 中的配置。兩個eureka-server互相注冊。下面所有demo將使用此注冊中心,啟動了不用關(guān)了。各模塊間的端口可能會有沖突,所以換模塊測試了最好關(guān)閉當(dāng)前模塊的application再測-
注冊中心查看:http://127.0.0.1:8001或http://127.0.0.1:8002 可看到兩個注冊中心App都在了。
注冊中心互相注冊,形成高可用狀態(tài)
使用eureka和zookeeper做注冊中心的區(qū)別:eureka保證的是AP zookeeper保證的是CP.
第一部分:服務(wù)提供者、消費者
-
服務(wù)提供者 代碼位置:
spring-cloud-01-provider
啟動它。PS:由于注冊中心為高可用故注冊在上面的服務(wù)需要配置所有注冊中心地址:
eureka:
client:
service-url:
##高可用配置
defaultZone: http://127.0.0.1:8001/eureka/,http://127.0.0.1:8002/eureka/
-
服務(wù)消費者 代碼位置:
spring-cloud-01-consumer
啟動它。同服務(wù)提供者。Spring Cloud的提供者和消費者沒有區(qū)別,他們的角色可以互相轉(zhuǎn)換。這點和dubbo需要指定不同。 -
程序啟動后的狀態(tài): 注冊中心狀態(tài)
- 此時訪問消費端的 http://localhost:7002/consumer/getByAppName 和 http://localhost:7002/consumer/getByUrl 可看到調(diào)用成功。區(qū)別:由于/getByAppName啟用了LoadBalance 。需要從注冊中心讀取application的name來進行調(diào)用。而/getByUrl是純粹的http url的調(diào)用,沒有從注冊中心獲取注冊列表。
第二部分:Ribbon負載均衡和Retry重試機制
啟動服務(wù)集群
spring-cloud-02-ribbon-client-1
和spring-cloud-02-ribbon-client-2
啟動消費者
spring-cloud-02-ribbon-request
查看eureka控制臺,保證都已注冊成功。
調(diào)用消費者API http://localhost:7003/get 發(fā)現(xiàn)交替返回兩個服務(wù)端的數(shù)據(jù)。負載均衡實現(xiàn)
-
重試機制。重試機制中的坑:只使用ribbon組件的話,ConnectTimeout和ReadTimeout是不起作用的
client-service: ## service的application name ribbon: ## 只使用ribbon組件下面這個配置 是不起作用的!!~~~ ConnectTimeout: 250 # 鏈接超時時間 ReadTimeout: 3000 # 處理超時時間 OkToRetryOnAllOperations: true #是否對所有的請求都進行重試 MaxAutoRetriesNextServer: 1 # 重試時切換實例的次數(shù) MaxAutoRetries: 5
需要在RestTemplate中傳入配置好ConnectTimeout和 ReadTimeout等參數(shù)的HttpComponentsClientHttpRequestFactory來讓重試生效。
-
重試機制測試 在上面1、2條的基礎(chǔ)上,再啟動
spring-cloud-02-ribbon-retry
請求 http://localhost:7004/retry 會發(fā)現(xiàn)請求了六次client-1之后,再請求了一次client-2.并返回了ret: client 2
可從配置的參數(shù)和client-1的代碼中解釋這個重試的現(xiàn)象。custom: rest: connect-timeout: 1000 connection-request-timeout: 1000 read-timeout: 3000 #讀取超時時間3s client-service: ## service的application name ribbon: OkToRetryOnAllOperations: true #是否對所有的請求都進行重試 MaxAutoRetriesNextServer: 1 # 重試時切換實例的次數(shù) MaxAutoRetries: 5 #一個實例中最大重試次數(shù)
@RequestMapping(value="/retry", method = {RequestMethod.GET}) public String retry(){ System.err.println("client 1 call ..........."); ///client 睡眠 6s 超過了配置的響應(yīng)等待3s。故會進行重試。超過五次后請求實例切換為client-2 try { Thread.sleep(6000); } catch (InterruptedException e) { e.printStackTrace(); } return "client 1"; }
6次請求
注意:由于有負載均衡,可能直接請求到了client2端,那就直接返回沒有觸發(fā)重試,可再請求一次,請求到client1里面,可看到連續(xù)的retry
第三部分:Hystrix 斷路器
由于hystrix 篇幅較大,故單獨成文,請見:
Spring Cloud Hystrix 斷路器
第四部分:Feign 聲明式服務(wù)調(diào)用
Feign 內(nèi)部集成了Ribbon和Hystrix
先后啟動生產(chǎn)者spring-cloud-04-feign-provider
、消費者spring-cloud-04-feign-consumer
調(diào)用消費端API
http://localhost:7002/hello
http://localhost:7002/hi (帶有Hystrix降級)
注意:在寫斷路器的實現(xiàn)HelloFeignClientHystrixFallback
時,不要忘了把它注入到spring容器中。
第五部分:Zuul 網(wǎng)關(guān)權(quán)限
Zuul 集成了Hystrix和Ribbon
核心功能:路由和權(quán)限的過濾驗證功能。所有請求先經(jīng)過zuul來進行路由到各個子服務(wù)系統(tǒng)中,token的驗證也往往放在這一層。這樣可以讓各個微服務(wù)的只關(guān)心自己的業(yè)務(wù)。
1.啟動兩個服務(wù) spring-cloud-05-hello-service
spring-cloud-05-luck-service
2.啟動網(wǎng)關(guān) spring-cloud-05-gateway
3.確認兩服務(wù)一網(wǎng)關(guān)都已開啟 http://localhost:8001/
zuul:
routes:
api-hello:
path: /hello-service/**
service-id: hello-service
api-luck:
path: /luck-service/**
service-id: luck-service
- 網(wǎng)關(guān)的配置如上,直接訪問網(wǎng)關(guān)服務(wù)所配置的path,將由網(wǎng)關(guān)路由到hello和luck服務(wù)。http://localhost:5000/luck-service/luck
http://localhost:5000/hello-service/hello - 訪問以上地址時會發(fā)現(xiàn)提示--------no token !--------- 那是因為在gateway里面配置了過濾器,用來做token權(quán)限的驗證。只需繼承ZuulFilter,并重寫其中的方法,注入到spring容器中。具體的過濾參數(shù)和方法見CustomAuthFilter類的注釋中。
-
CustomAuthFilter過濾器中驗證的為header中的token參數(shù)“123456”。故需要postman來進行測試。
POSTMAN
若更換token的value。也將驗證失敗。
- 將請求換成http://localhost:5000/luck-service/luck 將得到另外一個服務(wù)luck的返回串。
- 可在gateway中定義熔斷器。實現(xiàn)ZuulFallbackProvider接口,并注入即可。Demo中對luck-service做了熔斷。停止luck-service服務(wù),再請求時,會發(fā)現(xiàn)收到了熔斷器指定的返回內(nèi)容。
@Component
public class LuckServiceZuulFallBackProvider implements ZuulFallbackProvider {
// 指定要段熔的服務(wù)名字:appName
@Override
public String getRoute() {
return "luck-service";
}
@Override
public ClientHttpResponse fallbackResponse() {
return new ClientHttpResponse(){
@Override
public InputStream getBody() throws IOException {
return new ByteArrayInputStream("service error.... retry..".getBytes());
}
//response的響應(yīng)頭信息
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
//自定義響應(yīng)的狀態(tài)
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.BAD_REQUEST;
}
//自定義響應(yīng)的狀態(tài)碼
@Override
public int getRawStatusCode() throws IOException {
return this.getStatusCode().value(); //400
}
//狀態(tài)文本信息
@Override
public String getStatusText() throws IOException {
return this.getStatusCode().getReasonPhrase();
}
@Override
public void close() {
}};
}
}
第六部分:Config 配置中心
分布式的系統(tǒng)需要有一個統(tǒng)一的配置中心以方便管理
本部分Demo使用github上的在線配置方式。在本項目的config文件夾中
- 啟動配置中心
spring-cloud-06-config-server
。其配置文件如下:
spring:
application:
name: config-server
cloud:
config:
server:
git:
uri: https://github.com/xiaemperor/springcloud # prefix
search-paths: config #相對路徑
server:
context-path: /
port: 4000
- 啟動使用該配置的服務(wù)端
spring-cloud-06-config-client
服務(wù)端的配置路徑為上面的配置中心地址。服務(wù)名字與配置文件的前半部分一致。這里都為evn
。遵循了springboot約定優(yōu)于配置的原則。
配置內(nèi)容為:
spring:
application:
name: evn # name遵循配置文件evn-${value}.properties的約定
cloud:
config:
uri: http://localhost:4000/ #表示配置中心的地址
profile: dev
label: master
management:
security:
enabled: false #禁止之后動態(tài)刷新。/refresh就不需要密碼
server:
context-path: /
port: 7001
啟動之后訪問http://localhost:7001/from 會發(fā)現(xiàn)返回了git-dev
。正好是evn-dev.properties中的內(nèi)容
- 動態(tài)刷新:若更改了github上的配置文件信息,需要不重啟服務(wù)來刷新時,需要用post方式請求服務(wù)端的refresh接口http://localhost:7001/refresh。但是這樣的方式有一個缺點,要對每一個用到配置的服務(wù)進行該操作,系統(tǒng)龐大時,沒法維護。這時可以使用消息總線Bus模塊來進行動態(tài)刷新。
第七部分:Bus 消息總線
Bus支持的MQ為Kafka和RabbitMQ。本Demo使用RabbitMQ,需要自行安裝RabbitMQ進行測試。關(guān)于RabbitMQ的安裝見:
RabbitMQ安裝方式地址
- 啟動配置中心
spring-cloud-07-bus-config-server
Rabbitmq配置如下:
spring:
rabbitmq:
host: 172.16.0.96
port: 5672
username: guest
password: guest
management:
security:
enabled: false ##注意,此處需要配置為false,否則請求刷新API無效
- 啟動客戶端
spring-cloud-07-bus-config-client
Rabbitmq的配置和上面一致。 - 請求http://localhost:7001/from 這時的內(nèi)容為git-dev
- 改變github上的配置文件evn-dev.properties 中的內(nèi)容,用POST方式請求配置中心API http://localhost:4000/bus/refresh
- 再次請求http://localhost:7001/from 發(fā)現(xiàn)內(nèi)容已變?yōu)楦暮蟮膬?nèi)容。
此方式只需要管理配置中心這一端。不需要像第六部分中的config那樣在客戶端上刷新。這才是在真正的微服務(wù)項目中使用的方式
PS: 具體的完整項目中,一般使用Zuul+Feign(集成了Ribbon、Hystrix)+ Eureka + Config