Spring Cloud(Finchley.RELEASE版本)微服务学习实践:4.1服务容错-Hystrix

环境:

jdk1.8;spring boot2.0.3;spring cloud(Finchley.RELEASE版本);Maven3.3

摘要说明:

服务容错:微服务往往服务众多,各服务之间相互调用,若消费者在调用提供者时出现由网络、提供者服务自身问题等导致接口出现故障或延迟;此时消费者的请求不断增加,务必会出现因等待响应而造成任务积压,线程无法释放,进一步导致服务的瘫痪。微服务架构既然存在这样的隐患,那么针对服务的容错性必然会进行服务降级,依赖隔离,断路器等一线列的服务保护机制。

Spring Cloud HystrixSpring Cloud Hystrix中实现了线程隔离、断路器等一系列的服务保护功能。它也是基于Netflix的开源框架 Hystrix实现的,该框架目标在于通过控制那些访问远程系统、服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。Hystrix具备了服务降级、服务熔断、线程隔离、请求缓存、请求合并以及服务监控等强大功能。

步骤:

1.创建Hystrix(Hystrix服务容错)子项目

通过SPRING INITIALIZR工具选择Cloud Circuit Breaker:Hystrix模块构建Hystrix子项目引入依赖(pom.xnl):

此项目我们可以在消费者ribbon的基础上进行创建

<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">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>pers.cc</groupId>
		<artifactId>springCloud</artifactId>
		<version>0.0.1-SNAPSHOT</version>
	</parent>
	<artifactId>hystrix</artifactId>
	<name>hystrix</name>
	<description>hystrix服务容错</description>
	<dependencies>
		<!-- 引入服务容错hystrix的依赖 -->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
		</dependency>
		<!-- 引入服务消费者ribbon的依赖 -->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
		</dependency>
		<!-- 引入web的依赖 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<!-- 引入服务消费者eureka-client的依赖 -->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
		</dependency>
	</dependencies>
</project>

2.配置Hystrix(Hystrix服务容错)子项目

使用@EnableHystrix注解开启Hystrix的服务容错配置;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;


import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;


@SpringBootApplication
@EnableDiscoveryClient
@EnableHystrix
public class HystrixApplication {
	@Bean
	@LoadBalanced
	RestTemplate restTemplate() {
		return new RestTemplate();
	}


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


	// 此配置是为了服务监控而配置,与服务容错本身无关,
	// 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;
	}
}
	@Bean
	@LoadBalanced
	RestTemplate restTemplate() {
		return new RestTemplate();
	}


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


	// 此配置是为了服务监控而配置,与服务容错本身无关,
	// 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;
	}
}

 

配置application.properties

#配置服务名称及端口
spring.application.name=hystrix-consumer
server.port=5001
#服务注册中心实例的主机名
eureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/
#设置超时时间
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=3000
#设置请求总数下限
hystrix.command.default.circuitBreaker.requestVolumeThreshold=5
#错误百分比下限
hystrix.command.default.circuitBreaker.errorThresholdPercentage=60
#更多配置参考https://blog.csdn.net/harris135/article/details/77879148?locationNum=3&fps=1

3.开发Hystrix

服务降级:当消费者调用提供者接口时出现超时,异常等情况时,可以指定进行降级逻辑处理也可以理解为异常处理;

开发降级逻辑处理,使用@HystrixCommand注解指定接口降级处理方法:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;


import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;


@RestController
public class TestController {
	@Autowired
	TestService testService;
	@Autowired
	RestTemplate restTemplate;


	@RequestMapping(value = "/test", method = RequestMethod.GET)
	public String test() {
		return testService.test();
	}


	@RequestMapping(value = "/test1", method = RequestMethod.GET)
	public String test1() {
		return testService.test1();
	}


	@RequestMapping(value = "/test2", method = RequestMethod.GET)
	public String test2() {
		return restTemplate.getForObject("http://EUREKA-CLIENT/stop", String.class);
	}


	@Service
	class TestService {
		@Autowired
		RestTemplate restTemplate;


		// 使用@HystrixCommand注解指定接口降级处理方法
		@HystrixCommand(fallbackMethod = "fallback")
		public String test() {
			return restTemplate.getForObject("http://EUREKA-CLIENT/stop", String.class);
		}


