Cat原始碼分析(一):Client端
前言
cat的Client端所做的工作就是收集埋點資訊,將埋點資訊處理成messageTree,放到傳送佇列中,在啟動另一個執行緒,非同步消費佇列,進行訊息的傳送。
本文涉及到三個內容:
- 客戶端初始化:做了哪些準備工作
- message的建立過程
- 客戶端的傳送過程
客戶端初始化
我們首先看一下Cat這個類的初始化過程
public static void initialize(PlexusContainer container, File configFile) { ModuleContext ctx = new DefaultModuleContext(container); Module module = ctx.lookup(Module.class, CatClientModule.ID); if (!module.isInitialized()) { ModuleInitializer initializer = ctx.lookup(ModuleInitializer.class); ctx.setAttribute("cat-client-config-file", configFile); initializer.execute(ctx, module); } }
- 利用plexuscontainer,獲得defaultModelContext
- 利用context從plexus容器中獲得CatClientModule和ModuleInitializer
- Initializer.execute(ctx,module)
在這個方法中,涉及到最重要的方法就是啟動一個statusUpdateTask的執行緒,下邊就是這個執行緒run方法執行的流程,最重要的一步就是4,將m_statistic和m_jars傳入statusInfoCollector的建構函式進行例項化,然後使用訪問者模式,statusInfo類就可以set一些指標的了
訪問者模式:是物件的行為模式。訪問者模式的目的是封裝一些施加於某種資料結構元素之上的操作。一旦這些操作需要修改的話,接受這個操作的資料結構可以保持不變
- 抽象訪問者:IVisitor BaseVisitor
- 訪問者:statusInfoCollector
- 抽象元素類:IEntity
- 元素類:StatusInfo–>BaseEntity–>IEntity
StatusInfo通過訪問者StatusInfoCollector對自己進行賦值。如果set的欄位發生改變,只需要改變statusInfoCollector的visitStatus方法即可。
Transaction訊息建立的過程
- 從Cat中得到MessageProducer,通過producer來例項化Transaction
Transaction t = Cat.newTransaction("URL", pageName);
public static Transaction newTransaction(String type, String name) {
return Cat.getProducer().newTransaction(type, name);
}
- 判斷manager裡邊是否存在上下文,如果沒有,則建立一個上下文,這個上下文使用ThreadLocal進行儲存,在context的建構函式中,messageTree和Transaction棧被建立了,然後就會將建立好的context放到ThrealLocal中
@Override
public void setup() {
Context ctx;
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);
}
- 開啟一個事務
DefaultTransaction transaction = new DefaultTransaction(type, name, m_manager);
m_manager.start(transaction, false);
4.在start中,獲取上下文,呼叫上下文的start方法,下邊來看一下start方法
判斷m_stack是否為空,如果不為空,則呼叫addTransactionChild方法,為空則將Transaction加到MessageTree中,若不是fored型別,則將transaction入棧
public void start(Transaction transaction, boolean forked) {
if (!m_stack.isEmpty()) {
Transaction parent = m_stack.peek();
addTransactionChild(transaction, parent);
} else {
m_tree.setMessage(transaction);
}
if (!forked) {
m_stack.push(transaction);
}
}
判斷treePeriod和messagePeriod的大小或長度,是否滿足條件,如果滿足條件則傳送到server端,不滿足則add到List中
private void addTransactionChild(Message message, Transaction transaction) {
long treePeriod = trimToHour(m_tree.getMessage().getTimestamp());
long messagePeriod = trimToHour(message.getTimestamp() - 10 * 1000L); // 10 seconds extra time allowed
if (treePeriod < messagePeriod || m_length >= m_configManager.getMaxMessageLength()) {
m_validator.truncateAndFlush(this, message.getTimestamp());
}
transaction.addChild(message);
m_length++;
}
訊息棧Stack和MessageTree的關係是什麼??
訊息棧是用來儲存Transaction事務的,如果傳送一個Event型別的message,首先呼叫context的add方法。
Add方法:判斷棧是否為空,為空則說明event是一個獨立的訊息,沒有被巢狀在transaction中,這個訊息將被賦值到messageTree中,呼叫flush,將tree放到佇列中,非同步傳送;如果不為空,則代表Event被巢狀在transaction中,將會呼叫addTransactionChild方法將event訊息新增到Transaction的child中
訊息傳送
通過Transaction物件的complete方法完成,通過manager的end方法,呼叫context的end方法進行訊息的傳送
- 從棧頂彈出事務,判斷是否使我們傳入的事務,如果不是,則認為是被巢狀的子事務,繼續彈出,直到彈出我們需要的事務為止
- 判斷棧是否為空,如果為空,則end傳入的事務就是根事務,這個時候才會呼叫manager.flush方法將訊息樹上傳伺服器
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();
}
}
//如果為空,則任務傳入的是根節點,才會呼叫flush方法
if (m_stack.isEmpty()) {
//重新初始化一顆tree
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;
}
在flush方法中,會呼叫TcpSocketSender方法的send方法,根據tree的型別,將tree放入不同的佇列中
總結
訊息通過messageProducer進行建立,在建立的時候會初始化messageManager的上下文。訊息會依賴messageManager進行訊息的各種操作,上下文起到了至關重要的作用。建立好的event訊息,呼叫其complete()方法將呼叫上下文的add方法將訊息入棧或入佇列,這樣訊息的傳送就是非同步的過程