(五):C++分布式实时应用框架——微服务架构的演进

C++分布式实时应用框架——微服务架构的演进

 技术交流合作QQ群:436466587 欢迎讨论交流

上一篇:(四):C++分布式实时应用框架——状态中心模块

 

版权声明:本文版权及所用技术归属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是如何做到“编排”功能的。

  

  上面的第一张表,描述了所有的微服务列表,所有节点服务要向外通讯都必须到这张表中增加相应的服务名,这里的服务名是与前面配置文件中的服务名相对应的。第二张表描述了这些微服务名之间的通讯关系,比如第二条记录表达的是OCDis程序的OCDis2CDRDis到CDRDis的OCDis2CDRDis之间会有一个通讯关系。只要通过这个简单的配置,就可以完成两个节点间的通讯关系的建立。这样的设计会带来几个好处。

  1、对于一个复杂的系统,可能有几十类微服务节点,运行实例可能有上百个,如果有上面的表二,就可以容器的从上面的数据中画出整个集群的实时拓扑图,这个对于系统的监控是十分重要的。

  2、集群通讯关系的设计上升了一个等级,业务程序员只需要根据模块接口设计提供相应的微服务节点,而不需要关心与其它微服务是如何协调工作的。而这些微服务如何“编排”提升到了架构师的工作范围的层级。这显然是对复杂度进行分层隔离很好的一个范例。

  3、运维或者管理人员,通过表二的配置可以很容易地操作集群里的某个微服务下线或者上线。在一个庞大的集群里面,如果某类微服务出故障,而CDARF提供了这么一种手段可以去让这类故障微服务下线,将给系统的稳定性带来极大的可靠保证。

  4.、原来集群所有的通讯都配置在一个文件中,在分布式系统中就涉及文件的全局一致性的问题。解决的方案可能是,如果要上线一个新类型的配置文件(新增节点、删除节点、通讯关系改变等等),就要去更新所有在网节点的配置文件。但此时如果新的配置文件有bug,那么可能导致整个集群的故障,并且为了升级某个功能去升级整个集群所有节点的配置也是极不合理的。在新的方案中,节点的配置只定义节点内的通讯和对外提供的微服务名。那么如果要新增某种类型的微服务,不再需要去更新其它节点的配置,只需要将新节点上线,然后在上面的表一新增微服务名,表二增加连接关系就可以了。真正做到了增量升级!

 

  未完待续……

 

转载于:https://www.cnblogs.com/cdap/p/8204569.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/255744.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何通过软件项目开发来提高自身的实力。

在我们这个专业,大多数人都不会将软件开发当作自己的事业,因为若要在这个行业上能够立足,得需要一个好的基础,但是由于这个东西并不是可以通过书本能够彻底的理解和 掌握的,随着时间的变化,我们身边的科技也…

梦回JavaScript--数据类型之undefined

undefined类型只有一个值,即undefined。在使用var声明变量但未对其加以初始化时,这个变量的值就是undefined; var mes; alert(mes undefined) //true如果变量没有声明就会出现错误 var mes; alert(mes) //undefined alert(a)//error 然而有一…

Robot Application Builder

软件开发工具包 Robot Application Builder是安装在PC机(Windows 2000或Windows XP操作系统)上的一种独立开发工具,可用于创建运行于ABB FlexPendant示教器或PC机上的定制化操作界面。为此,该软件包由以下两部分组成:…

asp.net model 验证和取出 ErrorMessage 信息

为什么80%的码农都做不了架构师?>>> public class Users{public int Id { get; set; }public string Name { get; set; }[Required(ErrorMessage "邮箱不能为空")][EmailAddressAttribute(ErrorMessage "邮箱格式不正确")]public…

this

作者:李挺链接:https://www.zhihu.com/question/19636194/answer/123274198来源:知乎著作权归作者所有,转载请联系作者获得授权。关于 this 的描述,曾经在 stackoverflow 上看到了一篇回答写的非常详尽,下面…

DeviceNet 消息类型

DeviceNet是一种低成本的通讯总线链接,具有开放现场网络标准,规范和协议都是开放的。DeviceNet将控制和数据融合在一起,信息具有数据标识区,网络利用标识区进行优先级仲裁,可以高效传送I/O数据。 DeviceNet有两种不同类…

【pyqt5学习——信号与槽】实例计时器(解决界面卡顿问题)

目录 一、方法一:另开线程 1、什么是信号与槽 1)GUI控件(信号)与槽 2)自定义信号与槽 2、实战1:计时器(不自定义信号槽和不使用多线程) 1)界面设计——利用qt-desi…

【转】为什么螺丝都是六角的?