		@HystrixCommand(fallbackMethod = "fallback")
		public String test1() {
			return restTemplate.getForObject("http://EUREKA-CLIENT/stop", String.class);
		}


		public String fallback() {
			return "fallback";
		}


	}
}

先后启动:

服务注册中心(eurekaServer):eureka-server(1001)

服务消费者(hystrix):hystrix-consumer(5001)

此时访问http://localhost:5001/test;由于服务提供者未启动则访问异常返回“fallback”;

再启动服务:

服务提供者(eurekaDiscovery):eureka-client(2001)

此时服务提供者(eurekaDiscovery提供的方法:Thread.sleep(5000L)睡眠5秒,而上面服务消费者(hystrix)设置的超时时间为3秒hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=3000;故服务超时则返回“fallback”;

	@GetMapping("/stop")
	public String stop() throws InterruptedException {
		System.out.println("stop");
		Thread.sleep(5000L);
		return "stop";
	}

当然也可以试试修改超时时间为6秒测试降级处理此配置是否有效;

4.实现效果

断路器:如上述所述,当服务提供方服务响应超时,若超时时间设置的过长,消费者调用频率过高,即使使用服务降级仍然会造成线程堆积;对于此就产生了断路器,即设置一定规则如在快照时间窗内,请求总数超过规定数目,失败百分比超过规定下限则断路器打开,此时不会去调用服务提供方,直接进行服务降级处理;

即断路器是实现自动的发现错误将降级逻辑切换为主逻辑;

那问题又来了,若服务提供方问题修复好了,断路器如何关闭呢?对此,hystrix会启动一个休眠时间窗,在这个时间窗内就是上面说的将降级逻辑切换为主逻辑;休眠时间窗结束后断路器将进入半开状态,释放一次请求到原来的主逻辑上,如果此次请求正常返回,那么断路器将继续闭合,主逻辑恢复,如果这次请求依然有问题,断路器继续进入打开状态,休眠时间窗重新计时。

通过上面的机制,hystrix的断路器实现服务降级的自动实现以及主逻辑的自动恢复机制,使得服务更加智能,高效;

常用配置为,更多可参考

快照时间窗:即启动断路器统计的时间范围,如默认为最近10秒;即统计这最近十秒内的对应指标;

请求总数下线hystrix.command.default.circuitBreaker.requestVolumeThreshold ):即快照时间窗内,该接口必须达到请求总数下线才会进行熔断;默认20;即快照时间窗内请求数量为19个且全部失败也不会进行熔断;

错误百分比下限hystrix.command.default.circuitBreaker.errorThresholdPercentage):即快照时间窗内,该接口必须达到请求失败百分比>=该下限才会进行熔断;默认50;即快照时间窗内请求失败占比必须达到50%

触发短路的时间值hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds):即断路器触发后多长时间会拒绝请求;默认5000;即5秒内不进行主逻辑处理,5秒后会释放一次请求进行尝试进入一个周期;

依赖隔离:使用@HystrixCommand注解会默认进行依赖隔离,即会对每一个调用产生对应的线程池;

通过线程池隔离从而达到不会因为部分依赖调用异常而影响其他的依赖调用,使得服务更加健全

同时通过健康检查对线程池的清理,相比容器级别更加快捷;

可能会担心为每一个依赖服务都分配一个线程池是否会过多地增加系统的负载和开销。对于这一点,使用者不用过于担心,因为这些顾虑也是大部分工程师们会考虑到的,Netflix在设计Hystrix的时候,认为线程池上的开销相对于隔离所带来的好处是无法比拟的。同时,Netflix也针对线程池的开销做了相关的测试,以证明和打消Hystrix实现对性能影响的顾虑;

我们可以再上面操作的基础上进行操作验证依赖隔离

先快速多次调用http://localhost:5001/test;使该接口进行熔断;

在依次调用http://localhost:5001/test1及http://localhost:5001/test12;会发现这两个的降级服务和正常调用并不受影响;

5.源码地址

github地址:https://github.com/cc6688211/springCloud.git

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值