Skip to content

Commit 0bb4919

Browse files
author
Ding Bao Guo
committed
add
1 parent c62f953 commit 0bb4919

21 files changed

Lines changed: 642 additions & 5 deletions

_posts/2019-08-20-469-hystrix06.md

Lines changed: 173 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,182 @@ pid: 469
55
tags: [microservice, springcloud]
66
---
77

8+
伴随着微服务架构被宣传得如火如荼,一些概念也被推到了我们面前(管你接受不接受),其实大多数概念以前就有,但很少被提的这么频繁(现在好像不提及都不好意思交流了)。想起有人总结的一句话,微服务架构的特点就是:“一解释就懂,一问就不知,一讨论就吵架”。
89

10+
其实对老外的总结能力一直特别崇拜,Kevin Kelly、Martin Fowler、Werner Vogels……,都是著名的“演讲家”。正好这段时间看了些微服务、容器的相关资料,也在我们新一代产品中进行了部分实践,回过头来,再来谈谈对一些概念的理解。
911

10-
待更新!
12+
# 一、“服务熔断”和“服务降级”
1113

14+
今天先来说说“服务熔断”和“服务降级”。为什么要说这个呢,因为我很长时间里都把这两个概念同质化了,不知道这两个词大家怎么理解,一个意思or有所不同?现在的我是这么来看的:
1215

