1. 程式人生 > >Cat原始碼分析(一):Client端

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);
		}
	}

在這裡插入圖片描述

  1. 利用plexuscontainer,獲得defaultModelContext
  2. 利用context從plexus容器中獲得CatClientModule和ModuleInitializer
  3. 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訊息建立的過程

在這裡插入圖片描述

  1. 從Cat中得到MessageProducer,通過producer來例項化Transaction
Transaction t = Cat.newTransaction("URL", pageName);

public static Transaction newTransaction(String type, String name) {
   return Cat.getProducer().newTransaction(type, name);
}

  1. 判斷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);
	}
  1. 開啟一個事務
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方法進行訊息的傳送

  1. 從棧頂彈出事務,判斷是否使我們傳入的事務,如果不是,則認為是被巢狀的子事務,繼續彈出,直到彈出我們需要的事務為止
  2. 判斷棧是否為空,如果為空,則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方法將訊息入棧或入佇列,這樣訊息的傳送就是非同步的過程