服务雪崩的过程
上面是一组简单的服务依赖关系A,B服务同时依赖于基础服务C,基础服务C又调用了服务D
服务D是一个辅助类型服务,整个业务不依赖于D服务,某天D服务突然响应时间变长,导致了核心服务C响应时间变长,其上请求越积越多,C服务也出现了响应变慢的情况,由于A,B强依赖于服务C,故而一个无关紧要的服务却影响了整个系统的可用。
雪崩是系统中的蝴蝶效应导致其发生的原因多种多样,有不合理的容量设计,或者是高并发下某一个方法响应变慢,亦或是某台机器的资源耗尽。从源头上我们无法完全杜绝雪崩源头的发生,但是雪崩的根本原因来源于服务之间的强依赖,所以我们可以提前评估,做好熔断,隔离,限流。
熔断
熔断器
说到熔断器,java技术栈的同学第一时间会想到Hystrix。下面我们一起来看下熔断器的实现原理
熔断器实际上是一个简单的有限状态机(Finite State Machine)
1.请求错误率达到某一阈值,熔断器全开,产生熔断(熔断期间会对所有请求采用降级处理)
2.到熔断时间窗口之后,熔断器会进入半开状态,此时hystrix会放过1个试验性请求
3.如果该试验性请求成功,熔断器进入关闭状态
4.如果该试验性请求失败,熔断器重新进入全开状态
以下摘自hystrix官方文档
1.Assuming the volume across a circuit meets a certain threshold (HystrixCommandProperties.circuitBreakerRequestVolumeThreshold())...
2.And assuming that the error percentage exceeds the threshold error percentage (HystrixCommandProperties.circuitBreakerErrorThresholdPercentage())...
3.Then the circuit-breaker transitions from CLOSED to OPEN.
4.While it is open, it short-circuits all requests made against that circuit-breaker.
5.After some amount of time (HystrixCommandProperties.circuitBreakerSleepWindowInMilliseconds()), the next single request is let through (this is the HALF-OPEN state). If the request fails, the circuit-breaker returns to the OPEN state for the duration of the sleep window. If the request succeeds, the circuit-breaker transitions to CLOSED and the logic in 1. takes over again.
指标Metric
hystrix内部的统计指标是基于观察者模式的,只不过事件的统计方式1.4.x与1.5.0王后的版本有些许不同。
1.4.x版本实现
采用滑动窗口+滚筒的机制进行指标统计,一个完整的时间窗口被划分为多个bucket,图中的一个bucket统计的是1s内的计数指标,分别是success,failure,timeout,rejection,熔断器评判标准是整个时间窗口的成功失败比。
1.5.0以后版本
利用了hystirx大量引入了rxjava api,其保留了滚筒机制,只不过bucket内部的时间段指标统计利用了Observable.window 函数进行时间段内聚合统计。
Observable.window操作分为2个维度,分别是bucket级别和window级别。
bucket级别的数据聚合是在BucketedCounterStream.java类中执行,其功能是将服务调用级别的输入数据流 inputEventStream 以 bucketSizeInMs 毫秒为一个桶进行了汇总,汇总的结果输入到桶级别数据流 bucketedStream。
window级别的数据在BucketedRollingCounterStream.java类中进行聚合,numBuckets定义了window的大小,reduceWindowToSummary为窗口求和操作。
使用rxjava给hystrix带来了下面几点好处
无需再次编码
比如window中的"滚筒"操作直接可以利用rxjava的window函数并且在后台线程中运行,无需自己再编码实现
减少了线程同步
比如每条command emit的数据会发射到 thread-local级别的rx.Subject,无需再进行同步操作,
资源隔离
太空科幻题材电影永远是好莱坞最炙手可热的主题,而一部电影是否能够扣人心弦逃跑的桥段自然不能少,尤其在狭小的空间站中。《地心引力》中的女主逃离着火的国际空间站,《异行》中飞船上的伙计们逃离异行的追杀,《星际穿越》中库珀逃离旋转的空间站,似乎大家面对危险的第一反应就是“关上舱门”,把危险隔离在外。
防水仓
实际上船舱分开设计本身就是一种隔离的思想,一个防水仓进水不会导致整艘轮船沉没。如果把我们整个系统比作海上漂浮的一艘轮船,那么我们系统的各个服务就好比轮船上的各个密封舱,服务A如果强依赖于服务B那么他们就在一个舱里。
应用级别隔离
常见的应用界别隔离手段有线程池隔离,信号量隔离,连接池隔离,hystrix实现了前2种,其各自优缺点如下
硬件资源级别隔离
说到硬件级别的隔离就想到了近些年来大热的docker
docker的sandbox特性可以让我们的应用如图中的集装箱一个个彼此分开,单个集装箱的损坏不会影响到整个服务器环境。docker为我们提供了以下几种硬件隔离方式。
docker中计算机资源隔离
docker 通过 cgroup 来控制容器使用的资源配额,包括 CPU、内存、磁盘IO三大方面
cgroup 是 Control Groups 的缩写,是 Linux 内核提供的一种可以限制、记录、隔离进程组所使用的物理资源(如 cpu、memory、磁盘IO等等) 的机制,被 LXC、docker 等很多项目用于实现进程资源控制。
CPU
CPU份额控制
通过-c或者–cpu-shares参数,在创建容器时指定容器所使用的 CPU 份额值
CPU周期控制
-cpu-period、--cpu-quota两个参数控制容器可以分配到的 CPU 时钟周期
CPU核心数控制
使用–cpuset-cpu s和–cpuset-mems参数可以控制容器运行限定使用哪些 CPU 内核和内存节点
内存
-m 或 --memory:设置内存的使用限额,例如 100M, 2G。
--memory-swap:设置 内存+swap 的使用限额。
IO
block IO 权重
--blkio-weight参数可以改变容器 block IO 的优先级
限制 bps 和 iops
bps 是 byte per second,每秒读写的数据量。iops 是 io per second,每秒 IO 的次数。
--device-read-bps,限制读某个设备的 bps。
--device-write-bps,限制写某个设备的 bps。
--device-read-iops,限制读某个设备的 iops。
--device-write-iops,限制写某个设备的 iops。
docker中内核资源隔离
docker网络隔离
none 网络
host 网络
bridge 网络
User-defined 网络
资料参考:
http://reactivex.io/documentation/operators.html
https://segmentfault.com/a/1190000005988895
https://github.com/Netflix/Hystrix/wiki
https://blog.csdn.net/hustspy1990/article/details/77978329
http://blog.51cto.com/wzlinux/2046566