作者 | Eaton
导语 | 服务上线运营后,出现异常是难免的事,通常情况下会通过服务日志排查问题。然而这种排查问题的方式有时候是低效的,特别是日志比较多,还不知道如何下手的时候,非常麻烦。那让服务主动报告出现的错误不就行了吗?本文将对 TARS 中的几种信息上报方式进行介绍。
目录
- 简介
- 状态统计上报
- 异常上报
- 属性统计上报
- 总结
简介
TARS 框架中集成了服务信息上报的功能,包含了服务的状态统计上报、异常上报、属性统计上报等三个信息上报统计方式,实现对服务健康程度的多方面监控。这三种信息上报方式分别通过 Stat、Notify、Property 这三个节点实现。如下图
服务通过向这三个节点报告自身不同维度的信息,实现对服务状态的监控。那么接下来我们来了解这三种信息上报方式吧。
状态统计上报
所谓的状态统计上报,就是在 TARS 框架中,服务向 Stat
上报自身的调用耗时、超时率、异常率等状态信息并进行统计。
服务调用上报接口后,实际上是先暂存在内存中,当到达某个时间点后才正式上报到 Stat
服务(默认是1分钟上报一次)。我们称两个上报时间点之间的时间为一个统计区间,在一个统计区间相同 key 进行累加、对比等操作。
状态统计上报一般不需用户额外开发,服务在 TARS 框架中正确配置并部署后,即可自动上报。
打开 TarsWeb 的服务管理页面,在具体服务的 服务监控
页面中可以看到对应服务相关的状态信息,包括流量、平均耗时、超时率等,如下图。
异常上报
服务状态统计上报能够直观地了解服务的状态和健康度。不过实际使用场景中,仅统计一些服务的调用信息是不够的。为了更好监控,TARS 框架支持在服务中将异常直接上报到 Notify,并可以在 TarsWeb 管理页面上查看到,或结合别的告警软件或平台直接通知用户。TARS 不同语言版本中都提供了异常上报方式。本部分将分别基于 TarsCpp 和 TarsGo 举例介绍,其他语言版本类似。
TarsCpp
TarsCpp 中提供了一种异常上报方式,即使用 RemoteNotify::report
来上报异常,用法如下
RemoteNotify::getInstance()->report(info);
参数 info
为需要上报的异常信息,类型为 string
字符串, 可以直接上报字符串到 Notify,页面可以看到上报的字符串。例如我们创建一个服务名为 Demo.DemoServer.DemoObj
的 TarsCpp 服务,创建命令如下
/usr/local/tars/cpp/script/cmake_tars_server.sh TestDemo DemoServer Demo
项目目录结构如下
DemoServer
├── build # 构建目录
├── CMakeLists.txt # cmake 构建文件
└── src # 源文件目录
├── CMakeLists.txt
├── Demo.h # Demo.tars 生成文件
├── DemoImp.cpp # 接口实现文件
├── DemoImp.h # 接口实现头文件
├── DemoServer.cpp # 服务实现文件
├── DemoServer.h # 服务实现头文件
└── Demo.tars # tars 接口定义文件
接着在 DemoServer.cpp
的服务初始化函数 DemoServer::initialize
中上报一条信息,这样服务在启动时,就会上报一条 DemoServer Start
的信息,如下
void
DemoServer::initialize()
{
addServant<DemoImp>(ServerConfig::Application + "." + ServerConfig::ServerName + ".DemoObj");
// 上报信息
RemoteNotify::getInstance()->report("DemoServer Start");
}
将服务编译部署后,可以在 TarsWeb 上服务的实时状态中看到服务上报的信息,如下图
TarsGo
TarsGo 中提供了如下函数来上报异常信息,用法和前面 TarsCpp 的相似
func ReportNotifyInfo(level int32, info string)
level
指的是异常等级,分为 NOTIFY_NORMAL
, NOTIFY_WARN
, NOTIFY_ERROR
,info
则为上报的信息。
我们通过以下命令创建一个 TarsGo 服务 Demo.NotifyDemo.DemoObj
$GOPATH/src/github.com/TarsCloud/TarsGo/tars/tools/cmake_tars_server.sh Demo NotifyDemo Demo github.com/ETZhangSX/NotifyDemo
项目目录结构如下
NotifyDemo
├── build
├── client
│ ├── client.go
│ └── CMakeLists.txt
├── CMakeLists.txt
├── config.conf
├── debugtool
│ └── dumpstack.go
├── demo_imp.go
├── Demo.tars
├── go.mod
├── go.sum
├── main.go
├── start.sh
└── tars-protocol
同 TarsCpp 相似,我们在 demo_imp.cpp
的 Init
中添加异常上报
func (imp *DemoImp) Init() (error) {
tars.ReportNotifyInfo(tars.NOTIFY_ERROR, "ssart")
return nil
}
构建部署该服务后,同样可以在服务实时状态中看到
可见,异常上报是一个主动上报的过程,开发者可以通过异常上报,主动上报服务错误,比如在 try...catch
捕捉到错误时,进行上报。
属性统计上报
除了状态统计上报和异常上报,TARS 中也提供了属性统计的功能,开发者能够上报业务相关属性并进行统计。为了方便业务使用,TARS 目前自带了以下几种统计类型:
- 求和(sum)
- 平均(avg)
- 分布(distr)
- 最大值(max)
- 最小值(min)
- 计数(count)
TarsCpp 中,可以通过 createPropertyReport()
来创建并配置一个属性上报对象,并通过调用该对象的方法 report
来上报属性值。例如,我们想监控一个数组大小的最大值,我们创建一个属性名为 array_size
的属性上报对象,并配置上报方法为 max
,即最大值,可以通过如下步骤实现。
PropertyReportPtr reportPtr = Application::getCommunicator()
->getStatReport()
->createPropertyReport("array_size", PropertyReport::max());
// vector<int> a;
reportPtr->report(a.size());
接下来我们以一个 C++ 实现的简单队列服务为例,该服务包含两个队列操作接口
pop
: 用于弹出队列最前端的数字push
: 用于添加一个数字到队列中
并对服务中队列的大小进行属性统计上报。
首先,我们新建一个服务名为 Demo.PropertyDemo.TestObj
的服务,并新建文件 Queue.h
,项目结构如下
PropertyDemo
├── build
├── CMakeLists.txt
└── src
├── CMakeLists.txt
├── PropertyDemo.cpp
├── PropertyDemo.h
├── Queue.h
├── Test.h
├── TestImp.cpp
├── TestImp.h
└── Test.tars
在 Queue.h
中实现一个简单的线程安全队列类,实现如下
#ifndef _QUEUE_H_
#define _QUEUE_H_
#include "util/tc_singleton.h"
#include "util/tc_thread_rwlock.h"
class Queue : public tars::TC_Singleton<Queue>
{
public:
void Push(int value)
{
TC_ThreadWLock wlock(rw_locker_);
q_.push(value);
}
void Pop()
{
TC_ThreadWLock wlock(rw_locker_);
if (!q_.empty()) q_.pop();
}
int GetSize()
{
TC_ThreadRLock rlock(rw_locker_);
return q_.size();
}
private:
std::queue<int> q_;
tars::TC_ThreadRWLocker rw_locker_; // 读写锁
};
#endif
可以看到 Queue
继承自 TC_Singleton
,TC_Singleton
是 TarsCpp 中提供的一个单件类组件,通过继承该类,使 Queue
成为单件类。
修改 Test.tars
,我们新增两个接口 pop
, push
用于操作服务的队列。如下
module Demo
{
interface Test
{
int test();
int push(int value);
int pop();
};
};
然后在 TestImp.h
中添加接口的声明,如下
virtual int push(int value, tars::TarsCurrentPtr current);
virtual int pop(tars::TarsCurrentPtr current);
并在 TestImp.cpp
中实现这两个接口,如下
#include "Queue.h"
...
int TestImp::push(int value, tars::TarsCurrentPtr current)
{
Queue::getInstance()->Push(value);
return 0;
}
int TestImp::pop(tars::TarsCurrentPtr current)
{
Queue::getInstance()->Pop();
return 0;
}
最后,在 PropertyDemo.cpp
中,添加对队列大小的上报,如下
#include "Queue.h"
...
void *reportFunc(void *pArg)
{
static PropertyReportPtr reportPtr = NULL;
// 初始化分布数据范围
vector<int> v;
v.push_back(10);
v.push_back(30);
v.push_back(50);
v.push_back(80);
v.push_back(100);
// 创建queuelength属性,该属性用到了所有的集中统计方式,注意distrv的初始化
reportPtr = Application::getCommunicator()
->getStatReport()
->createPropertyReport("queuelength",
PropertyReport::sum(),
PropertyReport::avg(),
PropertyReport::count(),
PropertyReport::max(),
PropertyReport::min(),
PropertyReport::distr(v));
// 定时上报
while (1)
{
// 上报的属性仅支持整型
reportPtr->report(Queue::getInstance()->GetSize());
sleep(1);
}
return NULL;
}
int
main(int argc, char* argv[])
{
try
{
pthread_t hThread;
g_app.main(argc, argv);
// 创建一个线程运行reportFunc,上报属性信息
pthread_create(&hThread, NULL, reportFunc, NULL);
g_app.waitForShutdown();
}
...
}
在 reportFunc
中,我们创建了一个属性 queuelength
的上报对象 reportPtr
,添加上述的六种统计策略,并定时上报;然后在 main
函数中创建一个线程来运行 reportFunc
。
构建并部署服务后,我们就可以在 TarsWeb 页面服务的特性监控中看到属性的统计值了,如下图
如果看不到统计信息可以间隔5分钟,监控信息每5分钟同步一次。
从上图中,可以看到六种统计策略的值,分别是 Queue
中队列大小的求和、最小值、最大值、分布、计数、平均值。通过调用服务的接口 pop
和 push
,即向Queue队列中添加或弹出对象,改变队列大小,这些值也会相应的变化。
我们可以根据自身业务需求选择策略,比如在流量统计方面可以使用求和的方式。
总结
本文对 TARS 三种信息上报方式及如何使用进行了介绍。通过这三种服务信息上报方式,开发者能够对服务进行多维度的监控,了解服务和业务实时健康状态、异常信息及业务相关属性,帮助开发者更好地管理服务。
TARS 可以在考虑到易用性和高性能的同时快速构建系统并自动生成代码,帮助开发人员和企业以微服务的方式快速构建自己稳定可靠的分布式应用,从而令开发人员只关注业务逻辑,提高运营效率。多语言、敏捷研发、高可用和高效运营的特性使 TARS 成为企业级产品。
TARS微服务助您数字化转型,欢迎访问:
TARS官网:https://TarsCloud.org
TARS源码:https://github.com/TarsCloud
Linux基金会官方微服务免费课程:https://www.edx.org/course/building-microservice-platforms-with-tars
获取《TARS官方培训电子书》:https://wj.qq.com/s2/6570357/3adb/
或扫码获取: