springcloud使用hystrix组件实现微服务集群降级、熔断、限流

spring-cloud-hystrix实现微服务集群降级、熔断、限流

参考:https://windmt.com/2018/04/15/spring-cloud-4-hystrix/

https://blog.csdn.net/varyall/article/details/99722594?spm=1001.2014.3001.5502

https://www.cnblogs.com/sword-successful/p/13548233.html

注意:springcloud使用Hystrix在springcloud整合ribbon和feign的基础上

一、Hystrix实现降级和隔离

服务降级场景:

  • A服务调用B服务的一些接口,现在为了防止B服务的接口出现因为服务宕机造成的问题,需要对B服务的接口增加降级处理
  • B服务的接口调用方式
    • 采用@FeignClient注解的方式进行调用
    • 采用RestTemplate的方式
  • 因此服务的降级和服务的隔离,都是建立在A服务调用B服务的接口处理上。
1、Hystrix快速引入
(1)添加依赖
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-hystrix</artifactId>
	<version>1.4.7.RELEASE</version> 
</dependency>
(2)启动类开启Hystrix
  • @EnableCircuitBreaker 启用开启断路器熔断命令
  • @EnableHystrix 开启断路器组件
@EnableHystrix
@EnableCircuitBreaker
@SpringBootApplication
public class ConsumerMovieFeignApplication {
   public static void main(String[] args) {
      SpringApplication.run(ConsumerMovieFeignApplication.class, args);
   }
}
(3)常用配置
# 开启熔断机制
feign:
  hystrix:
    enabled: true
# ribbon配置
ribbon:
  # 开启eureka与ribbon的集成
  eureka:
    enabled: true
  # 暂不开启熔断机制
  hystrix:
    enabled: false
  # 配置ribbon默认的超时时间
  ConnectTimeout: 20000
  ReadTimeout: 20000
  # 是否开启重试
  OkToRetryOnAllOperations: true
  # 重试的时候实例切换次数
  MaxAutoRetriesNextServer: 3
  # 每个实例重试次数
  MaxAutoRetries: 2
  
## hystrix相关配置
## hystrix默认会读取classpath下的config.properties文件,application会覆盖config.properties中的属性
hystrix:
  threadpool:
    # 指定服务的配置
    user-service:
      coreSize: 20
      maxQueueSize: 200
      queueSizeRejectionThreshold: 3
    # userThreadPool是UserTimeOutCommand中配置的threadPoolKey
    userThreadPool:
      coreSize: 20
      maxQueueSize: 20
      queueSizeRejectionThreshold: 3
    # 这是默认的配置
    default:
      coreSize: 10
      maxQueueSize: 200
      queueSizeRejectionThreshold: 2
  command:
    # 指定feign客户端中具体的方法
    UserService#timeout():
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 20000
    userCommandKey:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 15000
    # 这是默认的配置
    default:
      execution:
        timeout:
          enabled: true
         # 隔离配置
        isolation:
          strategy: THREAD 
          thread:
            timeoutInMilliseconds: 15000
            interruptOnTimeout: true
            interruptOnFutureCancel: false
          semaphore:
            maxConcurrentRequests: 2
      # 降级配置
      fallback:
        enabled: true
        isolation:
          semaphore:
            maxConcurrentRequests: 10
      # 断路器配置
      circuitBreaker:
        enabled: true
        forceOpen: false
        forceClosed: false
        requestVolumeThreshold: 4
        errorThresholdPercentage: 50
        sleepWindowInMilliseconds: 10000
      metrics:
        rollingStats:
          timeInMilliseconds: 5000
          numBuckets: 10
        rollingPercentile:
          enabled: true
          timeInMilliseconds: 60000
          numBuckets: 6
          bucketSize: 100
        healthSnapshot:
          intervalInMilliseconds: 500
2、常用基于FeignClient注解实现
(1)@FeignClient+回调工厂方式
  • @FeignClient的fallbackFactory使用工厂方式
@FeignClient(name = "microservice-provider-user",
             fallbackFactory = HelloRollBackFactory.class)
public interface UserFeignClient {

  @RequestMapping(value = "/simple/{id}", method = RequestMethod.GET)
  public User findById(@PathVariable("id") Long id);
}
@Slf4j
@Component
public class HelloRollBackFactory implements FallbackFactory<HelloService> {

    @Override
    public HelloService create(Throwable throwable) {
        //可以抓取异常
        log.info("fallback; reason was: {}", throwable.getMessage());
        //返回实现的一个回调接口
        return new HelloService(){
            @Override
            public User findById(String name) {
                log.info("UserFeignClient#findById#id={}服务降级了",id);
                User user = new User();
                user.setId(0L);
                return user;
            }
        }
    }
}
(2)、@FeignClient+继承接口方式
@FeignClient(name = "microservice-provider-user", fallback = HystrixClientFallback.class)
public interface UserFeignClient {
  @GetMapping("/simple/{id}")
  public User findById(@PathVariable("id") Long id);
}
@Slf4j
@Component
public class HystrixClientFallback implements UserFeignClient {
  //回调类,返回一个默认的user,防止调用出现空指针的问题
  @Override
  public User findById(Long id) {
    log.info("UserFeignClient#findById#id={}服务降级了",id);
    User user = new User();
    user.setId(0L);
    return user;
  }
}
(3)通过@HystrixCommand+RestTemplate

https://www.cnblogs.com/zou-rong/p/12589390.html

  • 实现接口隔离(线程隔离和信号量隔离)
  • 实现调用的负载均衡
  • 实现调用过程中降级
#设置线程隔离超时时间为5S
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000
//在配置类中引入RestTemplate并使用Ribbon的@LoadBalanced注解
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
    return new RestTemplate();
}
@RestController
public class MovieController {
    
    @Autowired
    private RestTemplate restTemplate;

   //==========================线程隔离,缺点比较费资源=========================
    @GetMapping("/user/{id}")
    @HystrixCommand(groupKey="UserGroup", 
                    commandKey = "GetUserByIdCommand",
                    fallbackMethod = "findByIdFallback",
                    commandProperties = {
                        //超时时间,单位毫秒。超时进fallback
                        @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "100"),
                        //判断熔断的最少请求数,默认是10;只有在一定时间内请求数量达到该值,才会进行成功率的计算
                        @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
                        //熔断的阈值默认值50,表示在一定时间内有50%的请求处理失败,会触发熔断
                        @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "10"),
                    },
                    //线程池参数配置
                    threadPoolProperties = {
                        @HystrixProperty(name = "coreSize", value = "30"),
                        @HystrixProperty(name = "maxQueueSize", value = "101"),
                        @HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),
                        @HystrixProperty(name = "queueSizeRejectionThreshold", value = "15"),
                        @HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "12"),
                        @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "1440")
                    })
    public User findById(@PathVariable Long id) {
        //通过restTemplate进行调用
        return this.restTemplate
            .getForObject("http://microservice-provider-user/simple/" + id,
                          User.class);
    }
    public User findByIdFallback(Long id) {
        log.info("UserFeignClient#findById#id={}服务降级了",id);
        User user = new User();
        user.setId(0L);
        return user;
    }
    
    //========================信号量隔离,建议使用========================
    //注意下面这一个注解里面的内容,这样配置更加详细一点
    @GetMapping("/user2/{id}")
    @HystrixCommand(fallbackMethod = "findByIdFallback", 
         commandProperties = @HystrixProperty(
             		name = "execution.isolation.strategy", 
                    value = "SEMAPHORE"))
    public User findById(@PathVariable Long id) {
        return this.restTemplate
            .getForObject("http://microservice-provider-user/simple/" + id, 
                          User.class);
    }
	//服务降级出口
    public User findByIdFallback(Long id) {
        log.info("UserFeignClient#findById#id={}服务降级了",id);
        User user = new User();
        user.setId(0L);
        return user;
    }
    
}
//=========================可以配置统一的服务降级的处理==============//
@RestController
@RequestMapping("/consumer")
@Slf4j
@DefaultProperties(defaultFallback = "getUserByIdFallBack")//指定默认降级函数
public class ConsumerController {

