之前幾章演示的熔斷,降級 都是 RestTemplate + Ribbon 和 RestTemplate + Hystrix ,但是在實際開發並不是這樣,實際開發中都是 Feign 遠程接口調用。
Feign + Hystrix 演示:
eruka(略)
order 服務工程:
pom.xml
<?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.tuling.cloud</groupId> <artifactId>microservice-consumer-order-feign-hystrix-fallback</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>07-ms-consumer-order-feign-hystrix-fallback</name> <!-- 引入spring boot的依賴 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.9.RELEASE</version> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</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-netflix-eureka-client</artifactId> </dependency> <!-- feign 已經依賴hystrix core 包,並不像之前的restTemplate + hystrix 需要引入全部的jar <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> </dependencies> <!-- 引入spring cloud的依賴 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Edgware.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <!-- 添加spring-boot的maven插件 --> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
ConsumerOrderApplication_07 啟動類
package com.jiagoushi.cloud.study; 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; @EnableDiscoveryClient @SpringBootApplication @EnableFeignClients // feign-hystrix 支持, 不用在寫@EnableCircuitBreaker public class ConsumerOrderApplication_07 { public static void main(String[] args) { SpringApplication.run(ConsumerOrderApplication_07.class, args); } }
UserFeignClient接口:
package com.jiagoushi.cloud.study.user.feign; import org.springframework.cloud.netflix.feign.FeignClient; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import com.jiagoushi.cloud.study.user.entity.User; /** * Feign的fallback測試 * 使用@FeignClient的fallback屬性指定回退類 */ @FeignClient(name = "microservice-provider-user", fallback = FeignClientFallback.class /*, configuration = FeignDisableHystrixConfiguration.class 指定該類來設置禁用該接口的Hystrix*/) public interface UserFeignClient {
@RequestMapping(value = "/{id}", method = RequestMethod.GET) public User findById(@PathVariable("id") Long id);
} /** * 降級方法 FeignClientFallback 需實現Feign Client接口 * FeignClientFallback也可以是class,沒有區別 * 不是每個接口都必須要有降級的。重要的方法才會有 */ @Component class FeignClientFallback implements UserFeignClient { @Override public User findById(Long id) { User user = new User(); user.setId(-1L); user.setUsername("降級用戶"); return user; } }
-
feign注解 configuration = FeignDisableHystrixConfiguration.class 標識該feign接口禁用Hystrix
FeignDisableHystrixConfiguration類:
package com.jiagoushi.cloud.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Scope; import feign.Feign; /**
注意:該類不能被啟動掃描到。不然就是全局設置。 * 為Feign禁用Hystrix功能的配置類,可單獨用在具體的Feign接口上 * 為什么要禁用? 因為Hystrix在接口上做了很多封裝,限制接口線程池10個,有熔斷,降級等功能, * 有的接口不需要這么多功能所以要禁用 */ @Configuration public class FeignDisableHystrixConfiguration { FeignDisableHystrixConfiguration(){ }
@Bean @Scope("prototype") public Feign.Builder feignBuilder() { return Feign.builder(); } }
applcation.yml:
server: port: 9010 spring: application: name: microservice-consumer-order eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/ instance: prefer-ip-address: true # 開啟feign支持hystrix, # https://blog.csdn.net/mxmxz/article/details/84633098 這個鏈接關於feign+hystrix超時時間設置 # http://blog.java1234.com/blog/articles/440.html 這個鏈接關於feign+hystrix超時時間設置 feign: hystrix: enabled: true #配置這個是無效的,因為feign 也有一個超時時間的設置, #當然feign底層是 ribbon的封裝,所以 直接配置ribbon,ribbon默認超時也是1秒。 #所以這里都是強制要求,ribbon的超時時間要大於hystrix的超時時間, #否則 hystrix自定義的超時時間毫無意義。所以還得加個 ribbon超時時間設置 #hystrix: # command: # default: # execution: # isolation: # thread: # timeoutInMilliseconds: 3000 ribbon: ReadTimeout: 10000 ConnectTimeout: 10000
# 解釋一下:這種配置會出現這樣的現象:
# order服務Feign調用user服務時,因為user睡眠4S,所以瀏覽器會一直轉4s等待,這種情況只會等待三次,之后仍然繼續
# 調用user服務時,Feign 直接降級,瀏覽器轉的時間很短。說明被降級了服務器響應快。
hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 3000 #feign整合hystrix 光設置Hystrix 超時沒用的要配合ribbon超時 circuitBreaker: requestVolumeThreshold: 3 #默認20 ,熔斷的閾值,如何user服務報錯滿足3次,熔斷器就會打開,就算order之后請求正確的數據也不行。 sleepWindowInMilliseconds: 8000 #默認5S , 等5S之后熔斷器會處於半開狀態,然后下一次請求的正確和錯誤講決定熔斷器是否真的關閉和是否繼續打開 # 說明:請務必注意,從Spring Cloud Dalston開始,Feign默認是不開啟Hystrix的。 # 因此,如使用Dalston請務必額外設置屬性:feign.hystrix.enabled=true,否則斷路器不會生效。 # 而,Spring Cloud Angel/Brixton/Camden中,Feign默認都是開啟Hystrix的。無需設置該屬性。
user服務工程:
pom.xml:
<?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.tuling.cloud</groupId> <artifactId>microservice-provider-user</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>07-provider-user</name> <!-- 引入spring boot的依賴 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.9.RELEASE</version> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</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-netflix-eureka-client</artifactId> </dependency> </dependencies> <!-- 引入spring cloud的依賴 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Edgware.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <!-- 添加spring-boot的maven插件 --> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
application.yml:
server: port: 8010 spring: application: name: microservice-provider-user jpa: generate-ddl: false show-sql: true hibernate: ddl-auto: none datasource: # 指定數據源 platform: h2 # 指定數據源類型 schema: classpath:schema.sql # 指定h2數據庫的建表腳本 data: classpath:data.sql # 指定h2數據庫的數據腳本 logging: # 配置日志級別,讓hibernate打印出執行的SQL level: root: INFO org.hibernate: INFO org.hibernate.type.descriptor.sql.BasicBinder: TRACE org.hibernate.type.descriptor.sql.BasicExtractor: TRACE eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/ instance: prefer-ip-address: true
UserComtroller:
package com.tuling.cloud.study.controller; import java.util.Random; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.serviceregistry.Registration; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import com.tuling.cloud.study.entity.User; import com.tuling.cloud.study.repository.UserRepository; @RestController public class UserController { private final Logger logger = Logger.getLogger(getClass()); @Autowired private UserRepository userRepository; @Autowired private Registration registration; @GetMapping("/{id}") public User findById(@PathVariable Long id) throws Exception { // 模擬hystrix 超時降級 Thread.sleep(4000); //測試熔斷,傳入不存在的用戶id模擬異常情況 /* if (id == 10) { throw new NullPointerException(); }*/
logger.info("用戶中心接口:查詢用戶"+ id +"信息"); User findOne = userRepository.findOne(id); return findOne; } @GetMapping("/getIpAndPort") public String findById() { return registration.getHost() + ":" + registration.getPort(); } }
ProviderUserApplication類:
package com.tuling.cloud.study; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @EnableDiscoveryClient @SpringBootApplication public class ProviderUserApplication { public static void main(String[] args) { SpringApplication.run(ProviderUserApplication.class, args); } }
當user服務sleep(4000) 效果圖:
測試Hystrix 閾值設置3時把注釋去掉: