ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Spring Cloud - 03 (Hystrix)

2021-09-22 13:33:12  阅读:22  来源: 互联网

标签:服务 hystrix Hystrix Spring 线程 超时 public Cloud


目录


Hystrix的作用是为了帮服务节点度过他们的困难时期(缓解异常、雪崩带来的影响),它有一套佛系的设计理念,分别对应Hystrix中三个特色功能:

  • 断:服务熔断
  • 舍:服务降级
  • 离:线程隔离

下面,我们来品味下其“断舍离”的智慧。

服务降级

微服务架构强调高可用,但并非高一致性,在一致性方面远比不上银行的大型机系统。也就是说,在日常服务调用阶段会出现一系列的调用异常。一个服务的调用异常可能会引发一些更严重的问题,例如引起服务雪崩效应(一种因“服务提供者的不可用”导致“服务调用者不可用”,并将不可用逐渐放大的现象),最终会导致整个系统被拖垮。

为了解决上面的问题,服务降级就是个很好的方案。当服务间请求发生异常(exception),这时Hystrix会自动把这个请求转发到降级(fallback)逻辑中,由服务调用方来编写异常处理逻辑。而且对于响应超时的场景来说,我们可以通过配置Hystrix的超时等待时间,把超时响应的服务调用也当做是异常情况,转发到fallback逻辑中进行处理。

image

代码实操

1、pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring-cloud-demo</artifactId>
        <groupId>com.jinsh</groupId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath>../../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>hystrix-consumer</artifactId>

    <dependencies>
        <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.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>
</project>

2、启动类,使用@EnableCircuitBreaker注解开启熔断器,也可以使用@EnableHystrix这个注解

package com.jinsh.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@EnableCircuitBreaker
public class HystrixApplication {

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

3、配置文件

spring.application.name=hystrix-client

server.port=51000

spring.main.allow-bean-definition-overriding=true
eureka.client.service-url.defaultZone=http://localhost:20000/eureka/

# 开启hystrix
feign.hystrix.enabled=true

Feign+Hystrix实现Fallback降级

配置@FeignClient注解的fallback属性,用于配置降级类,此类必须实现由@FeignClient注释的接口,并且是一个有效的spring bean。

@FeignClient(name = "feign-client", fallback = FallBack.class)
public interface IService {

    @GetMapping("sayHi")
    String sayHi(int a);
}

FallBack降级类:

@Component
public class FallBack implements IService {
    public String sayHi(int a){
        return "这里是降级逻辑";
    }
}

注意:FallBack方法的形参要和原方法的形参一致。

Hystrix实现timeout降级

当请求响应超时时触发降级。
通过配置文件配置实现,以下配置是全局的:

# 全局超时
hystrix.command.default.execution.timeout.enabled=true
# 超时时间设为2秒
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=2000
# 超时以后终止线程
hystrix.command.default.execution.isolation.thread.interruptOntimeout=true
# 取消的时候终止线程
hystrix.command.default.execution.isolation.thread.interruptOnCancel=true

Hystrix与Ribbon的超时配置

Feign集成了Ribbon和Hystrix两个组件,它俩都各自有一套超时配置,那到底哪个超时配置是最终生效的那个呢?

首先Ribbon的超时时间计算公式:

最大超时时间=(连接超时时间+接口超时时间)*(当前节点重试次数+1)*(换节点重试次数+1)

如果Hystrix的超时时间设置比Ribbon配置的时间短,那么不等Ribbon重试结束,Hystrix判定超时后就会直接执行熔断逻辑。因此,Hystrix和Ribbon是一个共同作用的关系,谁先到达超时指标就会率先起作用。

通常来讲,Hystrix的熔断时间要比Ribbon的最长超时时间设置的略长一些,这样就可以让Ribbon的重试机制充分发挥作用,以免出现还没来得及重试就进入fallback逻辑的情况发生。

hystrix方法级别超时配置

方法级别超时配置优先级高于全局的配置,它有3种方式:

1、注解配置

@RestController
public class Controller {
	@HystrixCommand(
            commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")}
    )
	@GetMapping("sayHi")
    public String sayHi(@RequestParam("parm") int parm) {
        return IService.sayHi(parm);
    }
}

2、配置文件配置

hystrix.command.IService#sayHi(int).execution.isolation.thread.timeoutInMilliseconds=1000

其中IService#sayHi(int)就是一串类名+方法名+方法参数的组合,对于复杂的方法,人工拼出这一套组合字符串也挺费脑子的,Feign提供了一个简单的工具根据反射机制生成字符串:

Feign.configKey(IService.class, MyService.class.getMethod("sayHi", int.class))

3、注解+配置文件配置
注解

@HystrixCommand(commandKey = "sayHiKey")
public String sayHi(int parm) {
	return IService.sayHi(parm);
}

配置文件

hystrix.command.sayHiKey.execution.isolation.thread.timeoutInMilliseconds=1000

@HystrixCommand注解实现局部降级

上文中的@FeignClient(name = "feign-client", fallback = FallBack.class)实现的降级是针对feign-client整个服务的,如果只想针对某一个方法实现降级怎么办呢?可以通过@HystrixCommand注解的fallbackMethod属性,fallbackMethod属性用于配置降级的fallback方法,该fallback方法必须和 @HystrixCommand注解的方法在同一个类里。

public class Service{
	@HystrixCommand(commandKey = "sayHiKey", fallbackMethod="sayHiFallBack")
	public String sayHi(int parm) {
		return IService.sayHi(parm);
	}

	public String sayHiFallBack(int parm) {
		return "这里是降级逻辑";
	}
}

如果降级方法中再出现异常,我们还可以多级降级:

public class Service{
	@HystrixCommand(fallbackMethod="fallBackOne")
	public String sayHi(int parm) {
		return IService.sayHi(parm);
	}

	@HystrixCommand(fallbackMethod="fallBackTwo")
	public String fallBackOne(int parm) {
		return "一级降级逻辑";
	}

	public String fallBackTwo(int parm) {
		return "二级降级逻辑";
	}
}

image

Hystrix实现Request Cache减压

Request Cache是Hystrix的一个缓存功能,是一种性能的优化措施。主要通过两个注解来实现@CacheResult和@CacheKey。
例:

@Service
public class RequestCacheService {

	// 使用 @CacheResult注解,必须要先有 @HystrixCommand注解
    @CacheResult
    @HystrixCommand
    public String requestCache(@CacheKey String name) {
        return name;
    }
}

Controller调用

@RestController
public class Controller {
	@Autowired
    private RequestCacheService requestCacheService;

	@GetMapping("cacheTest")
    public Friend cacheTest(@RequestParam("name") String name) {
        // 缓存只在Hystrix上下文中起作用,因此要先获取Hystrix上下文,@Cleanup是lombok注解,起关闭上下文的作用
        @Cleanup HystrixRequestContext context = HystrixRequestContext.initializeContext();
        return requestCacheService.requestCache(name);
    }
}

@CacheResult注解的意思是该方法的结果可以被Hystrix缓存起来,@CacheKey指定了这个缓存结果的业务ID是什么。在一个Hystrix上下文范围内,如果使用相同的参数对@CacheResult修饰的方法发起了多次调用,Hystrix只会在首次调用时向服务节点发送请求,后面的几次调用实际上是从Hystrix的本地缓存里读取数据。

服务熔断

服务熔断是建立在服务降级之上的一个异常处理措施,你可以将它看做是服务降级的升级版。服务降级需要等待HTTP请求从服务节点返回异常或超时,再转向fallback逻辑,但是服务熔断引入了一种叫“断路器/熔断器”的机制,当断路器打开的时候,对服务的调用请求不会发送到目标服务节点,直接转向fallback逻辑。

image

熔断器三状态

熔断器有三个状态:开启,关闭,半开启。
当熔断器开启,所有请求管你有没有异常,都将进入fallback。熔断器关闭,那就都不会触发熔断功能了,请求超时或异常,该进fallback的还是会进fallback。
熔断器半开启状态是指当服务熔断后(即熔断器开启状态),到达一定的时间点会再次尝试发送请求到目标服务节点,如果还是异常或超时,那么熔断器继续开启,反之熔断器将关闭。

熔断配置

# 熔断器功能开关(默认true)
hystrix.command.default.circuitBreaker.enabled=true
# 强制开启熔断开关(默认false),如果为true,将拒绝所有请求
hystrix.command.default.circuitBreaker.forceOpen=false
# 强制关闭熔断开关(默认false),如果为true,无论错误百分比如何,都允许请求
hystrix.command.default.circuitBreaker.forceClosed=false

# 配置时间窗口(单位ms,默认10000)
hystrix.command.default.metrics.rollingStats.timeInMilliseconds=20000
# 在时间窗口内,当请求数量达第5个,会开始熔断逻辑(默认20)
hystrix.command.default.circuitBreaker.requestVolumeThreshold=5
# 熔断逻辑判断:超过50%的请求失败,则开启熔断 (默认也是是50%)
hystrix.command.default.circuitBreaker.errorThresholdPercentage=50
# 开启熔断后,经过15秒进入半开启状态(默认5000)
hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds=15000

达到进入熔断逻辑的条件:

