Hystrix服务隔离及熔断

上次说了使用RxJava并行提升API效率,但是如何避免项目中一个服务拖慢整体的性能?hystrix优雅的解决了这个问题。

先贴个图,网上找的,我觉得这个图就可以说明问题了

hystrix-1

当依赖服务A挂了,直接就会影响到整个服务的性能,如果连接一直被A暂用,最终将导致服务发生雪崩

所以我们要保证核心服务的稳定,其他非核心的服务可以不那么重要,即使出现了错误数据可以丢弃或不用保证实时准确,再以SOA项目中的getDynamicInfo为例,如下:

87194035-3D85-4860-AB44-35EA71697A44

getFlight为核心的业务,必须要获取到数据,获取不到那就整个服务不可用了

getEntryCard、getPlane、getRefund、getWeather、getPortaitUser等方法为非核心业务,我们允许其数据丢失的情况,要保证整个服务的可用

首先我们需要把这些服务使用hystrix分离出来,先对这几个服务做先分组:

pydyn:getEntryCard、getPlane、getRefund、getWeather

portrait:getPortaitUser

Maven配置

	<dependency>
	  <groupId>com.netflix.hystrix</groupId>
	  <artifactId>hystrix-core</artifactId>
	  <version>1.5.8</version>
	</dependency>

以getEntryCard服务为例,继承HystrixCommand<T>,重写run方法

public class GetEntryCardCommand extends HystrixCommand<String>{

	private String arrCode;
        private GetEntryCardService service;

	public GetEntryCardCommand(String arrCode) {
		super(setter());
		this.arrCode = arrCode;
	}

	private static Setter setter() {
		return PySetter.setter().andCommandKey(HystrixCommandKey.Factory.asKey("getEntryCard"));
	}

	@Override
	protected String run() throws Exception {
		// TODO Auto-generated method stub
		return service.getEntryCardCity(arrCode);
	}

	@Override
	protected String getFallback() {
		return service.getFromCache(arrCode);
	}

}

run方法要执行的业务逻辑,如果出现异常则会调用getFallback方法。

public class PySetter {

	public static Setter setter() {
		// 服务分组
		HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey("pydyn");
		// 服务标识
		//HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey("getEntryCard");
		// 线程池名称
		HystrixThreadPoolKey threadPoolKey = HystrixThreadPoolKey.Factory.asKey("pydyn-pool");
		// 线程配置
		HystrixThreadPoolProperties.Setter threadPoolProperties = HystrixThreadPoolProperties.Setter()
				.withCoreSize(10)
				.withKeepAliveTimeMinutes(5)
				.withMaxQueueSize(Integer.MAX_VALUE)
				.withQueueSizeRejectionThreshold(10000);

		//命令属性配置
		HystrixCommandProperties.Setter commandProperties = HystrixCommandProperties.Setter()
				.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD)
                                .withExecutionIsolationThreadInterruptOnTimeout(true).withExecutionTimeoutInMilliseconds(3000) //设置超时3秒自动熔断
				.withCircuitBreakerErrorThresholdPercentage(30);//失败率达到30%自动熔断

		return HystrixCommand.Setter
				.withGroupKey(groupKey)
				//.andCommandKey(commandKey)
				.andThreadPoolKey(threadPoolKey)
				.andThreadPoolPropertiesDefaults(threadPoolProperties)
				.andCommandPropertiesDefaults(commandProperties);
	}

}

以上是对服务的线程池熔断配置

HystrixCommandGroupKey:服务分组,以上pydyn分组就包括4个服务,必填选项

HystrixCommandKey:服务的名称,唯一标识,如果不配置,则默认是类名

HystrixThreadPoolKey:线程池的名称,相同线程池名称的线程池是同一个,如果不配置,默认为分组名

HystrixThreadPoolProperties:线程池的配置,coreSize配置核心线程池的大小,maxQueueSize线程池队列的最大大小,queueSizeRejectionThreshold,限制当前队列的大小,实际队列大小由这个参数决定,即到达队列里面条数到达10000,则都会被拒绝。

