【云原生】springcloud11——Hystrix是怎样让微服务“易凡峰顺”的2

简介: 【云原生】springcloud11——Hystrix是怎样让微服务“易凡峰顺”的

4 JMeter高并发测试

直到目前为止,一切都是"易凡峰顺"的,来点异常场景。

4.1 JMeter简介

JMeter是开源软件Apache基金会下的一个性能测试工具,用来测试部署在服务器端的应用程序的性能。近来,JMeter因为其使用简单,现在也被社区作为接口测试工具… 举个栗子,你开了一个网店,兴冲冲地准备双十一大干一把,没想当天活动的时候大量用户一访问你的网店,你的网店挂了,那怎么办?办法就是在实际搞活动之前,先测试一下以确认系统能承受那么多的用户,当然测试的时候我们不需要请真正的这么多实际用户,否则得花多少钱啊,JMeter就是那个能帮助模拟大量用户访问你网站的一个软件。

229003a4321f40a296dc15090e4f85c3.png

4.2 下载、安装JMeter

JMeter下载地址:http://jmeter.apache.org/download_jmeter.cgi


67e9942c86b74c6b9a33b51dc878df75.png

下载后解压到你系统下的任意目录,然后运行%JMETER_HOME%\bin\jmeter.bat文件

4.3 JMeter压力测试

新建测试计划,鼠标右键点击计划设置200*100 = 20000并发量。

c21cb3ac8f1a4a5fb24843b7d0648c51.png

新增http请求。

ba940a1784f64d65a14357d82c059207.png

配置如下。524ea74346f04dd694c579f68a9b3656.png


保存,启动压测。

后台不断地看到接收到的请求。

f5cd9629208c49ac9b5d1bb3d6fcbfd4.png

这个时候你访问:http://localhost:8001/payment/hystrix/ok/1

发现也开始转圈圈了,访问速度肉眼可见变慢。


这是因为tomcat的默认工作线程被占满了,没有多余的线程来分解压力和处理了。


如果此时外部消费者80也来访问,只能干等,最终导致消费者80不满意。8001被拖死。

5 客户端测试集成

5.1 建项目

新建cloud-consumer-feign-hystrix-order80(一般将Hystrix用于客户端,但其实她也可以应用于服务端)

8b7fde06203240db97bd4db8947e0b38.png

5.2 写pom

<dependencies>
    <!-- openfeign -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    <!--   hystrix     -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>
    <!--eureka client-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <!-- 引用自己定义的api通用包,可以使用Payment支付Entity -->
     <dependency>
            <groupId>com.wangzhou.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
     </dependency>
    <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.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

5.3 写yml

server:
  port: 80
eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://localhost:7001/eureka
#需要加上,否则会报错
ribbon:
  ReadTimeout: 4000
  ConnectTimeout: 4000

5.4 主启动

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

5.5 业务类

(1)service

@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT")
public interface PaymentHystrixService {
    @GetMapping("/payment/hystrix/ok/{id}")
    public String paymentOk(@PathVariable("id")Integer id);
    @GetMapping("/payment/hystrix/timeout/{id}")
    public String paymentInfo_timeout(@PathVariable("id") Integer id);
}

(2)Controller

@Slf4j
@RestController
public class OrderHystrixController {
    @Resource
    private PaymentHystrixService paymentHystrixService;
    @GetMapping("/consumer/payment/hystrix/ok/{id}")
    public String paymentOk(@PathVariable("id")Integer id) {
        String result = paymentHystrixService.paymentOk(id);
        return result;
    }
    @GetMapping("/consumer/payment/hystrix/timeout/{id}")
    public String paymentInfo_timeout(@PathVariable("id") Integer id) {
        String result = paymentHystrixService.paymentInfo_timeout(id);
        return result;
    }
}

5.6 正常测试

启动80

http://localhost/consumer/payment/hystrix/ok/1

447a5e7c94d14e7db9dbde5d9d33ba5b.png

