目录

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服务端(二):网络io模型和线程模型

【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 17890sync-invoke-timeout=10000async-invoke-timeout=60000refresh-endpoint-interval=60000stat=tars.tarsstat.StatObjproperty=tars.tarsproperty.PropertyObjreport-interval=60000asyncthread=3modulename=tars.tarsregistrytimeout-queue-size=100</client><server>app=tarsserver=tarsregistrylocalip=192.168.118.138basepath=/usr/local/app/tars/tarsregistry/bindatapath=/usr/local/app/tars/tarsregistry/datanetthread=2logpath=/usr/local/app/tars/app_loglogsize=10Mconfig=tars.tarsconfig.ConfigObjdeactivating-timeout=2000logLevel=DEBUGnotify=tars.tarsnotify.NotifyObjlocal=tcp -h 127.0.0.1 -p 17890 -t 3000node=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 10000allowmaxconns=100000threads=5queuecap=100000queuetimeout=6000servant=tars.tarsregistry.QueryObj</tars.tarsregistry.QueryObjAdapter><tars.tarsregistry.RegistryObjAdapter>endpoint=tcp -h 192.168.118.138 -p 17891 -t 30000allowmaxconns=100000threads=5queuecap=100000queuetimeout=6000servant=tars.tarsregistry.RegistryObj</tars.tarsregistry.RegistryObjAdapter></server></application><db>dbhost=192.168.118.138dbname=db_tarsdbuser=tarsAdmindbpass=Tars@2019dbport=3306charset=utf8dbflag=CLIENT_MULTI_STATEMENTS</db><reap>loadObjectsInterval=30queryInterval=150loadObjectsInterval1=13LeastChangedTime1=600loadObjectsInterval2=3601nodeTimeout=250registryTimeout=150querylesttime=300asyncthread=6openDayLog=N</reap><objname>patchServerObj=tars.tarspatch.PatchObjQueryObjName=tars.tarsregistry.QueryObjRegistryObjName=tars.tarsregistry.RegistryObj</objname><objcache>min_block=50max_block=200factor=1.2FilePath=/usr/local/app/tars/tarsregistry/util/objectCache.datFileSize=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);
#endifif(_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网络模块及业务处理模块分析

NetThread: 收发包,连接管理,多线程(可配置),采用epoll ET触发(边沿触发)实现,支持tcp/udp;

ET和LT

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?

【TARS】理解RegistryServer相关推荐

  1. 【TARS】基于TARS的调试

    ref ref2 man gdb GDB调试手册 GDB调试动态库 GDB如何调试动态库2 Linux下的动态库和静态库 DWARF DWARF官网安装包 DWARF官网的安装路径 开源中国提供的DW ...

  2. Soul网关发布里程碑的2.3.0版本,新增支持GRPC,Tars,Sofa协议

    距离上一次发布长达半年之久,在这半年的时间里,我与我的社区小伙伴们,做了太多太多的事情.完成了将近200 多次PR,发表了将近300 篇文章的源码解析,新增贡献者 120 多位,晋升了 7位commi ...

  3. 深入理解 RPC : 基于 Python 自建分布式高并发 RPC 服务

    RPC(Remote Procedure Call)服务,也即远程过程调用,在互联网企业技术架构中占据了举足轻重的地位,尤其在当下微服务化逐步成为大中型分布式系统架构的主流背景下,RPC 更扮演了重要 ...

  4. 面试精讲之面试考点及大厂真题 - 分布式专栏 05 公司使用什么RPC框架,聊聊你理解的RPC原理

    05 公司使用什么RPC框架,聊聊你理解的RPC原理 引言 前些年我们在做一个规模不大的系统的时候,也就是单体架构,一台服务器部署上一个应用和数据库也就够了.但是现代化互联网公司业务逐渐扩大,服务逐渐 ...

  5. 《深入理解分布式事务》第八章 TCC 分布式事务原理

    <深入理解分布式事务>第八章 TCC 分布式事务原理 文章目录 <深入理解分布式事务>第八章 TCC 分布式事务原理 一.TCC 核心思想 二.TCC 实现原理 1.TCC 核 ...

  6. 总结:云原生架构理解

    一. 为什么需要云原生架构? 企业内部 IT 建设如果都基于最底层 IDC 设施独自向上构建,都需要单独分配硬件资源,这就造成资源被大量占用且难以被共享. 但是上云之后,由于云厂商提供了统一的 Iaa ...

  7. 来!带你深入理解分布式事务:原理与实战!

    随着互联网的不断发展,互联网企业的业务在飞速变化,推动着系统架构也在不断地发生变化.总体来说,系统架构大致经历了 单体应用架构→垂直应用架构→分布式架构→SOA架构→微服务架构的演变. 如今微服务技术 ...

  8. 腾讯与阅文技术合作 微服务框架Tars再添PHP

    引言 TARS作为由腾讯公司开源的优秀RPC框架与服务部署运维解决方案,被阅文集团引入了实际实践中,同时阅文集团对TARS在PHP语言层面进行了能力的补全,令TARS如虎添翼.TARS-PHP的解决方 ...

  9. 【新书速递】分布式事务开山之作,带你深入理解分布式事务

    随着互联网的不断发展,互联网企业的业务在飞速变化,推动着系统架构也在不断地发生变化.总体来说,系统架构大致经历了 单体应用架构→垂直应用架构→分布式架构→SOA架构→微服务架构的演变.如今微服务技术越 ...

最新文章

  1. IOS开发-地图 (mapkit)实验
  2. 对于url出现jsessionid问题
  3. ansible service模块使用示例
  4. NEUQ 2015: Bitmap(二维hash)
  5. 吃鸡服务器8月10日维护,《黑潮之上》2021年8月10日不停服维护公告
  6. 【C++学习笔记一】C++类和对象详解
  7. Openbiz Cubi 企业级应用程序开发(一)
  8. tm是什么域名_天猫入驻条件门槛是什么意思?企业入驻天猫详细解析
  9. 【转】查看linux服务器的系统信息
  10. pip安装pytorch(CPU)附上whl文件
  11. Linux下mysql数据库的自动备份与还原 远程备份和本地备份
  12. php企鹅号_腾讯内容开放平台
  13. DiscuzNT 交易插件设计之商品添加,编辑和删除(CUD)
  14. 数据中心常说的IDC,EDC,ODC,DC分别指什么类型机房?
  15. 计算机毕业设计ssm电影售票管理系统n9y72系统+程序+源码+lw+远程部署
  16. 无尘间手把手教你西数开盘
  17. python脚本实现GNSS数据自动下载
  18. 删除的微信聊天记录怎么恢复?高手指导还原教程,99%的人用了都说好
  19. HTML学生个人网站作业设计:电影网站设计——叮当电影(5页) HTML+CSS+JavaScript 简单DIV布局个人介绍网页模板代码 DW学生个人网站制作成品下载
  20. 在 Jenkins 上轻松重用 Tekton 和 Jenkins X

热门文章

  1. scp免密登录,同时也适应ssh免密登录
  2. Ubuntu下工作空间的创立以及思岚系列激光雷达的使用(详细)和驱动安装及地图创建
  3. 2020年,急需提及的十大最受欢迎的编程语言
  4. Kotlin基础学习 09
  5. 将大数据运用于投资,结果会如何?
  6. WebStorm去掉编辑区的竖线
  7. java文件下载框架,使用Struts 2框架实现文件下载 - 消逝の纸屑
  8. Adobe注销账户登录
  9. python安装模块方法_python安装模块方法汇总
  10. timer 在滚动的时候停止了的解决办法