ICode9

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

Spring Cloud - 03 (Hystrix)

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

标签:服务 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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

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

ICode9版权所有