5.CAT-客户端 cat-clinet
客户端 代码 在04.服务端 有所 提及。
5.1 环境配置
5.1.1 源码中加载配置
public class DefaultClientConfigManager implements LogEnabled, ClientConfigManager, Initializable {
private static final String CAT_CLIENT_XML = "/META-INF/cat/client.xml";
private static final String PROPERTIES_CLIENT_XML = "/META-INF/app.properties";
private static final String XML = "/data/appdatas/cat/client.xml";
......
@Override
public void initialize() throws InitializationException {
File configFile = new File(XML);
initialize(configFile);
}
@Override
public void initialize(File configFile) throws InitializationException {
try {
ClientConfig globalConfig = null;
ClientConfig clientConfig = null;
if (configFile != null) {
if (configFile.exists()) {
String xml = Files.forIO().readFrom(configFile.getCanonicalFile(), "utf-8");
globalConfig = DefaultSaxParser.parse(xml);
m_logger.info(String.format("Global config file(%s) found.", configFile));
} else {
m_logger.warn(String.format("Global config file(%s) not found, IGNORED.", configFile));
}
}
// load the client configure from Java class-path
clientConfig = loadConfigFromEnviroment();
if (clientConfig == null) {
clientConfig = loadConfigFromXml();
}
// merge the two configures together to make it effected
if (globalConfig != null && clientConfig != null) {
globalConfig.accept(new ClientConfigMerger(clientConfig));
}
if (clientConfig != null) {
clientConfig.accept(new ClientConfigValidator());
}
m_config = clientConfig;
} catch (Exception e) {
throw new InitializationException(e.getMessage(), e);
}
}
}
涉及到配置文件有
/data/appdatas/cat/client.xml
/META-INF/app.properties
/META-INF/cat/client.xml
其中
/data/appdatas/cat/client.xml
获取服务端的地址进行连通
app.properties
为了获取项目名,只有获取项目名后,才可以进行埋点数据上报,因为项目名 是 服务端的domain
值
cat/client.xml
是在app.properties
获取不到数据时,才从cat/client.xml
获取。简单的说,获取项目名的逻辑顺序是app.properties
高于cat/client.xml
。
5.1.2 配置模板
client.xml 模板如下
<?xml version="1.0" encoding="utf-8"?>
<config mode="client" xmlns:xsi="http://www.w3.org/2001/XMLSchema" xsi:noNamespaceSchemaLocation="config.xsd">
<servers>
<!-- Local mode for development -->
<server ip="127.0.0.1" port="2280" http-port="2281" />
<!-- If under production environment, put actual server address as list. -->
<!--
<server ip="192.168.7.71" port="2280" />
<server ip="192.168.7.72" port="2280" />
-->
</servers>
</config>
app.properties模板
app.name=cat
client.xml模板
<config mode="client" xmlns:xsi="http://www.w3.org/2001/XMLSchema" xsi:noNamespaceSchemaLocation="config.xsd">
<domain id="Cat"/>
</config>
5.2 消息编码
消息编码,这里只关注消息的编码,消息结构 请参照 消息结构 章节
5.3 消息发送
消息发送离不开 消息发射器,通过消息发射器异步进行消息发送。
5.3.1 消息发送接口定义
MessageProducer
public interface MessageProducer {
.....
/**
* Create a new event with given type and name.
*
* @param type
* event type
* @param name
* event name
*/
public Event newEvent(String type, String name);
/**
* Create a new trace with given type and name.
*
* @param type
* trace type
* @param name
* trace name
*/
public Trace newTrace(String type, String name);
/**
* Create a new heartbeat with given type and name.
*
* @param type
* heartbeat type
* @param name
* heartbeat name
*/
public Heartbeat newHeartbeat(String type, String name);
/**
* Create a new metric with given type and name.
*
* @param type
* metric type
* @param name
* metric name
*/
public Metric newMetric(String type, String name);
/**
* Create a new transaction with given type and name.
*
* @param type
* transaction type
* @param name
* transaction name
*/
public Transaction newTransaction(String type, String name);
/**
* Create a forked transaction for child thread.
*
* @param type
* transaction type
* @param name
* transaction name
* @return forked transaction
*/
public ForkedTransaction newForkedTransaction(String type, String name);
/**
* Create a tagged transaction for another process or thread.
*
* @param type
* transaction type
* @param name
* transaction name
* @param tag
* tag applied to the transaction
* @return tagged transaction
*/
public TaggedTransaction newTaggedTransaction(String type, String name, String tag);
说明
从接口定义上,CAT支持的埋点类型
A. Event
B. Trace
C. Heartbeat
D. Metric
E. Transaction
5.3.2 消息发射器 MessageProducer
DefaultMessageProducer 是 MessageProducer 的默认实现
public class DefaultMessageProducer implements MessageProducer {
@Inject
private MessageManager m_manager;
@Inject
private MessageIdFactory m_factory;
.....
CAT 调用消息生成器
package com.dianping.cat;
public class Cat {
private static Cat s_instance = new Cat();
public static void logTrace(String type, String name, String status, String nameValuePairs) {
Cat.getProducer().logTrace(type, name, status, nameValuePairs);
}
....
DefaultMessageProducer 具体产生消息
public class DefaultMessageProducer implements MessageProducer {
@Override
public void logTrace(String type, String name, String status, String nameValuePairs) {
if (m_manager.isTraceMode()) {
Trace trace = newTrace(type, name);
if (nameValuePairs != null && nameValuePairs.length() > 0) {
trace.addData(nameValuePairs);
}
trace.setStatus(status);
trace.complete();
}
}
@Override
public Trace newTrace(String type, String name) {
if (!m_manager.hasContext()) {
m_manager.setup();
}
if (m_manager.isMessageEnabled()) {
DefaultTrace trace = new DefaultTrace(type, name, m_manager);
return trace;
} else {
return NullMessage.TRACE;
}
}
MessageManager 站点 接收消息
public class DefaultTrace extends AbstractMessage implements Trace {
private MessageManager m_manager;
public DefaultTrace(String type, String name) {
super(type, name);
}
public DefaultTrace(String type, String name, MessageManager manager) {
super(type, name);
m_manager = manager;
}
@Override
public void complete() {
setCompleted(true);
if (m_manager != null) {
m_manager.add(this);
}
}
}
核心代码 m_manager.add(this)
DefaultMessageManager
public class DefaultMessageManager extends ContainerHolder implements MessageManager, Initializable, LogEnabled {
private ThreadLocal<Context> m_context = new ThreadLocal<Context>();
@Override
public void add(Message message) {
Context ctx = getContext();
if (ctx != null) {
ctx.add(message);
}
}
private Context getContext() {
if (Cat.isInitialized()) {
Context ctx = m_context.get();
if (ctx != null) {
return ctx;
} else {
if (m_domain != null) {
ctx = new Context(m_domain.getId(), m_hostName, m_domain.getIp());
} else {
ctx = new Context("Unknown", m_hostName, "");
}
m_context.set(ctx);
return ctx;
}
}
return null;
}
public boolean end(DefaultMessageManager manager, Transaction transaction) {
if (!m_stack.isEmpty()) {
Transaction current = m_stack.pop();
if (transaction == current) {
m_validator.validate(m_stack.isEmpty() ? null : m_stack.peek(), current);
} else {
while (transaction != current && !m_stack.empty()) {
m_validator.validate(m_stack.peek(), current);
current = m_stack.pop();
}
}
if (m_stack.isEmpty()) {
MessageTree tree = m_tree.copy();
m_tree.setMessageId(null);
m_tree.setMessage(null);
if (m_totalDurationInMicros > 0) {
adjustForTruncatedTransaction((Transaction) tree.getMessage());
}
manager.flush(tree);
return true;
}
}
return false;
}
5.3.3 消息异步发送
public class DefaultMessageManager extends ContainerHolder implements MessageManager, Initializable, LogEnabled {
....
public void flush(MessageTree tree) {
if (tree.getMessageId() == null) {
tree.setMessageId(nextMessageId());
}
MessageSender sender = m_transportManager.getSender();
if (sender != null && isMessageEnabled()) {
sender.send(tree);
reset();
} else {
m_throttleTimes++;
if (m_throttleTimes % 10000 == 0 || m_throttleTimes == 1) {
m_logger.info("Cat Message is throttled! Times:" + m_throttleTimes);
}
}
}
class Context {
....
public void add(Message message) {
if (m_stack.isEmpty()) {
MessageTree tree = m_tree.copy();
tree.setMessage(message);
flush(tree);
} else {
Transaction parent = m_stack.peek();
addTransactionChild(message, parent);
}
}
public boolean end(DefaultMessageManager manager, Transaction transaction) {
if (!m_stack.isEmpty()) {
Transaction current = m_stack.pop();
if (transaction == current) {
m_validator.validate(m_stack.isEmpty() ? null : m_stack.peek(), current);
} else {
while (transaction != current && !m_stack.empty()) {
m_validator.validate(m_stack.peek(), current);
current = m_stack.pop();
}
}
if (m_stack.isEmpty()) {
MessageTree tree = m_tree.copy();
m_tree.setMessageId(null);
m_tree.setMessage(null);
if (m_totalDurationInMicros > 0) {
adjustForTruncatedTransaction((Transaction) tree.getMessage());
}
manager.flush(tree);
return true;
}
}
return false;
}