16+
两个示例:
17+
18+
**示例1:**
19+
20+
故事的背景是这样的:由于小强在工作中碰到一些问题,于是想请教一下业界大牛小壮。于是发生了下面的两个场景:
21+
22+
小强在拿起常用手机拨号时发现该手机没有能够拨通,所以就拿出了备用手机拨通了某A的电话,这个过程就叫做降级(主逻辑失败采用备用逻辑的过程)。
23+
24+
由于每次小壮的解释都属于长篇大论,不太容易理解,所以小强每次找小壮沟通的时候都希望通过常用手机来完成,因为该手机有录音功能,这样自己可以慢慢消化。由于上一次的沟通是用备用电话完成的,小强又碰到了一些问题,于是他又尝试用常用电话拨打,这一次又没有能够拨通,所以他不得不又拿出备用手机给某A拨号,就这样连续的经过了几次在拨号设备选择上的“降级”,小强觉得短期内常用手机可能因为运营商问题无法正常拨通了,所以,再之后一段时间的交流中,小强就不再尝试用常用手机进行拨号,而是直接用备用手机进行拨号,这样的策略就是熔断(常用手机因短期内多次失败,而被暂时性的忽略,不再尝试使用)
25+
26+
**示例2:**
27+
28+
+ 在股票市场,熔断这个词大家都不陌生,是指当股指波幅达到某个点后,交易所为控制风险采取的暂停交易措施。相应的,服务熔断一般是指软件系统中,由于某些原因使得服务出现了过载现象,为防止造成整个系统故障,从而采用的一种保护措施,所以很多地方把熔断亦称为过载保护。
29+
30+
+ 大家都见过女生旅行吧,大号的旅行箱是必备物,平常走走近处绰绰有余,但一旦出个远门,再大的箱子都白搭了,怎么办呢?常见的情景就是把物品拿出来分分堆,比了又比,最后一些非必需品的就忍痛放下了,等到下次箱子够用了,再带上用一用。而服务降级,就是这么回事,整体资源快不够了,忍痛将某些服务先关掉,待渡过难关,再开启回来。
31+
32+
所以从上述分析来看,两者其实从有些角度看是有一定的类似性的:
33+
34+
+ **目的很一致**,都是从可用性可靠性着想,为防止系统的整体缓慢甚至崩溃,采用的技术手段;
35+
+ **最终表现类似**,对于两者来说,最终让用户体验到的是某些功能暂时不可达或不可用;
36+
+ **粒度一般都是服务级别**,当然,业界也有不少更细粒度的做法,比如做到数据持久层(允许查询,不允许增删改);
37+
+ **自治性要求很高**,熔断模式一般都是服务基于策略的自动触发,降级虽说可人工干预,但在微服务架构下,完全靠人显然不可能,开关预置、配置中心都是必要手段;
38+
39+
而两者的区别也是明显的:
40+
41+
+ **触发原因不太一样**,服务熔断一般是某个服务(下游服务)故障引起,而服务降级一般是从整体负荷考虑;
42+
+ **管理目标的层次不太一样**,熔断其实是一个框架级的处理,每个微服务都需要(无层级之分),而降级一般需要对业务有层级之分(比如降级一般是从最外围服务开始)
43+
+ **实现方式不太一样**,这个区别后面会单独来说;
44+
45+
# 二、熔断器的状态机
46+
47+
当然这只是我个人对两者的理解,外面把两者归为完全一致的也不在少数,或者把熔断机制理解为应对降级目标的一种实现也说的过去,可能“一讨论就吵架”也正是这个原因吧!
48+
49+
概念算是说完了,避免空谈,我再总结下对常用的实现方法的理解。对于这两个概念,号称支持的框架可不少,Hystrix当属其中的佼佼者。
50+
51+
先说说最裸的熔断器的设计思路,下面这张图大家应该不陌生(我只是参考着又画了画),简明扼要的给出了好的熔断器实现的三个状态机:
52+
53+
![](/uploads/2019/08/21-01.png)
54+
55+
+ Closed:熔断器关闭状态,调用失败次数积累,到了阈值(或一定比例,默认是50%)则启动熔断机制;
56+
+ Open:熔断器打开状态,此时对下游的调用都内部直接返回错误,不走网络,但设计了一个时钟选项,默认的时钟达到了一定时间(这个时间一般设置成平均故障处理时间,也就是MTTR),到了这个时间,进入半熔断状态;
57+
+ Half-Open:半熔断状态,允许定量的服务请求,如果调用都成功(或一定比例)则认为恢复了,关闭熔断器,否则认为还没好,又回到熔断器打开状态;
58+
59+
那Hystrix,作为Netflix开源框架中的最受喜爱组件之一,是怎么处理依赖隔离,实现熔断机制的呢,他的处理远比我上面说个实现机制复杂的多,一起来看看核心代码吧,我只保留了代码片段的关键部分:
60+
61+
```java
62+
63+
public abstract class HystrixCommand<R> extends AbstractCommand<R> implements HystrixExecutable<R>, HystrixInvokableInfo<R>, HystrixObservable<R> {
64+
65+
protected abstract R run() throws Exception;
66+
67+
protected R getFallback() {
68+
throw new UnsupportedOperationException("No fallback available.");
69+
}
70+
71+
@Override
72+
final protected Observable<R> getExecutionObservable() {
73+
return Observable.defer(new Func0<Observable<R>>() {
74+
@Override
75+
public Observable<R> call() {
76+
try {
77+
return Observable.just(run());
78+
} catch (Throwable ex) {
79+
return Observable.error(ex);
80+
}
81+
}
82+
});
83+
}
84+
85+
@Override
86+
final protected Observable<R> getFallbackObservable() {
87+
return Observable.defer(new Func0<Observable<R>>() {
88+
@Override
89+
public Observable<R> call() {
90+
try {
91+
return Observable.just(getFallback());
92+
} catch (Throwable ex) {
93+
return Observable.error(ex);
94+
}
95+
}
96+
});
97+
}
98+
99+
public R execute() {
100+
try {
101+
return queue().get();
102+
} catch (Exception e) {
103+
throw decomposeException(e);
104+
}
105+
}
106+
107+
```
108+
109+
HystrixCommand是重重之重,在Hystrix的整个机制中,涉及到依赖边界的地方,都是通过这个Command模式进行调用的,显然,这个Command负责了核心的服务熔断和降级的处理,子类要实现的方法主要有两个:
110+
111+
+ run方法:实现依赖的逻辑,或者说是实现微服务之间的调用;
112+
+ getFallBack方法:实现服务降级处理逻辑,只做熔断处理的则可不实现;
113+
114+
使用时,可参考如下方式:
115+
116+
```
117+
118+
public class TestCommand extends HystrixCommand<String> {
119+
120+
protected TestCommand(HystrixCommandGroupKey group) {
121+
super(group);
122+
}
123+
124+
@Override
125+
protected String run() throws Exception {
126+
//这里需要做实际调用逻辑
127+
return "Hello";
128+
}
129+
130+
public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {
131+
TestCommand command = new TestCommand(HystrixCommandGroupKey.Factory.asKey("TestGroup"));
132+
133+
//1.这个是同步调用
134+
command.execute();
135+
136+
//2.这个是异步调用
137+
command.queue().get(500, TimeUnit.MILLISECONDS);
138+
139+
//3.异步回调
140+
command.observe().subscribe(new Action1<String>() {
141+
public void call(String arg0) {
142+
143+
}
144+
});
145+
}
146+
}
147+
148+
```
149+
150+
细心的同学肯定发现Command机制里大量使用了Observable相关的API,这个是什么呢?原来其隶属于RxJava,这个框架就不多介绍了 --- 响应式开发,也是Netflix的作品之一,具体大家可参考这系列博客,我觉得作者写的很通俗:http://blog.csdn.net/lzyzsd/article/details/41833541/
151+
152+
接着呢,大家一定会问,那之前说的熔断阈值设置等,都在哪块做的呢?再来看看另一块核心代码:
153+
154+
```
155+
156+
public abstract class HystrixPropertiesStrategy {
157+
158+
public HystrixCommandProperties getCommandProperties(HystrixCommandKey commandKey, HystrixCommandProperties.Setter builder) {
159+
return new HystrixPropertiesCommandDefault(commandKey, builder);
160+
}
161+
162+
......
163+
}
164+
165+
```
166+
167+
这个类作为策略类,返回相关的属性配置,大家可重新实现。而在具体的策略中,主要包括以下几种策略属性配置:
168+
169+
170+
+ circuitBreakerEnabled:是否允许熔断,默认允许;
171+
+ circuitBreakerRequestVolumeThreshold:熔断器是否开启的阀值,也就是说单位时间超过了阀值请求数,熔断器才开;
172+
+ circuitBreakerSleepWindowInMilliseconds:熔断器默认工作时间,超过此时间会进入半开状态,即允许流量做尝试;
173+
+ circuitBreakerErrorThresholdPercentage:错误比例触发熔断;
174+
+ ......
175+
176+
属性很多,这里就不一一说明了,大家可参考HystrixCommandProperties类里的详细定义。还有一点要着重说明的,在熔断器的设计里,隔离采用了线程的方式(据说还有信号的方式,这两个区别我还没搞太明白),处理依赖并发和阻塞扩展,示意图如下:
177+
178+
![](/uploads/2019/08/21-02.png)
179+
180+
如上图,好处也很明显,对于每个依赖都有独立可控的线程池,当然高并发时,CPU切换较多,有一定的影响。
181+
182+
啰嗦了一堆,最后总结一下,我认为服务熔断和服务降级两者是有区别的,同时通过对Hystrix的简单学习,了解了其实现机制,会逐步引入到我们的产品研发中。当然还有好多概念:服务限流、分流,请求与依赖分离等,后面有时间一一与大家分享。
13183

