本示例主要介紹 Spring Cloud 系列中的 Eureka,如何使用Hystrix熔斷器容錯保護咱們的應用程序。java
在微服務架構中,系統被拆分紅不少個服務單元,各個服務單元的應用經過 HTTP 相互調用、依賴,在某個服務因爲網絡或其餘緣由自身出現故障、延遲時,調用方也會出現延遲。若調用方請求不斷增長,可能會造成任務積壓,最終致使調用方服務癱瘓,服務不可用現象逐漸放大。git
Spring Cloud Hystrix 是一個專用於服務熔斷處理的開源項目,實現了一系列服務保護措施,當依賴的服務方出現故障不可用時,hystrix實現服務降級、服務熔斷等功能,對延遲和故障提供強大的容錯能力,從而防止故障進一步擴大。github
服務熔斷web
Hystrix 會記錄各個服務的請求信息,經過 成功、失敗、拒絕、超時 等統計信息判斷是否打開斷路器,將某個服務的請求進行熔斷。一段時間後切換到半開路狀態,若是後面的請求正常則關閉斷路器,不然繼續打開斷路器。spring
服務降級apache
服務降級是請求失敗時的後備方法,故障時執行降級邏輯。網絡
線程隔離架構
Hystrix 經過線程池實現資源的隔離,確保對某一服務的調用在出現故障時不會對其餘服務形成影響。app
建立三個項目來完成示例,分別爲:服務註冊中心hystrix-eureka-server,服務提供者hystrix-service-provider,服務消費者hystrix-service-consumer負載均衡
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.easy</groupId> <artifactId>hystrix-eureka-server</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>hystrix-eureka-server</name> <description>Demo project for Spring Boot</description> <parent> <artifactId>cloud-hystrix</artifactId> <groupId>com.easy</groupId> <version>1.0.0</version> </parent> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
server: port: 8761 spring: application: name: eureka-server eureka: instance: hostname: localhost # eureka 實例名稱 client: register-with-eureka: false # 不向註冊中心註冊本身 fetch-registry: false # 是否檢索服務 service-url: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ # 註冊中心訪問地址
package com.easy.eurekaServer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @EnableEurekaServer @SpringBootApplication public class HystrixEurekaServerApplication { public static void main(String[] args) { SpringApplication.run(HystrixEurekaServerApplication.class, args); } }
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.easy</groupId> <artifactId>hystrix-service-provider</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>hystrix-service-provider</name> <description>Demo project for Spring Boot</description> <parent> <artifactId>cloud-hystrix</artifactId> <groupId>com.easy</groupId> <version>1.0.0</version> </parent> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
spring: application: name: hystrix-service-provider eureka: client: service-url: defaultZone: http://localhost:8761/eureka/ # 實例一 server: port: 8081
package com.easy.serviceProvider.web; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @GetMapping("hello") public String hello(@RequestParam String p1, @RequestParam String p2) throws Exception { // 用來測試服務超時的狀況 // int sleepTime = new Random().nextInt(2000); // System.out.println("hello sleep " + sleepTime); // Thread.sleep(sleepTime); return "hello, " + p1 + ", " + p2; } }
package com.easy.serviceProvider; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @EnableDiscoveryClient @SpringBootApplication public class HystrixServiceProviderApplication { public static void main(String[] args) { SpringApplication.run(HystrixServiceProviderApplication.class, args); } }
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.easy</groupId> <artifactId>hystrix-service-consumer</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>hystrix-service-consumer</name> <description>Demo project for Spring Boot</description> <parent> <artifactId>cloud-hystrix</artifactId> <groupId>com.easy</groupId> <version>1.0.0</version> </parent> <dependencies> <!-- eureka 客戶端 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!-- ribbon --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
spring: application: name: hystrix-eureka-server eureka: client: service-url: defaultZone: http://localhost:8761/eureka/ hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 1000 # 默認超時時間
異常處理類NotFallbackException.java
package com.easy.serviceConsumer.exception; public class NotFallbackException extends Exception { }
服務層HelloService.java
package com.easy.serviceConsumer.service; import com.easy.serviceConsumer.exception.NotFallbackException; 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 RestTemplate restTemplate; private static final String HELLO_SERVICE = "http://hystrix-service-provider/"; @HystrixCommand(fallbackMethod = "helloFallback", ignoreExceptions = {NotFallbackException.class} , groupKey = "hello", commandKey = "str", threadPoolKey = "helloStr") public String hello(String p1, String p2) { return restTemplate.getForObject(HELLO_SERVICE + "hello?p1=" + p1 + "&p2=" + p2, String.class); } private String helloFallback(String p1, String p2, Throwable e) { System.out.println("class: " + e.getClass()); return "error, " + p1 + ", " + p2; } }
控制器ConsumerController.java
package com.easy.serviceConsumer.web; import com.easy.serviceConsumer.service.HelloService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class ConsumerController { @Autowired HelloService helloService; @GetMapping("hello") public String hello(@RequestParam String p1, @RequestParam String p2) { System.out.println("hello"); return helloService.hello(p1, p2); } }
package com.easy.serviceConsumer; import org.springframework.boot.SpringApplication; import org.springframework.cloud.client.SpringCloudApplication; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @SpringCloudApplication public class HystrixServiceConsumerApplication { @Bean @LoadBalanced RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(HystrixServiceConsumerApplication.class, args); } }
分別運行3個服務,HystrixEurekaServerApplication.java(服務註冊中心),HystrixServiceProviderApplication.java(服務提供者),HystrixServiceConsumerApplication.java(服務消費者)