(五):C++分布式实时应⽤框架——微服务架构的演进
C++分布式实时应⽤框架——微服务架构的演进
技术交流合作QQ:436466587 欢迎讨论交流
上⼀篇:
版权声明:本⽂版权及所⽤技术归属smartguys团队所有,对于抄袭,⾮经同意转载等⾏为保留法律追究的权利!
OCS(online charging system,在线计费系统)在进⾏云化改造的过程中,从实⽤主义⾓度出发,微服务架构并不是我们的⽬标。虽然我们也对系统进⾏了容器化改造(Docker),并根据业务进程的功能将系统分成了好⼏类的容器,但这⼀切多是出于对系统中的某些处理节点进⾏动态扩缩容的需要,跟微服务半点关系没有。随着系统改造的深⼊,系统的通讯关系复杂程度开始超过我们之前的估计。如果说数量众多的功能节点还有⼈可以勉强掌握,这些节点间错综复杂的通讯关系连线已超过程序员可以驾驭的范畴。在讨论如何简化程序员实现整个系统各类节点的通讯关系的配置过程中,节点微服务化的理念渐渐进⼊我们的脑海之中……
下⾯先给⼤家介绍下我们所⾯临的困境,下⾯的图是我们系统⼀部分节点的通讯关系总图(注意,只是
其中⼀部分):
还记得第⼆篇《基于ZeroMQ的实时通讯平台》中那个我们引以为傲的通讯配置⽂件吗,就是程序中所有的通讯连接关系不再是写死在代码中,⽽是通过AppInit.json配置⽂件进⾏配置,程序启动的时候再由CDRAF进⾏实时加载。当初酷炫的功能,现在却成我们的恶梦。此时AppInit.json这个⽂件已到达1700多⾏,你没看错,⼀个配置⽂件1700多⾏,并且还不是全部,还会继续变⼤。
"OLC" : {
"AUTO_START" : "YES",
"ENDPOINTS" : [
{ // ⽤于与SmartMonitor建⽴⼼跳
"name" : "MonitorSUB",
"zmq_socket_action" : "CONNECT", // ZMQ的连接模式
"zmq_socket_type" : "ZMQ_SUB"// ZMQ的通讯模式
},
{ // 下发消息给OCDis,这边存在转发功能,⽀持业务实现按条件转发
"downstream" : [ "OCDis2OLC"],
"name" : "NE2OLC", // 根据这个名字在业务代码中实现转发
"zmq_socket_action" : "BIND",
"zmq_socket_type" : "ZMQ_STREAM"
},
{ // OLC到OCDis的链路
"name" : "OCDis2OLC",
"statistics_on" : true,
"zmq_socket_action" : "CONNECT",
"zmq_socket_type" : "ZMQ_DEALER"
},
{ // OCDis回OLC的链路,之所以来去分开,主要⽤于实现优雅启停功能(启停节点保证不丢消息)
"name" : "OCDis2OLC_Backway",
"statistics_on" : true,
"zmq_socket_action" : "CONNECT",
"zmq_socket_type" : "ZMQ_DEALER",
"backway_pair" : "OCDis2OLC"
},
{ // ⽤于与SmartMonitor的命令消息链路
"name" : "OLC2Monitor",
"zmq_socket_action" : "CONNECT",
"zmq_socket_type" : "ZMQ_DEALER"
},
],
"ENDPOINT_TO_MONITOR" : "OLC2Monitor",
"INSTANCE_GROUP" : [
{
"instance_endpoints_address" : [
{
"endpoint_name" : "NE2OLC",
"zmq_socket_address" : "tcp://*:6701"
},
{
"endpoint_name" : "OCDis2OLC",
"zmq_socket_address" : [
"tcp://127.0.0.1:7201"// 跨机的IP地址与端⼝,配合状态中⼼可实现⾃动管理,⽆需⼈⼯参与配置
]
},
{
"endpoint_name" : "OCDis2OLC_Backway",
"zmq_socket_address" : [
"tcp://127.0.0.1:7202"
]
},
{
"endpoint_name" : "OLC2Monitor",
"zmq_socket_address" : "ipc://Monitor2Business_IPC"
},
{
"endpoint_name" : "MonitorSUB",
"zmq_socket_address" : "ipc://MonitorPUB"
}
],
"instance_group_name" : "1"
}
]
},
⼀个业务程序员如果要调整系统中某个程序的通讯连接,⼀定得盯着上⾯那副图研究半天,并且要搞明⽩“CONNECT”、“BIND"、”ZMQ_ROUTER"、“ZMQ_DEALER"等等这些zeromq专业词汇的含义,才可能进⾏准确配置,我们隐隐感到这已是⼀个mission impossible。如何简化这个配置⽂件,如何对系统的复杂度进⾏分层,让不同层级的⼈员仅仅只需关注⾃⾝层级情况,再通过我们的CDRAF最终将这些散落的配置、代码组成⼀个完成可运⾏的系统才是我们现在亟需解决的问题。相信这也是每个系统架构师所⾯临的问题,当⼀个系统的复杂度超过单个⼈可承受能⼒范围,就要对这个系统进⾏适当分层,分模块。让每个⼈去管理⼀⼩部分复杂点,并且⼤家只需实现好⾃⼰的模块,⽆需去关⼼别的模块的实现细节。通过事先设计好的接⼝,各个模块可以相互协作,整体系统是可以依此完美地运⾏的。这⾥CDARF正是起这么⼀个不同模块的桥梁(接⼝)的作⽤。
⼀、节点间通讯模式的统⼀
原来节点内的应⽤程序都是通讯全能应⽤程序,所谓全能是指应⽤程序既可以跟节点内的进程进⾏通
讯也可以跟节点外的任意进程进⾏通讯。这样乍看起来没啥问题,但⼀旦节点数和进程数变多后,通讯关系将是⼀个指数级增长的过程。如下图,如果再增加⼀个CDR节点,或者OCS节点,连接数都将增加⾮常多。
我们的解决办法是统⼀节点的通讯模式,每个节点内都有⼀个Dis进程,统⼀对外负责跟其他节点进⾏通讯。在收到外部发给节点的消息后,根据功能和负载转发给内部业务处理进程。业务进程如果有消息需要发往别的节点,就直接发给Dis进程,由它进⾏转发。统⼀通讯模式带来的好处除了在节点和进程增多后,通讯关系不会变得太复杂以外。由于模式统⼀, CDARF可以替业务程序员完成很多⼯作,直接的好处就是业务程序员不再需要配置很多与业务⽆关的配置。最⼤化的将通讯模块的复杂度留给CDRAF去处理,业务程序员将更加专注于⾃⾝的业务逻辑。下⾯的图中其实系统开始已经有微服务的样⼦,但我们希望做到的不仅是从系统架构上是微服务架构,在程序员开发程序的时候,也应该是带着微服务思维的,我们的CDRAF应该提供这么⼀种能⼒来⽀持这种开发模式。
⼆、配置⽂件的简化
通讯模式统⼀后,我们对通讯配置⽂件进⾏了⼀次较⼤的简化,从原来1700⾏减少到了200⾏左右。这当中省去了很多冗余的配置项,通讯配置⽂件不再是对系统通讯简单直接的对应,⽽更多的是对节点通讯能⼒的⼀种表述。
应⽤程序分为Dis和⾮Dis两类,Dis类程序主要承担节点间的通讯和节点内的消息转发,⾮Dis类程序就是普通的业务处理进程。从下⾯的⽂件中可以看到“OCDis”进程中分为“InterContainerEndpoints”和“InnerContainerEndpoints”两⼤类,分别表⽰节点间的通讯和节点内的通讯。对于节点间的通讯,每个服务端⼝只要写上相应的“服务名字”就可以以了,配置中的“OCDisCDRDis”表⽰OCSDis与CDRDis的通讯,“OLCDisOLCProxy”、“OCDis_SyDis_SNR”也是类似。当业务侧程序需要对外提供⼀个服务(或者说与外部进⾏通讯),只需要写⼀个服务名字,⽽如:端⼝、机器的IP地址、服务端还是客户端、通讯模式等等都完全不需要去关⼼,这是多⼤⼀种便利。配置中的注释部分是不需要业务程序员去填的,⽽是由CDRAF的状态中⼼,根据集节点的实时情况⾃动⽣成,并进⾏连接和维护。
{
"OCDis": {
"MaxInstanceGroupNum": 3,
"InterContainerEndpoints":
{
"OCDisCDRDis":
{
//"Port": [6001, 6002, 6003],
//"Cluster": ["10.45.4.10:6001", "10.45.4.10:6001"]
},
"OCDisOLCProxy":
{
//"Port": [6101, 6102, 6103],
"DownStreams": ["OCDis2IN", "OCDis2PS", "OCDis2SMS", "OCDis2ISMP", "OCDis2IMS"],
"router": true
},
分布式和微服务的关系"OCDis_SyDis_SNR":
{
//"Peer": "ZSmartSyDis.OCDis_SyDis_SNR"
}
},
"InnerContainerEndpoints":
{
"OCPro_OCDis_CDR": { "DownStreams": ["OCDisCDRDis"] },
"OCPro_OCDis_SNR": { "DownStreams": ["OCDis_SyDis_SNR"] },
}
},
"OCPro": {
"Groups": ["IN", "PS", "SMS", "IMS", "ISMP"],
"InnerContainerEndpoints": {
"OCPro2OCDis": {
"PeerMap": [
"OCDis.OCDis2IN",
"OCDis.OCDis2PS",
"OCDis.OCDis2SMS",
"OCDis.OCDis2ISMP",
"OCDis.OCDis2IMS"
]
},
"OCPro_OCDis_SNR": {"Peer": "OCDis.OCPro_OCDis_SNR"},
"OCPro_OCDis_CDR": {"Peer": "OCDis"}
}
},
"CDRDis": {
"InterContainerEndpoints":
{
"OCDisCDRDis" :
{
"DownStreams": ["CDRDisCDR"],
//"Peer": "OCDis"
}
}
},
"CDR": {
"InnerContainerEndpoints":
{
"CDRDisCDR" : {"Peer": "CDRDis"}
}
}
}
想像⼀下,对于每⼀个业务节点,开发⼈员仅需考虑节点内的业务实现逻辑,并为本节点对外所提供的服务起个名字,⽽不再需要关⼼这个服务到底是提供给谁,更不⽤操⼼谁会来连我的进程,怎么连。这是多么精妙的事情!我们不仅是从架构上做到了微服务架构,程序员在开发业务程序的时候,不需要去关⼼除了⾃⾝模块以外的其它复杂信息,从此可以轻装上阵,⽽不再需要负重前⾏。这应该就是CDRAF对微服务架构提供的最直接、最好的⽀持了,帮助业务程序员从传统的开发模式转变,进⽽适应微服务的思维⽅式。
三、节点间的通讯关系配置
上⾯我们提到配置⽂件只定义了节点的服务名,那么这么多的微服务节点是如何组合起来⼯作的?⼀个业务应⽤系统会由许多的微服务⼀起协同提供服务,这些服务对于每个不同的现场可能功能是不⼀样的,或者说微服务集合是不⼀样的。那么,对这些微服务的组合的过程就像⼀个“编排”的过程。通过“编排”,选择合适的微服务进⾏搭配组合提供服务,⽽编排的过程就是我们通讯建⽴的过程。下⾯我们就来看⼀下CDRAF是如何做到“编排”功能的。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论