在分佈式系統中,根據業務來拆分成一個個的微服務,服務與服務之間可以相互調用(RPC),在spring cloud 中可以用RestTemplate+ribbon和feign來調用。爲了保證其高可用,單個服務有時候會集羣部署,由於網絡或程序自躾 的原因,服務並不能保證百分百可靠可用,如果單個服務出現問題,調用這個服務就出現線程阻塞,此時若有大量的請求涌入,servlet容器的線程資源就會被消耗完畢導致服務癱瘓。服務與服務之間的依賴性,故障會傳播,會對整個微服務系統造成不可估量的嚴重後果,這就是常說的服務故障的“雪崩效應”。爲了解決這個問題,有人就提出了一種解決問題的思路,斷路器模型。就是每一個調用服務的接口處加一個斷路器,默認是關閉的,當對服務調用時,不可用的次數達到一個閥值時,斷路器就會打開,通過回調方法迅速返回一個值結束調用,避免出現連鎖故障。
一、改造上一篇文章的zhangsanService(ribbon+restTemplate方式),在pom.xml中加入hystrix的依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
改造啓動類ZhangsanServiceApplication.java:
package com.gaox.zhangsanService; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.netflix.hystrix.EnableHystrix; import org.springframework.context.annotation.Bean; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @SpringBootApplication @EnableEurekaClient @EnableHystrix @RestController public class ZhangsanServiceApplication { public static void main(String[] args) { SpringApplication.run(ZhangsanServiceApplication.class, args); } @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); } @Autowired private RestTemplate restTemplate; @RequestMapping("/hello") @HystrixCommand(fallbackMethod = "helloError") public String hello(String name){ String result=restTemplate.getForObject("http://HELLOSERVICE/hello?name="+name,String.class); return result; } public String helloError(String name){ return "hello ,"+name+"! sorry ,error !"; } }
依次啓動,serviceCenter,helloService:8762,helloService:8763,zhangsanService,在瀏覽器中訪問http://localhost:8764/hello?name=zhangsan
瀏覽器依次會顯示
你好,zhangsan。我是helloService,端口是8762
你好,zhangsan。我是helloService,端口是8763
此時關閉helloService:8763, http://localhost:8764/hello?name=zhangsan
瀏覽器依次會顯示
你好,zhangsan。我是helloService,端口是8762
hello ,zhangsan! sorry ,error !
這說明當zhangsanService調用的服務helloService:8763不可用時,會執行快速失敗,直接返回一個字符串,而不是等待響應超時,這就很好的控制了當大量請求涌入的時候線程不會阻塞
二、feign是自帶斷路器的,spring cloud的有些版本中,默認是關閉的,需要在配置文件中打開它,下面上一篇文章中的lisiService爲基礎來改造一下
Application.properties:
server.port=8765 spring.application.name=lisiService eureka.client.service-url.defaultZone=http://localhost:8761/eureka/ feign.hystrix.enabled=true
LisiServiceApplication.java:
package com.gaox.lisiService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.netflix.feign.EnableFeignClients; import org.springframework.cloud.netflix.feign.FeignClient; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication @EnableEurekaClient @EnableFeignClients @RestController public class LisiServiceApplication { public static void main(String[] args) { SpringApplication.run(LisiServiceApplication.class, args); } @FeignClient(value = "helloService" ,fallback = HelloError.class) public interface HelloService { @RequestMapping(value = "/hello", method = RequestMethod.GET) String hello(@RequestParam("name") String name); } @Component public class HelloError implements HelloService { @Override public String hello(String name){ return "hello ,"+name+"! sorry ,error !"; } } @Autowired private HelloService helloService; @RequestMapping("/hello") public String hello(@RequestParam("name")String name){ return helloService.hello(name); } }
依次啓動serviceCenter,helloService:8762,helloService:8763,lisiService,然後在瀏覽器輸入http://localhost:8765/hello?name=lisi,瀏覽器會交替顯示
你好,lisi。我是helloService,端口是8762
你好,lisi。我是helloService,端口是8763
此時關閉helloService:8763, http://localhost:8764/hello?name=zhangsan
瀏覽器依次會顯示
你好,lisi。我是helloService,端口是8762
hello , lisi! sorry ,error !
這說明斷路器起作用了,當lisiService調用的服務helloService:8763不可用時,會執行快速失敗,直接返回一個字符串,而不是等待響應超時,這就很好的控制了當大量請求涌入的時候線程不會阻塞
三、在feign中使用hystrix dashboard(儀表盤)功能
Pom.xml添加以下內容:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId> </dependency>
LisiServiceApplication.java啓動類上加兩個註解@EnableHystrixDashboard 和@EnableCircuitBreaker
package com.gaox.lisiService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@RestController
@EnableHystrixDashboard
@EnableCircuitBreaker
publicclass LisiServiceApplication {
public static void main(String[]args) {
SpringApplication.run(LisiServiceApplication.class, args);
}
@FeignClient(value = "helloService",fallback = HelloError.class)
public interface HelloService{
@RequestMapping(value = "/hello", method =RequestMethod.GET)
String hello(@RequestParam("name") Stringname);
}
@Component
public class HelloError implements HelloService{
@Override
public String hello(Stringname){
return "hello ,"+name+"! sorry ,error !";
}
}
@Autowired
private HelloService helloService;
@RequestMapping("/hello")
public String hello(@RequestParam("name")Stringname){
return helloService.hello(name);
}
}
打開瀏覽器:訪問http://localhost:8765/hystrix,界面如下:
第一個輸入框:http://localhost:8765/hystrix.stream
Delay:默認不變即可
Title:(這個可以隨便輸入)
然後點擊按鈕進入,如圖
四、在ribbon中使用hystrix dashboard(儀表盤)功能
Pom.xml添加以下內容:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId> </dependency>
LisiServiceApplication.java啓動類上加個註解@EnableHystrixDashboard package com.gaox.zhangsanService; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.netflix.hystrix.EnableHystrix; import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; import org.springframework.context.annotation.Bean; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @SpringBootApplication @EnableEurekaClient @EnableHystrix @EnableHystrixDashboard @RestController public class ZhangsanServiceApplication { public static void main(String[] args) { SpringApplication.run(ZhangsanServiceApplication.class, args); } @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); } @Autowired private RestTemplate restTemplate; @RequestMapping("/hello") @HystrixCommand(fallbackMethod = "helloError") public String hello(String name){ String result=restTemplate.getForObject("http://HELLOSERVICE/hello?name="+name,String.class); return result; } public String helloError(String name){ return "hello ,"+name+"! sorry ,error !"; } }
然後打開瀏覽器之後的操作和上面feign方法的一樣。