tars开源框架地址:https://github.com/Tencent/Tars
系列文章:
鹅厂开源框架tars之日志服务
鹅厂开源框架tars之运营监控服务
鹅厂开源框架tars之基础组件
鹅厂开源框架tars之网络层实现
简介:Tars是腾讯从2008年到今天一直在使用的后台逻辑层的统一应用框架TAF(Total Application Framework),目前支持C++,Java,PHP,Nodejs语言。该框架为用户提供了涉及到开发、运维、以及测试的一整套解决方案,帮助一个产品或者服务快速开发、部署、测试、上线。 它集可扩展协议编解码、高性能RPC通信框架、名字路由与发现、发布监控、日志统计、配置管理等于一体,通过它可以快速用微服务的方式构建自己的稳定可靠的分布式应用,并实现完整有效的服务治理。目前该框架在腾讯内部,各大核心业务都在使用,颇受欢迎,基于该框架部署运行的服务节点规模达到上万个。
一、运维工具
如上图:可以清楚看到tar框架的运维服务和工具:包括主控Resistry、配置文件中心Config、异常信息Nofify、指标统计Stat、远程log、发布平台Patch、业务信息Property等主要服务,再结合web平台来对这些服务进行可视化操作和运用,对开发和运维人员算是非常方便和人性化了
二、配置中心 config 服务
config服务用于提供整套框架的配置文件保存和读取等操作;后台使用mysql存储
使用例子:
addAppConfig("DBConnection.conf"); //添加应用级别的配置文件
调用TafRemoteConfig::getInstance()->addConfig(filename, result, true)函数:
addConfig函数调用getRemoteFile函数远程从config server获取配置文件信息:
rpc调用:_configPrx->loadConfig(_sApp, (bAppConfigOnly ? "" : _sServerName), sFileName, stream);
跳转到:Config server 的接口函数ConfigImp::loadConfig 函数,如果是应用级别的配置文件:则执行“”select id,config from t_config_files...“” 并且把结果使用string的方式回传给调用者
调用者获取到文件内容,根据文件的名字生成在本地以供后续程序使用文件:std::ofstream out(newFile.c_str());
addConfig(ServerConfig::ServerName + ".conf"); //添加当前进程所在的本地文件
调用:TafRemoteConfig::getInstance()->addConfig(filename, result, false) 注意:最后一个参数bAppConfigOnly设置为false,
调用int ret = _configPrx->loadConfig(_sApp, (bAppConfigOnly ? "" : _sServerName), sFileName, stream); 可见和加载应用级别的配置文件有所不同,这里调用_configPrx服务器的参数,增加了_sServerName。则rpc调用loadConfig接口的时候服务器参数不为空,改掉用loadConfigByHost:同样通过mysql查询t_config_files表格但增加了where查询条件"where server_name = '" + _mysqlConfig.escapeString(appServerName)。至此,实现了从指定服务器拉取配置文件的功能 (这里还关系到引用配置等功能,多个同名文件还涉及到文件合并等,这里先不做详细说明)
三、Nofify服务,官方文档定义为异常信息。输出的信息可以在tars运营平台里面看到
TafRemoteNotify::getInstance()->report("[info]: white list open for ["+ _sUinList + "]");
源码实现:
if(_notifyPrx)
{
if(!bSync)
{
_notifyPrx->async_reportServer(NULL, _sApp + "." + _sServerName, TC_Common::tostr(pthread_self()), sResult);
}
else
{
_notifyPrx->reportServer(_sApp + "." + _sServerName, TC_Common::tostr(pthread_self()), sResult);
}
}
Nofify 服务实现,主要就是把数据插入t_server_notifys_数据库。这里默认都是使用异步的接口,否则估计效率容易有问题,毕竟是直接操作db
string sql;
TC_Mysql::RECORD_DATA rd;
rd["server_name"] = make_pair(TC_Mysql::DB_STR, sServerName);
rd["server_id"] = make_pair(TC_Mysql::DB_STR, sServerName + "_" + current->getIp());
rd["thread_id"] = make_pair(TC_Mysql::DB_STR, sThreadId);
rd["result"] = make_pair(TC_Mysql::DB_STR, sResult);
rd["notifytime"] = make_pair(TC_Mysql::DB_INT, "now()");
string sTable = "t_server_notifys_" + TC_Common::tm2str(TNOW,"%Y%m%d");
try
{
_mysqlConfig.insertRecord(sTable, rd);
四、Stat服务——性能指标统计
服务性能数据采集:调用时间、成功次数、超时次数、异常次数、耗时分布等信息。服务间调用关系链采样。
Stat服务接口:reportProxyMicMsg:udp上报模块间调用采样信息
五、Property —业务指标统计
/**
* 上报属性信息
* @param statmsg, 上报信息
* @return int, 返回0表示成功
*/
virtual int reportPropMsg(const map
六、Registry ——主控服务:
提供TAF框架核心管理功能 :服务部署、服务起停、服务状态信息查询、发布、配置管理、命令通知
Registry 提供registerNode(node节点注册)、keepAlive(node上报心跳负载)、getServers(获取在该node部署的server列表)、updateServer(更新server状态)、
同时主控提供了AdminRegImp(关联控制接口类),提供如下接口:
getAllApplicationNames(获取application列表)、getAllNodeNames(获取node列表)、shutdownNode(停止 node)、getServerState(获取特定server状态)、startServer(启动特定server)、restartServer( 重启特定server)、notifyServer(通知服务)、patchServer(发布特定server)
例子:startServer接口实现
//更新数据库server的设置状态
_db.updateServerState(application, serverName, nodeName, "setting_state", taf::Active);
判断是否为dns:
DNS: 通过db修改
iRet = _db.updateServerState(application, serverName, nodeName, "present_state", taf::Active);
非DNS:通过node节点启动(下面介绍node节点的作用)
NodePrx nodePrx = _db.getNodePrx(nodeName);
iRet = nodePrx->startServer(application, serverName, result);
七、Node ——服务节点:
同一服务器上的服务监控,异常退出、僵死等监控重启
node提供的接口:
patch(patch指定服务)、stopAllServers(关闭nodes上所有服务)、
startServer(启动指定服务):
使用sFullExeFileName //默认使用启动脚本路径 stopServer接口也是同理
getState获取指定服务状态:
ServerObjectPtr pServerObjectPtr = ServerFactory::getInstance()->getServer( application, serverName );
if ( pServerObjectPtr )
{
result += "succ";
return pServerObjectPtr->getState();
}
八、patch服务
——统一发布
listFileInfo(获取路径下所有文件列表信息)、download(下载文件)
九、Taf-Log ——日志中心
日志服务器,用于接收远程日志。业务服务以框架层的api异步发送日志到日志服务器
例子:
#define LOG_STAT(type) FDLOG(type)
其中FDLOG的定义在库文件的taf_logger.h定义如下:
#define FDLOG(x) (TafTimeLogger::getInstance()->logger(x)->any())
TafTimeLogger::getInstance()->logger(x)函数生成并返回:TimeLogger
typedef TC_Logger
typedef TC_Logger
TafTimeLogger::getInstance()->setLogInfo(_communicator, ServerConfig::Log, ServerConfig::Application, ServerConfig::ServerName, ServerConfig::LogPath);
这里底层实现函数如下:会获取远程日志服务器的地址
具体写日志的时候,TimeWriteT类重载了operator() ,会调用远程日志服务的logger接口写远程日志,从而实现了日志从本地到远程日志服务器的功能
_logPrx->logger(DYEING_DIR, DYEING_FILE, "day", "%Y%m%d", vDyeingLog);
十、WEB — 管理平台