    @Autowired
    private RestTemplate template;


    @GetMapping("/{id}")
    @HystrixCommand //启用熔断降级
    public String getUserById(@PathVariable("id")Long id){
        String url="http://user-service/user/"+id;
        long start=System.currentTimeMillis();
        String user = template.getForObject(url, String.class);
        long end=System.currentTimeMillis();
        log.debug("调用时长:{}",end-start);
        return user;
    }

  //注意此时的返回值没有限制,不能写参数,因为这是当前类通用的降级方法
    public String getUserByIdFallBack(){
        return "很抱歉,服务器正忙,请稍后再试。";
    }

}
(4)HystrixCommand注解源码
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface HystrixCommand {
    //配置全局唯一标识服务的名称
    String groupKey() default "";
    
    //配置全局唯一标识服务分组的名称,比如,库存系统、订单系统、系统用户就是一个单独的服务分组
    String commandKey() default "";

    //对线程池进行设定,细粒度的配置,相当于对单个服务的线程池信息进行设置,也可多个服务设置同一个threadPoolKey构成线程组
    String threadPoolKey() default "";

    //执行快速失败的回调函数,@HystrixCommand修饰的函数必须和这个回调函数定义在同一个类中,因为定义在了同一个类中,所以fackback method可以是public/private均可
    String fallbackMethod() default "";

    //配置该命令的一些参数,如executionIsolationStrategy配置执行隔离策略,默认是使用线程隔离
    HystrixProperty[] commandProperties() default {};

    //线程池相关参数设置,具体可以设置哪些参数请见:com.netflix.hystrix.HystrixThreadPoolProperties
    HystrixProperty[] threadPoolProperties() default {};

    //调用服务时,除了HystrixBadRequestException之外,其他@HystrixCommand修饰的函数抛出的异常均会被Hystrix认为命令执行失败而触发服务降级的处理逻辑(调用fallbackMethod指定的回调函数),所以当需要在命令执行中抛出不触发降级的异常时来使用它,通过这个参数指定,哪些异常抛出时不触发降级(不去调用fallbackMethod),而是将异常向上抛出
    Class<? extends Throwable>[] ignoreExceptions() default {};

    //定义hystrix observable command的模式
    ObservableExecutionMode observableExecutionMode() default ObservableExecutionMode.EAGER;

    //任何不可忽略的异常都包含在HystrixRuntimeException中
    HystrixException[] raiseHystrixExceptions() default {};

    //默认的回调函数,该函数的函数体不能有入参,返回值类型与@HystrixCommand修饰的函数体的返回值一致。如果指定了fallbackMethod,则fallbackMethod优先级更高
    String defaultFallback() default "";
}
(5)通过OrderHystrixCommand编码实现
//线程池隔离
@Slf4j
public class OrderHystrixCommand extends HystrixCommand<String> {
    //这种方式可以调用Feign的接口
    OrderService orderService;
    
    //设置线程池隔离
    public OrderHystrixCommand(OrderService orderService) {
        super(Setter
           //服务分组
           .withGroupKey(HystrixCommandGroupKey.Factory.asKey("orderGroup")) 
           //命令分组
           .andCommandKey(HystrixCommandKey.Factory.asKey("orderCommandKey"))
           //线程池配置
           .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("OrderPool"))
           .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter()
               .withCoreSize(10).withKeepAliveTimeMinutes(5)
               .withMaxQueueSize(10).withQueueSizeRejectionThreshold(10000))
            //断路器配置
            .andCommandPropertiesDefaults(
                //设置线程池隔离
                HystrixCommandProperties.Setter().withExecutionIsolationStrategy(
                    	HystrixCommandProperties.ExecutionIsolationStrategy.THREAD))
        );
        //设置Service
        this.orderService = orderService;
    }
    
	// 方式一:通过线程池的run方法执行getOrderList 最后得到结果
    @Override
    protected List run() throws Exception {
        return orderService.getOrderList();
    }
    
    /***  方式二:采用Rest调用的方式 直接采用父类的实现
     public OrderHystrixCommand() {
        super(HystrixCommandGroupKey.Factory.asKey("orderGroup"));
    }
	
    //这里也可以使用RestTemplate进行调用
    @Override
    protected String run() throws Exception {
        LOGGER.info("start query timeout endpoint");
        URL url = new URL("http://localhost:8002/user/timeout");
        byte[] result = new byte[1024];
        url.openStream().read(result);
        return new String(result);
    }
 
    @Override
    protected String getFallback() {
        // 执行超时、出错或者开启熔断之后,使用这个方法返回
        // 这种策略称为服务降级
        // 这里可以是一个固定返回值,查询缓存等
        return "服务降级,暂时不可用";
    }
   ***/     
}