http://localhost/consumer/payment/hystrix/tmeout/1


424018128ae44c1b960172ff74f952bd.png

5.7 高并发测试

启动JMeter对8001的压力测试,再通过80访问

http://localhost/consumer/payment/hystrix/ok/1

开始转圈圈了。狂点还可能报错。


0d8a3eff0b6a4c209085ec65ba71b186.png


服务端这么慢,用户不得骂你吗?我们来看看怎么解决。

6 Hystrix的服务降级

6.1 降级容错解决的维度要求


image.png

6.2 服务端的降级处理

在8001的timeout接口新增@HystrixCommand注解,进行如下改造,设置兜底机制。

   /*
      通过@HystrixCommand来指定哪个方法由Hystrix来接管
         fallbackMethod属性: 指定哪个方法作为兜底方法
     */
    @HystrixCommand(fallbackMethod ="paymentInfo_TimeoutHandler", commandProperties = {
            //设置自身超时调用时间的峰值为 3 秒,峰值内可以正常运行,超过了需要有兜底的方法处理,服务降级fallback
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
    })
    public String paymentInfo_Timeout(Integer id) throws InterruptedException {
        int timeout = 3;
        // Hystrix底层调用的是Tomcat的线程池,我们在这里将线程名放回
        TimeUnit.SECONDS.sleep(timeout);
        return "线程:  "+Thread.currentThread().getName()+"  paymentInfo_OK, id = "+id + "\t" + "耗时"+ timeout +"s";
    }
    public String paymentInfo_TimeoutHandler(Integer id) {
        return "线程:  "+Thread.currentThread().getName()+"  paymentInfo_TimeoutHandler, id = "+id + "\t/(ㄒoㄒ)/~~";
    }

主启动类增加@EnableCircuitBreaker注解触发熔断功能

257a119ffe0747d9b64040c6d2c9f3ce.png

测试下,访问:http://localhost/consumer/payment/hystrix/timeout/1

0328744b4d0f49c899fe27458389573a.png

可以注意到,此时处理的线程也变成了Hystrix...开头了。说明对于超时的情况使用了其它线程池的线程进行单独处理了。

将接口的核心方法改造下。从超时的情况改造称为异常。

    @HystrixCommand(fallbackMethod ="paymentInfo_TimeoutHandler", commandProperties = {
            //设置自身超时调用时间的峰值为 3 秒,峰值内可以正常运行,超过了需要有兜底的方法处理,服务降级fallback
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
    })
    public String paymentInfo_Timeout(Integer id) throws InterruptedException {
        int timeout = 3;
        int temp = 3 / 0;
        // Hystrix底层调用的是Tomcat的线程池,我们在这里将线程名放回
//        TimeUnit.SECONDS.sleep(timeout);
        return "线程:  "+Thread.currentThread().getName()+"  paymentInfo_OK, id = "+id + "\t" + "耗时"+ timeout +"s";
    }

再测试。

5bd2b78b3fc44bb7b5e4605a470d14f4.png

这说明对于异常或者超时的情况都将会使用兜底方案。

6.3 客户端的降级处理

之前我们说过,一般会将服务降级放在客户端,这是为了在上游及时发先问题,及时处理。现在就来实践下。

在80的yml中添加。

@SpringBootApplication
@EnableFeignClients
@EnableEurekaClient
@EnableHystrix
public class OrderHystrixMain80 {
    public static void main(String[] args) {
        SpringApplication.run(OrderHystrixMain80.class, args);
    }
}

改造80 controller中的paymentInfo_timeout接口。

   @GetMapping("/consumer/payment/hystrix/timeout/{id}")
    @HystrixCommand(fallbackMethod ="paymentInfo_TimeoutHandler", commandProperties = {
            //设置自身超时调用时间的峰值为 2 秒,峰值内可以正常运行,超过了需要有兜底的方法处理,服务降级fallback
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000")
    })
    public String paymentInfo_timeout(@PathVariable("id") Integer id) {
        String result = paymentHystrixService.paymentInfo_timeout(id);
        return result;
    }
    public String paymentInfo_TimeoutHandler(Integer id) {
        return "我是消费者80,对方系统繁忙,请稍后再试,/(ㄒoㄒ)/~~";
    }

