Hystrx权威指南--Hystrix隔离策略
来源:互联网 发布:1024为熟知端口 编辑:程序博客网 时间:2024/06/03 13:33
Hystrix组件提供了两种隔离的解决方案:线程池隔离和信号量隔离。两种隔离方式都是限制对共享资源的并发访问量,线程在就绪状态、运行状态、阻塞状态、终止状态间转变时需要由操作系统调度,占用很大的性能消耗;而信号量是在访问共享资源时,进行tryAcquire,tryAcquire成功才允许访问共享资源。
protected HelloCommandIsolateThreadPool(String name) { super(HystrixCommand.Setter. //设置GroupKey 用于dashboard 分组展示 withGroupKey(HystrixCommandGroupKey.Factory.asKey("hello")) //设置commandKey 用户隔离线程池,不同的commandKey会使用不同的线程池 .andCommandKey(HystrixCommandKey.Factory.asKey("hello" + name)) //设置线程池名字的前缀,默认使用commandKey .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("hello$Pool" + name)) //设置线程池相关参数 .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter() .withCoreSize(15) .withMaxQueueSize(10) .withQueueSizeRejectionThreshold(2)) //设置command相关参数 .andCommandPropertiesDefaults(HystrixCommandProperties.Setter() //是否开启熔断器机制 .withCircuitBreakerEnabled(true) //舱壁隔离策略 .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD) //circuitBreaker打开后多久关闭 .withCircuitBreakerSleepWindowInMilliseconds(5000))); }
.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD)设置隔离策略是关键,默认是信号隔离,如果不设置为线程池隔离,上面设置的线程池相关的参数都无意义。开启熔断器机制,如果在10秒内50%以上的请求都失败,回路就会被断开,后面的请求都会直接返回失败,即 Fast Fail 策略。withCircuitBreakerSleepWindowInMilliseconds 5秒后会尝试闭合电路 。
在系统内部,线程池存放在一个ConcurrentHashMap中,key是commandKey ,value就是线程池。线程池的名字是 ThreadPoolKey值。
为避免在系统运行过程中,频繁的创建新的线程,过段时间又销毁线程,在Hystrix系统内部,线程池的最大线程数和核心线程数是同样大小,所以设置时,只有一个CoreSize参数需要设置。
- 线程池名字andThreadPoolKey ,
- coreSize(核心线程池大小) ,
- KeepAliveTimeMinutes(线程存存活时间),
- MaxQueueSize(最大队列度),
- QueueSizeRejectionThreshold(拒绝执行的阀值)等等。
.andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter()
.withCoreSize(resourcesManager.getThreadPoolProperties(platformProtocol.getAppId()).getCoreSize())
.withKeepAliveTimeMinutes(resourcesManager.getThreadPoolProperties(platformProtocol.getAppId()).getKeepAliveSeconds()) .withMaxQueueSize(resourcesManager.getThreadPoolProperties(platformProtocol.getAppId()).getMaxQueueSize()) .withQueueSizeRejectionThreshold(resourcesManager.getThreadPoolProperties(platformProtocol.getAppId()).getQueueSizeRejectionThreshold()))threadPoolKey 也是线程池的名字的前缀,默认前缀是 hystrix 。在Hystrix中,核心线程数和最大线程数是一致的,减少线程临时创建和销毁带来的性能开销。线程池的默认参数都在HystrixThreadPoolProperties中,重点讲解一下参数queueSizeRejectionThreshold 和maxQueueSize 。
- queueSizeRejectionThreshold默认值是5,允许在队列中的等待的任务数量。
- maxQueueSize默认值是-1,队列大小。如果是Fast Fail 应用,建议使用默认值。线程池饱满后直接拒绝后续的任务,不再进行等待。
@Override public boolean isQueueSpaceAvailable() { if (queueSize <= 0) { // we don't have a queue so we won't look for space but instead // let the thread-pool reject or not return true; } else { return threadPool.getQueue().size() < properties.queueSizeRejectionThreshold().get(); } }
线程池一旦创建完成,相关参数就不会更改,存放在静态的ConcurrentHashMap中,key是对应的commandKey 。而queueSizeRejectionThreshold是每个命令都是设置的。线程池的相关参数都保存在HystrixThreadPool这个类文件中,线程池的创建方法getThreadPool则在HystrixConcurrencyStrategy类文件中。从getThreadPool方法可以看出线程池的名字就是hystrix-threadPoolKey-threadNumber.
@Override public Thread newThread(Runnable r) { Thread thread = new Thread(r, "hystrix-" + threadPoolKey.name() + "-" + threadNumber.incrementAndGet()); thread.setDaemon(true); return thread; }
/* package */final static ConcurrentHashMap<String, HystrixThreadPool> threadPools = new ConcurrentHashMap<String, HystrixThreadPool>(); String key = threadPoolKey.name(); // this should find it for all but the first time HystrixThreadPool previouslyCached = threadPools.get(key); if (previouslyCached != null) { return previouslyCached; } // if we get here this is the first time so we need to initialize synchronized (HystrixThreadPool.class) { if (!threadPools.containsKey(key)) { threadPools.put(key, new HystrixThreadPoolDefault(threadPoolKey, propertiesBuilder)); } } return threadPools.get(key);
final protected Observable<R> getExecutionObservable() { return Observable.create(new OnSubscribe<R>() { @Override public void call(Subscriber<? super R> s) { try { s.onNext(run()); s.onCompleted(); } catch (Throwable e) { s.onError(e); } } }); }
在这个Call方法中执行了具体的业务逻辑run() ;
线程隔离的优点:
- 使用线程可以完全隔离第三方代码,请求线程可以快速放回。
- 当一个失败的依赖再次变成可用时,线程池将清理,并立即恢复可用,而不是一个长时间的恢复。
- 可以完全模拟异步调用,方便异步编程。
线程隔离的缺点:
- 线程池的主要缺点是它增加了cpu,因为每个命令的执行涉及到排队(默认使用SynchronousQueue避免排队),调度和上下文切换。
- 对使用ThreadLocal等依赖线程状态的代码增加复杂性,需要手动传递和清理线程状态。
注: Netflix公司内部认为线程隔离开销足够小,不会造成重大的成本或性能的影响。Netflix 内部API 每天100亿的HystrixCommand依赖请求使用线程隔,每个应用大约40多个线程池,每个线程池大约5-20个线程。
Semaphore that only supports tryAcquire and never blocks and that supports a dynamic permit count.Using AtomicInteger increment/decrement instead of java.util.concurrent.Semaphore since we don't need blocking and need a custom implementation to get the dynamic permit count and since AtomicInteger achieves the same behavior and performance without the more complex implementation of the actual Semaphore class using AbstractQueueSynchronizer.
在开发时,跟线程池隔离类似,同样是继承HystrixCommand类,在run方法中实现业务逻辑,通过getFallback 实现优雅降级。只是在设置隔离策略及相关参数数有较小的变化:
protected HelloCommandIsolateSemaphore(String key, int semaphoreCount) { super(HystrixCommand.Setter //设置GroupKey 用于dashboard 分组展示 .withGroupKey(HystrixCommandGroupKey.Factory.asKey("hello")) //设置CommandKey 用于Semaphore分组,相同的CommandKey属于同一组隔离资源 .andCommandKey(HystrixCommandKey.Factory.asKey("hello" + key)) //设置隔离级别:Semaphore .andCommandPropertiesDefaults(HystrixCommandProperties.Setter() //是否开启熔断器机制 .withCircuitBreakerEnabled(true) //舱壁隔离策略 .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE) //设置每组command可以申请的permit最大数 .withExecutionIsolationSemaphoreMaxConcurrentRequests(50) //circuitBreaker打开后多久关闭 .withCircuitBreakerSleepWindowInMilliseconds(5000))); }
TryableSemaphore 接口定义了信号隔离的行为,内部借助AtomicInteger类实现资源的分配。HystrixProperty<Integer> numberOfPermits 存储可分配的资源,AtomicInteger count存储已分配的资源。numberOfPermits 在类初始化时就需要赋值,所以定义成了final类型。
protected final HystrixProperty<Integer> numberOfPermits;private final AtomicInteger count = new AtomicInteger(0);
@Override public boolean tryAcquire() { int currentCount = count.incrementAndGet(); if (currentCount > numberOfPermits.get()) { count.decrementAndGet(); return false; } else { return true; } }
- Hystrx权威指南--Hystrix隔离策略
- Hystrx权威指南--Hystrix属性配置策略
- 【Hystrix权威指南二】Hystrix隔离策略
- Hystrx权威指南--Hystrix是什么
- Hystrx权威指南--Hystrix调用方法解析
- Hystrx权威指南--Hystrix的Timeout解析
- Hystrx权威指南--Hystrix执行流程
- Hystrx权威指南--Hystrix实现原理
- Hystrx权威指南--Hystrix的注解方式
- 【Hystrix权威指南三】Hystrix隔离策略源码分析一
- 【Hystrix权威指南四】Hystrix隔离策略源码分析二
- Hystrx权威指南--Hystrix的类是如何组织的
- Hystrx权威指南--Hystrix的线程池解析
- Hystrx权威指南--Hystrix的熔断机制解析
- Hystrx权威指南--Hystrix请求缓存和COLLAPSER
- 十三、断路器-Hystrix 的隔离策略
- Hystrix系列-5-Hystrix的资源隔离策略
- 【Hystrix权威指南一】Hystrix开发之旅
- 周总结1
- 在O(1)时间删除链表结点
- sql-server 不允许保存更改,解决办法
- Android Studio项目基本文件结构
- iOS NSDictionary(字典)或者NSArray(数组)和JSON串的转换
- Hystrx权威指南--Hystrix隔离策略
- CentOS 7 安装 MySQL
- oracle服务器更改IP地址后,启动不了
- shell脚本编写中的#!shebang详解
- 【剑指offer】面试题24:反转链表
- vs2015 C# 和R语言并行开发
- 最短路径算法—Dijkstra(迪杰斯特拉)
- html 滑动卡顿问题
- IP 地址分类(A、B、C、D、E类)