//测试采用命令方式调用
public  class UnitTest {
    @Test
    public void testGetOrder(){
        Future<List> future =new GetOrderCommand("hystrix-order").queue();
    }
}

//信号量隔离
public class QueryUserIdCommand extends HystrixCommand<Integer> {
    private final static Logger logger = LoggerFactory.getLogger(QueryUserIdCommand.class);
    //调用Feign的接口
    private UserServiceProvider userServiceProvider;
	public QueryUserAgeCommand(UserServiceProvider userServiceProvider) {
    	super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("userService"))
            	.andCommandKey(HystrixCommandKey.Factory.asKey("queryByUserId"))
	            .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                         //至少有10个请求,熔断器才会开始进行错误率计算
    	                .withCircuitBreakerRequestVolumeThreshold(10)
                         //熔断器中断请求,5秒后会进入一个半打开状态,放开部分请求去进行重试
        	            .withCircuitBreakerSleepWindowInMilliseconds(5000)
                         错误率达到50%就开启熔断保护   
            	        .withCircuitBreakerErrorThresholdPercentage(50)  
                         //设置为信号量隔离
                	    .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE) 
                    	.withExecutionTimeoutEnabled(true))
	            .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties
    	                .Setter().withCoreSize(10)));
    	this.userServiceProvider = userServiceProvider;
	}
 
	@Override
	protected Integer run() {
    	return userServiceProvider.queryByUserId();
	}
 
	@Override
	protected Integer getFallback() {
    	   return -1;
	}
}

//异步的方式调用
Future<List> future =new QueryUserIdCommand("hystrix-order").queue();
//同步的方式调用
new UserExceptionCommand().execute();

/**响应式**/
//立即执行
Observable<String> ho = new UserExceptionCommand().observe();
//只有当被订阅的时候才会开始执行
Observable<String> co = new UserExceptionCommand().toObservable();
//阻塞的
ho.toBlockingObservable().single();
//非阻塞的
ho.subscribe(new Observer<String>() {
    @Override
    public void onCompleted() {
        // nothing needed here
    }
    @Override
    public void onError(Throwable e) {
        e.printStackTrace();
    }
    @Override
    public void onNext(String v) {
        System.out.println("onNext: " + v);
    }
});
// 非阻塞的
ho.subscribe(new Action1<String>() {
    @Override
    public void call(String v) {
        System.out.println("onNext: " + v);
    }
});

二、Hystrix实现熔断

@RestController
public class DeptController
{
    @Autowired
    private DeptService service;

    @RequestMapping(value = "/dept/add", method = RequestMethod.POST)
    public boolean add(@RequestBody Dept dept)
    {
        return service.add(dept);
    }

    @RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET)    //一旦调用服务方法失败抛出“错误信息后,胡自动范湖用@HystrixCommand标注好的fallbackMethod调用类中的指定方法”
    @HystrixCommand(fallbackMethod = "processHystrixGet")
    public Dept get(@PathVariable("id") Long id)
    {
        Dept dept = service.get(id);

        //模拟异常
        if(null == dept)
        {
            throw new RuntimeException("该id的信息不存在! id=" + id);
        }
        return service.get(id);
    }

    public Dept processHystrixGet(@PathVariable("id") Long id)
    {
        return new Dept().setDname("id没有对应的信息, null -- @HystrixCommand, id=" + id)
                .setDb_source("no this database in MySQL.");
    }
}

三、Hystrix实现限流

https://blog.csdn.net/qq_40990836/article/details/108500728

