SpringCloud之Hystrix

SpringCloud之Hystrix

Hystrix学习笔记

Hystrix是什么,能干什么

Hystrix是一个用于处理分布式系统的掩延时和容错的开源库,在分布式系统中,很多依赖会因为超时,异常等调用失败,Hystrix能保证在某一个依赖出错时,不会导致整体任务失败,避免计量级联故障。
断路器本身是一种开关装置。当某个服务原件发生故障,通过断路器的故障监控(类似熔断保险丝),向调用方法返回一个预期的可处理的备选响应。而不是长时间的等待,或者抛出调用方无法处理的异常。这样就避免线程被长时间占用,而导致服务“雪崩”

三个重要概念

  1. 服务降级:服务器忙的时候不让服务器等待,返回一个友好提示。fallback
    哪些情况会降级?
    a. 程序运行异常
    b. 超时
    c. 服务熔断触发降级
    d. 线程池/信号量打满也会导致服务降级
  2. 服务熔断:达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级返回一个友好提示
  3. 服务限流:秒杀高并发等操作,严禁服务器一窝蜂的进来,排队一个一个进

一,服务降级的具体案例及实现方法

构建一个支付微服务8001(端口号,以下用端口号代替模块名)

注册进eureka服务注册中心,使用openFeign进行服务调用

  1. pom.xml
    除了其他配置外,加上hystrix依赖
<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
  1. yml配置文件
server:
  port: 8001

spring:
  application:
    name: cloud-payment-hystrix-service

eureka:
  client:
    register-with-eureka: true     #是否将自己注册进eureka-server
    fetch-registry: true           #是否从eureka-server抓取已有的注册信息,默认为true,集群必须设置为true才能配合ribbon使用负载均衡
    service-url:
      #defaultZone: http://localhost:7001/eureka         #注册地址
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka

ribbon:
  eureka:
    enabled: true
  1. service方法
    两个方法,一个是模拟正常调用,一个是模拟超时
@Service
public class PaymenthystrixService {

    public String paymentInfo_ok(Integer id){
        return "线程池:"+Thread.currentThread().getName()+"  paymentInfo_ok,id: " +id+"  成功";
    }

    public String paymentInfo_timeout(Integer id){
        try {
            TimeUnit.SECONDS.sleep(3);  //程序暂停三秒,模拟超时的情况
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "线程池:"+Thread.currentThread().getName()+"  paymentInfo_ok,id: " +id+"  超时";
    }

}

  1. contorller方法
    分别调用service的两个方法
@RestController
@Slf4j
public class PaymentHystrixContorller {

    @Resource
    private PaymenthystrixService paymenthystrixService;

    @Value("${server.port}")
    private String serverPort;

    @RequestMapping("/payment/hystrix/ok/{id}")
    public String PaymentInfo_ok(@PathVariable("id") Integer id){
        return paymenthystrixService.paymentInfo_ok(id);
    }

    @RequestMapping("/payment/hystrix/timeout/{id}")
    public String PaymentInfo_timeout(@PathVariable("id") Integer id){
        return paymenthystrixService.paymentInfo_timeout(id);
    }

}
  1. 加入Hystrix相关配置
    在主启动类加入@EnableCircuitBreaker标签
@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class})
@EnableEurekaClient
@EnableCircuitBreaker
public class PaymentHystrixMain8001 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentHystrixMain8001.class,args);
    }
}

在service方法上加入 @HystrixCommand(fallbackMethod = “”,commandProperties = {
@HystrixProperty(name="",value = “”) }) 标签,并写上备用方法

@Service
public class PaymenthystrixService {

    public String paymentInfo_ok(Integer id){
        return "线程池:"+Thread.currentThread().getName()+"  paymentInfo_ok,id: " +id+"  成功";
    }


    @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = { //fallbackMethod指定备用方法
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "3000")//规定三秒钟以内属于正常,超过三秒调用备用方法
    })
    public String paymentInfo_timeout(Integer id){
        try {
            TimeUnit.SECONDS.sleep(5);  //程序暂停五秒,模拟超时的情况,此时规定的超时时间最多是3秒,运行之后请求该方法会直接超时,然后调用备用方法
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "线程池:"+Thread.currentThread().getName()+"  paymentInfo_timeout,id: " +id+"  超时";
    }
    //出现异常时调用的备用方法
    public String paymentInfo_TimeOutHandler(Integer id){
        return "线程池:"+Thread.currentThread().getName()+"  paymentInfo_TimeOutHandler,id: " +id+"  fallback";
    }

}

