Hystrix解读

Hystrix 简介

HystrixNetlifx 开源的一款容错框架,防雪崩利器,具备服务降级,服务熔断,依赖隔离,监控 Hystrix Dashboard 等功能

官网:https://github.com/Netflix/Hystrix/wiki/How-To-Use

Hystrix 服务熔断产生的背景

分布式系统环境下,服务间类似依赖非常常见,一个业务调用通常依赖多个基础服务。如下图,对于同步调用,当库存服务不可用时,商品服务请求线程被阻塞,当有大批量请求调用库存服务时,最终可能导致整个商品服务资源耗尽,无法继续对外提供服务。并且这种不可用可能沿请求调用链向上传递,这种现象被称为雪崩效应

在这里插入图片描述

引起雪崩效应常见场景

  • 硬件故障:如服务器宕机,机房断电,光纤被挖断等
  • 流量激增:如异常流量,重试加大流量等
  • 缓存击穿:一般发生在应用重启,所有缓存失效时,以及短时间内大量缓存失效时。大量的缓存不命中,使请求直击后端服务,造成服务提供者超负荷运行,引起服务不可用
  • 程序 BUG:如程序逻辑导致内存泄漏,JVM 长时间 FullGC
  • 同步等待:服务间采用同步调用模式,同步等待造成的资源耗尽

Hystrix 入门

项目概览

在这里插入图片描述

Hystrix 的使用

添加依赖

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

Hystrix-study-user 服务的主启动类

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@EnableCircuitBreaker // 开启断路器
@EnableHystrixDashboard // 开启 Hystrix 的监控仪表盘
public class UserApplication {

	private static final Logger log = LoggerFactory.getLogger(UserApplication.class);

	public static void main(String[] args) {
		SpringApplication.run(UserApplication.class, args);
		log.info("===============springcloud user启动了=================");
	}

	// 解决 hystrix-dashBoard 仪表盘不能访问
	@SuppressWarnings({ "rawtypes", "unchecked" })
	@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-study-user 服务的 Feign 的客户端接口

@FeignClient(name = "STUDY-ACTIVITY", fallback = UserFeignFallback.class)
public interface UserFeign {

	@RequestMapping(path = { "/activity/getCoupon" }, method = RequestMethod.POST)
	String getCoupon(@RequestBody Integer id);

	@RequestMapping(path = { "/activity/getCouponTimeOut" }, method = RequestMethod.POST)
	String getCouponTimeOut(@RequestBody Integer id);

	@RequestMapping(path = { "/demo/timeOut" }, method = RequestMethod.POST)
	String timeOut(@RequestParam Integer mills);

	@RequestMapping(path = { "/timeOut" }, method = RequestMethod.POST)
	String tripTest(@RequestParam Integer mills);
}

Hystrix-study-user 服务降级处理类

@Component
public class UserFeignFallback implements UserFeign {

	@Override
	public String getCoupon(Integer id) {
		return null;
	}

	@Override
	public String getCouponTimeOut(Integer id) {
		return "------超过2000毫秒时,直接进入服务降级处理------";
	}

	@Override
	public String timeOut(Integer mills) {
		return "---------------您的请求【超时】或【失败】,已进入服务降级模式了----------------";
	}

	@Override
	public String tripTest(Integer mills) {
		 return "-------------请求未通过-----------";
	}
}

Hystrix-study-user 服务的 Service 实现类

@Service
public class UserServiceImpl implements UserService {

	private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);

	@Autowired
	private UserFeign userFeign;

	// 使用注解 @HystrixCommand 指定服务降级的方法
	@HystrixCommand(fallbackMethod = "firstLoginError")
	@Override
	public String firstLogin(Integer id) {
		// 采用 Feign 客户端来调用服务 Hystrix-study-activity
		String result = userFeign.getCoupon(id);
		log.info("===================result的值为:" + result + "======================");
		return result;
	}

	// 当服务 Hystrix-study-activity 不可用时,这时让其回调回退方法
	public String firstLoginError(Integer id) {
		return "---------您请求的服务暂时不可用,请稍后再试--------------";
	}
}	

Hystrix-study-user 服务的配置文件

server.port=8080

spring.application.name=study-user

eureka.client.service-url.defaultZone=http://eureka7001.com:8761/eureka/
#事实上,springcloud默认已为Feign整合了Hystrix,要想为Feign打开Hystrix支持,只需要设置feign.hystrix.enabled=true即可。
feign.hystrix.enabled=true
#补充:在springcloud Dalston之前的版本中,Feign默认开启Hystrix支持,无需设置feign.hystrix.enabled=true.
#从springcloud Dalston版本开始,Feign的Hystrix支持默认关闭,需要手动设置开启

#配置Hystrix的超时时间
#default全局有效,service id指定应用有效
hystrix.command.default.execution.timeout.enabled=true #默认为true
#默认值为 1000毫秒(对于每一个标有注解 @FeignClient 的接口的所有抽象方法生效)
#hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=9000

#显示服务器详细的健康信息
management.endpoint.health.show-details=always