HystrixCommandProperties:配置命令的一些参数,如executionIsolationStrategy,配置执行隔离策略,默认是使用线程隔离,THREAD即为线程池隔离,ExecutionIsolationThreadInterruptOnTimeout和ExecutionTimeoutInMilliseconds配置了启用超时和最大执行时间,这里为3s,circuitBreakerErrorThresholdPercentage失败率配置,默认为50%,这里配置的为30%,即失败率到达30%触发熔断

接下来就是调用GetEntryCardCommand获取数据

String result = new GetEntryCardCommand(request.getArrCode()).execute();

我使用的是阻塞的方式获取,当然也可以使用异步的方法,得到Future对象,使用

Future<String> = new GetEntryCardCommand(request.getArrCode()).queue();

其他getPlane、getRefund、getWeather方法同GetEntryCardCommand类,

getPortaitUser也一样,只需要更改下自己的分组为portait,线程池名称,并配置自己的线程池参数

好了,这样就实现了多个服务之间的隔离和熔断,不同的服务分组使用不同的线程池,即使getDynamicInfo并发量突然剧增,也不会对getPlane、getRefund、getWeather等产生影响

下面来说说对服务的监控,hystrix已经帮我们实现了数据的记录,只需要安装他们的管理后台就可以查看数据,他们家有2个监控后台:hystrix-dashboard和Turbine,第一个是监控单个项目的日志,第二个可以把多个项目的日志聚合在一起

hystrix-dashboard监控:

maven配置:

	 <dependency>  
	     <groupId>com.netflix.hystrix</groupId>  
	     <artifactId>hystrix-metrics-event-stream</artifactId>  
	     <version>1.1.2</version>  
 	</dependency>

web.xml配置

    <servlet>  
	    <display-name>HystrixMetricsStreamServlet</display-name>  
	    <servlet-name>HystrixMetricsStreamServlet</servlet-name>  
	    <servlet-class>com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet</servlet-class>  
	</servlet>  
	<servlet-mapping>  
	    <servlet-name>HystrixMetricsStreamServlet</servlet-name>  
	    <url-pattern>/hystrix.stream</url-pattern>  
	</servlet-mapping>

访问http://ip:port/projectName/hystrix.stream查看是否有监控数据

hystrix-dashboard:先安装hystrix-dashboard.war,下载后放入web容器(Tomcat或Jetty)中,启动容器,访问http://ip:port/hystrix-dashboard,如下的界面:

371D6055-27B8-41ED-ADF3-544E92ECC934

最后点击Monitor Stream就可以看到数据了

EFD10FC9-9BAB-43DE-B5EB-09082222744B

可以看到每个服务及他们的线程池使用情况

CirCuit主要监控成功的请求数,请求超时数,失败率,平均耗时、90%,99%,99.5%的耗时(将鼠标移动到对于数字位置可以看到描述)

Thread Pools主要监控线程池的配置数,线程队列的配置数,最大活跃线程

 

Turbine监控(将多个实例的数据聚合起来)

1)下载turbine-web-1.0.0.war,并将war放入web容器中。

2)在容器下路径为turbine-web-1.0.0/WEB-INF/classes下新建config.properties文件。

InstanceDiscovery.impl=com.netflix.turbine.discovery.ConfigPropertyBasedDiscovery
#cluster
turbine.aggregator.clusterConfig=dynamicAPI
turbine.instanceUrlSuffix=:8080/projectName/hystrix.stream
turbine.ConfigPropertyBasedDiscovery.dynamicAPI.instances=127.0.0.1,127.0.0.2

这里我配置了2个实例127.0.0.1和127.0.0.2,他会同时调用http://127.0.0.1:8080/projectName/hystrix.stream和http://127.0.0.2:8080/projectName/hystrix.stream,然后根据分组名,服务名,线程池名将两个实例数据合并

3)调用http://ip:port/turbine-web-1.0.0/turbine.stream?cluster=dynamicAPI,即可查看到聚合的数据

4)打开hystrix-dashboard配置turbine

766892D0-ADE9-4BC4-A991-7844965A1557

点击Monitor Stream即可查看到两个实例监控的数据

5EC0BFFE-3C41-4F14-980D-39614A231A16

 

参考资料:

http://tech.lede.com/2017/06/15/rd/server/hystrix/

http://www.cnblogs.com/java-zhao/archive/2016/09/01/5831002.html

 

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注