测试。

在测试之前记得将支付微服务8001的paymentInfo_timeout逻辑从异常改为sleep哟。

9aaba4010bde4593af38e39ceba79d2c.png

测试结果如下。

1aa70f59b7d74c07a5ff7bdd20103868.png

6.4 全局服务降级的配置

上面的代码有如下问题。

  • 业务逻辑和异常处理被我们混在一块了,耦合度极高。
  • 每一个方法都需要有兜底方法

解决方法,使用@DefaultProperties设置全局的fallback方法。


d50bf29054354a9daeb09af4437c9fa0.png

80的OrderHystrixController中添加全局fallback方法:

    //全局fallback方法,不能有传参
    public String payment_Global_FallbackMethod(){
        return "Global异常处理信息,请稍后再试!";
    }

并在OrderHystrixController类上加上@DefaultProperties(defaultFallback = “payment_Global_FallbackMethod”),指定设置全局fallback方法。

de825d17f6ff4d358a0540d1f5105f4d.png

将之前的@HystrixCommand注掉,稍微对方法进行下改动。重启微服务80.

    @GetMapping("/consumer/payment/hystrix/timeout/{id}")
//    @HystrixCommand(fallbackMethod ="paymentInfo_TimeoutHandler", commandProperties = {
//            //设置自身超时调用时间的峰值为 2 秒,峰值内可以正常运行,超过了需要有兜底的方法处理,服务降级fallback
//            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000")
//    })
    @HystrixCommand
    public String paymentInfo_timeout(@PathVariable("id") Integer id) {
        int var = 10/0;
        String result = paymentHystrixService.paymentInfo_timeout(id);
        return result;
    }

结果如下。

b1017b8b10764f9bb202b2713564397f.png

6.5 通配服务降级

前面一节我们已经解决了代码膨胀的问题,接下来解决下代码耦合度过高的问题。

在80的service包下新建PaymentFallbackService类,实现PaymentHystrixService接口

public class PaymentFallbackService implements PaymentHystrixService{
    @Override
    public String paymentOk(Integer id) {
        return "------PaymentFallbackService  paymentOk /(ㄒoㄒ)/~~";
    }
    @Override
    public String paymentInfo_timeout(Integer id) {
        return "------PaymentFallbackService  paymentInfo_timeout /(ㄒoㄒ)/~~";
    }
}

PaymentHystrixService中的@FeignClient注解增加fallback参数即可。

41e3813bad11438c965fdf3e8fa4000a.png

访问ok接口:http://localhost/consumer/payment/hystrix/ok/1,没有任何毛病

7d545b8df1de404da5ad8a60ca236c5c.png

7 Hystrix的服务熔断

7.1 熔断理论


0252a19497ac4d6ca46ce8265fec64b1.png

前面介绍过服务熔断,这里再解释下,帮助读者理解。

(1)调用正常方法失败会触发降级,而降级会触发fallback方法

(2)但无论如何,降级一定是先调用正常方法,再调用fallback方法

(3)假如单位时间降级次数过多,会触发熔断

(4)熔断以后将会跳过正常方法直接调用fallback方法

(5)所谓的熔断后服务不可用,就是因为跳过了正常方法,而直接执行了fallback方法

7.2 服务熔断案例

在8001的PaymentService中添加

 //====服务熔断
    @HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
            @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),                      //开启断路器
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),         //请求总数阈值(默认20)
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),   //休眠时间窗口期(休眠多久进入半开模式(单位毫秒,默认5秒))
            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60"),       //请求次数的错误率达到多少跳闸(百分率%,默认50%)
    })
    public String paymentCircuitBreaker(@PathVariable("id") Integer id) {
        if(id < 0){
            throw  new RuntimeException("****id 不能为负数");
        }
        String serialNumber = IdUtil.simpleUUID();
        return  Thread.currentThread().getName() + "\t" + "调用成功,流水号:" + serialNumber;
    }
    public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id){
        return "id 不能为负数,请稍后再试, id: " + id;
    }

