RocetMQ助力微众银行打造金融级消息服务平台

RocetMQ助力微众银行打造金融级消息服务平台,第1张

近年来,随着微服务架构的流行,分布式消息引擎在物联网、分布式事务、实时计算和大规模缓存同步等场景中的应用日益增多。本文将分享微众银行基于 RocketMQ 构建消息服务平台的实践,并通过添加诸多高级特性来解决消息收发过程中遇到的各种问题,通过此文,您将了解到:

不管是银行的系统还是其他一些传统企业的系统,他们在最早的时候都使用到了服务总线,即 ESB 或者某种形式存在于 SOA 架构中,目的是把所有的服务都串起来,让服务之间能够形成一个调用。但这类服务架构其实是比较重的,所有的服务架构都要经过总线,总线成为了架构上的瓶颈。很多商业化的 ESB 总线大家可能都用过,像 Oracle、IBM 等都有。从服务调用的维度来看,银行的应用架构的演进经历了以下 3 个阶段。

这个阶段的架构具有以下 3 个特点:

这个阶段引入了 ESB 总线的理念:

ESB 总线为渠道、核心和外围系统建立了一座桥梁,提供完全统一的接口标准协议,提升了系统发布的实时性。但同时,ESB 成为了最大的单点,要支持大并发高 TPS 低延时,所以 HA 和性能要求非常高,变更需要相当谨慎。

到了 2012 年以后,随着 Facebook、Amazon 等开放平台获得的巨大成功,BAT 都逐步将自己的接口开放出来,并实施了开放平台生态圈战略,从而推动了 SOA 服务化的快速发展。

左边是之前的传统银行集中式总线架构,右边是互联网服务化架构,包含了开放平台、服务注册和发现,以及服务化产品系统。

通过开放平台对外提供接口暴露,可以发现这种架构在保障传统银行系统稳定性的同时也可以满足互联网金融需求的快速迭代实施,并且也使用了新兴的互联网分布式技术,来降低开发和运维的成本。

微众银行基于 Apache RocketMQ 构建了自己的分布式消息服务架构,我们以 RMB(Reliable Message Bus)为接入层,以基于 Apache RocketMQ 定制开发的 WeMQ(WeBank Message Queue)为消息服务核心,通过 GSL(Global Service Location)进行服务定位,通过 SGS(Service Governance System)进行服务请求和服务响应的服务治理,整个分布式链路的追踪日志会上报到 Log 中。

接下来,我们来看看我们基于 RocketMQ 改造使用到的常见的消息服务模式:

Consumer 可以是一个或者多个,但是一个消息会被多个不同系统的其中一个 consumer 收到。

多个在线的 Consumer 会同时收到广播消息。

生产者只有一个,消费者有多个,但是作为 HA,只有一个 Active,其他都是 StandBy。当 Active 挂掉一个,Standby 会迅速接管。

发送请求 - 等待响应结果。在发送方做了一个线程的等待,要等待结果的 notify。

在分布式消息系统的构建过程中,基于业务的需求,我们在 RocketMQ 的消息系统中添加了多项高级特性,包括多中心多活、灰度发布、熔断机制、消息存活期、流量的权重、消息去重、惊群效应问题的解决、背压模式、消息服务治理、MQTT 消息服务等。

DC 级别的多活希望解决的问题是,不仅消息不能丢,还要保证服务不能中断。这里有两个层面的故障,一个是应用全部宕机,那么希望被其他 IDC 的应用能够迅速来接管消息,另外一个是消息中间件宕机,那么希望生产者能够切换到其他 IDC 的中间件进行发送,并且这个中间件的消息在其他 IDC 有备份,能够进行消费。微众已经通过 IDC 断网演练检验同城多活能力。

灰度发布希望解决的问题是,同一个消费组内不同的实例有监听不一样的 topic 时,能保证不同 topic 的消息被正确的实例消费。

(灰度发布示意图)

当希望消息的堆积到一定程度时,可能是消费者出现了故障,我们希望能够提醒生产者。

熔断机制示意图