最终访问会超时的方法时,一旦超时,则会调用备用方法
运行结果

构建一个订单微服务80(端口号,以下用端口号代替模块名)

80调用8001服务,也使用Hystrix服务降级
一般Hystrix服务降级实际开发只需要放在客户端

  1. yml文件
server:
  port: 80

spring:
  application:
    name: cloud-order-hystrix-service

eureka:
  client:
    register-with-eureka: true     #是否将自己注册进eureka-server
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:7001/eureka         #注册地址
      #defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
hystrix:
  metrics:
    enabled: true

  1. 主启动类添加 @EnableCircuitBreaker
@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class})
@EnableEurekaClient
@EnableFeignClients
@EnableCircuitBreaker
public class OrderHystrixMain80 {
    public static void main(String[] args) {
        SpringApplication.run(OrderHystrixMain80.class,args);
    }
}

  1. 业务类
    conntorller //客户端的Hystrix处理是放在contorller中
@RestController
@Slf4j
public class OrderContorller {

    @Resource
    private OrderService orderService;

    @RequestMapping("/consumer/payment/hystrix/ok/{id}")
    public String PaymentInfo_ok(@PathVariable("id") Integer id){
        return orderService.PaymentInfo_ok(id);
    }


    @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = { //fallbackMethod指定备用方法
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "3000")//规定三秒钟以内属于正常,唱超过三秒调用备用方法
    })
    @RequestMapping("/consumer/payment/hystrix/timeout/{id}")
    public String PaymentInfo_timeout(@PathVariable("id") Integer id){
        return orderService.PaymentInfo_timeout(id);
    }

    public String paymentInfo_TimeOutHandler(@PathVariable("id") Integer id){
        return "我是消费者80,支付系统繁忙";
    }
}

service

@Component
@FeignClient("CLOUD-PAYMENT-HYSTRIX-SERVICE")
public interface OrderService {

    @RequestMapping("/payment/hystrix/ok/{id}")
    public String PaymentInfo_ok(@PathVariable("id") Integer id);


    @RequestMapping("/payment/hystrix/timeout/{id}")
    public String PaymentInfo_timeout(@PathVariable("id") Integer id);

}

这样做的缺点,① 每个方法都有一个对应的服务降级备用方法,导致冗余,代码膨胀 ② fallback和业务代码混在一块

针对于上述问题的改进

① 除了个别重要的业务需要单独配置一个备用降价方法之外,其余的可以通过@DefaultProperties(defaultFallBack="")统一跳转到统一的处理结果页面

@RestController
@Slf4j
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod") //指定全局方法使用的fallback方法
public class OrderContorller {

    @Resource
    private OrderService orderService;

    @RequestMapping("/consumer/payment/hystrix/ok/{id}")
    public String PaymentInfo_ok(@PathVariable("id") Integer id){
        return orderService.PaymentInfo_ok(id);
    }

    @HystrixCommand //这个标签表示该方法故障时,调用类名上@DefaultProperties所指定的类
    @RequestMapping("/consumer/payment/hystrix/timeout/{id}")
    public String PaymentInfo_timeout(@PathVariable("id") Integer id){
        return orderService.PaymentInfo_timeout(id);
    }

    //下边是全局fallback方法
    public String payment_Global_FallbackMethod(){
        return "信息处理异常,稍后再试";
    }
}

总结:三步,再类名和方法名加上注解,再写一个fallback方法

② fallback和业务代码混在一块

新建一个类FallbackService,实现service接口,重写所有的方法,然后全部作为fallback方法,再到添加@FeignClient(value = “CLOUD-PAYMENT-HYSTRIX-SERVICE”,fallback = FallbackService.class)
当service中的调用故障时,自动通过fallback属性跳转执行service的实现类对应重写的方法

service

@Component
@FeignClient(value = "CLOUD-PAYMENT-HYSTRIX-SERVICE",fallback = FallbackService.class) //g该注解表示,当service中的方法出现异常时,会去调用service的实现类FallbackService重写的方法
public interface OrderService {

    @RequestMapping("/payment/hystrix/ok/{id}")
    public String PaymentInfo_ok(@PathVariable("id") Integer id);


    @RequestMapping("/payment/hystrix/timeout/{id}")
    public String PaymentInfo_timeout(@PathVariable("id") Integer id);

}

service的实现类

