熔断机制产生的背景是“雪崩效应”,在分布式系统中,一个服务提供者的不可用会导致服务消费者的不可用,一个服务的异常,最终会影响其他服务,造成线程阻塞,资源耗尽,如果有大量的请求,会导致服务瘫痪,引蝴蝶效应造成系统宕机。
如果一个服务的错误过多,短时间内得不到修复,就可以开启熔断机制,防止多次没有意义的调用。服务调用方会定时重试,如果可用,就继续使用。
熔断有以下几种状态:
闭合状态 添加一个计数器,如果失败的次数在指定时间内超过一定阈值,则开启熔断,此时处于断开状态,此时开启一个计时器,如果过了指定时间,则切换到半断开,服务可被尝试调用。
断开状态 服务如果被调用会立即返回错误信息,减少资源损耗。
半开状态 允许一定数量的请求调用服务,如果请求调用成功,则关闭熔断,认为服务的错误已经修正。如果还是发生错误,则切换到断开状态。
Hystrix 是知名视频网站 Netflix 开源的框架,完美解决了分布式系统中所面临的这个技术问题,解决了雪崩效应。
首先搭建 maven 环境,父 pom 如下
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <spring-cloud.version>Finchley.RELEASE</spring-cloud.version> </properties> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.5.RELEASE</version> <relativePath/> </parent> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>maven 依赖
<parent> <groupId>com.hly</groupId> <artifactId>04-spring-cloud-hystrix</artifactId> <version>1.0-SNAPSHOT</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <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-web</artifactId> </dependency> </dependencies>application.yml 配置文件
server: port: 8761 eureka: instance: hostname: localhost client: registerWithEureka: false fetchRegistry: false serviceUrl: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ spring: application: name: service-serverSpringBoot 启动类
@SpringBootApplication @EnableEurekaServer public class ServiceServerApplication { public static void main(String[] args) { SpringApplication.run(ServiceServerApplication.class, args); } }maven 依赖
<parent> <groupId>com.hly</groupId> <artifactId>04-spring-cloud-hystrix</artifactId> <version>1.0-SNAPSHOT</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <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>application.yml
server: port: 8762 spring: application: name: service-client eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/SpringBoot 启动类
@SpringBootApplication @RestController @EnableEurekaClient public class ServiceClientApplication { public static void main(String[] args) { SpringApplication.run(ServiceClientApplication.class, args); } @Value("${server.port}") String port; @RequestMapping("/hello") public String home(@RequestParam(value = "name", defaultValue = "hly") String name) { return "hi " + name + " ,i am from port:" + port; } }maven 依赖
<parent> <groupId>com.hly</groupId> <artifactId>04-spring-cloud-hystrix</artifactId> <version>1.0-SNAPSHOT</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <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> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> </dependencies>application.yml
eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/ server: port: 8765 spring: application: name: service-feign # 开启熔断 feign: hystrix: enabled : true服务层接口
//服务名指定调用哪个服务 @FeignClient(value = "service-client",fallback =HiHystrixServiceImpl.class) public interface HiHystrixService { //这里的映射名和需要调用的服务的映射名一样 @RequestMapping(value = "/hello",method = RequestMethod.GET) String sayHiFromClientOne(@RequestParam(value = "name") String name); }指定实行快速失败的方法
@Component public class HiHystrixServiceImpl implements HiHystrixService { @Override public String sayHiFromClientOne(String name) { return "sorry"+name; } }Controller 层
@RestController public class HiController { @Autowired HiHystrixService hiHystrixService; /** * 消费服务 * @param name * @return */ @GetMapping(value = "/hi") public String sayHi(@RequestParam String name) { return hiHystrixService.sayHiFromClientOne( name ); } }SpringBoot 启动类
@SpringBootApplication @EnableEurekaClient @EnableDiscoveryClient @EnableFeignClients public class ServiceFeignHystrixApplication { public static void main(String[] args) { SpringApplication.run(ServiceFeignHystrixApplication.class, args); } }1、依次启动 注册中心 server ,启动两个 client(启动一个后,修改application.yml 配置文件的端口,再次启动一次),接着启动 Feign 客户端,访问: http://localhost:8765/hi?name=hly 当关掉客户端后,将会执行快速失败策略。
04-spring-cloud-hystrix:https://github.com/huangliangyun/Spring-Cloud-Finchley
我的 Github:Github : 个人网站: sirius 的博客 E-mail: 1136513099@qq.com
推荐阅读 史上最全,最完美的 JAVA 技术体系思维导图总结,没有之一!