• tars framework 源码解读(四) servant部分章节。服务端部分1。服务端各种关键类简洁


    Servant类。

    翻译成中文就是 仆人的意思。顾名思义,是真正干活的类

    在我们实际上写tars代码时候,一般先定义好一个tars文件。比如tars源码的cpp/examples/CoroutineDemo/BServer.这个工程来说.有个BServant.tars文件。内容如下

    module Test

    {

    interface BServant

    {

    int test();

    int testCoroSerial(string sIn, out string sOut);

    int testCoroParallel(string sIn, out string sOut);

    };

    };

    对应有个BServantImp类。这个类继承于class BServantImp : public Test::BServant.

    BServant就是上面.tars文件中声明的类。如果撸过tars或者taf代码的话,那么应该都知道无论是make release或者是啥工具平台处理这个.tars文件,肯定会对应这个BServer.tars(或者jce)文件生成BServant.hBServant.hpp文件.在这2个自动生成的文件中,会有对class BServant上面标注的3个函数的同步异步协程函数声明和基础实现。而BServant是继承于tars::Servant类的.

     

    对于一次tars请求,服务端响应实现流程的概述

    对应于 客户端流程部分 的《完整的tars调用流程详解》。服务端,是怎么实现tars请求过来后的响应的呢?

    根据上面内容,可以知道我们写业务代码实现的Imp类最终是继承于Servant类的。

    比如 管理控制接口类 class AdminRegistryImp: public AdminReg -> 而找.tars文件转成的.h文件中 class AdminReg : public tars::Servant

    而网络收发,那肯定是通过Epoll实现的,消息是怎么从epoll中收到包之后,调到我们的Imp类中对应的函数来的呢?另外,tars服务端总的又有多少个线程呢?

    首先说说实现服务端的线程,tars有两类线程:

    一种是网络线程:

    通过读Application::initializeServer发现,线程数配置在/tars/application/server<netthread>中,默认是1个,代码保护是1->15个之间。我看了下tars默认给的模板conf文件,此值配成2.在创建

    TC_EpollServer类时候将线程数传入,其内部会 new TC_EpollServer::NetThread(netthread)个真正处理网络请求的网络线程。

     

    一种是业务线程,基类是 ServantHandle class ServantHandle : public TC_EpollServer::Handle

    有个很重要的类,没法绕开。那就是BindAdapter。在每个.config.conf文件的<server>下面,必然有一个或者多个Adapter.比如tars.tarsnode.config.conf文件中:

    <NodeAdapter>

    endpoint=tcp -h localip.tars.com -p 19385 -t 60000

    allow

    maxconns=1024

    threads=5

    queuecap=10000

    queuetimeout=4000

    servant=tars.tarsnode.NodeObj

    </NodeAdapter>

    <ServerAdapter>

    endpoint=tcp -h localip.tars.com -p 19386 -t 60000

    allow

    maxconns=1024

    threads=5

    queuecap=10000

    queuetimeout=4000

    servant=tars.tarsnode.ServerObj

    </ServerAdapter>

    对应的NodeServer.cpp文件的NodeServer::initialize()函数中,有如下语句:

    string sNodeObj = ServerConfig::Application + "." + ServerConfig::ServerName + ".NodeObj";

    string sServerObj = ServerConfig::Application + "." + ServerConfig::ServerName + ".ServerObj";

    addServant<NodeImp>(sNodeObj);

    addServant<ServerImp>(sServerObj);

    这是啥玩意儿?Adapter这个单词,字面翻译是适配器。这个适配器是怎么干活的呢?

    首先在 Application::bindAdapter()中,会轮询出配置中配的所有Adapter,设置好各种参数,有个重点,是与TC_EpollServer对象bind上(完全看不懂系列,此bind在当网络线程数大于1的时候,貌似只绑定第一个网络线程,其它网络线程都只是设置一个参数?啥意思?),这个bind最终会调用TC_EpollServer::NetThread这些网络收发线程,创建一个socket,兵根据Adapter中配置的<endpoint>调用tcp/ip::bind()把指定的端口与ip 与此socket bind上,并且把Adapter的指针与此socket 生成k->v 缓存在线程的一个map中。那么很明显,只要此socket上来了消息,都可以找到此Adapter了。

    上面部分做完 epollAdapter之间就搞上关系了。

    另一个很重要的部分就是。在Application::main()中,会将这些Adapter遍历一遍,调用setHandle().这个函数后面流程简单来说,先会找下 Adapter对象对应的HandleGroupNameHandleGroup,如果没找到,则new一个HandleGroup,最终找到或者生成好HandleGroup后,将此adapter对象与此HandleGroup互相关联起来(HandleGroupName默认与AdapterName一样)。 重要的来了,在new HandleGroup的时候,会生成指定threadsHandleT。。这个东东在代码中是模板,实际实现的HandleTServantHandleServantHandlepublic TC_EpollServer::Handle.这个东东,就是业务处理线程。。

    所以对应的线程数有多少个,就能够清楚的知道了。

     

    暂且不论收到网络包后消息是具体是怎么跑的。还有个重要的问题,就是一开头讲的Imp类,或者说Servant类,是怎么与上面啰嗦了一坨的各种类搭上关系的呢?怎么从ServerHandle线程调用到Servant类中来呢?

     

    这个重点还是在两个步骤:

    步骤1 addServant()执行时候..比如 addServant<NodeImp>(sNodeObj)..[很明显,NodeImp就是我们实现功能的Servant类,而sNodeObj是与配置中对应的ServantName] .这一句实际上会new一个ServantCreation<NodeImp>对象,并与 sNodeObj 建立 k->v,存在ServantHelperManagermap _servant_creator.

    步骤2 在每个 ServantHandle::initialize()时,会遍历它的HandleGroup上所有绑定的Adapter,并且调用ServantHelperManager::create(AdapterName)。这个函数会根据AdapterName查到对应的ServantName,有了ServantName,就可以从 步骤1 中的_servant_creator中查到对应的ServantCreation<NodeImp> 并执行->create(ServantName).这个函数才会创建真正的NodeImp这个Servant类指针,并与ServantNamek->v.保存在ServantHandle_servants这个map.并且在每个Servant类中也绑定上ServantHandle自己的this指针.另外执行此Imp->initialize(),做些初始化事宜。

    所以,总的有多少个Servant类对象呢?可以说一个ServantHandle就有一组其绑定的Adapter所配的Servant对象。

    当有消息过来时候,如果是tars协议消息,根据TarsCurrentPtr->getServantName(),找到servantName对应的Servant类,调用其dispatch(TarsCurrentPtr)。走到这个Servant类的ServantImp的业务逻辑处理函数中。这样整个消息环节就串起来了。

    这样 Servant类就与整个消息流程搭上了..

     

    ServantHelperManager

    通过ServantHelperManager->setAdapterServant(servantName, adapterName) servant名与adapter名做个key:value互映射.

    在所有服务的进程,Application启动时候.会做两个与servant相关的操作:

    1、在Application::initializeServer函数中,建立一个名字叫AdminObjservant(类实现是AdminServant).

    这个servant中实现了AdminF.tars的方法,实现了shutdown()notify()。通过这个servant,就可以实现通知消息的处理(实际处理是走到NotifyObserver相关流程)

    并且ServantHelperManager->setAdapterServant("AdminAdapter", "AdminObj").这两个字符串做好互映射。

    另外会给此adapter设置一个叫"AdminAdapter"handleGroup,并且设置此adapterhandleNum1.

    这两个值作用在下面部分说明.

    2、在bindAdapter 绑定server配置的Adapter和对象的时候,遍历出"/tars/application/server"配置中全部的Adapter.并将下级配置中"/tars/application/server/" + adapterName + "<servant>"配置的servantNameadapterName做映射.

    ServantHelperManager::getInstance()->setAdapterServant(adapterName, servantName)

    另外会给每个adapter设置一个配置在"/tars/application/server/" + adapterName + "<handlegroup>"名字(如果未配此名字,默认用adapterNamehandlegroup)handleGroup,并且设置此adapterhandleNum"/tars/application/server/" + adapterName + "<threads>"(没配置则默认为0).

     

  • 相关阅读:
    css3常见水平垂直居中的方法
    小程序iPhonex适配
    css3实现常用效果
    匿名函数自调用函数
    parseInt ()和parseFloat()
    作用域
    可变传参
    函数调用实例:学生管理系统(前面的优化版),可以弹出窗口。
    java基础:学员状态查询
    java基础:模拟ATM取款机
  • 原文地址:https://www.cnblogs.com/yylingyao/p/12198339.html
Copyright © 2020-2023  润新知