鹅厂开源框架tars之网络层实现

   tars开源框架地址:https://github.com/Tencent/Tars

系列文章:

鹅厂开源框架tars之日志服务

鹅厂开源框架tars之运营监控服务

鹅厂开源框架tars之基础组件

鹅厂开源框架tars之网络层实现

 

 简介:Tars是腾讯从2008年到今天一直在使用的后台逻辑层的统一应用框架TAF(Total Application Framework),目前支持C++,Java,PHP,Nodejs语言。该框架为用户提供了涉及到开发、运维、以及测试的一整套解决方案,帮助一个产品或者服务快速开发、部署、测试、上线。 它集可扩展协议编解码、高性能RPC通信框架、名字路由与发现、发布监控、日志统计、配置管理等于一体,通过它可以快速用微服务的方式构建自己的稳定可靠的分布式应用,并实现完整有效的服务治理。目前该框架在腾讯内部,各大核心业务都在使用,颇受欢迎,基于该框架部署运行的服务节点规模达到上万个。

2021-02-15 春节假期,翻开博客看到这篇文章还有接近2千个阅读量,虽然已经离开工作6年的鹅厂了,但当时写这个文章一方面为了自己巩固,一方面希望好的开源框架能够帮助更多的人提高开发效率和品质,不只在鹅厂可以使用,之前文章的图用viso画的,自己画图能力确实太弱,这次准备利用春节假期重新用processon在线画图工具重新理下,希望能表达得更清晰些

一、客户端同步调用远程服务部分:

使用过tars的人应该都能感受到RPC功能的方便性,tars调用远程服务的方法如下:

m_pRoleServantPrx = Application::getCommunicator()->stringToProxy<RoleServantPrx>(GAMECONFIG->sRoleServerObj);

这样一句代码就可以实现获取远程服务RoleServer的rpc接口了,接着接口调用远程服务RoleServer实现定义好的接口

例如getRoleInfo();m_pRoleServantPrx->getRoleInfo(iUin, vData); 两行代码完成一个rpc调用。是我用过的最方便的rpc框架没有之一了

 

具体框架源码做了什么事情,通过下图然后后续展开来分析

上图已经用数字标注了步骤

对应上图的第一步:图上看到做为客户端进行rpc调用时候使用了:Application::getCommunicator() 获取通讯器:获取到了之后调用stringToProxy模板函数传入参数为obj名字例如上图的:

RoleServerObj(在我们游戏中RoleServer它是一个负责维护角色信息的微服务,格式:servant=HERMAN.RoleServer.RoleServantObj),stringToProxy会根据传参obj的名字调用ServantProxyFactory::getServantProxy获得远程服务对应的servant代理类:(如果_servantProxy没有该obj对应的servant则新new一个并且放入到map<string, ServantPrx> _servantProxy;)。这里要注意可以配置多个客户端网络线程(默认一个),所以new ObjectProxy是一个指针数组ObjectProxy ** ppObjectProxy = new ObjectProxy * [_comm->getClientThreadNum()];new好ObjectProxy然后调用ppObjectProxy[i] = _comm->getCommunicatorEpoll(i)->getObjectProxy(name, setName)根据_communicatorEpoll和sObjectProxyName生成ObjectProxy并且赋值给ppObjectProxy[i] ;注意在框架启动时候Communicator::initialize初始化的时候就已经根据配置的客户端网络线程数生成对应个数的CommunicatorEpoll,所以getCommunicatorEpoll(i)是从数组里面去把CommunicatorEpoll取出来,并且initialize会启动CommunicatorEpoll的线程函数。

接下来构造好ObjectProxy之后,构造new ServantProxy需要传入ObjectProxy为参数,每个objectname在每个客户端网络线程中有唯一一个objectproxy。所以前面stringToProxy不出意外必然会获得一个obj对应的代理类ServantProxy,然后根据传入函数模板的T强制转换下:proxy = (typename T::element_type*)(pServantProxy);例如我这里就是转成:RoleServantPrx,RoleServantPrx是继承于ServantProxy类的(由rpc定义的接口文件自动生成例如.h文件,例如GateServant.h)。

到这里有了调用stringToProxy就干完活了,总结下来就是获取一个ServantProxy,这个对象就是远程要调用的服务的代理类了,然后如上图,就是调用远程服务器定义好的rpc接口,例如上图就是getRoleInfo接口,需要入参uin,出参vData返回根据账号获取到的角色信息了。

有了RoleServantPrx之后,是怎么实现远程rpc调用的呢,为啥RoleServantPrx类调用getRoleInfo函数就可以获取到远程服务返回的信息,这里先看下tars的远程服务接口定义:

例如:GateServant.tars(.tars是tars框架统一的接口定义格式文件) 这是我这边一个网关服务(负责玩家长连接的)的接口定义文件,定义了doRequest接口,调用框架自带的脚本:tars2cpp(通常安装路径在:/home/herman/Tars-master/framework/tarscpp/tools/tars2cpp)会自动生成对应的.h头文件

例如这里就是GateServant.h,GateServant到底生成了啥代码,需要打开看下:

GateServant.tars文件:

module HERMAN
{

interface GateServant
{
    int test();
    int doRequest(string sRequest, out string sResponse);
};

};

生成的.h文件代码片段

class GateServantProxy : public tars::ServantProxy

{

  tars::Int32 doRequest(const std::string & sRequest,std::string &sResponse,const map<string, string> &context = TARS_CONTEXT(),map<string, string> * pResponseContext = NULL 

   {
            tars::TarsOutputStream<tars::BufferWriterVector> _os;
            _os.write(sRequest, 1);
            _os.write(sResponse, 2);
            std::map<string, string> _mStatus;
            shared_ptr<tars::ResponsePacket> rep = tars_invoke(tars::TARSNORMAL,"doRequest", _os, context, _mStatus);
            if(pResponseContext)
            {   
                pResponseContext->swap(rep->context);
            }   

   }

}

重点看下生成的tars_invoke函数调用,传入第一个参数tars::TARSNORMAL(还有TARSONEWAY代表单向调用不接收返回值等),第二个参数接口名,第三个os,就是入参和出参,

第四个参数上下文,第五个参数状态,注意这里tars_invoke是GateServantProxy的基类ServantProxy的成员函数:tars_invoke的实现如下,这里新版本ResponsePacket返回包作为引用出参,老版本应该是直接返回

void ServantProxy::tars_invoke(char  cPacketType,
                              const string& sFuncName,
                              const vector<char>& buf,
                              const map<string, string>& context,
                              const map<string, string>& status,
                              ResponsePacket& rsp)
{
    ReqMessage * msg = new ReqMessage();

    msg->init(ReqMessage::SYNC_CALL,NULL,sFuncName); //SYNC_CALL代表同步调用,tars

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值