#暴露全部的监控信息(解决hystrix-dashBoard仪表盘不能访问)
management.endpoint.web.exposure.include="*"

hystrix 降级测试

分别启动 Hystrix-study-activity,Hystrix-study-eureka,Hystrix-study-user 三个服务,使用 Postman 测试如下

在这里插入图片描述
此时再关闭 Hystrix-study-activity 服务

在这里插入图片描述
说明执行了 @HystrixCommand(fallbackMethod = "firstLoginError") 服务降级的方法

@HystrixCommand 注解使用

Hystrix-study-user 服务的 Service 类添加方法

execution.isolation.thread.timeoutInMilliseconds:该属性用来配置方法执行的超时时间。我们在之前对于降级处理时间的配置,都是在全局配置文件 application.yml 中配置的,commandProperties 可以让我们在一些具有独特要求的方法上,单独进行一些配置操作

// 设置Hystrix的服务降级超时时间,超过2000毫秒时,直接进入服务降级处理	
@HystrixCommand(commandProperties = {
		@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000") })
@Override
public String firstLoginTimeOut(Integer id) {
	String result = userFeign.getCouponTimeOut(id);
	log.info("===================result的值为:" + result + "======================");
	return result;
}

Hystrix-study-activity 服务的 Controller

@Controller
@RequestMapping(path = { "/activity" })
public class ActivityController {

	private static final Logger log = LoggerFactory.getLogger(ActivityController.class);

	@RequestMapping(path = { "/getCouponTimeOut" }, method = RequestMethod.POST)
	@ResponseBody
	public String getCouponTimeOut(@RequestBody Integer id) {
		try {
			Random random = new Random();
			TimeUnit.SECONDS.sleep(random.nextInt(10) % (7) + 4);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		log.info("=============该用户首次登陆(注册),领取优惠券失败============");
		return "error";
	}
}

hystrix 降级测试

分别启动 Hystrix-study-activity,Hystrix-study-eureka,Hystrix-study-user 三个服务,使用 Postman 测试如下

在这里插入图片描述
说明执行了 UserFeignFallback 服务降级处理类的相应方法

修改 Hystrix-study-activity 服务的 Controller 类再测试

@Controller
@RequestMapping(path = { "/activity" })
public class ActivityController {

	private static final Logger log = LoggerFactory.getLogger(ActivityController.class);

	@RequestMapping(path = { "/getCouponTimeOut" }, method = RequestMethod.POST)
	@ResponseBody
	public String getCouponTimeOut(@RequestBody Integer id) {
		log.info("=============该用户首次登陆(注册),领取优惠券成功============");
		return "SUCCESS";
	}
}

再测试结果

在这里插入图片描述

@HystrixCommand 注解的常见使用

@Service
public class UserServiceImpl implements UserService {

    private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);

    @Autowired
    private UserFeign userFeign;
   
    @HystrixCommand(threadPoolKey = "time", threadPoolProperties = {
            @HystrixProperty(name = "coreSize", value = "2"),
            @HystrixProperty(name = "maxQueueSize", value = "20")}, commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "9000")})
    @Override
    public String timeOut(Integer mills) {
        log.info("-----------mills:的值为:" + mills + "--------------");
        return userFeign.timeOut(mills);
    }

    @HystrixCommand(threadPoolKey = "time_1", threadPoolProperties = {
    		@HystrixProperty(name = "coreSize", value = "2"),
            @HystrixProperty(name = "maxQueueSize", value = "20")}, commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "9000")})
    @Override
    public String timeOut_1(Integer mills) {
        log.info("-----------mills:的值为:" + mills + "--------------");
        return userFeign.timeOut(mills);
    }

  /**
    * Hystrix的断路器测试
    * 
    * 模拟测试:3秒钟内,请求次数达到2次,并且失败率在50%以上,断路器做跳闸动作。跳闸后的活动窗口设置为3秒
    *
    * 服务的健康状态检查:http://ip:port/actuator/health
    * 	Hystrix的健康状态为:status: "UP"
    * 	Hystrix的断路器跳闸后状态为:status: "CIRCUIT_OPEN"
    * 	此时要删除服务的降级处理类 UserFeignFallback,才能查看健康状态
     */
    @HystrixCommand(commandProperties = {
            @HystrixProperty(name = "metrics.rollingPercentile.timeInMilliseconds", value = "3000"),
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "2"),
            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "3000")})
    @Override
    public String tripTest(Integer mills) {
        return userFeign.tripTest(mills);
    }
}

鉴于篇幅,文章不再赘述,详情可以查看:https://www.cnblogs.com/zhenbianshu/p/9630167.html

