【TARS】深入理解RegistryServer

目录

 

0.说明

1.入门简介

1.1 功能

1.2 源码,可执行程序及启动

1.3 相关命令

 1.4 分析方向

2.观察与分析

2.1 配置文件展示

2.2 RegistryServer线程观察

2.2.1 主线程 

2.2.2 Application.cpp中initializeServer产生7个线程

2.2.3 第9-20个线程

2.2.4 第21-35个线程,退出三个线程,新加入15个线程,至此共32个线程

2.2.5 程序启动后产生的所有线程一览

2.2.6 所有线程堆栈细节

2.2.7 线程分布快表

2.3 产生线程的代码分析

2.3.1 一号线程-主线程

2.3.2 Application::initializeServer中产生2-8的线程

2.3.3 将会消失的9号线程

2.3.4 RegistryServer::initialize()中产生10-20的线程

2.3.5 会消失的21号和22号线程

2.3.6 线程23-35的13个线程

2.4 端口观察

2.5 内存分布观察

3.源码阅读

3.1 主流程

3.2 g_app.main(argc,argv)详细分析

3.2.0总览

      3.2.0.1 Application::main(int argc, char *argv[])

      3.2.0.2 Application::main(const TC_Option &option)

      3.2.0.3 Application::main(const string &config)

3.2.1 解析配置文件-parseConfig(config)

3.2.2 初始化客户端-initializeClient()

3.2.3 初始化服务器端-initializeServer()

3.2.4 绑定对象和端口-bindAdapter(adapters)

3.2.5 输出所有Adapter-outAllAdapter(cout)

3.2.6 业务应用初始化-RegistryServer::initialize()

3.3 g_app.waitForShutdown()详细分析

3.3.0 总览

3.3.1 设置waitForShutdown线程发送心跳的函数

3.3.2 设置netthread网络线程发送心跳的函数

3.3.3  网络模块及业务模块的处理【超级重要】

      3.3.3.1 启动业务线程

      3.3.3.2 生成epoll

      3.3.3.3 启动网络线程

      3.3.3.4 主线程的处理

      3.3.3.5 处理网络线程

3.3.4 销毁程序

3.3.5 上报停止状况

3.3.6 稍微休息一下, 让当前处理包能够回复

3.4 网络线程详细分析

3.4.1 数据结构

3.3.2 问题

3.5 业务处理线程详细分析

3.5.1 相关类的设计

3.5.2 流程

3.6 队列设计总结

3.6.1 发送队列

3.6.2 接受队列

3.7 网络线程及业务线程总结

4. 问题集锦

4.1 Program received signal SIGPIPE, Broken pipe.啥意思?

4.2 每一个注册的Servant中的rpc接口是如何被调用的呢?

4.2.1 引入

4.2.2  tars.tarsregistry.RegistryObj和tars.tarsregistry.QueryObj的RPC接口如何注册和被调用的?

4.2.3 addServant((*g_pconf)["/tars/objname"]);这一步执行的时候是否创建了一个RegistryImp的对象?

4.2.4 答案与反射性能的思考

4.2.5 辅助理解的图

4.3 基类pmap _procFunctors包含的内容是什么?functor如何能够正确打印?

4.4 如何理解Servant?


0.说明

本文基于框架版本为2.4.14

与本文强关联文章:

Epoll回顾链接

内核中的epoll

Tars之RegistryServer

TarsdocGitHub在线文档-框架简介

tars服务端(一):server的启动流程

Tars C++框架服务线程组成

TARS基金会CSDN-微服务开源框架TARS的RPC源码解析 之 初识TARS C++服务端

TAF server的大致组件结构

GDB调试手册

【TARS】TARS-CPP服务器端数据结构总结

【TARS】TARS-CPP客户端学习一

【TARS】基于TARS的调试

我的系列文章:

【TARS】初识TARS

【TARS】源码方式部署TARS

【TARS】Centos下用Docker部署TARS

【TARS】用TarsCpp-创建第一个服务

【TARS】TARS学习文章链接(感谢哈)及自己的链接

【TARS】基于TARS的调试

【TARS】TARS协议的编解码

【TARS】TARS架构支持的协议汇总

【TARS】TARS-CPP客户端学习一

【TARS】TARS-CPP客户端学习二

【TARS】我的测试环境的常用路径

【TARS】TARS-CPP服务器端数据结构总结

【TARS】扩容与负载均衡

【TARS】开发测试及运维记录

【TARS】网关TarsGateway

【TARS】压测工具TarsBenchmark

【TARS】分布式存储系统DCache

【TARS】远程调用方式

【TARS】TarsCpp-Http服务示例

【TARS】PUSH功能

【TARS】日志服务

【TARS】鉴权功能

【TARS】Mysql操作

【TARS】TARS中的nodejs

【TARS】深入理解RegistryServer

1.入门简介

1.1 功能

RegistryServer提供路由+管理服务,提供服务节点的地址查询、发布、启停、管理等操作,以及对服务上报心跳的管理,通过它实现服务的注册与发现.

1.2 源码,可执行程序及启动

/home/muten/module/TARS/TarsFramework/build/servers/tarsregistry/bin/tarsregistry --config=/usr/local/app/tars/tarsnode/data/tars.tarsregistry/conf/tars.tarsregistry.config.conf

