Spring Cloud
代碼地址:https://github.com/jedyang/springCloud
springcloud中文社區(qū)給的定義是微服務架構(gòu)集大成者亚斋,云計算最佳業(yè)務實踐咏瑟。
我的理解是分布式服務全家桶酪捡。
如果你有dubbo或其他分布式框架的使用經(jīng)驗,那么對springcloud提供的特性是很好理解的恩静。
springcloud提供了如下特性:
- 分布式配置
- 服務注冊和發(fā)現(xiàn)
- 路由
- 遠程服務調(diào)用
- 負載均衡
- 斷路器
- 全局鎖
- 主從選舉和集群狀態(tài)
- 分布式消息
主要項目
spring cloud config
springcloud的配置管理項目焕毫,使用git管理∈磺可根據(jù)不同環(huán)境管理不同參數(shù)。也可用于非spring應用循签。
Spring Cloud Netflix
Spring Cloud包含了非常多的子框架级乐,其中,Spring Cloud Netflix是其中一套框架县匠,由Netflix開發(fā)后來又并入Spring Cloud大家庭风科,它主要提供的模塊包括:服務發(fā)現(xiàn)(Eureka),斷路器(Hystrix)乞旦,智能路由(Zuul)和客戶端負載平衡(Ribbon)贼穆。
Spring Cloud Bus
事件總線。使用分布式消息將服務和服務實例聯(lián)系起來兰粉。在集群的狀態(tài)變化傳播中作用很大故痊。
Spring Cloud for Cloud Foundry
讓你的應用同Cloudfoundry進行整合。(Cloudfoundry是最近很熱的開源PaaS云平臺)玖姑。讓你很容易實現(xiàn)SSO(單點登錄)愕秫、OAuth2(一個關(guān)于授權(quán)的開放網(wǎng)絡標準)功能,以及Cloudfoundry的服務分發(fā)器焰络。
Spring Cloud Cloud Foundry Service Broker
提供了一個擴展點戴甩,以便于開發(fā)基于 Cloud Foundry管理的服務分發(fā)器。
Spring Cloud Cluster
主從選舉闪彼√鸸拢基于zookeeper,redis,hazelcast(hazelcast是一個開放源碼集群和高度可擴展的數(shù)據(jù)分發(fā)平臺)缴川,consul(支持多數(shù)據(jù)中心下茉稠,分布式高可用的,服務發(fā)現(xiàn)和配置共享)的抽象和實現(xiàn)二跋。
Spring Cloud Consul
基于Consul實現(xiàn)的服務發(fā)現(xiàn)和配置管理
Spring Cloud Security
提供了對OAuth2 負載均衡的客戶端战惊,以及基于Zuul代理的頭部校驗。
Spring Cloud Sleuth
對spring cloud分布式應用的服務鏈路追蹤
Spring Cloud Data Flow
一個建立數(shù)據(jù)集成和實時處理管道的工具集扎即。
簡化了應用程序的開發(fā)和部署 將精力集中到數(shù)據(jù)處理的用例上吞获。
Spring Cloud Stream
一個輕量級的事件驅(qū)動的微服務框架⊙璞桑可以快速構(gòu)建應用和外部系統(tǒng)對接各拷。可以在springboot應用之間通過簡單的聲明模型闷营,基于kafka或rabbitMq交互消息烤黍。
Spring Cloud Stream App Starters
一個基于springboot的同外部系統(tǒng)的集成應用
Spring Cloud Task
短時任務處理框架。如定時任務傻盟。
Spring Cloud Task App Starters
對應的具體應用速蕊。
Spring Cloud for Amazon Web Services
方便同AWS服務集成
Spring Cloud Connectors
使各種PaaS平臺應用連接基礎(chǔ)后端服務(如數(shù)據(jù)庫服務、消息中間件)更容易娘赴。
Spring Cloud Starters
用于使基于springcloud的依賴管理更方便
Spring Cloud CLI
是一個插件规哲,可以用groovy語言快速創(chuàng)建spring cloud應用
Spring Cloud Contract
是一個消費者驅(qū)動的、面向Java的契約框架诽表。
Spring Cloud Netflix
Netflix是spring cloud的核心框架唉锌,必學必用。
微服務架構(gòu)
首先竿奏,我們來看看一般的微服務架構(gòu)需要的功能或使用場景:
我們把整個系統(tǒng)根據(jù)業(yè)務拆分成幾個子系統(tǒng)袄简。
每個子系統(tǒng)可以部署多個應用,多個應用之間使用負載均衡泛啸。
需要一個服務注冊中心绿语,所有的服務都在注冊中心注冊,負載均衡也是通過在注冊中心注冊的服務來使用一定策略來實現(xiàn)平痰。
所有的客戶端都通過同一個網(wǎng)關(guān)地址訪問后臺的服務汞舱,通過路由配置,網(wǎng)關(guān)來判斷一個URL請求由哪個服務處理宗雇。請求轉(zhuǎn)發(fā)到服務上的時候也使用負載均衡昂芜。
服務之間有時候也需要相互訪問。例如有一個用戶模塊赔蒲,其他服務在處理一些業(yè)務的時候泌神,要獲取用戶服務的用戶數(shù)據(jù)良漱。
需要一個斷路器,及時處理服務調(diào)用時的超時和錯誤欢际,防止由于其中一個服務的問題而導致整體系統(tǒng)的癱瘓母市。
還需要一個監(jiān)控功能,監(jiān)控每個服務調(diào)用花費的時間等损趋。
Netflix
Spring Cloud Netflix框架剛好就滿足了上面所有的需求患久,而且最重要的是,使用起來非常的簡單浑槽。Spring Cloud Netflix包含的組件及其主要功能大致如下:
Eureka蒋失,服務注冊和發(fā)現(xiàn),它提供了一個服務注冊中心桐玻、服務發(fā)現(xiàn)的客戶端篙挽,還有一個方便的查看所有注冊的服務的界面。 所有的服務使用Eureka的服務發(fā)現(xiàn)客戶端來將自己注冊到Eureka的服務器上镊靴。
Zuul铣卡,網(wǎng)關(guān),所有的客戶端請求通過這個網(wǎng)關(guān)訪問后臺的服務偏竟。他可以使用一定的路由配置來判斷某一個URL由哪個服務來處理煮落。并從Eureka獲取注冊的服務來轉(zhuǎn)發(fā)請求。
Ribbon踊谋,即負載均衡州邢,Zuul網(wǎng)關(guān)將一個請求發(fā)送給某一個服務的應用的時候,如果一個服務啟動了多個實例褪子,就會通過Ribbon來通過一定的負載均衡策略來發(fā)送給某一個服務實例。
Feign骗村,服務客戶端嫌褪,服務之間如果需要相互訪問,可以使用RestTemplate胚股,也可以使用Feign客戶端訪問笼痛。它默認會使用Ribbon來實現(xiàn)負載均衡。
Hystrix琅拌,監(jiān)控和斷路器缨伊。我們只需要在服務接口上添加Hystrix標簽,就可以實現(xiàn)對這個接口的監(jiān)控和斷路器功能进宝。
Hystrix Dashboard刻坊,監(jiān)控面板,他提供了一個界面党晋,可以監(jiān)控各個服務上的服務調(diào)用所消耗的時間等谭胚。
Turbine徐块,監(jiān)控聚合,使用Hystrix監(jiān)控灾而,我們需要打開每一個服務實例的監(jiān)控信息來查看胡控。而Turbine可以幫助我們把所有的服務實例的監(jiān)控信息聚合到一個地方統(tǒng)一查看。這樣就不需要挨個打開一個個的頁面一個個查看旁趟。
接下來一個一個看昼激。
Eureka
作為服務注冊與發(fā)現(xiàn)的中心。我們的demo分為兩部分锡搜,一個server橙困,一個client。 這里的server是指注冊中心余爆。client是指向注冊中心注冊或者訂閱服務的消費者纷宇。
創(chuàng)建一個maven主工程
建一個server模塊。
右鍵-->new module -->Spring Initializr -->next
-->填寫相關(guān)信息-->next
-->dependencies 選Cloud Discovery-->Eureka Server
-->finish-
第一個server
// 服務注冊中心注解 @EnableEurekaServer @SpringBootApplication public class ServerApplication { public static void main(String[] args) { SpringApplication.run(ServerApplication.class, args); } }
代碼非常簡單蛾方,就是在原來springboot的啟動類上加了一個注解像捶。
@EnableEurekaServer
將該實例注冊為一個Eureka的server角色。 -
配置文件
在resources下建一個appication.yml
配置如下:
server:
port: 8761eureka: instance: hostname: localhost client: registerWithEureka: false fetchRegistry: false serviceUrl: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
(注意桩砰,yml對于格式要求非常嚴格拓春,縮進不要搞錯)
- 啟動工程
訪問http://localhost:8761⊙怯纾可以看到注冊中心的界面硼莽。
此時還沒有服務提供者注冊過來。
同創(chuàng)建server一樣的步驟煮纵,建一個client
-
代碼如下
@SpringBootApplication @EnableEurekaClient @RestController public class ClientApplication { @Value("${server.port}") String port; @RequestMapping("/hi") public String home(@RequestParam String name) { return "hi "+name+",i am from port:" +port; } public static void main(String[] args) { SpringApplication.run(ClientApplication.class, args); } }
用
@EnableEurekaClient
表明這是一個client -
配置文件
eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/ server: port: 8762 spring: application: name: service-hi
啟動工程
查看http://localhost:8761已經(jīng)注冊進來
配置文件中spring.application.name指定的name就是注冊的服務名懂鸵。
其他應用調(diào)用也是根據(jù)這個name來找。
ribbon+restTemplate
微服務架構(gòu)中行疏,業(yè)務被拆分成單獨的服務匆光,服務之間通過rest相互調(diào)用。在springcloud中有兩種調(diào)用方式:ribbon+restTemplate和feign酿联。
ribbon是一個負載均衡客戶端终息,可以很好的控制http和tcp之上的行為,feign也是使用ribbon的贞让。
再啟動一個進程
為了嘗試負載均衡周崭,基于上面的工程。改一下client的配置端口喳张,將8762改為8763再啟動一個服務提供方client续镇。
tips:idea默認run 是單實例的,所以再次run main會讓你停掉之前的服務蹲姐。其實在run configration中配置一下磨取,把Single instance only選項勾掉就可以了人柿。查看注冊中心
測試應該看到
已經(jīng)有兩個服務注冊進來了。
建一個服務消費者
新建一個springboot工程:service-consumer
在dependency時勾選web忙厌、ribbon凫岖、eureka discovery-
在resources下新建application.yml
配置:eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/ server: port: 8764 spring: application: name: service-consumer
-
修改啟動類
@SpringBootApplication @EnableDiscoveryClient public class ServicveConsumerApplication { public static void main(String[] args) { SpringApplication.run(ServicveConsumerApplication.class, args); } @Bean @LoadBalanced RestTemplate restTemplate() { return new RestTemplate(); } }
注解@EnableDiscoveryClient作用是向注冊中心注冊自己為消費者。
@LoadBalanced表明開啟負載均衡功能逢净。
@Bean注解聲明一個RestTemplate bean -
建一個服務類
@Service public class HelloService { @Autowired RestTemplate restTemplate; public String sayHello(String name) { return restTemplate.getForObject("http://SERVICE-HI/hi?name=" + name, String.class); } }
-
建一個對應的測試用例
@RunWith(SpringRunner.class) @SpringBootTest(classes=ServicveConsumerApplication.class) public class HelloServiceTest { @Autowired HelloService helloService; @Test public void sayHello() throws Exception { for (int i = 0; i < 10; i++){ System.out.println(helloService.sayHello("yunsheng")); } } }
跑十次看一下結(jié)果哥放。
hi yunsheng,i am from port:8763
hi yunsheng,i am from port:8762
hi yunsheng,i am from port:8763
hi yunsheng,i am from port:8762
hi yunsheng,i am from port:8763
hi yunsheng,i am from port:8762
hi yunsheng,i am from port:8763
hi yunsheng,i am from port:8762
hi yunsheng,i am from port:8763
hi yunsheng,i am from port:8762
很明顯看到了負載均衡的效果。
當前我們的應用架構(gòu)是:
feign
Feign是一個聲明試的web服務客戶端爹土∩瘢可以讓你寫web service client更簡單。Feign默認集成了Ribbon胀茵。
還是使用上面的工程社露,知識需要加一個Feign的依賴。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
-
新建一個啟動類
@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients public class FeignConsumerApplication { public static void main(String[] args) { SpringApplication.run(FeignConsumerApplication.class, args); } }
新增一個
@EnableFeignClients
注解開啟Feign功能 -
新建一個feign服務類
@FeignClient(value = "service-hi") public interface FeignConsumeService { @RequestMapping(value = "/hi",method = RequestMethod.GET) String sayHiFromClientOne(@RequestParam(value = "name") String name); }
這里的功能就是一個服務代理的接口琼娘,只是內(nèi)部默認實現(xiàn)了負載均衡峭弟。
這里的requestMapping必須是服務提供者的RequestMapping保持一致。
-
controller層對外暴露一個服務調(diào)用
@RestController public class HiController { @Autowired FeignConsumeService feignConsumeService; @RequestMapping(value = "/feignHi", method = RequestMethod.GET) public String sayHi(@RequestParam(value = "name") String name){ return feignConsumeService.sayHiFromFeign(name); } }
這里的requestMapping隨便寫脱拼。
- 啟動瞒瘸。
可以看到消費者已經(jīng)注冊。
- 消費
因為我們開放的是rest服務熄浓,所以直接瀏覽器測試情臭。
瀏覽器多次訪問
http://localhost:8764/feignHi?name=yunsheng
可以看到負載均衡的效果,間隔調(diào)用8762和8763的服務赌蔑。
Hystrix斷路器
在微服務架構(gòu)中俯在,各個服務模塊獨立部署。但是由于各種原因娃惯,并不能保證服務100%成功朝巫。如果某個服務發(fā)送異常,產(chǎn)生線程阻塞石景。測試有大量請求進入,會導致servlet線程被耗盡拙吉。由于服務之間的依賴潮孽,導致耽擱服務的異常被傳播擴大,產(chǎn)生災難性后果筷黔。為了避免這種情況往史,業(yè)界采用斷路器模式,當服務不可用情況達到一定閾值后佛舱,斷路器打開椎例,避免故障傳播挨决。
-
添加依賴
基于service-consumer工程添加依賴<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency>
-
改造啟動類
@SpringBootApplication @EnableDiscoveryClient @EnableHystrix public class ServicveConsumerApplication { public static void main(String[] args) { SpringApplication.run(ServicveConsumerApplication.class, args); } @Bean @LoadBalanced RestTemplate restTemplate() { return new RestTemplate(); } }
添加
@EnableHystrix
注解,開啟斷路器功能 -
改造HelloService
@Service public class HelloService { @Autowired RestTemplate restTemplate; @HystrixCommand(fallbackMethod = "sayErr") public String sayHello(String name) { return restTemplate.getForObject("http://SERVICE-HI/hi?name=" + name, String.class); } public String sayErr(String name) { return "hi,"+name+",sorry,error!"; } }
給之前的sayHello方法添加@HystrixCommand注解订歪,并指定失敗時調(diào)用的方法脖祈。
-
測試
先將8762和8763兩個服務提供者關(guān)掉。
先將 @HystrixCommand(fallbackMethod = "sayErr")注釋掉刷晋,關(guān)閉斷路器盖高。
為了方便看出效果。也給ribbon方式新建一個rest的controller眼虱。@RestController public class HiController { @Autowired HelloService helloService; @RequestMapping(value = "/ribbonHi", method = RequestMethod.GET) public String sayHi(@RequestParam(value = "name") String name) { return helloService.sayHello("yys"); } }
瀏覽器訪問http://localhost:8764/ribbonHi?name=yys
需要等到響應超時才能得到錯誤頁面喻奥。
但是開啟了斷路器之后,
再次嘗試捏悬,可以看到很快輸出hi,yys,sorry,error!
撞蚕。