四、请求缓存功能@CacheResult

  • @CacheResult 该注解用来标记请求命令返回的结果应该被缓存,它必须与@HystrixCommand注解结合使用 cacheKeyMethod
  • @CacheRemove 该注解用来让请求命令的缓存失效,失效的缓存根据定义Key决定 commandKey, cacheKeyMethod
  • @CacheKey 该注解用来在请求命令的参数上标记,使其作为缓存的Key值,如果没有标注则会使用所有参数。如果同事还是使用了@CacheResult和@CacheRemove注解的cacheKeyMethod方法指定缓存Key的生成,那么该注解将不会起作用 value

附录、使用断路器监控

1、创建项目springcloud-hystrix-dashboard
(1)、添加依赖
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
(2)、新建项目 hstrix-dashboard

hstrix-dashboard支持的三种不同的集群监控

  1. 默认的集群监控:
    • 通过 URL:http://turbine-hostname:port/turbine.stream 开启,实现对默认集群的监控。
  2. 指定的集群监控:
    • 通过 URL:http://turbine-hostname:port/turbine.stream?cluster=[clusterName] 开启,实现对 clusterName 集群的监控。
  3. 单体应用的监控:
    • 通过 URL:http://hystrix-app:port/hystrix.stream 开启 ,实现对具体某个服务实例的监控。
    • 现在这里的 URL 应该为 http://hystrix-app:port/actuator/hystrix.stream,
    • Actuator 2.x 以后 endpoints 全部在 /actuator 下,可以通过 management.endpoints.web.base-path 修改)
  4. 前两者都对集群的监控,需要整合 Turbine 才能实现。这一部分我们先实现对单体应用的监控,这里的单体应用就用我们之前使用 Feign 和 Hystrix 实现的服务消费者
(3)、在已有项目上添加依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
(4)、启动类
  • @EnableHystrixDashboard 开启DashBoard面板
@EnableHystrixDashboard
@SpringBootApplication
public class HystrixDashboardApplication {

    public static void main(String[] args) {
        SpringApplication.run(HystrixDashboardApplication.class, args);
    }
}
(5)、配置文件
spring:
  application:
    name: hystrix-dashboard
server:
  port: 11000
(6)、使用Hystrix的客户端,为服务实例添加 endpoint
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2、使用Dashboard面板实现Hystrix监控
1、添加依赖
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
3、修改配置
server:
  port: 8030
4、启动类
  • @EnableDiscoveryClient 开启服务注册和发现
  • @EnableFeignClients 开启远程调用
  • @EnableHystrixDashboard 开启DashBoard面板
  • @EnableCircuitBreaker 开启断路器
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@EnableHystrixDashboard
@EnableCircuitBreaker
public class ConsumerApplication {

	public static void main(String[] args) {
		SpringApplication.run(ConsumerApplication.class, args);
	}
}
测试

打开http://localhost:9001/hystrix
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-93MLhG3Y-1655862891397)(…/…/typora-img/hystrix-dashboard-1.jpg)]
访问:http://localhost:9001/hystrix.stream
看到监控图形化显示
http://favorites.ren/assets/images/2017/springcloud/hystrix-dashboard-2.jpg

使用Turbine面板实现监控
添加依赖
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-turbine</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-netflix-turbine</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-hystrix-dashboard</artifactId>
</dependency>
添加配置
server:
  port: 8031
spring:
  application:
    name: microservice-hystrix-turbine
eureka:
  client:
    serviceUrl:
      defaultZone: http://user:password123@localhost:8761/eureka
  instance:
    prefer-ip-address: true
turbine:
  aggregator:
    clusterConfig: default
  appConfig: microservice-consumer-movie-ribbon-with-hystrix,microservice-consumer-movie-feign-with-hystrix
  clusterNameExpression: "'default'"
  • turbine.appConfig :配置Eureka中的serviceId列表,表明监控哪些服务
  • turbine.aggregator.clusterConfig :指定聚合哪些集群,多个使用”,”分割,默认为default。
  • turbine.clusterNameExpression : 1. clusterNameExpression指定集群名称,默认表达式appName
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值