/home/muten/module/TARS/TarsFramework/RegistryServer
/usr/local/app/tars/tarsregistry/bin/tarsregistry
release版本:
/usr/local/app/tars/tarsregistry/bin/tarsregistry --config=/usr/local/app/tars/tarsnode/data/tars.tarsregistry/conf/tars.tarsregistry.config.conf
debug版本:
/home/muten/module/TARS/TarsFramework/build/servers/tarsregistry/bin/tarsregistry --config=/usr/local/app/tars/tarsnode/data/tars.tarsregistry/conf/tars.tarsregistry.config.conf
总控启动:/usr/local/app/tars/tars-start.sh
总控停止:/usr/local/app/tars/tars-stop.sh

1.3 相关命令

ps -ef | grep tarsregistry      // 查看tarsregistry的进程情况
ps -Lw 【tarsregistry进程的PID】 // 查看tarsregistry的进程对应的线程
ps -ef | grep tarsregistry | grep -v grep |awk -F ' ' '{ print $2 }' // 查看PID
kill -9 `ps -efww | grep tars | grep -v grep | grep -v less | awk '{print $2}'` // 杀掉tars相关的进程

vim /home/muten/module/TARS/TarsFramework/tarscpp/servant/libservant/Application.cpp
(207行看Application::waitForShutdown相关函数)
vim /home/muten/module/TARS/TarsFramework/tarscpp/util/src/tc_epoll_server.cpp 
(2024行看TC_EpollServer::waitForShutdown相关函数)


gdb -q /home/muten/module/TARS/TarsFramework/build/servers/tarsregistry/bin/tarsregistry
>set args --config=/usr/local/app/tars/tarsregistry/conf/tars.tarsregistry.config.conf
>b 105
>b Application.cpp:1304
>b Application.cpp:207(断点打到这里,然后执行s,进入_epollServer->waitForShutdown()执行,此时已经有20个线程了)
>b tc_epoll_server.cpp:2030
>r
>l
>l
>l - (显示当前行后面的源程序)
>s(进入函数)
>n(下一步)
>s
>finish(退出堆栈)
>i thread(查看线程信息)
>thread 1(看线程1的信息)
>bt(查看堆栈)
>l /home/muten/module/TARS/TarsFramework/tarscpp/servant/libservant/Application.cpp:790
>l tc_epoll_server.cpp:2024,tc_epoll_server.cpp:2070(显示tc_epoll_server.cpp的2024-2070行代码)
>l(接着显示tc_epoll_server.cpp:2024后面的代码)


