-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy path15274398936380.html
514 lines (310 loc) · 21.8 KB
/
15274398936380.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
<!doctype html>
<html class="no-js" lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>
电商平台备战促销季的运维秘诀——高可用服务层 - Junkman
</title>
<link href="atom.xml" rel="alternate" title="Junkman" type="application/atom+xml">
<link rel="stylesheet" href="asset/css/foundation.min.css" />
<link rel="stylesheet" href="asset/css/docs.css" />
<script src="asset/js/vendor/modernizr.js"></script>
<script src="asset/js/vendor/jquery.js"></script>
<script src="asset/highlightjs/highlight.pack.js"></script>
<link href="asset/highlightjs/styles/github.css" media="screen, projection" rel="stylesheet" type="text/css">
<script>hljs.initHighlightingOnLoad();</script>
<script type="text/javascript">
function before_search(){
var searchVal = 'site:panlw.github.io ' + document.getElementById('search_input').value;
document.getElementById('search_q').value = searchVal;
return true;
}
</script>
</head>
<body class="antialiased hide-extras">
<div class="marketing off-canvas-wrap" data-offcanvas>
<div class="inner-wrap">
<nav class="top-bar docs-bar hide-for-small" data-topbar>
<section class="top-bar-section">
<div class="row">
<div style="position: relative;width:100%;"><div style="position: absolute; width:100%;">
<ul id="main-menu" class="left">
<li id=""><a target="self" href="index.html">Home</a></li>
<li id=""><a target="_self" href="archives.html">Archives</a></li>
</ul>
<ul class="right" id="search-wrap">
<li>
<form target="_blank" onsubmit="return before_search();" action="http://google.com/search" method="get">
<input type="hidden" id="search_q" name="q" value="" />
<input tabindex="1" type="search" id="search_input" placeholder="Search"/>
</form>
</li>
</ul>
</div></div>
</div>
</section>
</nav>
<nav class="tab-bar show-for-small">
<a href="javascript:void(0)" class="left-off-canvas-toggle menu-icon">
<span> Junkman</span>
</a>
</nav>
<aside class="left-off-canvas-menu">
<ul class="off-canvas-list">
<li><a href="index.html">HOME</a></li>
<li><a href="archives.html">Archives</a></li>
<li><a href="about.html">ABOUT</a></li>
<li><label>Categories</label></li>
<li><a href="Infra.html">Infra</a></li>
<li><a href="Coding.html">Coding</a></li>
<li><a href="Modeling.html">Modeling</a></li>
<li><a href="Archtecting.html">Archtecting</a></li>
</ul>
</aside>
<a class="exit-off-canvas" href="#"></a>
<section id="main-content" role="main" class="scroll-container">
<script type="text/javascript">
$(function(){
$('#menu_item_index').addClass('is_active');
});
</script>
<div class="row">
<div class="large-8 medium-8 columns">
<div class="markdown-body article-wrap">
<div class="article">
<h1>电商平台备战促销季的运维秘诀——高可用服务层</h1>
<div class="read-more clearfix">
<span class="date">2018/5/28</span>
<span>posted in </span>
<span class="posted-in"><a href='Microservice.html'>Microservice</a></span>
<span class="comments">
</span>
</div>
</div><!-- article -->
<div class="article-content">
<blockquote>
<p>曹林华 纯洁的微笑 2018/5/26</p>
<p><a href="https://mp.weixin.qq.com/s/5vVXBXkd-Ilh7zk5G6Wxcg">原文地址</a></p>
<p>高可用设计是互联网系统架构的基础之一,以天猫双十二交易数据为例,支付宝峰值支付次数超过 8 万笔。大家设想一下,如果这个时候系统出现不可用的情况,那后果将不可想象。<br/>
而解决这个问题的根本就是服务层的高可用。</p>
</blockquote>
<h2 id="toc_0">什么是服务层</h2>
<p>众所周知,服务层主要用来处理网站业务逻辑的,是大型业务网站的核心。比如下面三个业务系统就是典型的服务层,提供基础服务功能的聚合</p>
<ul>
<li><p>用户中心:主要负责用户注册、登录、获取用户用户信息功能</p></li>
<li><p>交易中心:主要包括正向订单生成、逆向订单、查询、金额计算等功能</p></li>
<li><p>支付中心:主要包括订单支付、收银台、对账等功能</p></li>
</ul>
<p><img src="https://mmbiz.qpic.cn/mmbiz_png/PgqYrEEtEnpebqWGB1ZhMvUTOhUfsMdlialgfhicgwy6oEeNHT1HKDxoPpnY0JKoKLZsfKd0Z7zxcfiaSzf0DdSqA/640?wx_fmt=png" alt=""/>电商平台备战促销季的运维秘诀——高可用服务层</p>
<h2 id="toc_1">整体架构</h2>
<p>业务发展初期主要以业务为导向,一般采用 「ALL IN ONE」的架构方式来开发产品,这个阶段用一句话概括就是 「糙猛快」。当发展起来之后就会遇到下面这些问题</p>
<ul>
<li><p>文件大:一个代码文件出现超过 2000 行以上</p></li>
<li><p>耦合性严重:不相关业务都直接堆积在 Serivce 层中</p></li>
<li><p>维护代价高:人员离职后,根本没有人了解里面的业务逻辑</p></li>
<li><p>牵一发动全身:改动少量业务逻辑,需要重新把所有依赖包打包并发布</p></li>
</ul>
<p>遇到这些问题,主要还是通过「拆」来解决</p>
<p><img src="https://mmbiz.qpic.cn/mmbiz_png/PgqYrEEtEnpebqWGB1ZhMvUTOhUfsMdlR3f0gpEglmktfDdT7JZsPNts8y6PwIDvpoVzYzXjriaLYicTz9sib2IYQ/640?wx_fmt=png" alt=""/>电商平台备战促销季的运维秘诀——高可用服务层</p>
<p>具体拆的方式,主要根据业务领域划分单元,进行垂直拆分。拆分开来的好处很明显,主要有以下这些:</p>
<ul>
<li><p>每个业务一个独立的业务模块</p></li>
<li><p>业务间完全解耦</p></li>
<li><p>业务间互不影响</p></li>
<li><p>业务模块独立</p></li>
<li><p>单独开发、上线、运维</p></li>
<li><p>效率高</p></li>
</ul>
<h2 id="toc_2">无状态设计</h2>
<p>对于业务逻辑服务层,一般会设计成无状态化的服务,无状态化也就是服务模块只处理业务逻辑,而无需关心业务请求的上下文信息。所以无状态化的服务器之间是相互平等且独立的。</p>
<p>只有服务变为无状态的时候,故障转移才会变的很轻松。通常故障转移就是在某一个应用服务器不能服务用户请求的时候,通过负责均衡的方式,转移用户请求到其他应用服务器上来进行业务逻辑处理</p>
<p><img src="https://mmbiz.qpic.cn/mmbiz_png/PgqYrEEtEnpebqWGB1ZhMvUTOhUfsMdlrVLkBKpJZja8eqMrcYwM3Mh3TVhgibEniaQickmJw3W7IiccBmMFm5sZAw/640?wx_fmt=png" alt=""/>电商平台备战促销季的运维秘诀——高可用服务层</p>
<h2 id="toc_3">超时设置</h2>
<p>一般网站服务都会有主调服务和被调服务之分。超时设置就是主调服务在调用被调服务的时候,设置一个超时等待时间 Timeout。主调服务发现超时后,就进入超时处理流程。</p>
<p><img src="https://mmbiz.qpic.cn/mmbiz_png/PgqYrEEtEnpebqWGB1ZhMvUTOhUfsMdlBhI38T15JZ8gV0fNRfWcjqFibk2VjNPedQAwdy5DOETk1TV43qic5aVA/640?wx_fmt=png" alt=""/>电商平台备战促销季的运维秘诀——高可用服务层</p>
<ol>
<li><p>主调服务 A 调用被调服务 B 时,设置超时等待时间为 3 秒,可能由于 B 服务宕机、网络情况不好或程序 BUG 之类,导致 B 服务不能及时响应 A 服务的调用。</p></li>
<li><p>此时 A 服务在等待 3 秒后,将触发超时逻辑而不再关心 B 服务的回复情况。</p></li>
<li><p>A 服务的超时逻辑可以依据情况而定,比如可以采取重试,对另一个对等的 B 服务去请求,或直接放弃结束这个请求调用。</p></li>
</ol>
<p>超时设置的好处在于当某个服务不可用时,不至于整个系统发生雪崩反应。</p>
<h2 id="toc_4">异步调用</h2>
<p>一般请求调用分为同步与异步两种。同步请求就像打电话,需要实时响应,而异步请求就像发送邮件一样,不需要马上回复。</p>
<p>这两种调用各有优劣,主要看面对哪种业务场景。比如在面对并发性能要求比较高的场景,异步调用就比同步调用有比较大的优势,这就好比一个人不能同时打多个电话,但是可以发送很多邮件。</p>
<p><img src="https://mmbiz.qpic.cn/mmbiz_png/PgqYrEEtEnpebqWGB1ZhMvUTOhUfsMdlrMaqR6L2RGN4ZNKoX60pk0ckuAypBfThcu227jxF2rbN8XENfgbLTg/640?wx_fmt=png" alt=""/>电商平台备战促销季的运维秘诀——高可用服务层</p>
<blockquote>
<p>那我们什么时候该采用异步调用?</p>
</blockquote>
<p>其实主要看业务场景,如果业务允许延迟处理,那就采用异步的方式处理</p>
<blockquote>
<p>那我们该怎么实现异步调用呢?</p>
</blockquote>
<p>通常采用队列的方式来实现业务上的延迟处理,比如像订单中心调用配送中心,这种场景下面,业务是能接受延迟处理的。</p>
<p>那消息队列主要有哪些功能呢?</p>
<ul>
<li><p>异步处理 - 增加吞吐量</p></li>
<li><p>削峰填谷 - 提高系统稳定性</p></li>
<li><p>系统解耦 - 业务边界隔离</p></li>
<li><p>数据同步 - 最终一致性保证</p></li>
</ul>
<p>那到底有多少种队列呢?其实主要看处理业务的范围大小</p>
<ul>
<li><p>应用内部 - 采用线程池,比如 Java ThreadPool 中 BlockingQueue 来做任务级别的缓冲与处理</p></li>
<li><p>应用外部 - 比如 RabbitMQ 、ActiveMQ 就是做应用级别的队列,方便进行业务边界隔离与提高吞吐量</p></li>
</ul>
<p><img src="https://mmbiz.qpic.cn/mmbiz_png/PgqYrEEtEnpebqWGB1ZhMvUTOhUfsMdlvaSzVuYpLsyDzfC4AJF2X5Ww7L1iaaQceaRwoSjwhL05oJdibvVMQIrw/640?wx_fmt=png" alt=""/>电商平台备战促销季的运维秘诀——高可用服务层</p>
<p>同时,技术上来讲,消息队列一般分为两种模型:Pull VS Push</p>
<ul>
<li><p>Pull 模型:消费者主动请求消息队列,获取队列中的消息。</p></li>
<li><p>Push 模型:消息队列主动推送消息到消费者</p></li>
</ul>
<p>其中 Pull 模式可以控制消费速度,不必担心自己处理不了消息,只需要维护队列中偏移量 Offset。所以对于消费量有限并且推送到队列的生产者不均匀的情况下,采用 Pull 模式比较合适。</p>
<p>Push 比较适合实时性要求比较高的情况,只要生产者消息发送到消息队列中,队列就会主动 Push 消息到消费者,不过这种模式对消费者的能力要求就提高很多,如果出现队列给消费者推送一些不能处理的消息,消费者出现 Exception 情况下,就会再次入队列,造成消费堵塞的情况。</p>
<p>不过互联网业界比较成熟的队列主要以采用 Pull 模式为主,像 Kafka、RabbitMQ(两种方式都支持)、RocketMQ 等</p>
<h2 id="toc_5">幂等</h2>
<blockquote>
<p>什么是幂等设计呢?</p>
</blockquote>
<p>其实很简单,就是一次请求和多个请求的作用是一样的。用数学上的术语,即是 f(x) = f(f(x))。</p>
<p>那我们为什么要做幂等性的设计呢?主要是因为现在的系统都是采用分布式的方式设计系统,在分布式系统中调用一般分为 3 个状态:成功、失败、超时。</p>
<p>如果调用是成功或者失败都不要紧,因为状态是明确和清晰,但是如果出现超时的情况,就不知道请求是成功还是失败的。</p>
<p><img src="https://mmbiz.qpic.cn/mmbiz_png/PgqYrEEtEnpebqWGB1ZhMvUTOhUfsMdla3ufL6yQt2W78GGKeq8M06eN6dmOWia6bSn1BwHwtdrw0gZf33g66wA/640?wx_fmt=png" alt=""/>电商平台备战促销季的运维秘诀——高可用服务层</p>
<p>如果出现这种情况,我们该怎么办呢?一般采取重试的操作,重新请求对应接口。如果请求接口是 Get 操作的话,那到还好,因为请求多次的效果是一样的。但是如果是 Post 、Put 操作的话,就会造成数据不一致,甚至数据覆盖等问题。</p>
<p>举个例子:在支付收银台页面进行支付的时候,因为网络超时的问题导致支付失败,这个时候我们都会再进行一次支付操作,但是当支付成功后,发现你的账户余额被减了 2 次,这个时候心里肯定很不爽,心里都要开始骂娘了…</p>
<p>造成这个问题的关键是:网络超时后,不知道支付是什么状态?成功还是失败呢?所以说幂等性设计是必须的,尤其在电商、金融、银行等对数据要求比较高的行业中。</p>
<p>一般在这种场景下我们该怎么解决呢?</p>
<ol>
<li><p>请求方一般会生产一个唯一性 ID 标识,这个标识可以具有业务一样,比如订单号或者支付流水号,在发起请求时候带上唯一性 ID。</p></li>
<li><p>接收者在收到请求后,第一步通过获取唯一性 ID 来查询接收端是否有对应的记录,如果有的话,就直接将上次请求的结果返回,如果没有的话,就进行操作,并在操作完成后记录到对应的表里</p></li>
</ol>
<p><img src="https://mmbiz.qpic.cn/mmbiz_png/PgqYrEEtEnpebqWGB1ZhMvUTOhUfsMdlCPmk4LiaX77Zhuuv3dgZDc6PR9elSx8CEGQGRfXaeuoWPMlScROZGfA/640?wx_fmt=png" alt=""/>电商平台备战促销季的运维秘诀——高可用服务层</p>
<h2 id="toc_6">服务降级</h2>
<p>服务降级主要解决资源不足和访问量过大的问题,比如电商平台在双十一、618 等高峰时候采用部分服务不提供访问,减少对系统的影响。</p>
<p>那降级的方式有哪些呢?</p>
<ul>
<li><p>延迟服务:比如春晚,微信发红包就出现抢到红包,但是账号余额并没有增加,要过几天才能加上去。其实这是微信内部采用延迟服务的方式来保证服务的稳定,通过队列实现记录流水账单</p></li>
<li><p>功能降级:停止不重要的功能是非常有用的方式,把相对不重要的功能暂停掉,让系统释放更多的资源。比如关闭相关文章的推荐、用户的评论功能等等,等高峰过去之后,在把服务恢复回来。</p></li>
<li><p>降低数据一致性:在大促的时候,我们发现页面上不显示真实库存的数据,只显示到底有还是没有库存这两种状态。</p></li>
</ul>
<p><img src="https://mmbiz.qpic.cn/mmbiz_png/PgqYrEEtEnpebqWGB1ZhMvUTOhUfsMdl8ZZdFwaAib5B4yibFvXffcW1I3libeD2icxf1AaNmfYuyT85pTke7wBU3Q/640?wx_fmt=png" alt=""/>电商平台备战促销季的运维秘诀——高可用服务层</p>
<p>刚刚说了降级的方式,那我们操作降级的时候有哪些注意点呢?</p>
<ol>
<li><p>清晰定义降级级别: 比如出现吞吐量超过 X,单位时间内响应时间超过 Y 秒、失败次数超过 Z 次等,这些阈值需要在准备的时候,通过压测的方式来确定。</p></li>
<li><p>梳理业务级别:降级之前,首先需要确定哪些业务是必须有,哪些业务是可以有的,哪些业务是可有可无的。</p></li>
<li><p>降级开关:可以通过接入配置中心(比如携程 Apollo、百度 Disconf )的方式直接后台降级。但是如果公司没有配置中心的话,可以封装一个 API 接口来切分,不过该 API 接口要做成幂等的方式,同时需要做一些简单的签名,来保证其一定的安全性。</p></li>
</ol>
<h2 id="toc_7">总结</h2>
<p>总结一下今天分享的主要内容</p>
<ul>
<li><p>整体架构:根据业务属性进行垂直拆分,减少项目依赖,单独开发、上线、运维</p></li>
<li><p>无状态设计:应用服务中不能保存用户状态数据,如果有状态就会出现难以扩容、单点等问题</p></li>
<li><p>超时设置:当某个服务不可用时,不至于整个系统发生连锁反应</p></li>
<li><p>异步调用:同步调用改成异步调用,解决远程调用故障或调用超时对系统的影响</p></li>
<li><p>服务降级:牺牲非核心业务,保证核心业务的高可用</p></li>
</ul>
<p>所有好的架构设计首要的原则并不是追求先进,而是合理性,要与公司的业务规模和发展趋势相匹配,任何一个公司,哪怕是现在看来规模非常大的公司,比如 BAT 之类,在一开始,其系统架构也应简单和清晰的。</p>
</div>
<div class="row">
<div class="large-6 columns">
<p class="text-left" style="padding:15px 0px;">
<a href="15277814705334.html"
title="Previous Post: 有货移动端DevOps-自建APM系统">« 有货移动端DevOps-自建APM系统</a>
</p>
</div>
<div class="large-6 columns">
<p class="text-right" style="padding:15px 0px;">
<a href="15274396845490.html"
title="Next Post: 面试-线程池的成长之路">面试-线程池的成长之路 »</a>
</p>
</div>
</div>
<div class="comments-wrap">
<div class="share-comments">
<script type="text/javascript" src="//s7.addthis.com/js/300/addthis_widget.js#pubid=ra-5ae58078c0d7b2ab"></script>
</div>
</div>
</div><!-- article-wrap -->
</div><!-- large 8 -->
<div class="large-4 medium-4 columns">
<div class="hide-for-small">
<div id="sidebar" class="sidebar">
<div id="site-info" class="site-info">
<div class="site-a-logo"><img src="./asset/img/logo.jpg" /></div>
<h1>Junkman</h1>
<div class="site-des">“拾荒者”一词来自凯文・凯利的《失控》中关于机器学习的故事(“收集癖好机”如何完成他的收集工作)。</div>
<div class="social">
<a target="_blank" class="github" target="_blank" href="https://github.com/panlw/" title="GitHub">GitHub</a>
<a target="_blank" class="rss" href="atom.xml" title="RSS">RSS</a>
</div>
</div>
<div id="site-categories" class="side-item ">
<div class="side-header">
<h2>Categories</h2>
</div>
<div class="side-content">
<p class="cat-list">
<a href="Infra.html"><strong>Infra</strong></a>
<a href="Coding.html"><strong>Coding</strong></a>
<a href="Modeling.html"><strong>Modeling</strong></a>
<a href="Archtecting.html"><strong>Archtecting</strong></a>
</p>
</div>
</div>
<div id="site-categories" class="side-item">
<div class="side-header">
<h2>Recent Posts</h2>
</div>
<div class="side-content">
<ul class="posts-list">
<li class="post">
<a href="15517999043443.html">The Art of Crafting Architectural Diagrams</a>
</li>
<li class="post">
<a href="15517997955971.html">为什么说我们需要软件架构图?</a>
</li>
<li class="post">
<a href="15516128677869.html">DNS Servers That Offer Privacy and Filtering</a>
</li>
<li class="post">
<a href="15516123108194.html">Airbnb's Migration from Monolith to Services</a>
</li>
<li class="post">
<a href="15516097487470.html">Events As First-Class Citizens</a>
</li>
</ul>
</div>
</div>
</div><!-- sidebar -->
</div><!-- hide for small -->
</div><!-- large 4 -->
</div><!-- row -->
<div class="page-bottom clearfix">
<div class="row">
<p class="copyright">Copyright © 2015
Powered by <a target="_blank" href="http://www.mweb.im">MWeb</a>,
Theme used <a target="_blank" href="http://github.com">GitHub CSS</a>.</p>
</div>
</div>
</section>
</div>
</div>
<script src="asset/js/foundation.min.js"></script>
<script>
$(document).foundation();
function fixSidebarHeight(){
var w1 = $('.markdown-body').height();
var w2 = $('#sidebar').height();
if (w1 > w2) { $('#sidebar').height(w1); };
}
$(function(){
fixSidebarHeight();
})
$(window).load(function(){
fixSidebarHeight();
});
</script>
<script src="asset/chart/all-min.js"></script><script type="text/javascript">$(function(){ var mwebii=0; var mwebChartEleId = 'mweb-chart-ele-'; $('pre>code').each(function(){ mwebii++; var eleiid = mwebChartEleId+mwebii; if($(this).hasClass('language-sequence')){ var ele = $(this).addClass('nohighlight').parent(); $('<div id="'+eleiid+'"></div>').insertAfter(ele); ele.hide(); var diagram = Diagram.parse($(this).text()); diagram.drawSVG(eleiid,{theme: 'simple'}); }else if($(this).hasClass('language-flow')){ var ele = $(this).addClass('nohighlight').parent(); $('<div id="'+eleiid+'"></div>').insertAfter(ele); ele.hide(); var diagram = flowchart.parse($(this).text()); diagram.drawSVG(eleiid); } });});</script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script><script type="text/x-mathjax-config">MathJax.Hub.Config({TeX: { equationNumbers: { autoNumber: "AMS" } }});</script>
</body>
</html>