目录
2.2.2 Application.cpp中initializeServer产生7个线程
2.2.4 第21-35个线程,退出三个线程,新加入15个线程,至此共32个线程
2.3.2 Application::initializeServer中产生2-8的线程
2.3.4 RegistryServer::initialize()中产生10-20的线程
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.1 设置waitForShutdown线程发送心跳的函数
4.1 Program received signal SIGPIPE, Broken pipe.啥意思?
4.2 每一个注册的Servant中的rpc接口是如何被调用的呢?
4.2.2 tars.tarsregistry.RegistryObj和tars.tarsregistry.QueryObj的RPC接口如何注册和被调用的?
4.2.3 addServant((*g_pconf)["/tars/objname"]);这一步执行的时候是否创建了一个RegistryImp的对象?
4.3 基类pmap _procFunctors包含的内容是什么?functor如何能够正确打印?
0.说明
本文基于框架版本为2.4.14
与本文强关联文章:
TARS基金会CSDN-微服务开源框架TARS的RPC源码解析 之 初识TARS C++服务端
我的系列文章:
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()
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