spring cloud
在微服務(wù)架構(gòu)中,業(yè)務(wù)都會(huì)被拆分成一個(gè)獨(dú)立的服務(wù),服務(wù)與服務(wù)的通訊是基于http restful的。
Spring提供了一系列工具,可以幫助開發(fā)人員迅速搭建分布式系統(tǒng)中的公共組件,協(xié)調(diào)分布式環(huán)境中各個(gè)系統(tǒng),為各類服務(wù)提供模板性配置
比如:配置管理,服務(wù)發(fā)現(xiàn),斷路器,智能路由,微代理,控制總線,一次性令牌,全局鎖,主節(jié)點(diǎn)選舉, 分布式session, 集群狀態(tài)等等。
另外說明spring cloud是基于springboot的
既然是管理分布式環(huán)境下的微服務(wù),必然存在服務(wù)的注冊問題。所以我們先從服務(wù)的注冊談起。既然是注冊,必然有個(gè)管理注冊中心的服務(wù)器,各個(gè)在Spring Cloud管理下的Spring Boot應(yīng)用就是需要注冊的client
Eureka? 是一個(gè)服務(wù)注冊和發(fā)現(xiàn)模塊。
啟動(dòng)一個(gè)服務(wù)注冊中心,只需要一個(gè)注解@EnableEurekaServer
eureka是一個(gè)高可用的組件,它沒有后端緩存,每一個(gè)實(shí)例注冊之后需要向注冊中心發(fā)送心跳(因此可以在內(nèi)存中完成),在默認(rèn)情況下erureka server也是一個(gè)eureka client ,必須要指定一個(gè) server
通過eureka.client.registerWithEureka:false和fetchRegistry:false來表明自己是一個(gè)eureka server.
server:
? port: 8761
eureka:
? instance:
? ? hostname: localhost
? client:
? ? registerWithEureka: false
? ? fetchRegistry: false
? ? serviceUrl:
? ? ? defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
服務(wù)提供者 (eureka client)
當(dāng)client向server注冊時(shí),它會(huì)提供一些元數(shù)據(jù),例如主機(jī)和端口,URL,主頁等。Eureka server 從每個(gè)client實(shí)例接收心跳消息。
如果心跳超時(shí),則通常將該實(shí)例從注冊server中刪除。
通過注解@EnableEurekaClient 表明自己是一個(gè)eurekaclient.
需要在配置文件中注明自己的服務(wù)注冊中心的地址spring.application.name,這個(gè)很重要,這在以后的服務(wù)與服務(wù)之間相互調(diào)用一般都是根據(jù)這個(gè)name 。
eureka:
? client:
? ? serviceUrl:
? ? ? defaultZone: http://localhost:8761/eureka/
server:
? port: 8762
spring:
? application:
? ? name: service-hi
Spring cloud有兩種服務(wù)調(diào)用方式,一種是ribbon+restTemplate,另一種是feign。
ribbon是一個(gè)負(fù)載均衡客戶端,可以很好的控制htt和tcp的一些行為。Feign默認(rèn)集成了ribbon。
工程的配置文件指定服務(wù)的注冊中心地址為? eureka的訪問地址
eureka:
? client:
? ? serviceUrl:
? ? ? defaultZone: http://localhost:8761/eureka/
server:
? port: 8764
spring:
? application:
? ? name: service-ribbon
在工程的啟動(dòng)類中,通過@EnableDiscoveryClient向服務(wù)中心注冊;并且向程序的ioc注入一個(gè)bean: restTemplate;
通過@LoadBalanced注解表明這個(gè)restRemplate開啟負(fù)載均衡的功能。
在ribbon中它會(huì)根據(jù)服務(wù)名來選擇具體的服務(wù)實(shí)例,根據(jù)服務(wù)實(shí)例在請求的時(shí)候會(huì)用具體的url替換掉服務(wù)名
通過調(diào)用restTemplate.getForObject()方法調(diào)用業(yè)務(wù)
Feign是一個(gè)聲明式的偽Http客戶端,它使得寫Http客戶端變得更簡單。使用Feign,只需要?jiǎng)?chuàng)建一個(gè)接口并注解。它具有可插拔的注解特性,可使用Feign 注解和JAX-RS注解。Feign支持可插拔的編碼器和解碼器。Feign默認(rèn)集成了Ribbon,并和Eureka結(jié)合,默認(rèn)實(shí)現(xiàn)了負(fù)載均衡的效果。
簡而言之:
Feign 采用的是基于接口的注解
Feign 整合了ribbon
eureka:
? client:
? ? serviceUrl:
? ? ? defaultZone: http://localhost:8761/eureka/
server:
? port: 8765
spring:
? application:
? ? name: service-feign
在程序的啟動(dòng)類加上@EnableFeignClients注解開啟Feign的功能:
定義一個(gè)feign接口,通過@ FeignClient(“服務(wù)名”),來指定調(diào)用哪個(gè)服務(wù)。
斷路器(Hystrix)
在微服務(wù)架構(gòu)中,根據(jù)業(yè)務(wù)來拆分成一個(gè)個(gè)的服務(wù),服務(wù)與服務(wù)之間可以相互調(diào)用(RPC),在Spring Cloud可以用RestTemplate+Ribbon和Feign來調(diào)用。為了保證其高可用,單個(gè)服務(wù)通常會(huì)集群部署。由于網(wǎng)絡(luò)原因或者自身的原因,服務(wù)并不能保證100%可用,如果單個(gè)服務(wù)出現(xiàn)問題,調(diào)用這個(gè)服務(wù)就會(huì)出現(xiàn)線程阻塞,此時(shí)若有大量的請求涌入,Servlet容器的線程資源會(huì)被消耗完畢,導(dǎo)致服務(wù)癱瘓。服務(wù)與服務(wù)之間的依賴性,故障會(huì)傳播,會(huì)對整個(gè)微服務(wù)系統(tǒng)造成災(zāi)難性的嚴(yán)重后果,這就是服務(wù)故障的“雪崩”效應(yīng)。
etflix開源了Hystrix組件,實(shí)現(xiàn)了斷路器模式,SpringCloud對這一組件進(jìn)行了整合。
較底層的服務(wù)如果出現(xiàn)故障,會(huì)導(dǎo)致連鎖故障。當(dāng)對特定的服務(wù)的調(diào)用的不可用達(dá)到一個(gè)閥值(Hystric 是5秒20次) 斷路器將會(huì)被打開。
斷路打開后,可用避免連鎖故障,fallback方法可以直接返回一個(gè)固定值。
在程序的啟動(dòng)類加@EnableHystrix注解開啟Hystrix:
在Service方法上加上@HystrixCommand注解。該注解對該方法創(chuàng)建了熔斷器的功能,并指定了fallbackMethod熔斷方法
Feign中使用斷路器
Feign是自帶斷路器的,在D版本的Spring Cloud中,它沒有默認(rèn)打開。需要在配置文件中配置打開它,在配置文件加以下代碼:
feign.hystrix.enabled=true
需要在FeignClient的Service接口的注解中加上fallback的指定類就行了
基于service-ribbon 改造
在主程序啟動(dòng)類中加入@EnableHystrixDashboard注解,開啟hystrixDashboard:
路由網(wǎng)關(guān)(zuul)
在SpringCloud微服務(wù)系統(tǒng)中,一種常見的負(fù)載均衡方式是,客戶端的請求首先經(jīng)過負(fù)載均衡(zuul、Ngnix),再到達(dá)服務(wù)網(wǎng)關(guān)(zuul集群)
,然后再到具體的服務(wù)統(tǒng)一注冊到高可用的服務(wù)注冊中心集群,服務(wù)的所有的配置文件由配置服務(wù)管理(下一篇文章講述),配置服務(wù)的配置文件放在git倉庫,方便開發(fā)人員隨時(shí)改配置。
Zuul的主要功能是路由轉(zhuǎn)發(fā)和過濾器。路由功能是微服務(wù)的一部分,比如/api/user轉(zhuǎn)發(fā)到到user服務(wù),/api/shop轉(zhuǎn)發(fā)到到shop服務(wù)。zuul默認(rèn)和Ribbon結(jié)合實(shí)現(xiàn)了負(fù)載均衡的功能。
在入口applicaton類加上注解@EnableZuulProxy,開啟zuul的功能:
eureka:
? client:
? ? serviceUrl:
? ? ? defaultZone: http://localhost:8761/eureka/
server:
? port: 8769
spring:
? application:
? ? name: service-zuul
zuul:
? routes:
? ? api-a:
? ? ? path: /api-a/**
? ? ? serviceId: service-ribbon
? ? api-b:
? ? ? path: /api-b/**
? ? ? serviceId: service-feign
首先指定服務(wù)注冊中心的地址為http://localhost:8761/eureka/,服務(wù)的端口為8769,服務(wù)名為service-zuul;以/api-a/ 開頭的請求都轉(zhuǎn)發(fā)給service-ribbon服務(wù);以/api-b/開頭的請求都轉(zhuǎn)發(fā)給service-feign服務(wù);?
zuul不僅只是路由,并且還能過濾,做一些安全驗(yàn)證 ?
@Component
public class MyFilter extends ZuulFilter{
? ? private static Logger log = LoggerFactory.getLogger(MyFilter.class);
? ? @Override
? ? public String filterType() {
? ? ? ? return "pre";
? ? }
? ? @Override
? ? public int filterOrder() {
? ? ? ? return 0;
? ? }
? ? @Override
? ? public boolean shouldFilter() {
? ? ? ? return true;
? ? }
? ? @Override
? ? public Object run() {
? ? ? ? RequestContext ctx = RequestContext.getCurrentContext();
? ? ? ? HttpServletRequest request = ctx.getRequest();
? ? ? ? log.info(String.format("%s >>> %s", request.getMethod(), request.getRequestURL().toString()));
? ? ? ? Object accessToken = request.getParameter("token");
? ? ? ? if(accessToken == null) {
? ? ? ? ? ? log.warn("token is empty");
? ? ? ? ? ? ctx.setSendZuulResponse(false);
? ? ? ? ? ? ctx.setResponseStatusCode(401);
? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ctx.getResponse().getWriter().write("token is empty");
? ? ? ? ? ? }catch (Exception e){}
? ? ? ? ? ? return null;
? ? ? ? }
? ? ? ? log.info("ok");
? ? ? ? return null;
? ? }
}
分布式配置中心(Spring Cloud Config)
在分布式系統(tǒng)中,由于服務(wù)數(shù)量巨多,為了方便服務(wù)配置文件統(tǒng)一管理,實(shí)時(shí)更新,所以需要分布式配置中心組件。在Spring Cloud中,有分布式配置中心組件spring cloud config ,它支持配置服務(wù)放在配置服務(wù)的內(nèi)存中(即本地),也支持放在遠(yuǎn)程Git倉庫中。在spring cloud config 組件中,分兩個(gè)角色,一是config server,二是config client。
Config Server
在程序的入口Application類加上@EnableConfigServer注解開啟配置服務(wù)器的功能
需要在程序的配置文件application.properties文件配置以下:
spring.application.name=config-server
server.port=8888
spring.cloud.config.server.git.uri=https://github.com/forezp/SpringcloudConfig/
spring.cloud.config.server.git.searchPaths=respo
spring.cloud.config.label=master
spring.cloud.config.server.git.username=your username
spring.cloud.config.server.git.password=your password
///參數(shù)詳解
spring.cloud.config.server.git.uri:配置git倉庫地址
spring.cloud.config.server.git.searchPaths:配置倉庫路徑
spring.cloud.config.label:配置倉庫的分支
spring.cloud.config.server.git.username:訪問git倉庫的用戶名
spring.cloud.config.server.git.password:訪問git倉庫的用戶密碼
config client
spring.application.name=config-client
spring.cloud.config.label=master
spring.cloud.config.profile=dev
spring.cloud.config.uri= http://localhost:8888/
server.port=8881