  • 在一定时间窗口内,发生异常的请求数量达到临界值
  • 在一定时间窗口内,发生异常的请求数量占请求总数量达到一定比例

线程隔离

大家知道Web容器通常有一个线程池来接待来访请求,如果并发量过高,线程池被打满了就会影响后面请求的响应。在我们应用内部,假如我们提供了3个微服务,分别是A,B,C。如果请求A服务的调用量过多,我们会发现所有可用线程都会逐渐被Service A占用,接下来的现象就是服务B和服务C没有系统资源可供调用。
Hystrix通过线程隔离的方案,将执行服务调用的代码与容器本身的线程池(比如tomcat thread pool)进行隔离,我们可以配置每个服务所需线程的最大数量,这样一来,即便一个服务的线程池被吃满,也不会影响其他服务。
image

线程池与信号量技术

Hystrix中的线程隔离,通常我们都采用基于线程池的实现方式,这也是最容易理解的方案。Hystrix还提供了另一种底层实现,那就是信号量隔离。

原理:

  • 线程池技术:它使用Hystrix自己内建的线程池去执行方法调用,而不是使用Tomcat的容器线程;
  • 信号量技术:它直接使用Tomcat的容器线程去执行方法,不会另外创建新的线程,信号量只充当开关和计数器的作用。获取到信号量的线程就可以执行方法,没获取到的就转到fallback。

性能:

  • 线程池技术:涉及到线程的创建、销毁和任务调度,而且CPU在执行多线程任务的时候会在不同线程之间做切换,我们知道在操作系统层面CPU的线程切换是一个相对耗时的操作,因此从资源利用率和效率的角度来看,线程池技术会比信号量慢;
  • 信号量技术:由于直接使用Tomcat容器线程去访问方法,信号量只是充当一个计数器的作用,没有额外的系统资源消费,所以在性能方面具有明显的优势。

超时判定:

  • 线程池技术:相当于多了一层保护机制(Hystrix自建线程),因此可以直接对“执行阶段”的超时进行判定;
  • 信号量技术:只能等待诸如网络请求超时等“被动超时”的情况。

使用场景:
根据官方建议,信号量适用在超高并发的非外部接口调用上(还是中文言简意赅),注意“the only time”,意思是官方只建议在上述场景中应用信号量技术,在其他场景上尽量使用线程池做线程隔离。
除此之外,线程池技术要特别注意ThreadLocal的数据传递作用,由于前后调用不在同一个线程内,也不在父子线程内,所以如果你在业务层面声明了ThreadLocal变量,将无法获取正确的值。

代码配置

线程池配置:

@HystrixCommand(
            commandProperties = {
                    // 隔离策略,有THREAD和SEMAPHORE
                    @HystrixProperty(name ="execution.isolation.strategy", value="THREAD")
            },
            threadPoolProperties = {
                    // 线程池核心线程数
                    @HystrixProperty(name = "coreSize", value = "3"),
                    // 队列最大长度
                    @HystrixProperty(name = "maxQueueSize", value = "5"),
                    // 排队线程数量阈值,默认为5,达到时拒绝,如果配置了该选项,队列的大小是该队列
                    @HystrixProperty(name = "queueSizeRejectionThreshold", value = "7")
            },
			fallbackMethod = "timeoutFallBack"
    )
    public String sayHi(@RequestParam("param") int param) {
        return iService.sayHi(param);
    }

信号量配置:

@HystrixCommand(
            commandProperties = {
                    @HystrixProperty(name ="execution.isolation.strategy", value="SEMAPHORE"),
               // 设置在使用时允许到HystrixCommand.run()方法的最大请求数。默认值:10 ,SEMAPHORE模式有效
                @HystrixProperty(name="execution.isolation.semaphore.maxConcurrentRequests", value="2")
            },
			fallbackMethod = "timeoutFallBack"
    )
    public String sayHi(@RequestParam("param") int param) {
        return iService.sayHi(param);
    }

文档:https://github.com/Netflix/Hystrix/wiki

Turbine聚合Hystrix信息

Turbine用来监控Hystrix的实时状态,知晓服务熔断、异常的数量变化等信息的。
image