说到流量的权重,有一个问题是,Topic 的 Q 值是在使用过程中手动设置的,当实例的数量超过 Q 的数量,那么超过部分的实例是收不到消息的。但是,如果你的实例数量小于 Q 的话,它们之间会由于负载均衡分 Q。根据负载均衡算法,分到的 Q 可能是不一致的。比如有的分到 2 个,有的分到 3 个。在这种集群消费的情况下,就会出现处理的不对等。比如当大流量到来的时候,分到 3 个 Q 的那个实例可能会出现一些问题,比如挂掉了。

所以我们希望,不同的实例拿到的消息量应该是对等的。所以,流量权重希望解决的问题是,随着实例数的动态增加和减少,能够动态调整 consumeQueue 的数量,不至于出现流量不均匀的情况。因此,我们做了一个自动伸缩 Q 的功能。默认 Topic 建成时,Q 的数量是 1,当启动一个新的实例的时候,会自动扩展一个,停掉一个实例的时候会自动缩一个。从而达到 Q 个数量和实例的数量是一一对等的。这解决了实例和消息量不对等的问题。

在负载均衡的一个很短时间内,当新上一个实例的时候,由于大家分到的 Q 都是相同的,当前一个分到 Q 的还在继续拉消息,下一个实例由于负载均衡很快做完,也分到 Q,就会去拿这个 Q 的消息,这个时候就会出现消息的重复。此时,通常会通过 Redis 等缓存方式进行去重,也可以在 Broker 上做一个简单的处理,例如用互斥锁,在竞争消费的短时间内,对其进行加锁,抢到锁的才能进行消费,同时占有锁的时间有限制,从而解决消息去重的问题。

消息服务去重原理图

消息的背压消费模式

背压模式示意图

在一些特殊场景下,需要对消息引擎做一些加强,例如背压模式。当消息拉到本地的消费线程池时,会出现一个问题。当要做一些例如 DB 的写的 *** 作导致出现线程卡死,处理能力会下降,服务出现降级,但是消息还在不停地往本地拉。

这个时候,我们希望达到一种效果,能够根据后续服务的治理能力决定拉的消息数量。当然 RocketMQ 的 ProcessQ 也能达到这个效果,但是还不够精细化。因为在金融场景下,交易一旦出现不一致或者超时,会很麻烦。所以我们希望在实时的交易链路上去解决这个问题。于是我们做了一个类似 Reactor 框架的背压处理,能够根据处理能力实时拉取消息。

当对消息的有效期有要求时,可以在消费消息时对存活时间进行判断,超时则丢弃。

对于存活期非常短和对延时要求比较低的消息,我们通过内存模式(不落盘)进行加速,降低延时。

因为负载均衡算法在客户端,客户端的连接和断开都会触发消费组内的所有实例会收到 notification 做负载均衡。比较理想的情况是,一个实例的掉线不能影响到其他实例,当监听的 topic 比较多时,会出现负载均衡慢的问题,因此我们希望负载均衡收敛到服务端来做,客户端只需要关注 topic,不需要关注 consumeQueue。

目前,我们团队已经参与到 Apache RocketMQ 的社区建设中,并对自用的消息服务以社区分支的形式在维护,希望各行业更多的开发者可以一起参与进来,以打造适用范围更广、更好用的分布式消息引擎。

作者介绍

陈广胜,Apache RocketMQ 资深 Contributor,曾就职于 IBM 和华为,现任职于微众银行,曾参与过运营商云和大数据平台的建设,以及银行的基础架构建设等