对上面的参数解释如下。


80e0a164f8904b759f59efed62a9f8d3.png

HystrixCommandProperties.class可用看到所有可配置的参数。

在8001的PaymentController中添加

@GetMapping("/payment/circuit/{id}")
public String paymentCircuitBreaker(@PathVariable("id") Integer id){
    String result = paymentService.paymentCircuitBreaker(id);
    log.info("******result:" + result);
    return result;
}

启动8001,执行测试。

正数放行,http://localhost:8001/payment/circuit/1

984a3fbec1ee4034bd51a531f1cc9779.png

负数,http://localhost:8001/payment/circuit/-1

f93e1349d93e46029155ca508c2d970d.png借助Jmeter大量进行如上请求,使服务熔断,熔断10秒内就算是正确的请求也返回错误信息

b77765eec96b49b5bf53a049dd71446b.png

10秒后进入半开模式,对请求进行处理,此时如果是正确的请求,那么就关闭熔断,否则再次进入熔断,10秒后再次开启半开模式,对请求进行处理,直到半开模式处理到正确请求。

140efa32ae03435d9be78468e1d79021.png

7.3 熔断规则总结

(1)熔断状态的变化

下图总结了熔断的状态变化的规则

f88e10e89cc04f529986cf482514febd.png

官网还给出了详细的流程图

c29745392f804ed0860d813e03c975b4.png

(2)熔断开启的条件

image.png

image.png

(3)断路器打开之后


8837907ec492482cb073b030318d2b48.png