  • 配置监控服务和集群:在Turbine里我们需要配置目标服务,也就是需要Turbine实时监控的服务名称。如果应用的部署结构比较复杂,比如说分了几个大集群,这时一个Turbine节点可能就无法监管这么多的服务节点了。我们可以启用多个Turbine聚合服务,每个服务指定一个集群,用来聚合这个集群下所有服务节点的Hystrix状态。在默认单cluster的部署结构下,Turbine默认监管default cluster(课程也是采用默认配置)。

  • 服务发现:连接Eureka注册中心,利用服务发现机制拉取服务节点列表,从中找到上一步中配置的指定服务都有哪些服务节点。

  • 聚合信息:这一步聚合操作是Turbine的核心功能,它并不是让各个服务节点把自己的信息上报给Turbine,因为对服务节点来说它们并不知道自己是否在Turbine的监控名单上。这一步其实是由Turbine主动发起的,从服务节点的指定"/actuator"路径下的Hystrix监控接口获取信息。

如果客户端集成了Hystrix,可以在“/actuator”服务中找到Hystrix的healthcheck url,Turbine正是从这个url获取Hystrix当前状态。

代码实现

pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring-cloud-demo</artifactId>
        <groupId>com.jinsh</groupId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath>../../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>hystrix-turbine</artifactId>

    <dependencies>
        <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-turbine</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </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>

    </dependencies>
</project>

启动类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.netflix.turbine.EnableTurbine;

@EnableDiscoveryClient
@EnableHystrix
@EnableTurbine
@EnableAutoConfiguration
public class TurbineApplication {

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

配置文件

spring.application.name=hystrix-turbine

server.port=61000

eureka.client.service-url.defaultZone=http://localhost:20000/eureka/

# 指定需要监控的服务名
turbine.app-config=hystrix-client
turbine.cluster-name-expression="default"
# 根据端口和hostname来区分不同服务(默认只根据hostname区分)
turbine.combine-host-port=true

turbine.instanceUrlSuffix.default=actuator/hystrix.stream

turbine.aggregator.cluster-config=default

Dashboard大盘监控

后台服务再牛,不叫人看到也没用。Hystrix提供了一个监控大盘的服务叫Dashboard,可以简单地通过@EnableHystrixDashboard注解直接开启,它会采用图形化的方式将每个服务的运行状态显示出来,它提供了两个维度的监控:

  • 单一节点监控:通过直接访问服务节点的“/actuator”接口,获取当前节点的Hystrix监控信息。
  • Turbine聚集信息监控:通过访问Turbine服务的“/actuator”接口,获取经过聚合后的Hystrix监控信息。

通过大盘监控,我们就可以实时掌握服务的健康度状态,知晓哪些服务正处于熔断状态,以便及时排查问题。

代码实现

pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring-cloud-demo</artifactId>
        <groupId>com.jinsh</groupId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath>../../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>hystrix-dashboard</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</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-hystrix-dashboard</artifactId>
        </dependency>
    </dependencies>
</project>

启动类

package com.jinsh.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;

@EnableHystrixDashboard
@SpringBootApplication
public class DashboardApplication {

    public static void main(String[] args) {

        SpringApplication.run(DashboardApplication.class, args);
    }
}

配置文件

spring.application.name=hystrix-dashboard
server.port=62000

hystrix.dashboard.proxy-stream-allow-list=localhost

使用介绍

使用Dashboard前要先启动Turbine服务。

这里有两个监控路径:

启动Dashboard服务后,浏览器访问 http://localhost:62000/hystrix
image

输入turbine 监控路径,点击监控按钮,就可以监控服务了。
image

标签:服务,hystrix,Hystrix,Spring,线程,超时,public,Cloud
来源: https://www.cnblogs.com/jinshengnianhua/p/15315697.html

专注分享技术,共同学习,共同进步。侵权联系[admin#icode9.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有