【FDBUS】高速分布式总线学习

FDBUS简介

fdbus内部结构图

image

  • Server层 - 提供服务名解析,组网,日志和调试服务
  • IPC层 - 进程间通信模型,包含实现IPC通信的基本组件
  • 高级平台抽象层 - 中间件进程模型,包含构成进程的基本组件
  • 基础平台抽象层 - 包含系统无关的抽象,用于适配不同的操作系统

基于FDBUS的中间件模型

image

中间件一般包含多个进程,每个进程包含多个线程,FDBUS在线程基础上运行特定的事件循环(event loop),将通用的线程增强为能够执行job,timer和watch的worker线程。

FDBUS的通信双方:client和server统称为endpoint。endpoint可以部署在不同的worker上;多个endpoint也可以共享同一个worker。

多线程协同工作要求线程之间能够传递消息和数据,例如文件下载完毕要通知endpoint做后续处理。在进程内由于可以访问同一地址空间,最好的通信载体是对象 - 既能承载数据,还能指定数据的处理方式。job就是FDBus在线程之间传递的对象,通过job在线程之间的传递和执行实现进程间通信。

FDBus更重要的功能是进程间通信(IPC)。进程之间无法直接传递对象,只能以消息形式交互,并且消息传输过程中需要做序列化,接收到消息后需要做反序列化。每种IPC机制,包括Binder,SOME/IP,DBus,都有自己的序列化方式。序列化方式的好坏直接影响到通信效率,负载,对数据结构的支持程度以及使用的便利性。FDBus没有自己的序列化方式,直接采用google protocol buffer,使用方便,功能齐全,并支持idl自动代码生成。数据在进程间的通信采用socket,包括Unix Domain Socket(UDS)和TCP socket。

FDBus寻址和组网

Server 地址

server地址是server在网络中的标识,通过该标识client可以找到指定的server并与之建立通信。前面提到,FDBus支持UDS和TCP socket,每种socket有自己的命名方式和命名空间。为了统一FDBus使用如下规则定义Server地址:

  • UDS:file://socket文件名
  • TCP socket:tcp://ip地址:端口号

不同于一般意义上的socket server,FDBus server可以同时绑定多个地址,每个地址都可接受client的连接。一旦连接上,每个地址都提供同样的服务,所以client可以选择任意一个地址建立连接。下面是一个FDBus server地址绑定示意图:

image

上图中,server绑定了一个UDS地址:file:///tmp/fdb-ipc1。同一主机上的client可以用该地址发起连接,当然也可连接到其它任意地址,但无疑使用UDS是最高效的,且UDS支持peer credentials,进而支持安全策略。

由于主机有多个网口,server还可以在每个网口上各绑定一个地址(端口号):tcp://192.168.1.2:60004和tcp://192.168.0.1:60004。每个地址用于连接对应网段的client。

Server命名和地址分配

用上述地址定位server使用不方便,不直观,地址会随组网方式变化而改变,无法灵活部署。

为此FDBus增加了一种寻址方式:server名字寻址。每个server可以拥有自己的名字;运行一个叫name server的服务,负责为server分配地址,管理server名字与地址的映射,解析server名字,发布server地址。

name server有点类似internet上的DNS。为支持server名字寻址,在两种URL之外增加一种格式,作为名字地址,如下所示:

  • svc://server名字

名字地址是虚拟地址。无论server位于何处,只要它的名字地址不变,client都可以通过该地址与之建立联系。

如果server调用bind()绑定名字地址(svc://开头的地址),name server会为其分配实际地址(tcp://或file://开头的地址),并将名字和地址注册到映射表里。如果client连接名字地址,name server会根据名字查找server的实际地址,选择一个最合适的实际地址发布给client。client通过该地址与server建立点对点的直连。下图是在name server协助下,client和Server利用名字地址建立连接的流程:

image

1.client调用connect("svc://medisServer")要求和名叫mediaServer的server建立连接。由于使用的是名字地址,FDBus会向name server索要mediaServer的实际地址。但现在mediaServer还没有上线,所以无法解析该名字,只是订阅了对该服务的上线通知。
2.不久后server调用bind("svc://mediaServer")上线,由于使用名字地址,同样会向name server发出请求。name server为其注册名字,分配UDS和TCP地址并返给server。server分别绑定每个实际地址,成功后通知name server。
3.name server向整个系统发布该server上线的消息以及server地址:广播给本地client的是UDS地址,广播给其它节点上client的是TCP地址。client利用收到的地址与server建立连接,同时client和server都能收到onOnline()的事件通知。

name server使用如下规则分配server地址:

Server TCP Address UDS Address
host server port No. 61000 /tmp/fdb-hs
name server port No. 61001 /tmp/fdb-ns
user servers Port 61002 – Port 65535或系统自动分配 /tmp/fdb-ipc0, /tmp/fdb-ipc1 …

多主机跨域组网

由于name server的地址是固定的,endpoint启动后,会自动连接到name server注册(server)或解析(client)名字。

如果有多台主机,每台主机上运行自己的name server,负责各自的名字服务,那么这些主机就成了孤岛,无法通过svc://server_name这样的服务名称互相连接。

当然client可以绕开name server,用实际地址直接连接上server,但这样无法灵活部署和组网。为了支持跨网络的名字解析,需要有一个服务来管理系统里所有主机,把主机信息同步给所有name server,进而这些name server之间可以建立连接,协同工作,共同完成全网范围内的名字服务。这个服务就是host server。

host server的工作原理是:整个网络运行一个host server,可以位于任意一台大家都能访问的主机上。所有主机的name server都连接到host server,向它注册自己所在的主机。host server维护一张包含各主机ip地址的主机列表,并把该表同步给网络里所有的name server。name server根据该表与网络里所有主机上的name server建立连接。

一旦所有主机上的name server都两两建立连接,就可以通过一套内部协议完成跨主机的服务名解析和服务上线通知。例如当一台主机上的client向本地name server请求解析服务名对应的地址,本地name server可以把该请求广播给所有相连的name server,在整个网络范围内查找服务。下面是整个系统组网示例图:

image

上图中,name server和host server之间建立星形连接,name server和name server之间两两相连,组成一张网。在这个系统里,name server和host server的主要任务是:

1.name sever连接到host server,把所在的主机注册进host server
2.host server收集所有主机信息,形成主机地址表
3.host server把主机地址表广播给所有name server
4.name server从表中获得其它主机上name server的地址,并与之建立连接
5.所有server与本地name server相连,向其注册服务名字。本地name server将新注册的服务广播给本地的client以及网络里所有其它的name server
6.其它name server收到该广播后,同样在本地做一个广播,通知给所有client。通过这种方式把服务上线的消息扩散到整个网络
7.所有Client与本地name server相连,申请服务名字解析。本地name server搜索自身的server地址映射表,同时把申请发送给其它所有name server
8.其它name server收到申请后,搜索各自的server地址映射表,并把结果返回给发起申请的name server
9.name server把收到的返回结果转发给发起申请的client,client利用结果里的实际地址与服务建立直连。通过这种方式可以找到所有主机上的服务

从上图也可以看出,一旦client和server之间建立连接,所有的通信都通过这个连接完成,无需经过中间环节转发。

服务名字的唯一性

由于每台主机都有自己的name server,所以在主机内部,服务不能重名,但是在不同主机上可以重名。在这种情况下,当client申请名字解析时,可能会收到来自不同主机反馈server信息。client可以定制连接策略:总是连接连接新的server,只连接第一个server,或是只连接指定的server。

心跳检测,超时重连和离线检测

为了使整个系统可靠运行,保证任何服务都可以优雅重启(重启后整个系统依旧正常工作),FDBus有完善的心跳检测和上线、离线检测机制:

  • endpoints和name server之间有重连机制,确保name server重启后endpoint总是能够与之建立连接
  • name server和host server之间有心跳检测;一旦心跳消失,name server会尝试与host server重连,确保name server与host server之间连接的可靠性
  • name server和name server之间的连接建立由host server来保证:当name server上线时,host server会通知所有其它name server与之建立连接,也会通知该name server与所有其它name server建立连接
  • client和server之间的连接建立由name server来保证:server上线时,name server会通知client与之建立连接

调试和日志

DBus提供的DBus monitor令人印象深刻:它可以抓取DBus总线上所有消息,还能设置过滤条件,抓取特定消息。抓取的消息具有很好地可读性,各种数据结构和字段名都能显示出来。类似的,FDBus也提供抓取消息的工具 - log server,并且其功能更强,除了FDBus消息,还支持调试log输出,并把FDBus消息和调试log合并在一起显示,便于时序分析。

log server是挂在FDBus上的一个普通server,每个endpoint都包含它的client端,如下图所示:

image

和普通server一样,log server运行起来后向name server注册,后者通过广播告知每个endpoint里的LogClient。以后当endpoint向外发送FDBus消息时,也会拷贝一份通过LogClient发给log server。发送的数据除了FDBus消息内容,还包含:

  • 时间戳
  • 发送端和接收端的名字
  • 消息类型(请求,答复,广播,订阅等)

protocol buffer在线路上以二进制格式传输,不能直接打印出来。为调试方便protocol buffer可以把消息转变成便于阅读的文本格式,直观显示消息里每个成员的名字和取值,并把数组类型(repeated类型)和嵌套类型展开显示。

只要log server启动起来,当endpoint通过API打印调试log时,这些log会通过LogClient发送给log server。log server可以把调试log和FDBus消息合并起来输出,也可以选择单独输出特定内容。

无论endpoint部署于哪台主机上,log server都能收集到它的FDBus消息和调试log。整个系统只能运行一个log server,不方便分布式调试。为此FDBus有另一个工具 - log viewer,多个log viewer可以同时启动,全部连接到log server上,获取当前的log信息,打印在标准输出。

参考文章:
https://blog.csdn.net/jeremy_cz/article/details/89060291

posted @ 2025-05-18 22:59  Emma1111  阅读(1317)  评论(0)    收藏  举报