HystrixRibbon 的超时时间

  • 如果 hystrix.command.default.execution.timeout.enabled = true(默认),则会有两个执行方法超时的配置:一个就是 ribbonReadTimeout,一个就是熔断器 hystrixtimeoutInMilliseconds,此时谁的值小谁生效
  • 如果 hystrix.command.default.execution.timeout.enabled = false,则熔断器不进行超时熔断,而是根据 ribbonReadTimeout 抛出的异常而熔断,也就是取决于 ribbon
  • ribbonConnectTimeout 配置的是请求服务的超时时间,除非服务找不到,或者网络原因,这个时间才会生效
  • ribbon 还有 MaxAutoRetries 对当前实例的重试次数,MaxAutoRetriesNextServer 对切换实例的重试次数,如果 ribbonReadTimeout 超时,或者 ConnectTimeout 连接超时,会进行重试操作
  • 由于 ribbon 的重试机制,通常熔断的超时时间需要配置的比 ReadTimeout 长,ReadTimeoutConnectTimeout 长,否则还未重试就熔断了
  • 为了确保重试机制的正常运作,理论上(以实际情况为准)建议 hystrix 的超时时间为:(1 + MaxAutoRetries + MaxAutoRetriesNextServer) * ReadTimeout

Hystrix 的断路器工作原理

在这里插入图片描述

断路器开启的条件

  • 当请求达到阀值时候:默认 10 秒内 20 次请求,当请求失败率达到默认 50% 的时,此时断路器将会开启,所有的请求都不会执行

断路器关闭的条件

  • 当断路器开启 5 秒(默认)时,这时断路器是半开状态, 会允许其中一个请求执行
  • 如果执行成功,则断路器会关闭;如果失败,则继续开启。循环重复这两个流程

服务降级与服务熔断区别

服务熔断

熔断机制是应对雪崩效应的一种微服务链路保护机制,一般来说,每个服务都需要熔断机制的

  • 如高压电路中,如果某个地方的电压过高,熔断器就会熔断,对电路进行保护。在微服务架构中,熔断机制也是起着类似的作用
  • 当扇出链路的某个微服务不可用,或响应超时,或宕机,或异常时,进而熔断该节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务调用响应正常后,恢复调用链路
/**
 * Hystrix的断路器测试
 * 模拟测试:3秒钟内,请求次数达到2次,并且失败率在50%以上,断路器做跳闸动作
 * 			跳闸后的活动窗口设置为3秒
 */
@HystrixCommand(commandProperties = {
		@HystrixProperty(name = "metrics.rollingPercentile.timeInMilliseconds", value = "3000"),
        @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "2"),
        @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),
        @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "3000")})
@Override
public String tripTest(Integer mills) {
	return userFeign.tripTest(mills);
}
  • 上面熔断机制中,熔断回退方法是写在 @FeignClientfallback 指定的类中的
  • 当然也可以使用注解 @HystrixCommand(fallbackMethod = " ") 来指定服务熔断的回退方法

服务降级

  • 降级是指自己的待遇下降了,从 RPC 调用环节来讲,就是说整体资源快不够了,忍痛将某些服务单元先关掉,关闭后还要返回一些可处理的备选方法,待渡过难关,再开启回来
  • 降级一般需要对业务有层级之分(比如降级一般是从最外围服务开始)

服务降级的实现方式

方式一

这种方式是整合 feign 时,建议,推荐使用的方式。当然也可以使用下面第二种方式

@FeignClient(name = "STUDY-ACTIVITY", fallback = UserFeignFallback.class)
public interface UserFeign {

	@RequestMapping(path = { "/activity/getCouponTimeOut" }, method = RequestMethod.POST)
	String getCouponTimeOut(@RequestBody Integer id);

	@RequestMapping(path = { "/demo/timeOut" }, method = RequestMethod.POST)
	String timeOut(@RequestParam Integer mills);
}
@Component
public class UserFeignFallback implements UserFeign {

	@Override
	public String getCouponTimeOut(Integer id) {
		return "------超过2000毫秒时,直接进入服务降级处理------";
	}

	@Override
	public String timeOut(Integer mills) {
		return "---------------您的请求【超时】或【失败】,已进入服务降级模式了----------------";
	}
}
方式二

这种方式也可以使用,但存在的问题,业务代码与降级代码耦合到一块不好管理

@Service
public class UserServiceImpl implements UserService {

	private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);

	@Autowired
	private UserFeign userFeign;

	// 使用注解 @HystrixCommand 指定服务降级的方法
	@HystrixCommand(fallbackMethod = "firstLoginError")
	@Override
	public String firstLogin(Integer id) {
		// 采用 Feign 客户端来调用服务 Hystrix-study-activity
		String result = userFeign.getCoupon(id);
		log.info("===================result的值为:" + result + "======================");
		return result;
	}

	// 当服务 Hystrix-study-activity 不可用时,这时让其回调回退方法
	public String firstLoginError(Integer id) {
		return "---------您请求的服务暂时不可用,请稍后再试--------------";
	}
}	

区别

相同点

  • 目的很一致,都是从服务的可用性可靠性着想,为防止系统的整体缓慢甚至崩溃,采用的技术手段
  • 最终表现类似,对于两者来说,最终让用户体验到的是某些功能暂时不可达或不可用

不同点

  • 触发原因不太一样:服务熔断一般是某个服务(下游服务)故障或异常引起,而服务降级一般是从系统整体负荷考虑的
  • 管理层次不太一样:熔断其实是一个框架级的处理,每个服务都需要(无层级之分),而降级一般需要对业务有层级之分(比如降级一般是从最外围服务开始)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值