-
Notifications
You must be signed in to change notification settings - Fork 683
/
Copy pathRocketMQ 面试题集锦.md.html
686 lines (589 loc) · 43.3 KB
/
RocketMQ 面试题集锦.md.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
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
<!DOCTYPE html>
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
<link rel="icon" href="/static/favicon.png">
<title>RocketMQ 面试题集锦.md.html</title>
<!-- Spectre.css framework -->
<link rel="stylesheet" href="/static/index.css">
<!-- theme css & js -->
<meta name="generator" content="Hexo 4.2.0">
</head>
<body>
<div class="book-container">
<div class="book-sidebar">
<div class="book-brand">
<a href="/">
<img src="/static/favicon.png">
<span>技术文章摘抄</span>
</a>
</div>
<div class="book-menu uncollapsible">
<ul class="uncollapsible">
<li><a href="/" class="current-tab">首页</a></li>
</ul>
<ul class="uncollapsible">
<li><a href="../">上一级</a></li>
</ul>
<ul class="uncollapsible">
<li>
<a href="/文章/AQS 万字图文全面解析.md.html">AQS 万字图文全面解析.md.html</a>
</li>
<li>
<a href="/文章/Docker 镜像构建原理及源码分析.md.html">Docker 镜像构建原理及源码分析.md.html</a>
</li>
<li>
<a href="/文章/ElasticSearch 小白从入门到精通.md.html">ElasticSearch 小白从入门到精通.md.html</a>
</li>
<li>
<a href="/文章/JVM CPU Profiler技术原理及源码深度解析.md.html">JVM CPU Profiler技术原理及源码深度解析.md.html</a>
</li>
<li>
<a href="/文章/JVM 垃圾收集器.md.html">JVM 垃圾收集器.md.html</a>
</li>
<li>
<a href="/文章/JVM 面试的 30 个知识点.md.html">JVM 面试的 30 个知识点.md.html</a>
</li>
<li>
<a href="/文章/Java IO 体系、线程模型大总结.md.html">Java IO 体系、线程模型大总结.md.html</a>
</li>
<li>
<a href="/文章/Java NIO浅析.md.html">Java NIO浅析.md.html</a>
</li>
<li>
<a href="/文章/Java 面试题集锦(网络篇).md.html">Java 面试题集锦(网络篇).md.html</a>
</li>
<li>
<a href="/文章/Java-直接内存 DirectMemory 详解.md.html">Java-直接内存 DirectMemory 详解.md.html</a>
</li>
<li>
<a href="/文章/Java中9种常见的CMS GC问题分析与解决(上).md.html">Java中9种常见的CMS GC问题分析与解决(上).md.html</a>
</li>
<li>
<a href="/文章/Java中9种常见的CMS GC问题分析与解决(下).md.html">Java中9种常见的CMS GC问题分析与解决(下).md.html</a>
</li>
<li>
<a href="/文章/Java中的SPI.md.html">Java中的SPI.md.html</a>
</li>
<li>
<a href="/文章/Java中的ThreadLocal.md.html">Java中的ThreadLocal.md.html</a>
</li>
<li>
<a href="/文章/Java线程池实现原理及其在美团业务中的实践.md.html">Java线程池实现原理及其在美团业务中的实践.md.html</a>
</li>
<li>
<a href="/文章/Java魔法类:Unsafe应用解析.md.html">Java魔法类:Unsafe应用解析.md.html</a>
</li>
<li>
<a href="/文章/Kafka 源码阅读笔记.md.html">Kafka 源码阅读笔记.md.html</a>
</li>
<li>
<a href="/文章/Kafka、ActiveMQ、RabbitMQ、RocketMQ 区别以及高可用原理.md.html">Kafka、ActiveMQ、RabbitMQ、RocketMQ 区别以及高可用原理.md.html</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB Buffer Pool.md.html">MySQL · 引擎特性 · InnoDB Buffer Pool.md.html</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB IO子系统.md.html">MySQL · 引擎特性 · InnoDB IO子系统.md.html</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB 事务系统.md.html">MySQL · 引擎特性 · InnoDB 事务系统.md.html</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB 同步机制.md.html">MySQL · 引擎特性 · InnoDB 同步机制.md.html</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB 数据页解析.md.html">MySQL · 引擎特性 · InnoDB 数据页解析.md.html</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB崩溃恢复.md.html">MySQL · 引擎特性 · InnoDB崩溃恢复.md.html</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · 临时表那些事儿.md.html">MySQL · 引擎特性 · 临时表那些事儿.md.html</a>
</li>
<li>
<a href="/文章/MySQL 主从复制 半同步复制.md.html">MySQL 主从复制 半同步复制.md.html</a>
</li>
<li>
<a href="/文章/MySQL 主从复制 基于GTID复制.md.html">MySQL 主从复制 基于GTID复制.md.html</a>
</li>
<li>
<a href="/文章/MySQL 主从复制.md.html">MySQL 主从复制.md.html</a>
</li>
<li>
<a href="/文章/MySQL 事务日志(redo log和undo log).md.html">MySQL 事务日志(redo log和undo log).md.html</a>
</li>
<li>
<a href="/文章/MySQL 亿级别数据迁移实战代码分享.md.html">MySQL 亿级别数据迁移实战代码分享.md.html</a>
</li>
<li>
<a href="/文章/MySQL 从一条数据说起-InnoDB行存储数据结构.md.html">MySQL 从一条数据说起-InnoDB行存储数据结构.md.html</a>
</li>
<li>
<a href="/文章/MySQL 地基基础:事务和锁的面纱.md.html">MySQL 地基基础:事务和锁的面纱.md.html</a>
</li>
<li>
<a href="/文章/MySQL 地基基础:数据字典.md.html">MySQL 地基基础:数据字典.md.html</a>
</li>
<li>
<a href="/文章/MySQL 地基基础:数据库字符集.md.html">MySQL 地基基础:数据库字符集.md.html</a>
</li>
<li>
<a href="/文章/MySQL 性能优化:碎片整理.md.html">MySQL 性能优化:碎片整理.md.html</a>
</li>
<li>
<a href="/文章/MySQL 故障诊断:一个 ALTER TALBE 执行了很久,你慌不慌?.md.html">MySQL 故障诊断:一个 ALTER TALBE 执行了很久,你慌不慌?.md.html</a>
</li>
<li>
<a href="/文章/MySQL 故障诊断:如何在日志中轻松定位大事务.md.html">MySQL 故障诊断:如何在日志中轻松定位大事务.md.html</a>
</li>
<li>
<a href="/文章/MySQL 故障诊断:教你快速定位加锁的 SQL.md.html">MySQL 故障诊断:教你快速定位加锁的 SQL.md.html</a>
</li>
<li>
<a href="/文章/MySQL 日志详解.md.html">MySQL 日志详解.md.html</a>
</li>
<li>
<a href="/文章/MySQL 的半同步是什么?.md.html">MySQL 的半同步是什么?.md.html</a>
</li>
<li>
<a href="/文章/MySQL中的事务和MVCC.md.html">MySQL中的事务和MVCC.md.html</a>
</li>
<li>
<a href="/文章/MySQL事务_事务隔离级别详解.md.html">MySQL事务_事务隔离级别详解.md.html</a>
</li>
<li>
<a href="/文章/MySQL优化:优化 select count().md.html">MySQL优化:优化 select count().md.html</a>
</li>
<li>
<a href="/文章/MySQL共享锁、排他锁、悲观锁、乐观锁.md.html">MySQL共享锁、排他锁、悲观锁、乐观锁.md.html</a>
</li>
<li>
<a href="/文章/MySQL的MVCC(多版本并发控制).md.html">MySQL的MVCC(多版本并发控制).md.html</a>
</li>
<li>
<a href="/文章/QingStor 对象存储架构设计及最佳实践.md.html">QingStor 对象存储架构设计及最佳实践.md.html</a>
</li>
<li>
<a class="current-tab" href="/文章/RocketMQ 面试题集锦.md.html">RocketMQ 面试题集锦.md.html</a>
</li>
<li>
<a href="/文章/SnowFlake 雪花算法生成分布式 ID.md.html">SnowFlake 雪花算法生成分布式 ID.md.html</a>
</li>
<li>
<a href="/文章/Spring Boot 2.x 结合 k8s 实现分布式微服务架构.md.html">Spring Boot 2.x 结合 k8s 实现分布式微服务架构.md.html</a>
</li>
<li>
<a href="/文章/Spring Boot 教程:如何开发一个 starter.md.html">Spring Boot 教程:如何开发一个 starter.md.html</a>
</li>
<li>
<a href="/文章/Spring MVC 原理.md.html">Spring MVC 原理.md.html</a>
</li>
<li>
<a href="/文章/Spring MyBatis和Spring整合的奥秘.md.html">Spring MyBatis和Spring整合的奥秘.md.html</a>
</li>
<li>
<a href="/文章/Spring 帮助你更好的理解Spring循环依赖.md.html">Spring 帮助你更好的理解Spring循环依赖.md.html</a>
</li>
<li>
<a href="/文章/Spring 循环依赖及解决方式.md.html">Spring 循环依赖及解决方式.md.html</a>
</li>
<li>
<a href="/文章/Spring中眼花缭乱的BeanDefinition.md.html">Spring中眼花缭乱的BeanDefinition.md.html</a>
</li>
<li>
<a href="/文章/Vert.x 基础入门.md.html">Vert.x 基础入门.md.html</a>
</li>
<li>
<a href="/文章/eBay 的 Elasticsearch 性能调优实践.md.html">eBay 的 Elasticsearch 性能调优实践.md.html</a>
</li>
<li>
<a href="/文章/不可不说的Java“锁”事.md.html">不可不说的Java“锁”事.md.html</a>
</li>
<li>
<a href="/文章/互联网并发限流实战.md.html">互联网并发限流实战.md.html</a>
</li>
<li>
<a href="/文章/从ReentrantLock的实现看AQS的原理及应用.md.html">从ReentrantLock的实现看AQS的原理及应用.md.html</a>
</li>
<li>
<a href="/文章/从SpringCloud开始,聊微服务架构.md.html">从SpringCloud开始,聊微服务架构.md.html</a>
</li>
<li>
<a href="/文章/全面了解 JDK 线程池实现原理.md.html">全面了解 JDK 线程池实现原理.md.html</a>
</li>
<li>
<a href="/文章/分布式一致性理论与算法.md.html">分布式一致性理论与算法.md.html</a>
</li>
<li>
<a href="/文章/分布式一致性算法 Raft.md.html">分布式一致性算法 Raft.md.html</a>
</li>
<li>
<a href="/文章/分布式唯一 ID 解析.md.html">分布式唯一 ID 解析.md.html</a>
</li>
<li>
<a href="/文章/分布式链路追踪:集群管理设计.md.html">分布式链路追踪:集群管理设计.md.html</a>
</li>
<li>
<a href="/文章/动态代理种类及原理,你知道多少?.md.html">动态代理种类及原理,你知道多少?.md.html</a>
</li>
<li>
<a href="/文章/响应式架构与 RxJava 在有赞零售的实践.md.html">响应式架构与 RxJava 在有赞零售的实践.md.html</a>
</li>
<li>
<a href="/文章/大数据算法——布隆过滤器.md.html">大数据算法——布隆过滤器.md.html</a>
</li>
<li>
<a href="/文章/如何优雅地记录操作日志?.md.html">如何优雅地记录操作日志?.md.html</a>
</li>
<li>
<a href="/文章/如何设计一个亿级消息量的 IM 系统.md.html">如何设计一个亿级消息量的 IM 系统.md.html</a>
</li>
<li>
<a href="/文章/异步网络模型.md.html">异步网络模型.md.html</a>
</li>
<li>
<a href="/文章/当我们在讨论CQRS时,我们在讨论些神马?.md.html">当我们在讨论CQRS时,我们在讨论些神马?.md.html</a>
</li>
<li>
<a href="/文章/彻底理解 MySQL 的索引机制.md.html">彻底理解 MySQL 的索引机制.md.html</a>
</li>
<li>
<a href="/文章/最全的 116 道 Redis 面试题解答.md.html">最全的 116 道 Redis 面试题解答.md.html</a>
</li>
<li>
<a href="/文章/有赞权限系统(SAM).md.html">有赞权限系统(SAM).md.html</a>
</li>
<li>
<a href="/文章/有赞零售中台建设方法的探索与实践.md.html">有赞零售中台建设方法的探索与实践.md.html</a>
</li>
<li>
<a href="/文章/服务注册与发现原理剖析(Eureka、Zookeeper、Nacos).md.html">服务注册与发现原理剖析(Eureka、Zookeeper、Nacos).md.html</a>
</li>
<li>
<a href="/文章/深入浅出Cache.md.html">深入浅出Cache.md.html</a>
</li>
<li>
<a href="/文章/深入理解 MySQL 底层实现.md.html">深入理解 MySQL 底层实现.md.html</a>
</li>
<li>
<a href="/文章/漫画讲解 git rebase VS git merge.md.html">漫画讲解 git rebase VS git merge.md.html</a>
</li>
<li>
<a href="/文章/生成浏览器唯一稳定 ID 的探索.md.html">生成浏览器唯一稳定 ID 的探索.md.html</a>
</li>
<li>
<a href="/文章/缓存 如何保证缓存与数据库的双写一致性?.md.html">缓存 如何保证缓存与数据库的双写一致性?.md.html</a>
</li>
<li>
<a href="/文章/网易严选怎么做全链路监控的?.md.html">网易严选怎么做全链路监控的?.md.html</a>
</li>
<li>
<a href="/文章/美团万亿级 KV 存储架构与实践.md.html">美团万亿级 KV 存储架构与实践.md.html</a>
</li>
<li>
<a href="/文章/美团点评Kubernetes集群管理实践.md.html">美团点评Kubernetes集群管理实践.md.html</a>
</li>
<li>
<a href="/文章/美团百亿规模API网关服务Shepherd的设计与实现.md.html">美团百亿规模API网关服务Shepherd的设计与实现.md.html</a>
</li>
<li>
<a href="/文章/解读《阿里巴巴 Java 开发手册》背后的思考.md.html">解读《阿里巴巴 Java 开发手册》背后的思考.md.html</a>
</li>
<li>
<a href="/文章/认识 MySQL 和 Redis 的数据一致性问题.md.html">认识 MySQL 和 Redis 的数据一致性问题.md.html</a>
</li>
<li>
<a href="/文章/进阶:Dockerfile 高阶使用指南及镜像优化.md.html">进阶:Dockerfile 高阶使用指南及镜像优化.md.html</a>
</li>
<li>
<a href="/文章/铁总在用的高性能分布式缓存计算框架 Geode.md.html">铁总在用的高性能分布式缓存计算框架 Geode.md.html</a>
</li>
<li>
<a href="/文章/阿里云PolarDB及其共享存储PolarFS技术实现分析(上).md.html">阿里云PolarDB及其共享存储PolarFS技术实现分析(上).md.html</a>
</li>
<li>
<a href="/文章/阿里云PolarDB及其共享存储PolarFS技术实现分析(下).md.html">阿里云PolarDB及其共享存储PolarFS技术实现分析(下).md.html</a>
</li>
<li>
<a href="/文章/面试最常被问的 Java 后端题.md.html">面试最常被问的 Java 后端题.md.html</a>
</li>
<li>
<a href="/文章/领域驱动设计在互联网业务开发中的实践.md.html">领域驱动设计在互联网业务开发中的实践.md.html</a>
</li>
<li>
<a href="/文章/领域驱动设计的菱形对称架构.md.html">领域驱动设计的菱形对称架构.md.html</a>
</li>
<li>
<a href="/文章/高效构建 Docker 镜像的最佳实践.md.html">高效构建 Docker 镜像的最佳实践.md.html</a>
</li>
</ul>
</div>
</div>
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
<div class="sidebar-toggle-inner"></div>
</div>
<script>
function add_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.add('show')
}
function remove_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.remove('show')
}
function sidebar_toggle() {
let sidebar_toggle = document.querySelector('.sidebar-toggle')
let sidebar = document.querySelector('.book-sidebar')
let content = document.querySelector('.off-canvas-content')
if (sidebar_toggle.classList.contains('extend')) { // show
sidebar_toggle.classList.remove('extend')
sidebar.classList.remove('hide')
content.classList.remove('extend')
} else { // hide
sidebar_toggle.classList.add('extend')
sidebar.classList.add('hide')
content.classList.add('extend')
}
}
function open_sidebar() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.add('show')
overlay.classList.add('show')
}
function hide_canvas() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.remove('show')
overlay.classList.remove('show')
}
</script>
<div class="off-canvas-content">
<div class="columns">
<div class="column col-12 col-lg-12">
<div class="book-navbar">
<!-- For Responsive Layout -->
<header class="navbar">
<section class="navbar-section">
<a onclick="open_sidebar()">
<i class="icon icon-menu"></i>
</a>
</section>
</header>
</div>
<div class="book-content" style="max-width: 960px; margin: 0 auto;
overflow-x: auto;
overflow-y: hidden;">
<div class="book-post">
<p id="tip" align="center"></p>
<div><h1>RocketMQ 面试题集锦</h1>
<h1>前言</h1>
<p>最近后端面试越来越难,不仅需要熟练使用算法、数据结构,懂各种开发框架的原理和源码,还需要懂各种中间件的原理和源码;不管在 BATJ 互联网大厂还是小公司都在使用 MQ 中间件,而且有着丰富的应用场景,所以关于 MQ 的原理和源代码的考察也成了资深程序员和架构师必须要掌握的重要技能,本场 Chat 罗列了 RocketMQ 的一些面试题帮助大家更加深入的了解 MQ 的底层设计原理。</p>
<h1>1. 什么是解耦?</h1>
<p>A 系统与 B、C、D 等其他系统严重耦合,不仅需要给其他系统发送数据维护发送数据的代码,还需要考虑其他系统调用自己的接口是否成功/失败/超时等异常情况;但是有了 MQ 之后,A 系统只需要将关键数据发送到 MQ 里,其他系统想要这个关键数据去消费 MQ ,如果不需要就取消消费就可以了。</p>
<h1>2. 什么是异步?</h1>
<p>异步是一种通讯方式,消息接收方不需要知道消息发送方什么时候发送,比如还款处理,用户点击还款后,可以把还款的消息放到消息队列里面,然后马上给用户返回还款中的状态;然后后台有一个还款消息消费的监听程序,最后借款状态更新为还款成功,当用户再次刷新页面会看到借款状态已经更新为还款成功啦。</p>
<h1>3. 什么是削峰填谷?</h1>
<p>如果一个系统的处理能力是每秒 200 个请求,在某一秒突然来了 350 个请求,需要把多余的 150 个请求均摊到之后的时间保证系统负载在系统的处理能力之内,同时让系统尽可能多地处理更多请求,最终起到“削峰填谷”的作用。如果一个系统的处理能力是每秒 200 个请求,在某一秒突然来了 350 个请求,需要把多余的 150 个请求均摊到之后的时间保证系统负载在系统的处理能力之内,同时让系统尽可能多地处理更多请求,最终起到“削峰填谷”的作用。</p>
<h1>4. RocketMQ 执行流程</h1>
<ol>
<li>先启动 NameServer 服务,起来后监听端口等待 Broker、Producer 和 Consumer,NameServer 是一个路由控制中心类似 ZooKeeper 之于 Kafka,提供服务注册、路由管理等服务。</li>
<li>启动 Broker 跟所有的 NameServer 保持长连接并且定期发送心跳包。</li>
<li>创建 Topic 并且指定其保存在哪些 Broker 里。</li>
<li>Producer 向 Master Broker 发送消息并且给消息打 Tag 标签进行分组。</li>
<li>Consumer 监听所有的 Master Broker 和 Slave Broker 里面的消息。</li>
</ol>
<h1>5. 怎么理解 Producer 的?</h1>
<ol>
<li>Producer 启动的时候会从 NameServer 服务集群找一台建立长连接并且定期发送心跳包,然后每隔 30s 从 NameServer 拉取 Topic-Broker 映射关系,缓存到本地内存,然后跟所有的 Master Broker 建立长连接并且每隔 30s 发送一次心跳包。</li>
<li>Producer 发送消息的时候有一个负载均衡的策略,当成功发送一次消息后,下一次消息将会换一个 Master Broker,使得消息可以均衡的分布在所有的 Broker 上。</li>
</ol>
<h1>6. 怎么理解 Consumer 的?</h1>
<ul>
<li>跟 Producer 类似。</li>
<li>消费端的负载均衡是由消费者的消费模式决定的。</li>
</ul>
<h1>7. 消费者消费模式有哪几种?</h1>
<p>集群消费和广播消费。</p>
<h1>8. 消费者获取消息有几种模式?</h1>
<p><strong>拉模式</strong></p>
<p>推模式:是一种特殊的拉模式,每隔 15s 向 Broker 发起长轮询,Broker 的 Topic 下如果没有消息那么线程阻塞,超过 5s 就再次发起查询,如果有消息线程被唤醒并且立马把消息返回给消费端。</p>
<h1>9. 定时消息是什么?怎样实现的?</h1>
<p>Producer 在发送消息的时候指定什么时刻发送,然后消息被发送到 Master Broker 并保存起来,在指定时刻的时候 Broker 会把消息推送给消费端。</p>
<h1>10. RocketMQ 如何保证高可用的?</h1>
<ul>
<li>多 Master 模式:该模式下必须设置为异步复制,因为没有 Slave,异步刷盘会丢失部分数据,同步刷盘不会丢失数据;如果有一台 Master Broker 宕机,那么其上的消息在服务恢复前不会推送给消费端,会对消息实时性造成影响。</li>
<li>多 Master 多 Slave 模式(异步复制):异步复制的模式下在 Master 宕机、磁盘损坏等情况下会造成数据部分丢失;但是消费端仍然可以从 Slave 拉取消息,所以消息的实时性不受影响。</li>
<li>多 Master 多 Slave 模式(同步双写):同步复制同步刷盘会导致整体性能比异步模式低一些,发送单个消息的 RT 也会变高,但是服务可用性和数据可用性都很高而且消息实时性不受影响。</li>
</ul>
<h1>11. 如何保证消息不被重复消费?或者说如何保证消息消费时的幂等性?</h1>
<p>数据库创建一个业务幂等表,第一次消费的时候插入一条幂等数据,把相关的业务 bizId 作为幂等 id,第二次消费的时候如何数据库中已经有幂等记录的话就抛出幂等异常。</p>
<h1>12. 如何保证消息的可靠性传输?要是消息丢失了怎么办?</h1>
<p><img src="assets/c75b8370-9ea4-11ea-9d24-cfb0df3065fc.png" alt="在这里插入图片描述" /></p>
<ul>
<li>Producer:生产者端默认采用同步阻塞式发送消息,如果状态返回 OK 表示消息一定发送到了 Broker,如果状态返回失败或者超时,会重试两次,重试次数用完后可能发送成功也可能继续失败。</li>
<li>Broker:发送到 Broker 的消息会通过同步刷盘或者异步刷盘的方式持久化到 CommitLog 里面,即时 Broker 服务宕机,服务恢复后仍然可以找到消息,除非磁盘损坏才会导致消息丢失;另外在多主多从的 Broker 集群模式下,采用同步复制将 Master Broker 中的消息同步到 Slave Broker,就是 Master Broker 宕机甚至磁盘损坏也可以找到该消息。</li>
<li>Consumer:消费端维护了一个 MessageQueue 的队列,不管消息消费成功或者失败都会将当前消息的消费进度 offset 持久化到 MessageQueue 里面;如果消息消费失败会把消息重新发回到 Broker,如果操作失败或者 Broker 挂掉了会定时重试,成功后更新本地的消费进度 offset;就算 Broker 和 Consumer 都挂掉了由于 Consumer 会定期持久化消费进度,等服务恢复后重新拉取 offset 之前的消息到消费端本地也可以保证消息不丢失;消费端消费失败后会把消息重新发回到 Broker 中的重试队列中,如果消费重试次数超过了最大消费重试次数的话 Broker 会把消息移到死信队列中,然后人工干预处理。</li>
</ul>
<h1>13. 如何保证消息的顺序性?</h1>
<p>顺序消息在日常的功能场景中很常见,比如点外卖生成外卖订单、付款、送餐的消息需要保证严格的顺序。</p>
<p><strong>全局顺序消息:</strong></p>
<p>RocketMQ 的一个 Topic 下默认有八个读队列和八个写队列,如果要保证全局顺序消息的话需要在生产端只保留一个读写队列,然后消费端只有一个消费线程,这样会降低 RocketMQ 的高可用和高吞吐量。</p>
<p><strong>分区顺序消息:</strong></p>
<p>分区顺序消息同样需要生产端和消费端配合,生产端根据同一个订单 ID 把消息路由到同一个 MessageQueue,消费端控制从同一个 MessageQueue 取出的消息不被并发处理。</p>
<p><strong>生成端发送分区顺序消息:</strong></p>
<pre><code>SendResult sendResult = Producer.send(msg , new MessageQueueSelector() {
Override
public MessageQueue select(List <MessageQueue> mqs , Message msg ,Object arg) {
// 生成外卖订单、付款、送餐使用同一个 bizId
Integer bizId = (Integer) arg ;
// 根据 bizId 路由这三种消息到同一个分区
int index = bizId%mqs.size();
return mqs.get(index) ;
}
}, orderid);
</code></pre>
<p>消费端消费分区消息:</p>
<pre><code>consumer.registerMessageListener(new MessageListenerOrderly() {
Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs ,ConsumeOrderlyContext context) {
System.out.printf("Receive order msg:" + new String(msgs.get(0).getBody()));
return ConsumeOrderlyStatus.SUCCESS ;
}
});
</code></pre>
<p>MessageListenerOrderly 底层会将 MessageQueue 锁住保证了同一个 MessageQueue 的消息不会并发消费。</p>
<h1>14. 如何解决消息队列的延时以及过期失效问题?</h1>
<p>当消费端宕机或者消费速度很慢导致 Broker 中消息大量积压,如有积压时长超过阈值就会被清理掉导致数据丢失。</p>
<p>RocketMQ 会把消息持久化到 CommitLog 文件里面,并且在以下的几种情况下会定期清理 CommitLog 文件:</p>
<ol>
<li>CommitLog 文件里面的消息积压超过了 72 小时,并且到了凌晨 4 点,清理线程会删除该文件;</li>
<li>CommitLog 文件里面的消息积压超过了 72 小时,并且磁盘占用率达到了 75%,清理线程也会删除该文件;</li>
<li>磁盘占用率达到了 85%,不管积压的消息是否过期都会触发线程批量清理文件,直到内存充足;</li>
<li>磁盘占用率达到了 90%,Broker 出于保护自身的目的将会拒绝新发送的消息。</li>
</ol>
<p>出现消息积压并且过期失效问题,会导致这部分消息丢失,可以写一段程序找到丢失的消息,然后等到凌晨用户都休息的时候,这个时候系统负载比较低,再把丢失的这部分消息重新发送到 Broker 中。</p>
<h1>15. 消息队列满了以后该怎么处理?有几百万消息持续积压几小时,说说怎么解决?</h1>
<ol>
<li>创建一个 Topic 并且把积压消息的 Topic 下的分区中的消息分发到新建的那个 Topic 中的更多分区中,新建 Topic 的分区数是积压消息的 Topic 的分区的 10 倍(具体多少分区根据具体业务场景决定)。</li>
<li>积压的消息分发完以后停掉旧的消费端实例。</li>
<li>每一个新建 Topic 的分区都对应启动一个消费端实例。</li>
<li>积压的消息都消费完后,再把旧的消费端实例恢复。</li>
</ol>
<p><img src="assets/da280630-9ea5-11ea-a506-f32f5295a5a9.png" alt="在这里插入图片描述" /></p>
<h1>16. 如何解决高性能读写数据的问题?</h1>
<p><strong>普通 IO 和 NIO</strong></p>
<ul>
<li>普通 IO 是基于字节的单向阻塞式传输;</li>
<li>NIO 引入 FileChannel 全双工通道和 ByteBuffer 数据容器,ByteBuffer 可以精准的控制写入磁盘的数据大小。</li>
</ul>
<p>如下图所示 FileChannel#write() 方法会将 ByteBuffer 里面的数据写到 PageCache 中,然后操作系统会定期将 PageCache 中的数据进行刷盘,如果想要立马通知操作系统进行刷盘可以调用 FileChannel#force() 方法。</p>
<p><img src="assets/0c805c40-9ea6-11ea-853e-a34978cba4d6.png" alt="在这里插入图片描述" /></p>
<p>不管是普通 IO 还是没有使用 MMAP 的 NIO 都会有多次数据拷贝的问题。</p>
<ul>
<li>读磁盘的过程:先把磁盘里面的数据 DMA 拷贝到内核态 IO 缓冲区,然后再把内核态的数据 CPU 拷贝到用户态内存空间;</li>
<li>写磁盘的过程:把用户态内存空间的数据 CPU 拷贝到内存态 IO 缓冲区,然后再把内核态的数据 DMA 拷贝到磁盘;</li>
<li>不管是读还是写都需要两次数据拷贝,如果采用 MMAP 技术就可以把磁盘里面的文件映射到用户态空间的虚拟内存中,省去了内核态到用户态的 CPU 拷贝。</li>
</ul>
<p><img src="assets/2cb23e20-9ea6-11ea-a7cd-ef13827cf727.png" alt="在这里插入图片描述" /></p>
<p><strong>内存映射 MMAP(零拷贝技术)</strong></p>
<p>NIO 可以采用 MappedByteBuffer 数据容器进行读写数据,而 MappedByteBuffer#map() 底层采用了 MMAP 技术进行了文件映射。</p>
<pre><code> MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, position, fileSize)
ize);
</code></pre>
<p>上文提到了 MMAP 技术就是进行文件映射和内存映射,把磁盘里面的文件映射到用户态的虚拟内存,还有将 PageCache 映射到用户态的虚拟内存,从而减少内核态到用户态的 CPU 拷贝。</p>
<p>MappedByteBuffer#write() 方法会将数据直接写入到 PageCache 中,然后操作系统会异步刷盘将 PageCache 中的数据写入到磁盘。</p>
<p>从磁盘读取数据的时候,首先会从 PageCache 中查看是否有数据,如果有的话就直接从 PageCache 加载数据,如果没有的话就去磁盘加载数据到 PageCache 中,PageCache 技术还会将要加载的数据的附近的其他数据块也一并加载进 PageCache,下文继续分析 PageCache 技术。</p>
<p><img src="assets/79b95410-9ea6-11ea-853e-a34978cba4d6.png" alt="在这里插入图片描述" /></p>
<p>MMAP 虽然可以提高磁盘读写的性能,但是仍然有诸多缺陷和限制,比如:</p>
<ul>
<li>MMAP 进行文件映射的时候对文件大小有限制,在 1.5GB~2GB 之间,所以 RocketMQ 设计 CommitLog 单个文件 1GB,ConsumeQueue 单个文件 5.7MB;</li>
<li>当不再需要使用 MappedByteBuffer 的时候,需要手动释放占用的虚拟内存。</li>
</ul>
<p><strong>PageCache 技术</strong></p>
<p>为了优化磁盘中数据文件的读写性能,PageCache 技术对数据文件进行了缓存。“对磁盘中数据文件的顺序读写性能接近于对内存的读写性能”,其主要原因就是 PageCache 对磁盘中数据文件的读写进行了优化。</p>
<ul>
<li>**PageCache 对数据文件的读优化:**由于读数据文件的时候会先从 PageCache 加载数据,如果 PageCache 没有数据的话,会从磁盘的数据文件中加载数据并且顺序把相邻的数据文件页面预加载到 PageCache 中,这样子如果之后读取的数据文件在 PageCache 中能找到的话就省去了去磁盘加载数据的操作相当于直接读内存。</li>
<li>**PageCache 对数据文件的写优化:**往数据文件中写数据的时候同样先写到 PageCache 中,然后操作系统会定期刷盘把 PageCache 中的数据持久化到磁盘中。</li>
</ul>
<p>那么 RocketMQ 具体是怎么使用 PageCache + Mmap 技术的呢?又做了哪些优化呢?</p>
<ul>
<li>首先将磁盘中的 CommitLog 数据文件映射到虚拟内存中;</li>
<li>生成端将消息顺序写到 PageCache 中,然后操作系统定期进行异步刷盘,将 PageCache 中的数据批量持久化到磁盘;</li>
<li>消费端随机读取 CommitLog 数据文件,由于程序的局部性热点原理,未来将要加载的数据和正在使用的数据在存储空间上是邻近的,所以要加载的数据基本可以在 PageCache 中找到,避免了缺页中断而去磁盘中加载数据。</li>
</ul>
<p><img src="assets/c7c074e0-9ea6-11ea-853e-a34978cba4d6.png" alt="在这里插入图片描述" /></p>
<p>虽然 PageCache 技术优化了数据文件的读写性能,但是仍然有一些影响其性能的问题:</p>
<p>操作系统的脏页回写(当空闲内存不足的时候操作系统会将脏页刷到磁盘释放内存空间)、内存回收、内存 SWAP(当内存不足的时候把一部分磁盘空间虚拟成内存使用) 等都会造成消息读写的延迟。</p>
<p>为了解决消息读写的延迟问题,RocketMQ 还引入了其他优化方案,下文继续分析。</p>
<p><strong>预分配 MappedFile(内存预分配)</strong></p>
<p>为了解决 PageCache 技术带来的消息读写延迟问题,RocketMQ 进行了内存预分配处理。</p>
<p>当往 CommitLog 写入消息的时候,会先判断 MappedFileQueue 队列中是否有对应的 MappedFile,如果没有的话会封装一个 AllocateRequest 请求,参数有:文件路径、下下个文件路径、文件大小等,并把请求放到一个 AllocateRequestQueue 队列里面;</p>
<p>在 Broker 启动的时候会自动创建一个 AllocateMappedFileService 服务线程,该线程不停的运行并从 AllocateRequestQueue 队列消费请求,执行 MappedFile 映射文件的创建和下下个 MappedFile 的预分配逻辑,创建 MappedFile 映射文件的方式有两个:一个是在虚拟内存中通过 MapperByteBuffer 即 Mmap 创建一个 MappedFile 实例,另一个是在堆外内存中通过 DirectByteBuffer 创建一个 MappedFile 实例。创建完当前 MappedFile 实例后还会将下一个 MappedFile 实例创建好并且添加到 MappedFileQueue 队列里面,即预分配 MappedFile。</p>
<p><img src="assets/251a5070-9ea7-11ea-bf38-950ba54cfedc.png" alt="在这里插入图片描述" /></p>
<p><strong>mlock 系统调用</strong></p>
<p>当内存不足的时候可能发生内存 SWAP,读写消息的进程所使用的内存可能会被交换到 SWAP 空间,为了保证 RocketMQ 的吞吐量和读写消息低延迟,肯定希望尽可能使用物理内存,所以 RocketMQ 采用了 mlock 系统调用将读写消息的进程所使用的部分或者全部内存锁在了物理内存中,防止被交换到 SWAP 空间。</p>
<p><strong>文件预热(内存预热)</strong></p>
<p>mlock 系统调用未必会锁住读写消息进程所使用的物理内存,因为可能会有一些内存分页是写时复制的,所以 RocketMQ 在创建 MapperFile 的过程中,会将 Mmap 映射出的虚拟内存中随机写入一些值,防止内存被交换到 SWAP 空间。</p>
<p>由于 Mmap 技术只是创建了虚拟内存地址至物理内存地址的映射表,并没有将磁盘中的数据文件加载到内存中来,如果内存中没有找到数据就会发生缺页中断,而去磁盘读取数据,所以 RocketMQ 采用了 madvise 系统调用将数据文件尽可能多的加载到内存中达到内存预热的效果。</p>
<h1>总结</h1>
<p>RocketMQ 的底层设计还是很有趣的,大家有空还是要看下其源代码,将其巧妙的设计转化为自己的技能。</p>
</div>
</div>
<div>
<div style="float: left">
<a href="/文章/QingStor 对象存储架构设计及最佳实践.md.html">上一页</a>
</div>
<div style="float: right">
<a href="/文章/SnowFlake 雪花算法生成分布式 ID.md.html">下一页</a>
</div>
</div>
</div>
</div>
</div>
</div>
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
</div>
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"709980150bf18b66","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
</body>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-NPSEEVD756');
var path = window.location.pathname
var cookie = getCookie("lastPath");
console.log(path)
if (path.replace("/", "") === "") {
if (cookie.replace("/", "") !== "") {
console.log(cookie)
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
}
} else {
setCookie("lastPath", path)
}
function setCookie(cname, cvalue) {
var d = new Date();
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
}
return "";
}
</script>
</html>