@Component
public class FallbackService implements OrderService {
    @Override
    public String PaymentInfo_ok(Integer id) {
        return "FallBack方法PaymentInfo_ok";
    }

    @Override
    public String PaymentInfo_timeout(Integer id) {
        return "FallBack方法PaymentInfo_timeout 超时";
    }
}

重要的是,要在yml文件中打开feign对hystrix的支持
feign:
hystrix:
enabled: true

contorller 删除了不使用的方法,避免阅读干扰

@RestController
@Slf4j
public class OrderContorller {

    @Resource
    private OrderService orderService;

    @RequestMapping("/consumer/payment/hystrix/ok/{id}")
    public String PaymentInfo_ok(@PathVariable("id") Integer id){
        return orderService.PaymentInfo_ok(id);
    }
}

测试:访问 http://localhost/consumer/payment/hystrix/ok/1 此时8001以已经宕机,80会尝试调用8001,但有8001宕机,会去执行service实现类的对应方法,得到重写方法的返回值

总结:两步:写一个service的实现类,作为fallback方法,然后再到service上添加feign注解(主要是配置注解中fallback的属性值),注意,要在yml文件中打开feign对hystrix的支持

二,服务熔断

是一种微服务链路保护机制,当链路上某个微服务出错,会进行服务降级,进而熔断该节点微服务的调用,快速返回错误响应信息,当检测到服务响应正常后,恢复调用链路。
在springcloud中熔断机制靠Hystrix实现,当失败调用到一定阈值,缺省是5秒20次调用失败,就会启动熔断机制。熔断的注解是:@HystrixCommand原理图

案例

在刚才8001项目的基础上:

contorller 修改为

@RestController
@Slf4j
public class PaymentHystrixContorller {

    @Resource
    private PaymenthystrixService paymenthystrixService;

   
    //------------------服务熔断-----------------
    @GetMapping("/payment/circuit/{id}")
    public String paymentCircuitBreaker(@PathVariable("id") Integer id){
        String result = paymenthystrixService.paymentCircuitBreaker(id);
        System.out.println("result__:"+result);
        return result;
    }

}

service修改为

@Service
public class PaymenthystrixService {

    //-------服务熔断-------------------------------------------------------------------------------------------------------------------------

    @HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
            @HystrixProperty(name = "circuitBreaker.enabled",value = "true"), //是否开启断路器
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"), //请求次数
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"), //时间窗口期 10秒中有多少次失败
            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"), //失败率达到多少后跳闸
    })
    public String paymentCircuitBreaker(@PathVariable("id") Integer id){
        if(id<0){
            throw new RuntimeException("不能为负数");
        }
        String serialNumber = IdUtil.simpleUUID();

        return Thread.currentThread().getName()+"\t"+"调用成功,流水号:"+serialNumber;
    }
    //fallback方法
    public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id){
        return "id不能为负数,稍后再试";
    }
}

最终运行:传入正数时运行正常,传入负数时,运行调用fallback方法,且短时间内即使再传入正数调用也会调用fallback方法
!!!

服务限流

该部分后面讲

图形化Dashboard搭建

建立新module
pom文件加入Dashboard依赖

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
    </dependency>

yml配置文件,只配置端口号为9001

server:
  port: 9001

主启动类

@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardMain {
    public static void main(String[] args) {
        SpringApplication.run(HystrixDashboardMain.class,args);
    }
}

再需要被监控的服务(8001)中加入actuator的依赖

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

注意:这里有一个坑,在8001的主启动类中要加入如下一个配置

@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class})
@EnableEurekaClient
@EnableCircuitBreaker
public class PaymentHystrixMain8001 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentHystrixMain8001.class,args);
    }

    /**
     * 此配置是为了服务监控而配置,与服务容错本身无关,springcloud升级后的坑
     * ServletRegistrationBean因为springboot的默认路劲不是“/hystrix.stream”
     * 只要在自己的项目配置中加上下面的servlet就可以了
     */
    @Bean
    public ServletRegistrationBean getServlet(){
        HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
        ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
        registrationBean.setLoadOnStartup(1);
        registrationBean.addUrlMappings("/hystrix.stream");
        registrationBean.setName("HystrixMetricsStreamServlet");
        return registrationBean;
    }
}

hystrix首页

接下来就是将8001使用9001监控起来

!!!

启动8001,然后监控

监控
对于服务的状态都可以直观的查看到

--------------------------------------------------------------------------------------------完---------------------------------------------------------------------------------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值