使用指南
框架使用指南
服务端开发
下面通过一个完整的 hello world 例子介绍如何实现自己的服务。
依赖配置
在构建项目pom.xml中添加依赖 jar 包
- 框架依赖配置
<dependency>
<groupId>com.tencent.tars</groupId>
<artifactId>tars-server</artifactId>
<version>1.7.2</version>
<type>jar</type>
</dependency>
- 插件依赖配置
<plugin>
<groupId>com.tencent.tars</groupId>
<artifactId>tars-maven-plugin</artifactId>
<version>1.6.1</version>
<configuration>
<tars2JavaConfig>
<tarsFiles>
<tarsFile>${basedir}/src/main/resources/hello.tars</tarsFile>
</tarsFiles>
<tarsFileCharset>UTF-8</tarsFileCharset>
<servant>true</servant>
<srcPath>${basedir}/src/main/java</srcPath>
<charset>UTF-8</charset>
<packagePrefixName>com.qq.tars.quickstart.server.</packagePrefixName>
</tars2JavaConfig>
</configuration>
</plugin>
接口文件定义
接口文件定义是通过 Tars 接口描述语言来定义,在 src/main/resources 目录下建立 hello.tars 文件,内容如下
module TestApp
{
interface Hello
{
string hello(int no, string name);
};
};
接口文件编译
提供插件编译生成 java 代码,在 tars-maven-plugin 添加生成 java 文件配置
<plugin>
<groupId>com.tencent.tars</groupId>
<artifactId>tars-maven-plugin</artifactId>
<version>1.7.2</version>
<configuration>
<tars2JavaConfig>
<!-- tars文件位置 -->
<tarsFiles>
<tarsFile>${basedir}/src/main/resources/hello.tars</tarsFile>
</tarsFiles>
<!-- 源文件编码 -->
<tarsFileCharset>UTF-8</tarsFileCharset>
<!-- 生成服务端代码 -->
<servant>true</servant>
<!-- 生成源代码编码 -->
<charset>UTF-8</charset>
<!-- 生成的源代码目录 -->
<srcPath>${basedir}/src/main/java</srcPath>
<!-- 生成源代码包前缀 -->
<packagePrefixName>com.qq.tars.quickstart.server.</packagePrefixName>
</tars2JavaConfig>
</configuration>
</plugin>
在工程根目录下执行 mvn tars:tars2java
@Servant
public interface HelloServant {
public String hello(int no, String name);
}
服务接口实现
新创建一个 HelloServantImpl.java 文件,实现 HelloServant.java 接口
public class HelloServantImpl implements HelloServant {
@Override
public String hello(int no, String name) {
return String.format("hello no=%s, name=%s, time=%s", no, name, System.currentTimeMillis());
}
服务暴露配置
在 WEB-INF 下创建一个 servants.xml 的配置文件,服务编写后需要进程启动时加载配置暴露服务,配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<servants>
<servant name="HelloObj">
<home-api>com.qq.tars.quickstart.server.testapp.HelloServant</home-api>
<home-class>com.qq.tars.quickstart.server.testapp.impl.HelloServantImpl</home-class>
</servant>
</servants>
说明:除了此方法之外,还可以采用 spring 模式来配置服务,详情见 tars_java_spring.md。
服务配置 ServerConfig
服务框架中有全局的结构 ServerConfig,它记录了服务的基本信息,在服务框架初始化时会自动从服务配置文件中初始化这些参数。参数说明:
- Application:应用名称,如果配置文件没有配置,默认为 UNKNOWN;
- ServerName:服务名称;
- BasePath:基本路径,通常表示可执行文件的路径;
- DataPath:数据文件路径,通常表示存在服务自己的数据;
- LocalIp:本地 ip,默认是本机非 127.0.0.1 的第一块网卡 IP;
- LogPath:日志文件路径,日志的写法请参考后续;
- LogLevel:滚动 log 日志级别;
- Local:服务可以有管理端口,可以通过管理端口发送命令给服务,该参数表示绑定的管理端口的地址,例如 tcp -h 127.0.0.1 -p 8899,如果没有设置则没有管理端口;
- Node:本地 NODE 地址,如果设置,则定时给 NODE 发送心跳,否则不发送心跳,通常只有发布到框架上面的服务才有该参数;
- Log:日志中心地址,例如:tars.tarslog.LogObj@tcp –h .. –p …,如果没有配置,则不记录远程日志;
- Config:配置中心地址,例如:tars.tarsconfig.ConfigObj@tcp –h … -p …,如果没有配置,则 addConfig 函数无效,无法从远程配置中心拉取配置;
- Notify:信息上报中心地址,例如:tars.tarsnotify.NotifyObj@tcp –h … -p …,如果没有配置,则上报的信息直接丢弃;
- SessionTimeOut:防空闲连接超时设置;
- SessionCheckInterval:防空闲连接超时检查周期;
服务的配置文件格式如下:
<tars>
<application>
<server>
#本地node的ip:port
node=tars.tarsnode.ServerObj@tcp -h x.x.x.x -p 19386 -t 60000
#应用名称
app=TestApp
#服务名称
server=HelloServer
#本机ip
localip=x.x.x.x
#管理端口
local=tcp -h 127.0.0.1 -p 20001 -t 3000
#服务可执行文件,配置文件等
basepath=/usr/local/app/tars/tarsnode/data/TestApp.HelloServer/bin/
#服务数据目录
datapath=/usr/local/app/tars/tarsnode/data/TestApp.HelloServer/data/
#日志路径
logpath=/usr/local/app/tars/app_log/
#配置中心的地址
config=tars.tarsconfig.ConfigObj
#通知上报的地址[可选]
notify=tars.tarsnotify.NotifyObj
#远程日志的地址[可选]
log=tars.tarslog.LogObj
#服务停止的超时时间
deactivating-timeout=2000
#日志等级
logLevel=DEBUG
#防空闲连接超时设置
sessionTimeOut=120000
#防空闲连接超时检查周期
sessionCheckInterval=60000
</server>
</application>
</tars>
Adapter
Adapter 表示了绑定端口。服务新增一个绑定端口,则新建立一个 Adapter,并设置相关的参数和处理对象则可以非常方便的完成对这个端口上的处理,通常用这个功能来完成在其他协议的支撑。
对于 TARS 服务而言,在服务配置文件中增加 adapter 项,即可以完成增加一个 Servant 处理对象。
Adapter 配置如下:
<tars>
<application>
<server>
#配置绑定端口
<TestApp.HelloServer.HelloObjAdapter>
#允许的IP地址
allow
#监听IP地址
endpoint=tcp -h x.x.x.x -p 20001 -t 60000
#处理组
handlegroup=TestApp.HelloServer.HelloObjAdapter
#最大连接数
maxconns=200000
#协议
protocol=tars
#队列大小
queuecap=10000
#队列超时时间毫秒
queuetimeout=60000
#处理对象
servant=TestApp.HelloServer.HelloObj
#当前线程个数
threads=5
</TestApp.HelloServer.HelloObjAdapter>
</server>
</application>
</tars>
添加 servant 项需要在 servants.xml 中完成配置
服务启动
服务启动时需要在启动命令中添加配置文件**-Dconfig=config.conf**, 注意:config.conf 为配置文件,服务端的配置文件和客户端的配置文件必须合并到这一个文件中。完整的如下:
<tars>
<application>
enableset=n
setdivision=NULL
<server>
#本地node的ip:port
node=tars.tarsnode.ServerObj@tcp -h x.x.x.x -p 19386 -t 60000
#应用名称
app=TestApp
#服务名称
server=HelloServer
#本机ip
localip=x.x.x.x
#管理端口
local=tcp -h 127.0.0.1 -p 20001 -t 3000
#服务可执行文件,配置文件等
basepath=/usr/local/app/tars/tarsnode/data/TestApp.HelloServer/bin/
#服务数据目录
datapath=/usr/local/app/tars/tarsnode/data/TestApp.HelloServer/data/
#日志路径
logpath=/usr/local/app/tars/app_log/
#配置中心的地址
config=tars.tarsconfig.ConfigObj
#通知上报的地址[可选]
notify=tars.tarsnotify.NotifyObj
#远程日志的地址[可选]
log=tars.tarslog.LogObj
#服务停止的超时时间
deactivating-timeout=2000
#日志等级
logLevel=DEBUG
#配置绑定端口
<TestApp.HelloServer.HelloObjAdapter>
#允许的IP地址
allow
#监听IP地址
endpoint=tcp -h x.x.x.x -p 20001 -t 60000
#处理组
handlegroup=TestApp.HelloServer.HelloObjAdapter
#最大连接数
maxconns=200000
#协议
protocol=tars
#队列大小
queuecap=10000
#队列超时时间毫秒
queuetimeout=60000
#处理对象
servant=TestApp.HelloServer.HelloObj
#当前线程个数
threads=5
</TestApp.HelloServer.HelloObjAdapter>
</server>
<client>
#主控的地址
locator=tars.tarsregistry.QueryObj@tcp -h x.x.x.x -p 17890
#同步超时时间
sync-invoke-timeout=3000
#异步超时时间
async-invoke-timeout=5000
#刷新ip列表的时间间隔
refresh-endpoint-interval=60000
#上报数据的时间间隔
report-interval=60000
#采样率
sample-rate=100000
#最大采样数
max-sample-count=50
#模版名称
modulename=TestApp.HelloServer
</client>
</application>
</tars>
异步嵌套
异步嵌套代表如下情况:
A 异步调用 B,B 接收到请求后再异步调用 C,等 C 返回后,B 再将结果返回 A。
通常情况下面,B 接收到请求后,在接口处理完毕以后就需要返回应答给 A,因此如果 B 在接口中又发起异步请求到 C,则无法实现。
因此需要在实现接口方法中,声明启动异步来实现跨服务的异步调用,//声明启动异步上下文 AsyncContext context = AsyncContext.startAsync(); //接口实现 …
//在异步处理后回包
context.writeResult(...);
客户端开发
客户端可以不用写任何协议通信代码即可完成远程调用。
通信器
完成服务端以后,客户端对服务端完成收发包的操作是通过通信器Communicator来实现的。
通信器的初始化如下:
CommunicatorConfig cfg = CommunicatorConfig.load("config.conf");
//构建通信器
Communicator communicator = CommunicatorFactory.getInstance().getCommunicator(cfg);
说明:
- 通信器的配置文件格式后续会介绍;
- 通信器缺省不采用配置文件也可以使用,所有参数都有默认值;
- 通信器也可以直接通过属性来完成初始化;
- 如果需要通过名字来获取客户端调用代理,则必须设置 locator 参数;
通信器属性说明:
- locator: registry 服务的地址,必须是有 ip port 的,如果不需要 registry 来定位服务,则不需要配置;
- connect-timeout:网络连接超时时间,毫秒,没有配置缺省为 3000
- connections;连接数,默认为 4;
- sync-invoke-timeout:调用最大超时时间(同步),毫秒,没有配置缺省为 3000
- async-invoke-timeout:调用最大超时时间(异步),毫秒,没有配置缺省为 5000
- refresh-endpoint-interval:定时去 registry 刷新配置的时间间隔,毫秒,没有配置缺省为 1 分钟
- stat:模块间调用服务的地址,如果没有配置,则上报的数据直接丢弃;
- property:属性上报地址,如果没有配置,则上报的数据直接丢弃;
- report-interval:上报给 stat/property 的时间间隔,默认为 60000 毫秒;
- modulename:模块名称,默认为可执行程序名称;
通信器配置文件格式如下:
<tars>
<application>
#set调用
enableset = N
setdivision = NULL
#proxy需要的配置
<client>
#地址
locator = tars.tarsregistry.QueryObj@tcp -h 127.0.0.1 -p 17890
#同步最大超时时间(毫秒)
connect-timeout = 3000
#网络连接数
connections = 4
#网络连接超时时间(毫秒)
sync-invoke-timeout = 3000
#异步最大超时时间(毫秒)
async-invoke-timeout = 5000
#刷新端口时间间隔(毫秒)
refresh-endpoint-interval = 60000
#模块间调用
stat = tars.tarsstat.StatObj
#属性上报地址
property = tars.tarsproperty.PropertyObj
#report time interval
report-interval = 60000
#模块名称
modulename = TestApp.HelloServer
</client>
</application>
</tars>
使用说明:
- 当使用 Tars 框架做服务端使用时,通信器不要自己创建,直接采用服务框架中的通信器就可以了,例如:CommunicatorFactory.getInstance().getCommunicator().stringToProxy(…),对于纯客户端情形,则要用户自己定义个通信器并生成代理(proxy);
- getCommunicator()是框架初始化,随时可以获取;
- 对于通信器创建出来的代理,也不需要每次需要的时候都 stringToProxy,初始化时建立好,后面直接使用就可以了;
- 代理的创建和使用,请参见下面几节;
- 对同一个 Obj 名称,多次调用 stringToProxy 返回的 Proxy 其实是一个,多线程调用是安全的,且不会影响性能;
超时控制
超时控制是对客户端 proxy(代理)而言。上节中通信器的配置文件中有记录:
#同步最大超时时间(毫秒)
sync-invoke-timeout = 3000
#异步最大超时时间(毫秒)
async-invoke-timeout = 5000
上面的超时时间对通信器生成的所有 proxy 都有效,如果需要单独设置超时时间,设置如下:
针对 proxy 设置(ServantProxyConfig 与 CommunicatorConfig 类似)
//设置该代理单独初始化配置
public <T> T stringToProxy(Class<T> clazz, ServantProxyConfig servantProxyConfig)
调用
本节会详细阐述远程调用的方式。
首先简述 tars 客户端的寻址方式,其次会介绍客户端的调用方式,包括但不限于单向调用、同步调用、异步调用、hash 调用等。
寻址方式简介
Tars 服务的寻址方式通常可以分为如下两种方式,即服务名在主控注册和不在主控注册,主控是指专用于注册服务节点信息的名字服务(路由服务)。 名字服务中的服务名添加则是通过操作管理平台实现的。
对于没有在主控注册的服务,可以归为直接寻址方式,即在服务的 obj 后面指定要访问的 ip 地址。客户端在调用的时候需要指定 HelloObj 对象的具体地址:
即:TestApp.HelloServer.HelloObj@tcp -h 127.0.0.1 -p 9985
TestApp.HelloServer.HelloObj:对象名称
tcp:tcp 协议
-h:指定主机地址,这里是 127.0.0.1
-p:端口地址,这里是 9985
如果 HelloServer 在两台服务器上运行,则 HelloPrx 初始化方式如下:
HelloPrx prx = c.stringToProxy("TestApp.HelloServer.HelloObj@tcp -h 127.0.0.1 -p 9985:tcp -h 192.168.1.1 -p 9983");
即,HelloObj 的地址设置为两台服务器的地址。此时请求会分发到两台服务器上(分发方式可以指定,这里不做介绍),如果一台服务器 down,则自动将请求分到另外一台,并定时重试开始 down 的那一台服务器。
对于在主控中注册的服务,服务的寻址方式是基于服务名进行的,客户端在请求服务端的时候则不需要指定 HelloServer 的具体地址,但是需要在生成通信器或初始化通信器的时候指定 registry(主控中心)的地址。
HelloPrx prx = c.stringToProxy<HelloPrx>("TestApp.HelloServer.HelloObj");
单向调用
所谓单向调用,表示客户端只管发送数据,而不接收服务端的响应,也不管服务端是否接收到请求。
HelloPrx prx = c.stringToProxy("TestApp.HelloServer.HelloObj");
//发起远程调用
prx.async_hello(null, 1000, "hello word");
同步调用
请看如下调用示例:
HelloPrx prx = c.stringToProxy("TestApp.HelloServer.HelloObj");
//发起远程调用
prx.hello(1000, "hello word");
异步调用
请看如下调用示例:
HelloPrx prx = c.stringToProxy("TestApp.HelloServer.HelloObj");
//发起远程调用
prx.async_hello(new HelloPrxCallback() {
@Override
public void callback_expired() {
}
@Override
public void callback_exception(Throwable ex) {
}
@Override
public void callback_hello(String ret) {
System.out.println(ret);
}
}, 1000, "hello word");
注意:
- 当接收到服务端返回时,HelloPrxCallback 的 callback_hello 会被响应。
- 如果调用返回异常或超时,则 callback_exception 会被调用,ret 的值定义如下:
set 方式调用
目前框架已经支持业务按 set 方式进行部署,按 set 部署之后,各个业务之间的调用对开业务发来说是透明的。但是由于有些业务有特殊需求,需要在按 set 部署之后,客户端可以指定 set 名称来调用服务端,因此框架则按 set 部署的基础上增加了客户端可以指定 set 名称去调用业务服务的功能。
详细使用规则如下,
假设业务服务端 HelloServer 部署在两个 set 上,分别为 Test.s.1 和 Test.n.1。那么客户端指定 set 方式调用配置 enableset = Y setdivision = Test.s.1
业务配置
Tars 服务框架提供了从 tarsconfig 拉取服务的配置到本地目录的功能。
使用方法很简单,服务在启动通过在注册监听器里面加载到服务 conf 目录。
以 HelloServer 为例:
public class AppStartListener implements AppContextListener {
@Override
public void appContextStarted(AppContextEvent event) {
ConfigHelper.getInstance().loadConfig("helloServer.conf");
}
@Override
public void appServantStarted(AppServantEvent event) {
}
}
在 servant.xml 中注册配置
<listener>
<listener-class>com.qq.tars.quickstart.server.AppStartListener</listener-class>
</listener>
说明:
- HelloServer.conf 配置文件可以在管理平台上配置;
- HelloServer.conf 拉取到本地后,业务只要通过 classloader 就可以加载;
- 配置文件的管理都在 web 管理平台上,同时管理平台可以主动 push 配置文件到 Server;
- 配置中心支持 ip 级别的配置,即一个服务部署在多台服务上,只有部分不同(与 IP 相关),这种情况下,配置中心可以支持配置文件的合并,同时支持在 web 管理平台查看和修改;
注意:
- 对于没有发布到管理平台上的服务,需要在服务的配置文件中指定 Config 的地址,否则不能使用远程配置。
服务日志
框架支持本地和远程日志,获取日志 Logger 对象如下
private final static Logger FLOW_LOGGER = Logger.getLogger("flow", LogType.LOCAL);
说明:打远程日志前需要预先申请远程日志服务
- LogType.LOCAL:只打本地日志
- LogType.REMOTE 只打远程日志
- LogType.All 打本地和远程日志
Logback 日志系统
TarsJava 1.7 版本之后使用了 Logback 作为 Tars 日志系统,Logback 提供了十分灵活的配置项,可以为用户提供更加强大的日志功能。
日志系统结构
Logback 日志系统由三部分组成,分别是 Logger, Appender, Layout:
-
Logger:日志记录器,每个 Logger 会附加到一个 LoggerContext 中,后者用于生成 Logger,并将它们排列成树状层次结构。Logger 是命名实体。它们的名称区分大小写,并且遵循分层命名规则:
命名层次结构:如果一个记录器的名称后有一个点,则该记录器是另一个记录器的祖先,该记录器的名称是该子记录器名称的前缀。如果记录器与子记录器之间没有祖先,则称该记录器为子记录器的父级
Logger 可以设置不同级别,分别是 TRACE、DEBUG、INFO、WARN 和 ERROR。若 Logger 的有效级别为 q,日志记录的请求级别为 p,只有当 p≥q 时,该日志请求才会被执行。(root Logger 默认有效级别是 DEBUG)
级别排序规则: TRACE < DEBUG < INFO < WARN < ERROR
如果没有为 Logger 设置一个级别,那么它将从其最接近的祖先那里继承一个已分配的级别,一个简单的示例如下:
Logger name Assigned level Effective level root DEBUG DEBUG X INFO INFO X.Y none INFO X.Y.Z none INFO -
Appender:用于编写日志事件的组件,可以指定日志输出到控制台、文件、远程服务器以及数据库等。
-
Layout:将日志信息进行格式化输出
配置文件
Logback 会首先会在类路径下寻找logback-test.xml和logback.xml配置文件,若没有找到,则会使用默认的BasicConfigurator。配置文件的基本结构为<configuration>元素中包含多个<appender>和<logger>元素和最多一个<root>元素,官方文档中给出了配置文件的结构图如下:
一个典型的 logback.xml 配置文件格式如下:
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<property name="Logname" value="demo" />
<contextName>${Logname}</contextName>
<appender>
···
</appender>
<logger>
···
</logger>
<root>
···
</root>
</configuration>
- <configuration>
<configuration>标签提供了三个配置选项:
- scan:true 表示当配置文件更改时,会自动重新加载配置文件,默认值为 true
- scanPeriod:扫描配置文件是否发生更改的时间间隔,默认时间单位为毫秒,可以自行设置单位为毫秒、秒、分钟或者小时
- debug:true 表示打印 Logback 内部日志信息,默认值为 false
<configuration scan="true" scanPeriod="30 seconds" debug="false">
...
</configuration>
- <contextName>
用于设置 LoggerContext 的名称,默认的上下文名称为”default”,一旦设置之后,上下文名字不能再被更改。
<contextName>myAppName</contextName>
- <property>
使用该变量可以在配置文件中定义变量,也可以从外部属性文件或者外部资源中批量加载。之后通过${变量名}的形式可以调用该变量。
<property name="USER_HOME" value="/home/log" />
- <appender>
负责写日志任务的组件,常用的有以下几类:
-
ConsoleAppender:将日志输出到控制台
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</pattern> </encoder> </appender>
-
FileAppender:将日志输出到文件
<appender name="FILE" class="ch.qos.logback.core.FileAppender"> <!-- 设置被写入的文件 --> <file>testFile.log</file> <!-- 是否追加到文件末尾 --> <append>true</append> <!-- 设置false可获取更高吞吐量 --> <immediateFlush>true</immediateFlush> <encoder> <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern> </encoder> </appender>
-
RollingFileAppender:可以先将日志输出到指定文件,一旦满足某个条件时,再将日志记录到另一个文件。(FileAppender 的子类)
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>logFile.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 按天滚动 --> <fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern> <!-- 保留30天的历史记录,上限为3GB --> <maxHistory>30</maxHistory> <totalSizeCap>3GB</totalSizeCap> </rollingPolicy> <encoder> <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern> </encoder> </appender>
除上述的时间滚动策略以外,还有固定窗口滚动策略FixedWindowRollingPolicy。
- <logger>
可以设置某一个包或者具体的某一个类的日志打印级别,并添加 appender。
- name:制定 logger 约束的某一个包或某一个类
- level:设置打印级别
- addtivity:是否向上级 logger 传递打印信息,默认为 true
- <appender-ref>:添加这个 appender 到 logger
<logger name="chapters.configuration" level="DEBUG">
<appender-ref ref="STDOUT" />
</logger>
- <root>
也是<logger>元素,是根 logger,仅有 level 一个属性用于设置打印级别,同时也可以使用<appender-ref>来添加 appender。
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
一个完整的 logback.xml 配置如下:
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>myApp.log</file>
<encoder>
<pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>
</encoder>
</appender>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<logger name="chapters.configuration">
<appender-ref ref="FILE" />
</logger>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
日志使用
通过调用 LoggerFactory.getLogger 方法即可获取相应的日志记录器,之后使用记录器来完成来完成日志的记录。
Logger logger = LoggerFactory.getLogger("name");
logger.info("Hello World!");
更多关于 Logback 的使用,请参考Logback 官方文档。
服务管理
服务框架可以支持动态接收命令,来处理相关的业务逻辑,例如:动态更新配置等。
- 发送管理命令
服务的管理命令的发送方式:通过管理平台,将服务发布到平台上,通过管理平台发送命令;TARS 服务框架目前内置命令:
- tars.help //查看所有管理命令
- tars.loadconfig //从配置中心, 拉取配置下来: tars.loadconfig filename
- tars.setloglevel //设置滚动日志的等级: tars.setloglevel [NONE, ERROR, WARN, DEBUG]
- tars.viewstatus //查看服务状态
- tars.connection //查看当前链接情况
- 自定义命令
服务的自定义命令发送方式,通过管理平台自定义命令发送; 只需注册相关命令以及命令处理类,如下
CustemCommandHelper.getInstance().registerCustemHandler("cmdName",new CommandHandler() {
@Override
public void handle(String cmdName, String params) {
}
});
异常上报
为了更好监控,框架支持在程序中将异常直接上报到 tarsnotify,并可以在管理平台页面上查看到。
框架提供异常上报工具,使用如下
NotifyHelper.getInstance().notifyNormal(info);
NotifyHelper.getInstance().notifyWarn(info);
NotifyHelper.getInstance().notifyError(info);
说明:
- notifyNormal 上报普通的信息
- notifyWarn 上报警告信息
- notifyError 上报错误信息
属性统计
为了方便业务做统计,框架中也支持能够在管理平台看上报的信息展示。
目前支持的统计类型包括以下几种:
- 求和(sum)
- 平均(avg)
- 分布(distr)
- 最大值(max)
- 最小值(min)
- 计数(count)
示例代码如下:PropertyReportHelper.getInstance().createPropertyReporter(“queue_size”); PropertyReportHelper.getInstance().reportPropertyValue(“queue_size”, 100);
说明:
- 上报数据是定时上报的,可以在通信器的配置中设置,目前是 1 分钟一次;
- 注意调用 createPropertyReport 时,必须在服务启动以后创建并保存好创建的对象,后续拿这个对象 report 即可,不要每次使用的时候 create;
染色日志
为了方便在 debug 时实时查看某个用户在调用某服务某接口后引起的后续相关调用消息流的日志,框架中支持将该用户触发的所有日志单独打印一份到一个指定日志文件中。
染色日志有主动打开和被动打开两种方法:
- 主动染色:
- 在发起请求的客户端显式调用框架中的染色开启接口,从打开开关到显式调用框架中染色关闭接口,中间的日志都会额外打印为染色日志。
- 在开启染色功能时,所有的发起的 taf 请求都会自动传递染色状态,被调服务接收到请求后产生的日志也会打印为染色日志,在该请求处理完成后,会自动关闭被调服务的染色开关。
示例代码如下:
DyeingSwitch.enableActiveDyeing("helloServer"); //主动打开开关接口,参数表示染色日志名称
...业务处理
loggerInnerImpl.info("hello world"); //此时出于染色开启状态,该条日志会额外打印一份到染色日志中
...业务处理
DyeingSwitch.closeActiveDyeing(); //主动关闭染色开关接口
说明:
- 开启染色日志时传递的参数推荐填写服务名,如果填写为 null 则默认名称为 default;
- 染色日志的日志级别和日志类型与原本日志相同,如果原本日志只打本地,那么染色日志也只打本地,原本日志要打远程染色日志才会打远程;
在新版的 TarsJava 中使用了 Logback 作为日志系统,因此可以使用 MDC 来实现对日志的染色。MDC 是 Logback 提供的在多线程中记录日志的一种技术,它的内部持有一个 ThreadLocal 对象,其中存储了一个 Map,因此用户可以根据需要向其中添加键值对。通过实现 Filter,配合 MDC 可以实现对日志的染色:
public class MyFilter implements Filter {
private static final String TRACE_ID = "traceId";
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
...
boolean success = putMDC(...);
...
try {
chain.doFilter(request, response);
} finally {
if(success) {
MDC.remove(TRACE_ID);
}
}
}
private boolean putMDC(...) {
if (...){
String traceId = ...
MDC.put(TRACE_ID, traceId);
return true;
}
return false;
}
-
被动染色:
- 在请求的服务端预设预先设定染色条件,如果接收到的请求满足染色条件,那么服务端框架层会自动打开染色开关;
- 染色状态传递的机制和主动染色相同,但是不需要在业务层显式关闭染色开关;
使用方法如下:
首先在需要染色的 Tars 接口上定义染色 routeKey,这个值就是判断是否开启染色的变量,示例如下:
@Servant
public interface HelloServant {
public String sayHello(@TarsRouteKey int no, String name); //使用注释routeKey来表示开启染色的参数变量
}
在定义染色 routeKey 之后,可以通过管理命令”tars.setdyeing”来设置需要染色用户标识(就是 routeKey 注释对应的值),远程对象名称和接口函数名(可选)。
管理命令格式如下:
tars.setdyeing dyeingKey dyeingServant [dyeingInterface] //三个参数分别对应上文所提值,接口名可选填
假设远程对象名称是 TestApp.HelloServer.HelloObj,请求接口名为 sayHello,需要染色的用户号码为 12345,对应的管理命令如下:
tars.setdyeing 12345 TestApp.HelloServer.HelloObj sayHello
-
染色日志查询:
- 本地染色日志:日志默认所在目录为/usr/local/app/tars/app_log/tars_dyeing/,对于主动染色,日志名为打开开关时传入的参数加固定后缀;对于被动染色,日志名为染色入口服务的 Server 名称加固定后缀。后缀为_dyeing。
- 远程日志:在 tarslog 服务打日志所在机器上 的/usr/local/app/tars/remote_app_log/tars_dyeing/dyeing/目录下,日志名为 tars_dyeing.dyeing_{此部分同本地染色日志名}。