雪崩效应

在微服务架构中通常会有多个服务层调用,基础服务的故障可能会导致级联故障,进而造成整个系统不可用的情况,这种现象被称为服务雪崩效应。服务雪崩效应是一种因 “服务提供者” 的不可用导致 “服务消费者” 的不可用, 并将不可用逐渐放大的过程。
如果下图所示:A 作为服务提供者,B 为 A 的服务消费者,C 和 D 是 B 的服务消费者。A 不可用引起了 B 的不可用,并将不可用像滚雪球一样放大到 C 和 D 时,雪崩效应就形成了。

Spring Cloud学习笔记【四】断路器Hystrix-LMLPHP

断路器

Netflix 创建了一个名为 Hystrix 的库, 实现了断路器的模式。“断路器” 本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。

Spring Cloud学习笔记【四】断路器Hystrix-LMLPHP

当然,在请求失败频率较低的情况下,Hystrix 还是会直接把故障返回给客户端。只有当失败次数达到阈值时,断路器打开并且不进行后续通信,而是直接返回备选响应。当然,Hystrix 的备选响应也是可以由开发者定制的。

Spring Cloud学习笔记【四】断路器Hystrix-LMLPHP

Ribbon 整合 Hystrix

新建spring start project,导入依赖

 <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
</dependencies>

属性配置(application.yml)

server:
port: 9001
spring:
application:
name: service-hystrix-ribbon
eureka:
client:
serviceUrl:
defaultZone: http://admin:123456@localhost:8761/eureka/

开启hystrix功能,在启动类上加上@EnableHystrix注解

 package com.carry.springcloud;

 import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate; @EnableHystrix
@EnableEurekaClient
@SpringBootApplication
public class ServiceHystrixRibbonApplication { public static void main(String[] args) {
SpringApplication.run(ServiceHystrixRibbonApplication.class, args);
} @Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
}

添加fallback方法

package com.carry.springcloud.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; @RestController
public class RibbonController { @Autowired
RestTemplate restTemplate; @Value("${server.port}")
String port; @GetMapping("/getPoducerInfo")
@HystrixCommand(fallbackMethod = "getPoducerInfoFallback")
public String getPoducerInfo() {
String result = this.restTemplate.getForObject("http://service-producer/getPortInfo", String.class);
return result;
} public String getPoducerInfoFallback(){
return "getPoducerInfo异常,端口:" + port;
}
}

测试

依次启动eureka-server、service-producer、service-hystrix-ribbon,如下图成功注册到eureka

Spring Cloud学习笔记【四】断路器Hystrix-LMLPHP

访问localhost:9001/getPoducerInfo,结果轮询显示8080与8081,停掉8081

Spring Cloud学习笔记【四】断路器Hystrix-LMLPHP

接着访问localhost:9001/getPoducerInfo,当访问到8081时出现以下结果,说明服务降级调用了fallback方法

Spring Cloud学习笔记【四】断路器Hystrix-LMLPHP

Feign 整合 Hystrix

新建项目并导入依赖

 <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>

配置属性

server:
port: 9002
spring:
application:
name: service-hystrix-feign
eureka:
client:
serviceUrl:
defaultZone: http://admin:123456@localhost:8761/eureka/
feign:
hystrix:
enabled: true

启动类开启Feign支持

 package com.carry.springcloud;

 import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients; @EnableFeignClients
@SpringBootApplication
public class ServiceHystrixFeignApplication { public static void main(String[] args) {
SpringApplication.run(ServiceHystrixFeignApplication.class, args);
}
}

添加Feign接口

 package com.carry.springcloud.api;

 import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping; import com.carry.springcloud.fallback.ConsumerFeignFallback; @FeignClient(name = "service-producer", fallback = ConsumerFeignFallback.class)
public interface ConsumerFeignClient { @GetMapping("/getPortInfo")
public String produce();
}

添加fallback类

 package com.carry.springcloud.fallback;

 import org.springframework.stereotype.Component;

 import com.carry.springcloud.api.ConsumerFeignClient;

 @Component
public class ConsumerFeignFallback implements ConsumerFeignClient { @Override
public String produce() {
return "服务调用失败!";
} }

控制层注入Feign接口

 package com.carry.springcloud.controller;

 import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController; import com.carry.springcloud.api.ConsumerFeignClient; @RestController
public class ConsumerController { @Autowired
private ConsumerFeignClient feignClient; @GetMapping("/getPoducerInfoByFeign")
public String getPoducerInfoByFeign() {
return feignClient.produce();
} }

测试

测试方法同上不再赘述

05-11 17:55