前言
上篇 面试笔试经验技巧篇
第1章 面试官箴言 2
11 有道无术,术可求;有术无道,止于术 2
12 求精不求全 3
13 脚踏实地,培养多种技能 4
14 保持空杯心态 6
15 职场是能者的舞台 7
16 学会“纸上谈兵” 8
17 小结 8
第2章 面试心得交流 9
21 心态决定一切 9
22 假话全不说,真话不全说 10
23 走自己的路,让别人去说吧 12
24 夯实基础谋出路 14
25 书中自有编程法 15
26 笔试成绩好,不会被鄙视 17
27 不要一厢情愿做公司的备胎 18
28 小结 19
第3章 企业面试笔试攻略 20
31 互联网企业 20
32 网络设备提供商 25
33 外企 29
34 国企 32
35 研究所 35
36 创业型企业 37
37 如何抉择 41
第4章 面试笔试技巧 42
41 不打无准备之仗 42
411 如何获取求职信息 42
412 如何制作一份受用人单位青睐的简历 43
413 如何高效地网申简历 47
414 面试考查什么内容 48
415 霸王面合适吗 50
416 非技术类笔试如何应答 50
417 什么是职场暗语 51
418 如何克服面试中的紧张情绪 54
419 面试礼仪有哪些 55
4110 面试需要准备什么内容 56
4111 女生适合做程序员吗 57
4112 程序员是吃青春饭的吗 58
4113 为什么会被企业拒绝 58
4114 如何准备集体面试 59
4115 如何准备电话面试 61
42 从容应对 62
421 如何进行自我介绍 63
422 你对我们公司有什么了解 64
423 如何应对自己不会回答的问题 65
424 如何应对面试官的“激将法”语言 65
425 如何处理与面试官持不同观点的问题 66
426 如果你在这次面试中没有被录用,你会怎么办 66
427 如果你被我们录取了,接下来你将如何开展工作 66
428 你怎么理解你应聘的职位 67
429 你有哪些缺点 67
4210 你有哪些优点 68
4211 你没有工作经验,如何能够胜任这个岗位 69
4212 你的好朋友是如何评价你的 69
4213 你与上司意见不一致时,该怎么办 70
4214 你能说说你的家庭吗 71
4215 你认为自己最适合做什么 72
4216 你如何看待公司的加班现象 72
4217 你的业余爱好是什么 73
4218 你和别人发生过争执吗?你怎样解决 74
4219 你如何面对压力 74
4220 你为什么离开了原来的单位 75
4221 你为什么更倾向于我们公司 75
4222 你觉得我们为什么要录用你 76
4223 你的职业规划是什么 76
4224 你对薪资有什么要求 77
4225 你有什么需要问我的问题吗 77
43 签约这点事 78
431 风萧萧兮易水寒,offer多了怎么办 78
432 签约、违约需要注意哪些事项 78
44 小结 81
第5章 英文面试攻略 82
51 注意事项 82
52 英文自我介绍 83
53 常见的英文面试问题 85
54 常见计算机专业词汇 94
541 计算机专业相关课程 94
542 *** 作系统相关术语 95
543 算法相关术语 96
544 数据结构相关术语 97
545 计算机网络相关术语 100
第6章 智力题攻略 102
61 推理类 102
62 博弈类 107
63 计算类 109
64 作图类 111
65 倒水类 112
66 称重类 113
67 最优化类 114
68 IT思想类 115
69 过桥类 118
610 概率类 119
下篇 面试笔试技术攻克篇
第7章 程序设计基础 122
71 C/C++关键字 122
711 static(静态)变量有什么作用 122
712 const有哪些作用 124
713 switch语句中的case结尾是否必须添加break语句?为什么 127
714 volatile在程序设计中有什么作用 128
715 断言ASSERT( )是什么 129
716 枚举变量的值如何计算 130
717 char str1[] = abc; char str2[] = abc; str1与str2不相等,为什么 130
718 为什么有时候main( )函数会带参数?参数argc与argv的含义是什么 131
719 C++里面是不是所有的动作都是main( )函数引起的 132
7110 p++与(p)++等价吗?为什么 132
7111 前置运算与后置运算有什么区别 132
7112 a是变量,执行(a++) += a语句是否合法 133
7113 如何进行float、bool、int、指针变量与“零值”的比较 134
7114 new/delete与malloc/free的区别是什么 135
7115 什么时候需要将引用作为返回值 137
7116 变量名为618Software是否合法 137
7117 C语言中,整型变量x小于0,是否可知x×2也小于0 138
7118 exit(status)是否跟从main( )函数返回的status等价 138
7119 已知String类定义,如何实现其函数体 138
7120 在C++中如何实现模板函数的外部调用 140
7121 在C++中,关键字explicit有什么作用 140
7122 C++中异常的处理方法以及使用了哪些关键字 141
7123 如何定义和实现一个类的成员函数为回调函数 141
72 内存分配 142
721 内存分配的形式有哪些 142
722 什么是内存泄露 143
723 栈空间的最大值是多少 144
724 什么是缓冲区溢出 144
73 sizeof 146
731 sizeof是关键字吗 146
732 strlen(\0)=?sizeof(\0)=? 146
733 对于结构体而言,为什么sizeof返回的值一般大于期望值 148
734 指针进行强制类型转换后与地址进行加法运算,结果是什么 149
74 指针 150
741 使用指针有哪些好处 150
742 引用还是指针 150
743 指针和数组是否表示同一概念 152
744 指针是否可进行>、<、>=、<=、==运算 152
745 指针与数字相加的结果是什么 152
746 野指针?空指针 153
75 预处理 154
751 C/C++头文件中的ifndef/define/endif的作用有哪些 154
752 #include <filenameh>和#include “filenameh” 有什么区别 155
753 #define有哪些缺陷 155
754 如何使用define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题) 155
755 含参数的宏与函数有什么区别 156
756 宏定义平方运算#define SQR(X) XX是否正确 156
757 不能使用大于、小于、if语句,如何定义一个宏来比较两个数a、b的大小 157
758 如何判断一个变量是有符号数还是无符号数 158
759 #define TRACE(S) (printf(%s\n, #S), S)是什么意思 159
7510 不使用sizeof,如何求int占用的字节数 160
7511 如何使用宏求结构体的内存偏移地址 161
7512 如何用sizeof判断数组中有多少个元素 162
7513 枚举和define有什么不同 162
7514 typdef和define有什么区别 162
7515 C++中宏定义与内联函数有什么区别 164
7516 定义常量谁更好?#define还是const 164
76 结构体与类 165
761 C语言中struct与union的区别是什么 165
762 C和C++中struct的区别是什么 165
763 C++中struct与class的区别是什么 166
77 位 *** 作 166
771 一些结构声明中的冒号和数字是什么意思 166
772 最有效的计算2乘以8的方法是什么 167
773 如何实现位 *** 作求两个数的平均值 167
774 unsigned int i=3;printf(%u\n,i-1)输出为多少 168
775 如何求解整型数的二进制表示中1的个数 169
776 不能用sizeof( )函数,如何判断 *** 作系统是16位还是32位的 170
777 嵌入式编程中,什么是大端?什么是小端 171
778 考虑n位二进制数,有多少个数中不存在两个相邻的1 174
779 不用除法 *** 作符如何实现两个正整数的除法 175
78 函数 179
781 怎么样写一个接受可变参数的函数 179
782 函数指针与指针函数有什么区别 179
783 C++函数传递参数的方式有哪些 183
784 重载与覆盖有什么区别 185
785 是否可以通过绝对内存地址进行参数赋值与函数调用 188
786 默认构造函数是否可以调用单参数构造函数 190
787 C++中函数调用有哪几种方式 191
788 什么是可重入函数?C语言中如何写可重入函数 192
79 数组 192
791 int a[2][2]={{1},{2,3}},则a[0][1]的值是多少 192
792 如何合法表示二维数组 193
793 a是数组,(int)(&a+1)表示什么意思 193
794 不使用流程控制语句,如何打印出1~1000的整数 194
795 char str[1024]; scanf(%s,str)是否安全 197
796 行存储与列存储中哪种存储效率高 197
710 变量 197
7101 全局变量和静态变量有什么异同 197
7102 局部变量需要“避讳”全局变量吗 199
7103 如何建立和理解非常复杂的声明 199
7104 变量定义与变量声明有什么区别 200
7105 不使用第三方变量,如何交换两个变量的值 201
7106 C与C++变量初始化有什么不同 202
711 字符串 202
7111 不使用C/C++字符串库函数,如何自行编写strcpy( )函数 203
7112 如何把数字转换成字符串 205
7113 如何自定义内存复制函数memcpy( ) 206
712 编译 207
7121 编译和链接的区别是什么 207
7122 编译型语言与解释型语言的区别是什么 208
7123 如何判断一段程序是由C编译程序还是由C++编译程序编译的 208
7124 在C++程序中调用被C编译器编译后的函数,为什么要加extern “C” 209
7125 两段代码共存于一个文件,编译时有选择地编译其中的一部分,如何实现 210
713 面向对象相关 210
7131 面向对象与面向过程有什么区别 210
7132 面向对象的基本特征有哪些 211
7133 什么是深复制?什么是浅复制 212
7134 什么是友元 213
7135 复制构造函数与赋值运算符的区别是什么 214
7136 基类的构造函数/析构函数是否能被派生类继承 216
7137 初始化列表和构造函数初始化的区别是什么 216
7138 类的成员变量的初始化顺序是按照声明顺序吗 217
7139 当一个类为另一个类的成员变量时,如何对其进行初始化 217
71310 C++能设计实现一个不能被继承的类吗 218
71311 构造函数没有返回值,那么如何得知对象是否构造成功 219
71312 C++中的空类默认产生哪些成员函数 219
71313 如何设置类的构造函数的可见性 219
71314 public继承、protected继承、private继承的区别是什么 220
71315 C++提供默认参数的函数吗 221
71316 C++中有哪些情况只能用初始化列表而不能用赋值 222
714 虚函数 223
7141 什么是虚函数 223
7142 C++如何实现多态 225
7143 C++中继承、虚函数、纯虚函数分别指的是什么 226
7144 C++中的多态种类有哪几种 226
7145 什么函数不能声明为虚函数 227
7146 是否可以把每个函数都声明为虚函数 229
7147 C++中如何阻止一个类被实例化 229
715 编程技巧 229
7151 当while( )的循环条件是赋值语句时会出现什么情况 229
7152 不使用if/:/switch及其他判断语句如何找出两个int型变量中的最大值和最小值 230
7153 C语言获取文件大小的函数是什么 231
7154 表达式a>b>c是什么意思 231
7155 如何打印自身代码 232
7156 如何实现一个最简单病毒 232
7157 如何只使用一条语句实现x是否为2的若干次幂的判断 233
7158 如何定义一对相互引用的结构 233
7159 什么是逗号表达式 234
71510 \n是否与\n\r等价 235
71511 什么是短路求值 235
71512 已知随机数函数rand7( ),如何构造rand10( )函数 236
71513 printf(%p\n,(void )x)与printf (%p\n,&x)有何区别 237
71514 printf( )函数是否有返回值 237
71515 不能使用任何变量,如何实现计算字符串长度函数Strlen( ) 237
71516 负数除法与正数除法的运算原理是否一样 238
71517 main( )主函数执行完毕后,是否可能会再执行一段代码 238
第8章 数据库 240
81 数据库概念 240
811 关系数据库系统与文件数据库系统有什么区别 240
812 SQL语言的功能有哪些 240
813 内连接与外连接有什么区别 242
814 什么是事务 243
815 什么是存储过程?它与函数有什么区别与联系 244
816 什么是主键?什么是外键 244
817 什么是死锁 245
818 什么是共享锁?什么是互斥锁 245
819 一二三四范式有何区别 246
8110 如何取出表中指定区间的记录 247
8111 什么是CHECK约束 247
8112 什么是视图 247
82 SQL高级应用 248
821 什么是触发器 248
822 什么是索引 249
823 什么是回滚 250
824 数据备份有哪些种类 251
825 什么是游标 251
826 并发环境下如何保证数据的一致性 252
827 如果数据库日志满了,会出现什么情况 252
828 如何判断谁往数据库中插入了一行数据 252
第9章 网络与通信 254
91 网络模型 254
911 OSI七层模型是什么 254
912 TCP/IP模型是什么 255
913 B/S与C/S有什么区别 255
914 MVC模型结构是什么 256
92 网络设备 258
921 交换机与路由器有什么区别 258
922 路由表的功能有哪些 259
93 网络协议 260
931 TCP和UDP的区别有哪些 260
932 什么叫三次握手?什么叫四次断开 260
933 什么是ARP/RARP 262
934 IP Phone的原理是什么?都用了哪些协议 263
935 Ping命令是什么 263
936 基本的>

1 编程方面:C、汇编
2 嵌入式系统基础
3硬件器件与平台:节点器件(T-Mote Sky、TI MSP430等);平台(Arduino,树莓派等)
3无线传感器网络:基础知识、协议栈(ZigBee,IETF 6LowPan, CoAP 等)
4无线传感器网络 *** 作系统(TinyOS, Contiki等)
5在网上找典型应用案例,学术性综述等,这一工作实际上不是在最后进行,而是贯穿在前面4步中


欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/dianzi/12883531.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-28
下一篇 2023-05-28

发表评论

登录后才能评论

评论列表(0条)

保存