SpringCloud(五):斷路器(Hystrix)
一、簡介 http://projects.spring.io/spring-cloud/spring-cloud.html#_circuit_breaker_hystrix_clients
Netflix開源了Hystrix元件,實現了斷路器模式,SpringCloud對這一元件進行了整合。 在微服務架構中,一個請求需要呼叫多個服務是非常常見的。
hystrix主要是用來防止服務雪崩效應的。
服務雪崩效應:是一種因服務提供者的不可用導致服務呼叫者的不可用,並將不可用逐漸放大的過程。
舉例:A為服務提供者, B為A的服務呼叫者, C和D是B的服務呼叫者. 當A的不可用,引起B的不可用,並將不可用逐漸放大C和D時, 服務雪崩就形成了。
如圖一個API可能同時呼叫多個微服務。
較低級別的服務中的服務故障可能導致服務級聯故障。當對特定服務的呼叫達到一定閾值時(Hystrix中的預設值為5秒內的20次故障),斷路器開啟。
斷路開啟後,回退可以是另一個Hystrix保護的呼叫,靜態資料或一個正常的空值。
二、Ribbon+Hystrix
依賴:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency> application.properties
spring.application.name=hello-consumer-ribbon-hystrix server.port=8041
eureka.client.serviceUrl.defaultZone=http://localhost:8001/eureka/ HelloService
package cn.saytime.service;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate;
@Service public class HelloService {
@Autowired private RestTemplate restTemplate;
@HystrixCommand(fallbackMethod = "fallback") public String hello (String name) { return restTemplate.getForEntity("http://HELLO-SERVICE/hello?name=" + name, String.class).getBody(); }
public String fallback (String name) { return "hello, hystrix === fail name:" + name; }
} application
package cn.saytime;
import cn.saytime.service.HelloService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalanced; 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 @EnableDiscoveryClient @EnableHystrix @RestController public class HelloConsumerRibbonHystrixApplication {
public static void main(String[] args) { SpringApplication.run(HelloConsumerRibbonHystrixApplication.class, args); }
@Bean @LoadBalanced public RestTemplate restTemplate () { return new RestTemplate(); }
@Autowired private HelloService helloService;
@RequestMapping("hello") public String hello (String name) { return helloService.hello(name); }
} 測試:
啟動eureka-server:8001, hello-service:8012,8002,hello-consumer-ribbon-hystrix:8041
訪問:http://localhost:8041/hello?name=ribbon_hystrix
hello, ribbon_hystrix 訪問正常,接下來我們把hello-service服務停了,再次訪問:
hello, hystrix === fail name:ribbon_hystrix 成功觸發熔斷。
然後我們再次啟動hello-service服務,然後訪問:
hello, ribbon_hystrix 沒有觸發熔斷,正常。
同樣我們測試訪問超時觸發熔斷的情況,我們在hello-service介面加上執行緒等待1s
@RequestMapping("hello") public String hello (String name) throws InterruptedException { Thread.sleep(1000); System.out.println("hello, " + name); return "hello, " + name; } 訪問,發現同樣觸發熔斷,因為hystrix預設超時1s觸發熔斷,我們可以通過修改屬性來改變超時時間。
這裡我們把超時時間修改為2s
@HystrixCommand(fallbackMethod = "fallback", commandProperties = { @HystrixProperty(name= "execution.isolation.thread.timeoutInMilliseconds", value="2000") }) public String hello (String name) { return restTemplate.getForEntity("http://HELLO-SERVICE/hello?name=" + name, String.class).getBody(); } 再次訪問,發現沒有觸發熔斷。
三、Feign With Hystrix Feign預設是自帶Hystrix的,所以依賴Jar的時候無需再依賴hystrix
pom.xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency> application.properties
spring.application.name=hello-consumer-feign-hystrix server.port=8051
eureka.client.serviceUrl.defaultZone=http://localhost:8001/eureka/
## 開啟hystrix feign.hystrix.enabled=true
## hystrix熔斷觸發預設超時時間 hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=2000 HelloService
package cn.saytime.service;
import org.springframework.cloud.netflix.feign.FeignClient; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(value = "hello-service", fallback = HelloServiceFallBack.class) public interface HelloService {
@RequestMapping("hello") String hello(@RequestParam(value = "name") String name) ; } 熔斷觸發類HelloServiceFallBack
package cn.saytime.service;
import org.springframework.stereotype.Component;
@Component public class HelloServiceFallBack implements HelloService{
@Override public String hello(String name) { return "hello, hystrix == fail name : " + name; } } 啟動類
package cn.saytime;
import cn.saytime.service.HelloService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.feign.EnableFeignClients; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients @RestController public class HelloConsumerFeignHystrixApplication {
public static void main(String[] args) { SpringApplication.run(HelloConsumerFeignHystrixApplication.class, args); }
@Autowired private HelloService helloService;
@RequestMapping("hello") public String hello(String name){ return helloService.hello(name); } } 測試:
啟動eureka-server:8001, hello-service:8011,hello-consumer-feign-hystrix:8051
訪問:http://localhost:8051/hello?name=feign_hystrix
hello, feign_hystrix 訪問成功
接下來關閉hello-service服務,再次訪問:
hello, hystrix == fail name : feign_hystrix 成功觸發熔斷
將hello-service服務重新啟動,訪問正常,沒有觸發熔斷。
將hello-service服務介面加上執行緒等待3s,重啟hello-service服務,再次呼叫
同樣成功觸發熔斷
修改application.properties裡面熔斷超時時間為4s,再次呼叫,沒有觸發熔斷。
四、Hystrix Dashboard (Hystrix 儀表盤) ribbon-hystrix 與 feign-hystrix 兩個專案的pom檔案都新增以下依賴
<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> 啟動類都加上如下註解:
@EnableHystrixDashboard 然後訪問
http://localhost:8041/hystrix http://localhost:8051/hystrix
輸入連結:http://localhost:8041/hystrix.stream
同理,如果是feign-hystrix專案,輸入 http://localhost:8051/hystrix.stream
點選Monitor Stream
然後我們訪問一下 http://localhost:8041/hello?name=ribbon_hystrix
會出現如下監控介面:
具體監控介面內容情況,就不詳細描述了