pomelo源码解析之模块解析(四)

  • pomelo-rpc

    RPC服务是用于进程间通讯的一项技术,底层通讯基于TCP或者更上层的mqtt,ws等通信协议
    对用户屏蔽底层细节,进程间通讯形如函数调用,用起来比较方便

    传统开发一个进程间通讯业务,例如logonServer和gameServer之间通讯,稍微封装下,接口大概是这样子的:

// logonServer.h
sendMsgToGameServer(msgID, msg);

这样也是屏蔽了底层通信细节,但调用了服务器哪个函数处理的却无法知道。
而RPC方式可以让你像调用函数一样进行进程间的通讯。一个好的RPC底层还会封装好几种协议格式。
整体来看还是比较方便的。

下边就来具体分析下pomelo-rpc的具体实现逻辑
  • pomelo-rpc/lib/rpc-server 服务器:

    RPC服务器就是监听一个端口,根据收到的RPC请求调用不同的模块处理并反馈

    先看一下服务器支持的通信协议 rpc-server/acceptors
    可以发现支持mqtt mqtt2 tcp ws ws2总共5种通信协议,处理逻辑没有区别,只是编码格式等有区别
    负责监听端口,收到消息后在processMsg中处理,会调用自身的acceptor.cb去处理

    对外接口只有一个create // pomelo-rpc/lib/server.js
    入参:

{
	paths: [
	{
		namespace: ?, // 自定义命名
		path: ?,  // 需要RPC的模块路径
	},
	],      
	context: ?, // 载入模块传入的构造参数
	port: ?, // 监听端口
	acceptorFactory: ?, // 采用的消息协议工厂方法 默认mqtt
	reloadRemotes: ?, // 是否动态重载 会启用文件监听 改变模块后自动重载
};

分析server.loadRemoteServices可以发现
server会根据传入的路径和命名,扫描该路径下的所有js文件。
最终生成形如:

res = {
	namespace: {
		filename: module,
	},
 }

然后传入gateway中,gateway是server的实际逻辑对象

gateway的构造函数中可以发现会创建一个接收器,并写入一个回调函数执行dispatch.route
在dispatch.route可以看到,最终调用了保存模块中的具体函数进行处理

再具体模块中处理后,可以调用回调,最终回到具体的接收器中processMsg,在这里给客户端做回馈

  • pomelo-rpc/lib/rpc-client 客户端

    客户端作为RPC的调用方主要是发送以及接收反馈

    跟服务器对应,支持5种通信协议
    mailboxes就是每种协议的具体实现,支持连接超时,心跳,心跳超时,反馈超时的处理:断开连接
    在发送消息时MailBox.prototype.send会把回调函数存起来,在收到消息后找到对应的回调调用processMsg

    对外接口create入参:

{
     context: ?,  // 载入模块传入的构造参数
     routeContext: ?, // 自定义路由参数
     router: ?,  // 自定义路由跳转
     routerType: ?,  // 设置路由类型
     rpcDebugLog: ?, // 是否需要日志
     mailboxFactory: ?, // 采用的消息协议工厂方法 默认mqtt
     pendingSize: ?,  // 发送缓冲大小
};

对于客户端来说,也需要知道能RPC到哪里去
添加服务器信息:服务器的连接信息存在client.addServer
添加对应的RPC信息:client.addProxies
在这里发现也需要跟服务器读取相同的文件夹,或者新建一个文件夹保持与服务器同步(这里只需要函数名即可)
在client.generateProxy中可以看到:
res[name] = Proxy.create一路跟进去到proxy.genFunctionProxy可以看到,实际上并没有载入具体的函数内容
而是根据函数名生成了一个具有两个函数的对象proxy和toServer,而回调是绑定在了client.proxCB上
最终client形如:

client = {
    ...
      proxies: {
          namespace: {
              servertype: {
                  methodname: method,  // 这个methond不是模块中的函数而是proxy生成的包含2个函数的对象
             };
         };
      };
  };

调用就是client.proxies.namespace.servertype.methodname.proxy(toServer);
最终还是回到client.proxyCB。之所以绕了这么一圈就是为了让通信调用过程像函数调用一样
toServer是指明某个server进行通信,proxy是按参数找一个通信

整体来说,采用这一套RPC做进程间全双工通讯的话,需要每一个进程都启动 RPC服务器 + RPC客户端

  • 总结

经过源码分析我们发现RPC的整体实现逻辑还是比较简单的。这主要得益于js本身是一个动态类型语言。最终调用方式跟函数调用看起来并没有什么区别。
而且这种动态语言的异步调用用起来也是比较舒服。

猜你喜欢

转载自blog.csdn.net/qq_37543025/article/details/85269309