之前几章演示的熔断,降级 都是 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); } }