14184
# 参考
15-
+ [服务容错保护断路器Hystrix之六:服务熔断和服务降级](https://www.cnblogs.com/duanxz/p/9641489.html)
185+
+ [服务容错保护断路器Hystrix之六:服务熔断和服务降级](https://www.cnblogs.com/duanxz/p/9641489.html)
186+
+ [RxJava2.0——从放弃到入门](https://www.jianshu.com/p/cd3557b1a474)

_posts/2019-08-20-470-hystrix07.md

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,29 @@ pid: 470
55
tags: [microservice, springcloud]
66
---
77

8+
高并发环境下如果能处理好缓存就可以有效的减小服务器的压力,Java中有许多非常好用的缓存工具,比如Redis、EHCache等,当然在Spring Cloud的Hystrix中也提供了请求缓存的功能,我们可以通过一个注解或者一个方法来开启缓存,进而减轻高并发环境下系统的压力。
89

9-
待更新!
10+
# 请求缓存的整个生命周期
1011

12+
下图关于是请求缓存的整个生命周期
13+
14+
![](/uploads/2019/08/21-03.png)
15+
16+
**缓存优势**
17+
+ 复用性:这里的复用性指的是代码复用性
18+
+ 一致性:也就是常说的幂等性,不管请求几次,得到的结果应该都是一样的
19+
+ 减少重复工作:由于请求缓存是在HystrixCommand的construct()或run()运行之前运行,所有可以有效减少线程的使用
20+
21+
**适用场景**
22+
+ 请求缓存的优势显而易见,但是也不是银弹。
23+
+ 在读少写多的场景就显得不太合适,对于读的请求,需要add缓存。对于增删改的请求,需要把缓存remove。在增加系统资源开销的同时,又很鸡肋。
24+
+ 所以一般适合读多写少的场景。似乎所有缓存机制都有这个局限性吧。
25+
26+
为了介绍Hystrix的缓存如何使用,先搭建一些服务做好准备工作:
1127

1228

1329
# 参考
14-
+ [服务容错保护断路器Hystrix之六:缓存功能的使用](https://www.cnblogs.com/duanxz/p/9668011.html)
30+
+ [服务容错保护断路器Hystrix之六:缓存功能的使用](https://www.cnblogs.com/duanxz/p/9668011.html)
31+
+ [深入理解幂等性](https://www.cnblogs.com/javalyy/p/8882144.html)
32+
+ [Spring Cloud中Hystrix的请求缓存](https://segmentfault.com/a/1190000011280116)
33+
+ [源代码](https://github.com/lenve/SimpleSpringCloud)

0 commit comments

Comments
 (0)