相关文章
|
3天前
|
Cloud Native 安全 数据安全/隐私保护
云原生架构下的微服务治理与挑战####
随着云计算技术的飞速发展,云原生架构以其高效、灵活、可扩展的特性成为现代企业IT架构的首选。本文聚焦于云原生环境下的微服务治理问题,探讨其在促进业务敏捷性的同时所面临的挑战及应对策略。通过分析微服务拆分、服务间通信、故障隔离与恢复等关键环节,本文旨在为读者提供一个关于如何在云原生环境中有效实施微服务治理的全面视角,助力企业在数字化转型的道路上稳健前行。 ####
|
5天前
|
运维 Kubernetes Cloud Native
云原生技术:容器化与微服务架构的完美结合
【10月更文挑战第37天】在数字化转型的浪潮中,云原生技术以其灵活性和高效性成为企业的新宠。本文将深入探讨云原生的核心概念,包括容器化技术和微服务架构,以及它们如何共同推动现代应用的发展。我们将通过实际代码示例,展示如何在Kubernetes集群上部署一个简单的微服务,揭示云原生技术的强大能力和未来潜力。
|
14天前
|
弹性计算 Kubernetes Cloud Native
云原生架构下的微服务设计原则与实践####
本文深入探讨了在云原生环境中,微服务架构的设计原则、关键技术及实践案例。通过剖析传统单体架构面临的挑战,引出微服务作为解决方案的优势,并详细阐述了微服务设计的几大核心原则:单一职责、独立部署、弹性伸缩和服务自治。文章还介绍了容器化技术、Kubernetes等云原生工具如何助力微服务的高效实施,并通过一个实际项目案例,展示了从服务拆分到持续集成/持续部署(CI/CD)流程的完整实现路径,为读者提供了宝贵的实践经验和启发。 ####
|
3天前
|
Cloud Native 安全 API
云原生架构下的微服务治理策略与实践####
—透过云原生的棱镜,探索微服务架构下的挑战与应对之道 本文旨在探讨云原生环境下,微服务架构所面临的关键挑战及有效的治理策略。随着云计算技术的深入发展,越来越多的企业选择采用云原生架构来构建和部署其应用程序,以期获得更高的灵活性、可扩展性和效率。然而,微服务架构的复杂性也带来了服务发现、负载均衡、故障恢复等一系列治理难题。本文将深入分析这些问题,并提出一套基于云原生技术栈的微服务治理框架,包括服务网格的应用、API网关的集成、以及动态配置管理等关键方面,旨在为企业实现高效、稳定的微服务架构提供参考路径。 ####
20 5
|
4天前
|
Kubernetes 负载均衡 Cloud Native
云原生架构下的微服务治理策略
随着云原生技术的不断成熟,微服务架构已成为现代应用开发的主流选择。本文探讨了在云原生环境下实施微服务治理的策略和方法,重点分析了服务发现、负载均衡、故障恢复和配置管理等关键技术点,以及如何利用Kubernetes等容器编排工具来优化微服务的部署和管理。文章旨在为开发者提供一套实用的微服务治理框架,帮助其在复杂的云环境中构建高效、可靠的分布式系统。
18 5
|
4天前
|
负载均衡 监控 Cloud Native
云原生架构下的微服务治理策略与实践####
在数字化转型浪潮中,企业纷纷拥抱云计算,而云原生架构作为其核心技术支撑,正引领着一场深刻的技术变革。本文聚焦于云原生环境下微服务架构的治理策略与实践,探讨如何通过精细化的服务管理、动态的流量调度、高效的故障恢复机制以及持续的监控优化,构建弹性、可靠且易于维护的分布式系统。我们将深入剖析微服务治理的核心要素,结合具体案例,揭示其在提升系统稳定性、扩展性和敏捷性方面的关键作用,为读者提供一套切实可行的云原生微服务治理指南。 ####
|
3天前
|
Cloud Native API 持续交付
云原生之旅:从容器到微服务的演进之路
【10月更文挑战第39天】在这篇文章中,我们将一起探索云原生技术的奥秘。通过浅显易懂的语言和生动的比喻,我们将了解云原生技术如何改变软件开发的世界。文章将带领读者从容器的基本概念出发,逐步深入到微服务架构的实践,揭示这些技术如何助力现代应用的快速迭代与可靠部署。准备好,让我们启程进入云原生的精彩世界吧!
|
6天前
|
Kubernetes Cloud Native Docker
云原生技术探索:容器化与微服务的实践之道
【10月更文挑战第36天】在云计算的浪潮中,云原生技术以其高效、灵活和可靠的特性成为企业数字化转型的重要推手。本文将深入探讨云原生的两大核心概念——容器化与微服务架构,并通过实际代码示例,揭示如何通过Docker和Kubernetes实现服务的快速部署和管理。我们将从基础概念入手,逐步引导读者理解并实践云原生技术,最终掌握如何构建和维护一个高效、可扩展的云原生应用。
|
13天前
|
Kubernetes Cloud Native 微服务
云原生之旅:从容器到微服务
【10月更文挑战第29天】在这篇文章中,我们将一起探索云原生的奥秘。云原生不仅仅是一种技术,更是一种文化和方法论。我们将从容器技术开始,逐步深入到微服务架构,最后探讨如何在云平台上实现高效的服务部署和管理。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的见解和实用的技能。让我们一起踏上这段激动人心的云原生之旅吧!
|
14天前
|
运维 Kubernetes Cloud Native
云原生之旅:容器化与微服务的融合
【10月更文挑战第28天】 在数字化转型的浪潮中,云原生技术如星辰般璀璨,引领着企业IT架构的未来。本文将带你穿梭于云原生的世界,探索容器化技术和微服务架构如何携手共舞,打造灵活、高效的应用部署和运维模式。我们将通过实际代码示例,揭示这股力量背后的奥秘,并展现它们是如何为现代软件开发带来革新。准备好了吗?让我们启航,驶向云原生技术的深海。