上次说了使用RxJava并行提升API效率,但是如何避免项目中一个服务拖慢整体的性能?hystrix优雅的解决了这个问题。
先贴个图,网上找的,我觉得这个图就可以说明问题了
当依赖服务A挂了,直接就会影响到整个服务的性能,如果连接一直被A暂用,最终将导致服务发生雪崩
所以我们要保证核心服务的稳定,其他非核心的服务可以不那么重要,即使出现了错误数据可以丢弃或不用保证实时准确,再以SOA项目中的getDynamicInfo为例,如下:
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,如下的界面:
最后点击Monitor Stream就可以看到数据了
可以看到每个服务及他们的线程池使用情况
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
点击Monitor Stream即可查看到两个实例监控的数据
参考资料:
http://tech.lede.com/2017/06/15/rd/server/hystrix/
http://www.cnblogs.com/java-zhao/archive/2016/09/01/5831002.html