6边形的螺丝拧60度就可以图形还原(不知道表述清楚没,见讨论中的解释)) 如果空间比较狭小,只要扳手能拧动60度就能安装上螺丝 这是在拧动角度和边长相互妥协后的产物 试想 如果是正方形,边长够长了&#xff…

用PHP和Websocket实现实时通讯

说到websocket大家一定不会陌生,WebSocket是HTML5一种新的协议。它实现了浏览器与服务器全双工通信(full-duplex)。一开始的握手需要借助HTTP请求完成,当浏览器和服务器握手成功后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可…

ABB SocketReceive 套接口 函数

SocketReceive函数使用方法: SocketReceive 从远程计算机接收数据。 SocketReceive 可以被客户端和服务器程序使用。 基本样例: 下述样例说明了 SocketReceive 的语法: Example 1 VAR string str_data; ... SocketRe…

【数据库学习笔记】——创建数据库连接对象connection

目录 connect函数的参数 创建连接对象连接MySQL代码 连接对象常见属性与方法 事务名词解释 课程视频链接: 第14节 Python操作数据库_哔哩哔哩_bilibili666https://www.bilibili.com/video/BV1q54y147KX?fromsearch&seid968950907021994347&spm_id_from3…

数据库常用增删改查记录等语句

1增 1.1【插入单行】insert [into] <表名> (列名) values (列值)例&#xff1a;insert into Strdents (姓名,性别,出生日期) values (开心朋朋,男,1980/6/15) 1.2【将现有表数据添加到一个已有表】insert into <已有的新表> (列名) select <原表列名> from &…

一个关于pynoi游戏的C语言编程

“去吧&#xff0c;秦&#xff0c;好好享受这个夜晚&#xff0c;我给你准备了一份礼物&#xff0c;希望你能喜欢。”小布莱克眨着眼睛笑道&#xff0c;狡猾的像一头小狐狸。转载于:https://www.cnblogs.com/jackey18/p/8260774.html

【数据库学习笔记】——cursor游标对象

目录 1、创建cursor对象 2、cursor对象常用方法 3、操作数据库的常见流程&#xff08;五部曲&#xff09; 课程视频链接&#xff1a; 第14节 Python操作数据库_哔哩哔哩_bilibili666https://www.bilibili.com/video/BV1q54y147KX?p2&spm_id_frompageDriver 1、创建cu…

ABB 压包指令PackRawBytes 解包指令UnpackRawBytes

ABB 压包指令PackRawBytes 解包指令UnpackRawBytes PackRawBytes- 将数据导入 rawbytes 数据。 使用方法 PackRawBytes 用于将 num, dnum, byte,或者 string类型的数据&#xff0c;打包到 rawbytes 类型的变量中. 基本举例 &#xff1a; VAR rawbytes …

Android Https相关完全解析 当OkHttp遇到Https

Android Https相关完全解析 当OkHttp遇到Https转载于:https://www.cnblogs.com/zhujiabin/p/5304617.html

基于ARM核AT75C220在指纹识别系统中应用

纹识别技术近年来逐渐成熟&#xff0c;在门禁、安防和金融等方面得到了越来越广泛的应用。典型的指纹识别系统是以指纹传感器和DSP处理器为核心构成。指纹传感器采集指纹图像&#xff0c;DSP处理器实时实现指纹识别算法。同时&#xff0c;通常的指纹识别系统还具有较强的通信能…

【数据库学习笔记】——创建数据库文件

目录 1、数据库基础知识 2、创建数据库文件 1、数据库基础知识 什么是数据库&#xff1f;——数据库是存放多个数据表的仓库&#xff0c;数据表可以理解为是二维数组 如上表所示&#xff0c;每个表由行列组成&#xff1a; 字段&#xff1a;每一列称之为一个字段&#xff0c;每…

岁月在流逝,从阿里退下来接近70后程序猿带给我的启示

大家好&#xff0c;今天为大家带来一个非常励志&#xff0c;让我反思的故事。今天为大家讲的是我父亲是如何用行动教会我成为程序员的。 岁月的锤炼 圆口接口的键盘&#xff0c;2003年的 有人问这个键盘难道没有擦过吗&#xff1f;不是的&#xff0c;是擦不掉的岁月痕迹。如果有…

浅谈工业机器人的运动停止

德系的工业机器人系统中&#xff0c;对于机器人停止运动&#xff0c;定义了3种模式&#xff0c;比如 KUKA 的工业机器人分别定义了 Stop 0 &#xff0c;Stop 1&#xff0c;Stop 2 (*注1)。这种定义模式是与机器人的机械结构和电气结构相关联的。 对于此&#xff0c;读者应该先了…