查看第23-35线程的调试方法:
gdb -q /home/muten/module/TARS/TarsFramework/build/servers/tarsregistry/bin/tarsregistry
>set args --config=/usr/local/app/tars/tarsregistry/conf/tars.tarsregistry.config.conf
>b tc_epoll_server.cpp:2246
>r
>i thread
>l
>b 2249(for (uint32_t i = 0; i < hds.size(); ++i) {这一行)
>n
>p _bindAdapters
>p bindAdapter
>p bindAdapter->_name
>p hds.size()
>finish

 1.4 分析方向

  •   线程模型
  •   网络与业务模型
  •   队列
  •   日志
  •   内存分析
  •   性能分析

2.观察与分析

2.1 配置文件展示

<tars>
  <application>
    <client>
      locator=tars.tarsregistry.QueryObj@tcp -h 192.168.118.138 -p 17890
      sync-invoke-timeout=10000
      async-invoke-timeout=60000
      refresh-endpoint-interval=60000
      stat=tars.tarsstat.StatObj
      property=tars.tarsproperty.PropertyObj
      report-interval=60000
      asyncthread=3
      modulename=tars.tarsregistry
      timeout-queue-size=100
    </client>
    <server>
      app=tars
      server=tarsregistry
      localip=192.168.118.138
      basepath=/usr/local/app/tars/tarsregistry/bin
      datapath=/usr/local/app/tars/tarsregistry/data
      netthread=2
      logpath=/usr/local/app/tars/app_log
      logsize=10M
      config=tars.tarsconfig.ConfigObj
      deactivating-timeout=2000
      logLevel=DEBUG
      notify=tars.tarsnotify.NotifyObj
      local=tcp -h 127.0.0.1 -p 17890 -t 3000
      node=tars.tarsnode.ServerObj@tcp -h 192.168.118.138 -p 19386 -t 60000
      <tars.tarsregistry.QueryObjAdapter>
        endpoint=tcp -h 192.168.118.138 -p 17890 -t 10000
        allow
        maxconns=100000
        threads=5
        queuecap=100000
        queuetimeout=6000
        servant=tars.tarsregistry.QueryObj
      </tars.tarsregistry.QueryObjAdapter>
      <tars.tarsregistry.RegistryObjAdapter>
        endpoint=tcp -h 192.168.118.138 -p 17891 -t 30000
        allow
        maxconns=100000
        threads=5
        queuecap=100000
        queuetimeout=6000
        servant=tars.tarsregistry.RegistryObj
      </tars.tarsregistry.RegistryObjAdapter>
    </server>
  </application>
  <db>
    dbhost=192.168.118.138
    dbname=db_tars
    dbuser=tarsAdmin
    dbpass=Tars@2019
    dbport=3306
    charset=utf8
    dbflag=CLIENT_MULTI_STATEMENTS
  </db>
  <reap>
    loadObjectsInterval=30
    queryInterval=150
    loadObjectsInterval1=13
    LeastChangedTime1=600
    loadObjectsInterval2=3601
    nodeTimeout=250
    registryTimeout=150
    querylesttime=300
    asyncthread=6
    openDayLog=N
  </reap>
  <objname>
    patchServerObj=tars.tarspatch.PatchObj
    QueryObjName=tars.tarsregistry.QueryObj
    RegistryObjName=tars.tarsregistry.RegistryObj
  </objname>
  <objcache>
    min_block=50
    max_block=200
    factor=1.2
    FilePath=/usr/local/app/tars/tarsregistry/util/objectCache.dat
    FileSize=8M
  </objcache>
  <nodeinfo>
    defaultTemplate=tars.tarsnode
  </nodeinfo>
</tars>

2.2 RegistryServer线程观察

为啥有时候31个有时候32个??
用的程序不一样,
一个是 /home/muten/module/TARS/TarsFramework/build/servers/tarsregistry/bin/tarsregistry
一个是 /usr/local/app/tars/tarsregistry/bin/tarsregistry 
第一个自己改了些东西.



主线程1个
业务处理线程2*5=10(AdapterName.size() is 2, _iHandleNum is 5)
网络线程就是ServerConfig::NetThread,是2个
AdminAdapter有1个线程
心跳检测线程1个
全量和增量加载路由信息的线程1个(RegistryServer.cpp--_reapThread)
检查node超时的线程1个(RegistryServer.cpp--_checkNodeThread)
监控所有服务状态的1个线程(RegistryServer.cpp--_checksetingThread)
异步处理线程6个(RegistryServer.cpp--_registryProcThread)
以上共24个线程
实际显示共32个线程
getInstance()获取单例对象的时候可能会有很多线程,再看看!!
RemoteLogger
RemoteNotify

2.2.1 主线程 

开始的样子

之后的样子

2.2.2 Application.cpp中initializeServer产生7个线程

2.2.3 第9-20个线程

(gdb) n
[New Thread 0x7fffeeffd700 (LWP 29550)]
797	                TC_Common::msleep(100);
(gdb) n
**********STEP6 keepActiving thread  ***********,  filename is Application.cpp,function_name is operator(),line = 783
filename is Application.cpp,function_name is operator(),line = 785initing is 1
799	                initialize();
(gdb) n
line = 25RegistryServer::initialize...开始进行业务处理代码的书写
exe = 
filename is RegistryServer.cpp,function_name is initialize,line = 42,sPrefix is tars.tarsregistry.
servant is tars.tarsregistry.QueryObj
adapterName[i] is tars.tarsregistry.QueryObjAdapter
ptcName is tars
servant is tars.tarsregistry.RegistryObj
adapterName[i] is tars.tarsregistry.RegistryObjAdapter
ptcName is tars
loadServantEndpoint() start
initFormat and enableRemote. 
[New Thread 0x7fffee7fc700 (LWP 29551)]
[New Thread 0x7fffedffb700 (LWP 29552)]
init and start _reapThread 
ReapThread init ok.
[New Thread 0x7fffed7fa700 (LWP 29553)]
init and start _checkNodeThread 
[New Thread 0x7fffecff9700 (LWP 29554)]
init and start _checkSetingThread 
CheckSettingStateThread init
CheckSettingStateThread init ok.
[New Thread 0x7fffd7fff700 (LWP 29555)]
CheckSettingState::run 
line 75 CheckSettingState ::run 
[New Thread 0x7fffd77fe700 (LWP 29556)]
[New Thread 0x7fffd6ffd700 (LWP 29557)]
[New Thread 0x7fffd67fc700 (LWP 29558)]
[New Thread 0x7fffd5ffb700 (LWP 29559)]
[New Thread 0x7fffd57fa700 (LWP 29560)]
[New Thread 0x7fffd4ff9700 (LWP 29561)]
filename is RegistryServer.cpp,function_name is initialize,line = 95,_registryProcThread num  is 6
RegistryServer::initialize OK!
800	                cout << "**********STEP6 business initialize  ***********, filename is "<<__FILE__<< ",function_name is " << __func__<<",line = "<<__LINE__<<endl;

2.2.4 第21-35个线程,退出三个线程,新加入15个线程,至此共32个线程

filename is Application.cpp,function_name is main,line = 777initing is 1
[New Thread 0x7fffeeffd700 (LWP 29817)]
**********STEP6 keepActiving thread  ***********,  filename is Application.cpp,function_name is operator(),line = 783
filename is Application.cpp,function_name is operator(),line = 785initing is 1
line = 25RegistryServer::initialize...开始进行业务处理代码的书写
exe = 
filename is RegistryServer.cpp,function_name is initialize,line = 42,sPrefix is tars.tarsregistry.
servant is tars.tarsregistry.QueryObj
adapterName[i] is tars.tarsregistry.QueryObjAdapter
ptcName is tars
servant is tars.tarsregistry.RegistryObj
adapterName[i] is tars.tarsregistry.RegistryObjAdapter
ptcName is tars
loadServantEndpoint() start
initFormat and enableRemote. 
[New Thread 0x7fffee7fc700 (LWP 29818)]
[New Thread 0x7fffedffb700 (LWP 29819)]
init and start _reapThread 
ReapThread init ok.
[New Thread 0x7fffed7fa700 (LWP 29820)]
init and start _checkNodeThread 
[New Thread 0x7fffecff9700 (LWP 29821)]
init and start _checkSetingThread 
CheckSettingStateThread init
CheckSettingStateThread init ok.
[New Thread 0x7fffdffff700 (LWP 29822)]
CheckSettingState::run 
line 75 CheckSettingState ::run 
[New Thread 0x7fffdf7fe700 (LWP 29823)]
[New Thread 0x7fffdeffd700 (LWP 29824)]
[New Thread 0x7fffde7fc700 (LWP 29825)]
[New Thread 0x7fffddffb700 (LWP 29826)]
[New Thread 0x7fffdd7fa700 (LWP 29827)]
[New Thread 0x7fffdcff9700 (LWP 29828)]
filename is RegistryServer.cpp,function_name is initialize,line = 95,_registryProcThread num  is 6
RegistryServer::initialize OK!
**********STEP6 business initialize  ***********, filename is Application.cpp,function_name is main,line = 800
filename is Application.cpp,function_name is main,line = 803,  initing is 0
filename is Application.cpp,function_name is main,line = 806, print after notify all.  
filename is Application.cpp,function_name is operator(),line = 791initing is 0
filename is Application.cpp,function_name is main,line = 821,动态加载配置文件.  
tars.loadconfig
filename is Application.cpp,function_name is main,line = 824,动态设置滚动日志等级.  
tars.setloglevel
filename is Application.cpp,function_name is main,line = 828,动态设置按天日志等级.  
tars.enabledaylog
filename is Application.cpp,function_name is main,line = 832,查看服务状态.  
tars.viewstatus
filename is Application.cpp,function_name is main,line = 836,查看当前链接状态.  
tars.connection
filename is Application.cpp,function_name is main,line = 840,查看编译的TARS版本.  
tars.viewversion
filename is Application.cpp,function_name is main,line = 844,查看服务buildid(编译时间).  
[Thread 0x7fffeeffd700 (LWP 29817) exited]
tars.bid
filename is Application.cpp,function_name is main,line = 848,加载配置文件中的属性信息.  
tars.loadproperty
filename is Application.cpp,function_name is main,line = 852,查看服务支持的管理命令.  
tars.help
filename is Application.cpp,function_name is main,line = 856,设置染色信息.  
tars.setdyeing
filename is Application.cpp,function_name is main,line = 860,设置服务的core limit.  
tars.closecore
filename is Application.cpp,function_name is main,line = 864,设置是否关闭stdcout/stderr/stdin 服务重启能才生效.  
tars.closecout
filename is Application.cpp,function_name is main,line = 868,通过命令动态加载配置文件,获取最新的locator,以方便应对主控便更. 
tars.reloadlocator
filename is Application.cpp,function_name is main,line = 872,设置查看服务资源 
tars.resource
filename is Application.cpp,function_name is main,line = 876,上报版本.  
filename is Application.cpp,function_name is main,line = 881,发送心跳给node, 表示启动了.  
filename is Application.cpp,function_name is main,line = 885,发送给notify表示服务启动了.  
filename is Application.cpp,function_name is main,line = 889,注册ctrl+C和终止信号.  
[New Thread 0x7fffeeffd700 (LWP 29829)]
[Thread 0x7fffeeffd700 (LWP 29829) exited]
[New Thread 0x7fffd7fff700 (LWP 29830)]
filename is Application.cpp,function_name is main,line = 917, get closeCout 信号.  
filename is Application.cpp,function_name is main,line = 923, fd is 29
filename is Application.cpp,function_name is main,line = 926, fd is not -1
[Thread 0x7fffd7fff700 (LWP 29830) exited]
[New Thread 0x7fffeeffd700 (LWP 29831)]
[New Thread 0x7fffd7fff700 (LWP 29832)]
[New Thread 0x7fffd77fe700 (LWP 29833)]
[New Thread 0x7fffd6ffd700 (LWP 29834)]
[New Thread 0x7fffd67fc700 (LWP 29835)]
[New Thread 0x7fffd5ffb700 (LWP 29836)]
[New Thread 0x7fffd57fa700 (LWP 29837)]
[New Thread 0x7fffd4ff9700 (LWP 29838)]
[New Thread 0x7fffc7fff700 (LWP 29839)]
[New Thread 0x7fffc77fe700 (LWP 29840)]
[New Thread 0x7fffc6ffd700 (LWP 29841)]
[New Thread 0x7fffc67fc700 (LWP 29842)]
[New Thread 0x7fffc5ffb700 (LWP 29843)]

vim /home/muten/module/TARS/TarsFramework/tarscpp/servant/libservant/Application.cpp
(207行看Application::waitForShutdown相关函数)
vim /home/muten/module/TARS/TarsFramework/tarscpp/util/src/tc_epoll_server.cpp 
(2024行看TC_EpollServer::waitForShutdown相关函数)

线程38199是线程23,调用tars::ServantHandle::run

线程38586是线程24,页调用tars::ServantHandle::run

没有啥情况,就是调试的时候还没反应过来,在创建一系列线程.

2.2.5 程序启动后产生的所有线程一览

2.2.6 所有线程堆栈细节

【thread1】-主线程

 【thread2】-tars::TC_LoggerThreadGroup::run

 【thread3,4,5】-tars::AsyncProcThread::run

【thread6】-tars::CommunicatorEpoll::run

【thread7】-tars::TC_TimeProvider::run

【thread8】-tars::StatReport::run

【thread10,11】-tars::TC_LoggerThreadGroup::run

【thread12】-ReapThread::run

【thread13】-CheckNodeThread::run

【thread14】-CheckSettingState::run

【thread15-20】-RegistryProcThreadRunner::run

【thread23-33】-tars::ServantHandle::run

线程34和35的堆栈信息

2.2.7 线程分布快表

2.3 产生线程的代码分析

2.3.1 一号线程-主线程

也许不必多说.

2.3.2 Application::initializeServer中产生2-8的线程

线程2(TC_LoggerThreadGroup对象中的线程)
源码位置:Application.cpp中Application::initializeServer函数中的_epollServer->setLocalLogger(LocalRollLogger::getInstance()->logger())
截图一为/home/muten/module/TARS/TarsFramework/tarscpp/servant/libservant/Application.cpp中Application::initializeServer()的部分代码,全代码是1116行到1403行,
图二中_epollServer->setLocalLogger(LocalRollLogger::getInstance()->logger()) 为Application::initializeServer()中的代码.

3-8线程

vim /home/muten/module/TARS/TarsFramework/tarscpp/servant/libservant/Communicator.cpp可以在626行看到Communicator::getServantProxy函数,

接着关注Communicator::initialize()函数:

VSCODE的图中的305行产生3,4,5这三个线程,

VSCODE的图中的317行产生6线程,

VSCODE的图中的362行产生7,8线程(7号TC_TimeProvider线程在317行货362行都可能产生,关键看哪里用了高精度时间类,反正单例的,只会出现一次)

Linux下的特写:

线程3,4,5

线程6

线程7,8

2.3.3 将会消失的9号线程

thread_local(__thread)产生的吗?蹲一个thread_local链接:链接 ,  一个__thread的链接 

不是thread_local(__thread)产生的,九号线程是Lamdda产生的,然后初始化号就退出了

vim /home/muten/module/TARS/TarsFramework/tarscpp/servant/libservant/Application.cpp
gdb -q /home/muten/module/TARS/TarsFramework/build/servers/tarsregistry/bin/tarsregistry
>set args --config=/usr/local/app/tars/tarsregistry/conf/tars.tarsregistry.config.conf
>b Application.cpp:784
>r

2.3.4 RegistryServer::initialize()中产生10-20的线程

D:\005-02-代码\016-TARS\TARS\TarsFramework\RegistryServer\RegistryServer.cpp

线程10,11的特写:

vim /home/muten/module/TARS/TarsFramework/tarscpp/servant/libservant/RemoteLogger.cpp

2.3.5 会消失的21号和22号线程

相关代码:

vim /home/muten/module/TARS/TarsFramework/RegistryServer/RegistryServer.cpp

断点调试:

gdb -q /home/muten/module/TARS/TarsFramework/build/servers/tarsregistry/bin/tarsregistry
>set args --config=/usr/local/app/tars/tarsregistry/conf/tars.tarsregistry.config.conf
>b Application.cpp:903
>b Application.cpp:909

代码关键字搜索:

TC_Port::registerCtrlC ,TC_Port::registerTerm

作用:

注册信号函数

2.3.6 线程23-35的13个线程

vim /home/muten/module/TARS/TarsFramework/tarscpp/servant/libservant/Application.cpp

vim /home/muten/module/TARS/TarsFramework/tarscpp/util/src/tc_epoll_server.cpp

vim /home/muten/module/TARS/TarsFramework/tarscpp/util/src/tc_epoll_server.cpp

细致分析这11个线程:

2.4 端口观察

2.5 内存分布观察

3.源码阅读

3.1 主流程

3.2 g_app.main(argc,argv)详细分析

3.2.0总览

总体流程图如下:

      3.2.0.1 Application::main(int argc, char *argv[])

      3.2.0.2 Application::main(const TC_Option &option)

      3.2.0.3 Application::main(const string &config)

3.2.1 解析配置文件-parseConfig(config)

3.2.2 初始化客户端-initializeClient()

使用linux的GDB打印STL

插件和Python两种方式来帮助我们打印STL容器中的值

STLSupport的附件 attachment:stl-views-1.0.3.gdb-下载链接

哈哈!红黑树自动排好序了,笑哭~ 

3.2.3 初始化服务器端-initializeServer()

3.2.4 绑定对象和端口-bindAdapter(adapters)

vim /home/muten/module/TARS/TarsFramework/tarscpp/servant/libservant/Application.cpp
gdb -q /home/muten/module/TARS/TarsFramework/build/servers/tarsregistry/bin/tarsregistry
>set args --config=/usr/local/app/tars/tarsregistry/conf/tars.tarsregistry.config.conf
>b Application.cpp:1450(进入到Application::bindAdapter中)
>r
>b Application.cpp:1468【不执行string servant = _conf.get("/tars/application/server/" + adapterName[i] + "<servant>");后停止】
>b Application.cpp:1469【执行string servant = _conf.get("/tars/application/server/" + adapterName[i] + "<servant>");后停止】
>b Application.cpp:1488【执行了ep.parse(_conf[sLastPath + "<endpoint>"]);--是1487行的代码】
>c


3.2.5 输出所有Adapter-outAllAdapter(cout)

3.2.6 业务应用初始化-RegistryServer::initialize()

这里产生了7个新线程

3.3 g_app.waitForShutdown()详细分析

3.3.0 总览

3.3.1 设置waitForShutdown线程发送心跳的函数

_epollServer->setCallbackFunctor(reportRspQueue);

将tars::TC_EpollServer::_hf设置成tars::reportRspQueue(tars::TC_EpollServer *epollServer).【TC_EpollServer的结构的链接:链接,搜索1.8 TC_EpollServer】

3.3.2 设置netthread网络线程发送心跳的函数

_epollServer->setHeartBeatFunctor(heartBeatFunc);

将tars::TC_EpollServer::_heartFunc设置成void tars::heartBeatFunc(const std::string &adapterName).

3.3.3  网络模块及业务模块的处理【超级重要】

_epollServer->waitForShutdown();

      3.3.3.1 启动业务线程

      3.3.3.2 生成epoll

   /*在RegistryServer中,此时 _listeners里面有:
     <15, tars::TC_EpollServer::BindAdapterPtr-AdminAdapter>
     <16, tars::TC_EpollServer::BindAdapterPtr-tars.tarsregistry.QueryObjAdapter>
     <17, tars::TC_EpollServer::BindAdapterPtr-tars.tarsregistry.RegistryObjAdapter>
    
     gdb -q /home/muten/module/TARS/TarsFramework/build/servers/tarsregistry/bin/tarsregistry
     >set args --config=/usr/local/app/tars/tarsregistry/conf/tars.tarsregistry.config.conf 
     >b tc_epoll_server.cpp:2293
     >l
     >r
     >p it->first
     >p it->second->_name
     >p it->second->_ep
    */

 

      3.3.3.3 启动网络线程

启动网络线程,不需要多说~

      3.3.3.4 主线程的处理

      3.3.3.5 处理网络线程

也无须多说~

3.3.4 销毁程序

3.3.5 上报停止状况

3.3.6 稍微休息一下, 让当前处理包能够回复

没啥好说的~

3.4 网络线程详细分析

3.4.1 数据结构

NetThread类的结构链接

代码关键字搜索:

class NetThread

TC_EpollServer::NetThread::run()

3.3.2 问题

1.NetThread对象和EpollServer对象的关系是什么?是多个NetThread对象对应一个TC_EpollServer对象吗?还是每一个NetThread对象都有一个EpollServer对象?

2.NetThread对象和TC_Epoller对象是一一对应的关系,NetThread中的_epoll是tars::TC_EpollServer::NetThread::_epoller

3.TC_EpollServer中也有一个TC_Epoller对象--tars::TC_EpollServer::_epoller

TC_EpollServer::TC_EpollServer(TC_EpollServer的构造函数)

TC_EpollServer::TC_EpollServer(unsigned int iNetThreadNum)
: _netThreadNum(iNetThreadNum)
, _bTerminate(false)
, _handleStarted(false)
, _pLocalLogger(NULL)
, _acceptFunc(NULL)
{
#if TARGET_PLATFORM_WINDOWS    
    WSADATA wsadata;
    WSAStartup(MAKEWORD(2, 2), &wsadata);
#endif

    if(_netThreadNum < 1)
    {
        _netThreadNum = 1;
    }

    //网络线程的配置数目不能15个
    if(_netThreadNum > 15)
    {
        _netThreadNum = 15;
    }

    //创建epoll
    _epoller.create(10240);
    // 问题 : 为什么 tc_epoller.cpp 中的 TC_Epoller::NotifyInfo::init(TC_Epoller *ep)  用的是 UDP协议呢? _notify.createSocket(SOCK_DGRAM, AF_INET);
    _notify.init(&_epoller);
    _notify.add(_notify.notifyFd());
   
    for (size_t i = 0; i < _netThreadNum; ++i)
    {
        TC_EpollServer::NetThread* netThreads = new TC_EpollServer::NetThread(this, i);// 实现在本文件的 line 1479 ,按这里写的如果 _netThreadNum 是2 的话,就会有两颗 epoll 树
        _netThreads.push_back(netThreads);
    }
}

定义的时候用的是指针,是多个NetThread对应一个TC_EpollServer对象. 看看NetThread的构造函数就知道了.

class NetThread : public TC_Thread, public TC_HandleBase
{
....
private:
    TC_EpollServer  *_epollServer; // 服务
    TC_Epoller       _epoller; // epoll
}

看看NetThread的构造

TC_EpollServer::NetThread::NetThread(TC_EpollServer *epollServer, int threadIndex)
: _epollServer(epollServer)
, _threadIndex(threadIndex)
, _bTerminate(false)
, _list(this)
, _bEmptyConnAttackCheck(false)
, _iEmptyCheckTimeout(MIN_EMPTY_CONN_TIMEOUT)
, _nUdpRecvBufferSize(DEFAULT_RECV_BUFFERSIZE)
{
    /*
    TC_Epoller _epoller;
    TC_Epoller::NotifyInfo  _notify; (NotifyInfo是在TC_Epoller中声明的类类型)
    */
    _epoller.create(10240);// 相当于C语言中做了epoll_create(intsize),创建epoll句柄
    _notify.init(&_epoller); // 实际上这步做了创建socket的工作,核心步骤是TC_Socket::createSocket中的socket(iDomain, iSocketType, 0);
    _notify.add(_notify.notifyFd());
}

3.5 业务处理线程详细分析

3.5.1 相关类的设计

ServantHandle类的结构链接,链接

3.5.2 流程

代码关键字搜索:

class ServantHandle

ServantHandle::run()

3.6 队列设计总结

【send_queue】-【recv_queue】,【1.21】,【1.22】

3.6.1 发送队列

typedef TC_ThreadQueue<shared_ptr<SendContext>> send_queue; // 发送队列

发送队列,send_queue _sbuffer;--发送队列,在D:\005-02-代码\016-TARS\TARS\TarsFramework\tarscpp\util\include\util\tc_epoll_server.h文件中的NetThread类中

NetThread类中有发送队列,搜索 TARS基金会CPP服务器文章链接的关键词“发送队列”

3.6.2 接受队列

typedef TC_ThreadQueue<shared_ptr<RecvContext>> recv_queue; // 接受队列

// 数据队列
struct DataQueue
{
			recv_queue      _rbuffer;// 接收的数据队列
			TC_ThreadLock   _monitor;// 锁
};
 
/**
* 每个线程都有自己的队列
* 0: 给共享队列模式时使用
* 1~handle个数: 队列模式时使用
* Every thread has its own queue.
* 0: Use when sharing queue mode
* 1~handle count: Use when queue mode
*/
 
class BindAdapter : public TC_HandleBase{
...
vector<shared_ptr<DataQueue>> _threadDataQueue;
...
}
 

3.7 网络线程及业务线程总结

【TARS】RegistryServer网络模块及业务处理模块分析

4. 问题集锦

4.1 Program received signal SIGPIPE, Broken pipe.啥意思?

是因为线程切换吗??并不是每一次线程切换都会报Program received signal SIGPIPE, Broken pipe

4.2 每一个注册的Servant中的rpc接口是如何被调用的呢?

4.2.1 引入

4.2.2  tars.tarsregistry.RegistryObj和tars.tarsregistry.QueryObj的RPC接口如何注册和被调用的?

D:\005-02-代码\016-TARS\TARS\TarsFramework\RegistryServer\RegistryImp.cpp 

D:\005-02-代码\016-TARS\TARS\TarsFramework\RegistryServer\RegistryImp.h

tars.tarsregistry.RegistryObj中提供给node调用的接口类是如何注册又是如何被调用的呢?

D:\005-02-代码\016-TARS\TARS\TarsFramework\RegistryServer\QueryImp.cpp

D:\005-02-代码\016-TARS\TARS\TarsFramework\RegistryServer\QueryImp.h

tars.tarsregistry.QueryObj这个Servant的RPC接口如何注册又是如何被调用的呢?

4.2.3 addServant<RegistryImp>((*g_pconf)["/tars/objname<RegistryObjName>"]);这一步执行的时候是否创建了一个RegistryImp的对象?

此处没有创建,调用的时候创建,性能有损耗.(这个理解对吗?)

vim /home/muten/module/TARS/TarsFramework/tarscpp/servant/servant/ServantHelper.h
gdb -q /home/muten/module/TARS/TarsFramework/build/servers/tarsregistry/bin/tarsregistry
>set args --config=/usr/local/app/tars/tarsregistry/conf/tars.tarsregistry.config.conf
>b ServantHelper.h:48 
>b RegistryServer.cpp:98

vim /home/muten/module/TARS/TarsFramework/tarscpp/servant/servant/ServantHelper.h
gdb -q /home/muten/module/TARS/TarsFramework/build/servers/tarsregistry/bin/tarsregistry
>set args --config=/usr/local/app/tars/tarsregistry/conf/tars.tarsregistry.config.conf
>source /home/muten/stl-views-1.0.3.gdb
>pmap  _servantHelper->_servant_creator string string

vim /home/muten/module/TARS/TarsFramework/tarscpp/servant/libservant/Application.cpp

vim /home/muten/module/TARS/TarsFramework/RegistryServer/RegistryServer.cpp

4.2.4 答案与反射性能的思考

答案是反射,链接一篇关于反射的性能思考的文章:JAVA中的反射基本入门-直接跳去看缺点

JAVA中的反射的,不知道TARS在这块的性能会不会因为反射机制的存在而有所损耗,需要继续探索.

查看TARS基金会的那篇关于TARSC++服务器端的介绍,点击链接在【业务模块的初始化】这一节或者搜索“反射”,可以找到答案.

如何让业务线程能够调用用户自定义的代码?这里引入了ServantHelperManager,先简单剧透一下,通过ServantHelperManager作为桥梁,

业务线程可以通过BindAdapter的ID索引到服务ID,然后通过服务ID索引到用户自定义的XXXServantImp类的生成器,有了生成器,业务线程

就可以生成XXXServantImp类并调用里面的方法了.

下面一步一步分析。

在Application::main()调用的Application::bindAdapter()中看到有下面的代码:

for (size_t i = 0; i < adapterName.size(); i++)
{
	……
     string servant = _conf.get("/tars/application/server/" + adapterName[i] + "<servant>");
     checkServantNameValid(servant, sPrefix);
 
     ServantHelperManager::getInstance()->setAdapterServant(adapterName[i], servant);
	……
}

举个例子,adapterNamei为MyDemo.StringServer.StringServantAdapter,而servant为MyDemo.StringServer.StringServantObj,这些都是在配置文件中读取的,前者是BindAdapter的ID,而后者是服务ID。在ServantHelperManager:: setAdapterServant()中,仅仅是执行:
 

void ServantHelperManager::setAdapterServant(const string &sAdapter, const string &sServant)
{
    _adapter_servant[sAdapter] = sServant;
    _servant_adapter[sServant] = sAdapter;
}

而这两个成员变量仅仅是:

    /**
     * Adapter包含的Servant(Adapter名称:servant名称)
     */
    map<string, string>                     _adapter_servant;
 
    /**
     * Adapter包含的Servant(Servant名称:Adapter名称)
     */
     map<string, string>                     _servant_adapter;

在这里仅仅是作一个映射记录,后续可以通过BindAdapter的ID可以索引到服务的ID,通过服务的ID可以利用简单的C++反射得出用户实现的XXXServantImp类,

从而得到用户实现的方法. 如何实现从服务ID到类的反射?(关注重点)同样需要通过ServantHelperManager的帮助.在Application::main()中, 执行完Application::bindAdapter()

会执行initialize(),这是一个纯虚函数,实际会执行派生类XXXServer的函数,类似:
 

void
StringServer::initialize()
{
    //initialize application here:
    //...
    addServant<StringServantImp>(ServerConfig::Application + "." + ServerConfig::ServerName + ".StringServantObj");
}

代码最终会执行ServantHelperManager:: addServant():

    template<typename T>
    void addServant(const string &id,bool check = false)
    {
        if(check && _servant_adapter.end() == _servant_adapter.find(id))
        {
            cerr<<"[TARS]ServantHelperManager::addServant "<< id <<" not find adapter.(maybe not conf in the web)"<<endl;
            throw runtime_error("[TARS]ServantHelperManager::addServant " + id + " not find adapter.(maybe not conf in the web)");
        }
        _servant_creator[id] = new ServantCreation<T>();
}

其中参数const string& id是服务ID,例如上文的MyDemo.StringServer.StringServantObj,T是用户填充实现的XXXServantImp类。

上面代码的_servant_creatorid = new ServantCreation()是函数的关键,_servant_creator是map<string, ServantHelperCreationPtr>,

可以通过服务ID索引到ServantHelperCreationPtr,而ServantHelperCreationPtr是什么?(重点关注)

是帮助我们生成XXXServantImp实例的类生成器,这就是简单的C++反射:

/**
 * Servant
 */
class ServantHelperCreation : public TC_HandleBase
{
public:
    virtual ServantPtr create(const string &s) = 0;
};
typedef TC_AutoPtr<ServantHelperCreation> ServantHelperCreationPtr;
 
//
/**
 * Servant
 */
template<class T>
struct ServantCreation : public ServantHelperCreation
{
    ServantPtr create(const string &s) { T *p = new T; p->setName(s); return p; }
};

以上就是通过服务ID生成相应XXXServantImp类的简单反射技术,业务线程组里面的业务线程只需要获取到所需执行的业务的BindAdapter的ID,

就可以通过ServantHelperManager获得服务ID,有了服务ID就可以获取XXXServantImp类的生成器从而生成XXXServantImp类执行里面由用户

定义好的RPC方法.

4.2.5 辅助理解的图

Adapter的名字   Servant的名字 Servant的实现类名 代码快速索引 源码路径
AdminAdapter AdminObj AdminServant _servantHelper->addServant<AdminServant>("AdminObj", this); *TarsFramework\tarscpp\servant\servant\AdminServant.h
tars.tarsregistry.QueryObjAdapter tars.tarsregistry.QueryObj QueryImp addServant<QueryImp>((*g_pconf)["/tars/objname<QueryObjName>"]) *TarsFramework\RegistryServer\QueryImp.h
tars.tarsregistry.RegistryObjAdapter tars.tarsregistry.RegistryObj RegistryImp addServant<RegistryImp>((*g_pconf)["/tars/objname<RegistryObjName>"]) *TarsFramework\RegistryServer\RegistryImp.h


4.5 打印_mapServantEndpoint的信息

vim /home/muten/module/TARS/TarsFramework/RegistryServer/RegistryServer.cpp

gdb -q /home/muten/module/TARS/TarsFramework/build/servers/tarsregistry/bin/tarsregistry
>set args --config=/usr/local/app/tars/tarsregistry/conf/tars.tarsregistry.config.conf
>source /home/muten/stl-views-1.0.3.gdb

>b RegistryServer.cpp:66
>pmap pmap _mapServantEndpoint string string

(gdb) source /home/muten/stl-views-1.0.3.gdb
(gdb) pmap _mapServantEndpoint string string 
elem[0].left: $1 = "AdminObj"
elem[0].right: $2 = "tcp -h 127.0.0.1 -p 17890 -t 3000"
elem[1].left: $3 = "tars.tarsregistry.QueryObj"
elem[1].right: $4 = "tcp -h 192.168.118.138 -p 17890 -t 10000"
elem[2].left: $5 = "tars.tarsregistry.RegistryObj"
elem[2].right: $6 = "tcp -h 192.168.118.138 -p 17891 -t 30000"
Map size = 3

4.3 基类pmap _procFunctors包含的内容是什么?functor如何能够正确打印?

# _procFunctors : map<string, TAdminFunc>

gdb -q /home/muten/module/TARS/TarsFramework/build/servers/tarsregistry/bin/tarsregistry
>set args --config=/usr/local/app/tars/tarsregistry/conf/tars.tarsregistry.config.conf
>source /home/muten/stl-views-1.0.3.gdb

>b Application.cpp:199

>pmap _procFunctors string string

>l main.cpp:80,main.cpp:120

4.4 如何理解Servant?

猜你喜欢

转载自blog.csdn.net/Edidaughter/article/details/114661646