前言: CAT是一個實時和接近全量的監控系統,它側重於對Java應用的監控,除了與點評RPC組件融合的很好之外,他將會能與Spring、MyBatis、Dubbo 等框架以及Log4j 等結合,支持PHP、C++、Go等多語言應用,基本接入了美團點評上海側所有核心應用。目前在中間件(MVC、RPC、數據庫、緩存等)框架中得到廣泛應用,為美團點評各業務線提供系統的性能指標、健康狀況、監控告警等,在微服務監控領域也是非常有用的一套組件。支撐這美團每天450億的消息,50TB的數據監控,應用於 7000+應用服務器,2000+的業務,單台機器能15W qps,15台CAT物理集群,有着極高的性能,同時CAT支持豐富的報表,架構擁有靈活的擴展性,用戶可以定制自己的監控、報表需求,本文將就CAT的部署、架構源碼以及多語言支持等方面展開剖析。CAT在攜程、陸金所、獵聘網、找鋼網、平安銀行等多家互聯網公司生產環境應用。
目錄:
(一) CAT簡介與部署
介紹
背景介紹
Cat系統的特性
消息樹
CAT服務端部署
CAT客戶端Demo
(二) CAT服務端初始化
Cat模塊
Cat-servlet初始化
plexus - IOC容器
模塊的加載 - 模型模式
cat-home的setup
TcpSocketReceiver--- netty reactor 模式的應用
消息的解碼
(三) CAT客戶端原理
cat客戶端部分核心類
消息的組織 - 消息樹
客戶端的初始化
消息生產 -- 入棧
Context 線程本地變量
Transaction事務的開啟
其他類型消息組合
消息的完成-出棧
消息的發送-隊列化
消息的序列化
MessageId的設計
(四) 服務端消息分發
分發架構
分析管理器的初始化
消費者與周期管理器的初始化
什么是周期?
周期任務-任務隊列
消息分發
周期策略
(五) 配置與數據庫操作
CAT配置
代碼自動生成
數據庫操作
數據庫連接管理
(六) 消息分析器與報表(一)
消息分析器的構建
TopAnalyzer
EventAnalyzer - 事件發生次數分析
MetricAnalyzer - 業務分析
ProblemAnalyzer -異常分析
TransactionAnalyzer - 事務分析
(七)消息分析器與報表(二)
CrossAnalyzer-調用鏈分析
StorageAnalyzer --數據庫/緩存分析
StateAnalyzer -- CAT狀態分析
HeartbeatAnalyzer -- 心跳分析
DumpAnalyzer -- 原始消息LogView存儲
自定義分析器與報表
(八) 報表持久化
周期結束
分析器的結束 -- 報表持久化
報表預處理
報表的文件存儲 -- 重入鎖
報表的數據庫存儲
定時任務
(九) 管理平台MVC框架
Servlet容器與請求生命周期
頁面路由初始化
請求處理流程
(十)與JAVA框架的集成
與Spring MVC集成
與Spring Boot 集成
與Spring Cloud 集成
與dubbo集成
與MyBatis集成
與Log4j集成
(十一) 其他語言支持
PHP語言
C++語言
LUA語言
Go語言
Python語言
Node.js語言
Android埋點
Object C -- IOS 埋點
(十二) 報警與監控提醒
短信通知
郵件通知
(十三) CAT與實時計算
hadoop模塊
spark實時計算模塊
介紹
大眾點評CAT系統原型和理念來源於eBay的CAL的系統,CAT系統第一代設計者吳其敏在eBay工作長達十幾年,對CAL系統有深刻的理解。CAT不僅增強了CAL系統核心模型,還添加了更豐富的報表。自2014年開源以來,CAT在攜程、陸金所、獵聘網、找鋼網等多家互聯網公司生產環境應用。
CAT是一個實時和接近全量的監控系統,它側重於對Java應用的監控,除了與點評RPC組件融合的很好之外,他將會能與Spring、MyBatis、Dubbo 等框架以及Log4j 等結合,不久將會支持PHP、C++、Go等多語言應用,基本接入了美團點評上海側所有核心應用。目前在中間件(MVC、RPC、數據庫、緩存等)框架中得到廣泛應用,為美團點評各業務線提供系統的性能指標、健康狀況、監控告警等,在微服務監控領域也是非常有用的一套組件。
在詳細了解CAT的整體設計細節之后,我們可以在CAT基礎之上輕松擴展我們自己的監控和數據收集模塊。
CAT項目的開源地址: https://github.com/dianping/cat
背景介紹
CAT整個產品研發是從2011年底開始的,當時正是大眾點評App Net遷移Java的核心起步階段。當初大眾點評App已經有核心的基礎中間件、RPC組件Pigeon、統一配置組件lion。整體Java遷移已經在服務化的路上。隨着服務化的深入,整體Java在線上部署規模逐漸變多,同時,暴露的問題也越來越多。典型的問題有:
大量報錯,特別是核心服務,需要花很久時間才能定位。
異常日志都需要線上權限登陸線上機器排查,排錯時間長。
有些簡單的錯誤定位都非常困難(一次將線上的庫配置到了Beta,花了整個通宵排錯)。
很多不了了之的問題都懷疑是網絡問題(從現在看,內網真的很少出問題)。
雖然那時候也有一些簡單的監控工具(比如Zabbix,自己研發的Hawk系統等),可能單個工具在某方面的功能還不錯,但整體服務化水平參差不齊、擴展能力相對較弱,監控工具間不能互通互聯,使得查找問題根源基本都需要在多個系統之間切換,有時候真的是靠“人品”才能找出根源。適逢吳其敏從eBay加入大眾點評成為首席架構師,eBay的CAL系統在內部非常成功,就在這樣天時地利與人和的情況下,我們開始研發了大眾點評App第一代監控系統——CAT。
Cat系統的特性
- 實時處理:信息的價值會隨時間銳減,尤其是事故處理過程中。全量數據:最開始的設計目標就是全量采集,全量的好處有很多。
- 高可用:所有應用都倒下了,需要監控還站着,並告訴工程師發生了什么,做到故障還原和問題定位。
- 故障容忍:CAT本身故障不應該影響業務正常運轉,CAT掛了,應用不該受影響,只是監控能力暫時減弱。
- 高吞吐:要想還原真相,需要全方位地監控和度量,必須要有超強的處理吞吐能力。
- 可擴展:支持分布式、跨IDC部署,橫向擴展的監控系統。
- 不保證可靠:允許消息丟失,這是一個很重要的trade-off,目前CAT服務端可以做到4個9的可靠性,可靠系統和不可靠性系統的設計差別非常大。
CAT支持的監控消息類型包括:
- Transaction 適合記錄跨越系統邊界的程序訪問行為,比如遠程調用,數據庫調用,也適合執行時間較長的業務邏輯監控,Transaction用來記錄一段代碼的執行時間和次數。
- Event 用來記錄一件事發生的次數,比如記錄系統異常,它和transaction相比缺少了時間的統計,開銷比transaction要小。
- Heartbeat 表示程序內定期產生的統計信息, 如CPU%, MEM%, 連接池狀態, 系統負載等。
- Metric 用於記錄業務指標、指標可能包含對一個指標記錄次數、記錄平均值、記錄總和,業務指標最低統計粒度為1分鍾。
- Trace 用於記錄基本的trace信息,類似於log4j的info信息,這些信息僅用於查看一些相關信息
消息樹
CAT監控系統將每次URL、Service的請求內部執行情況都封裝為一個完整的消息樹、消息樹可能包括Transaction、Event、Heartbeat、Metric和Trace信息,各個消息樹之間,通過 rootMessageId以及parentMessageId串聯起來,形成整個調用鏈條。

完整的消息樹:

可視化的消息樹:

分布式消息樹【一台機器調用另外一台機器】:

CAT服務端部署
CAT安裝環境:
- Linux 2.6以及之上(2.6內核才可以支持epoll),線上服務端部署請使用Linux環境,Mac以及Windows環境可以作為開發環境,美團點評內部CentOS 6.5
- Java 6,7,8,服務端推薦是用jdk7的版本,客戶端jdk6、7、8都支持
- Maven 3.3.3
- MySQL 5.6,5.7,更高版本MySQL都不建議使用,不清楚兼容性
- J2EE容器建議使用tomcat,建議版本7.0.70
- Hadoop環境可選,一般建議規模較小的公司直接使用磁盤模式,可以申請CAT服務端,500GB磁盤或者更大磁盤,這個磁盤掛載在/data/目錄上
目前我司線上環境:
Distributor ID: CentOS
Description: CentOS release 6.5 (Final)
Release: 6.5
Codename: Final
Server version: Apache Tomcat/8.0.30
Server built: Dec 1 2015 22:30:46 UTC
Server number: 8.0.30.0
OS Name: Linux
OS Version: 2.6.32-431.el6.x86_64
Architecture: amd64
JVM Version: 1.8.0_111-b14
JVM Vendor: Oracle Corporation
Maven 3.3.3
Mysql 5.6
Tomcat 7.0.70 建議使用此版本
我的開發環境:
操作系統: Windows 7
IDE: Intelij IDEA
JDK版本:1.8
Mysql: 5.6
Maven: 3.3.3
Server version:Apache Tomcat/8.0.30
安裝CAT集群大致步驟
初始化Mysql數據庫,一套CAT集群部署一個數據庫,初始化腳本在script下的Cat.sql
准備三台CAT服務器,IP比如為10.1.1.1,10.1.1.2,10.1.1.3,下面的例子會以這個IP為例子
初始化/data/目錄,配置幾個配置文件/data/appdatas/cat/*.xml 幾個配置文件,具體下面有詳細說明
打包cat.war 放入tomcat容器
修改一個路由配置,重啟tomcat
Tomcat啟動參數調整,修改 catalina.sh文件【服務端】
需要每台CAT集群10.1.1.1,10.1.1.2,10.1.1.3都進行部署
建議使用cms gc策略
建議cat的使用堆大小至少10G以上,開發環境啟動2G堆啟動即可
CATALINA_OPTS="$CATALINA_OPTS -server -Djava.awt.headless=true -Xms25G -Xmx25G -XX:PermSize=256m -XX:MaxPermSize=256m -XX:NewSize=10144m -XX:MaxNewSize=10144m -XX:SurvivorRatio=10 -XX:+UseParNewGC -XX:ParallelGCThreads=4 -XX:MaxTenuringThreshold=13 -XX:+UseConcMarkSweepGC -XX:+DisableExplicitGC -XX:+UseCMSInitiatingOccupancyOnly -XX:+ScavengeBeforeFullGC -XX:+UseCMSCompactAtFullCollection -XX:+CMSParallelRemarkEnabled -XX:CMSFullGCsBeforeCompaction=9 -XX:CMSInitiatingOccupancyFraction=60 -XX:+CMSClassUnloadingEnabled -XX:SoftRefLRUPolicyMSPerMB=0 -XX:-ReduceInitialCardMarks -XX:+CMSPermGenSweepingEnabled -XX:CMSInitiatingPermOccupancyFraction=70 -XX:+ExplicitGCInvokesConcurrent -Djava.nio.channels.spi.SelectorProvider=sun.nio.ch.EPollSelectorProvider -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.util.logging.config.file="%CATALINA_HOME%\conf\logging.properties" -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCApplicationConcurrentTime -XX:+PrintHeapAtGC -Xloggc:/data/applogs/heap_trace.txt -XX:-HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/applogs/HeapDumpOnOutOfMemoryError -Djava.util.Arrays.useLegacyMergeSort=true"
修改中文亂碼 tomcat conf 目錄下 server.xml:
<Connector port="8080" protocol="HTTP/1.1"
URIEncoding="utf-8" connectionTimeout="20000"
redirectPort="8443" /> 增加 URIEncoding="utf-8"
程序對於/data/目錄具體讀寫權限【包括客戶端&服務端】
- 注意無論是CAT客戶端和服務端都要求/data/目錄能進行讀寫操作,如果/data/目錄不能寫,建議使用linux的軟鏈接鏈接到一個固定可寫的目錄,軟鏈接的基本命令請自行搜索google
- 此目錄會存一些CAT必要的配置文件,運行時候的緩存文件,建議不要修改,如果想改,請自行研究好源碼里面的東西,在酌情修改,此目錄不支持進行配置化
- mkdir /data
- chmod 777 /data/ -R
- 如果是Windows開發環境則是對程序運行盤下的/data/appdatas/cat和/data/applogs/cat有讀寫權限,如果cat服務運行在e盤的tomcat中,則需要對e:/data/appdatas/cat和e:/data/applogs/cat有讀寫權限
- 如果windows實在不知道哪個盤,就所有盤都建好,最后看哪個盤多文件,就知道哪個了,當然你也可以通過配置系統環境變量CAT_HOME來指定服務器日志存儲的路徑,不過好像數據庫連接xml信息等好像不太好自己配置,最好還是采用系統默認的 /data 目錄。
配置/data/appdatas/cat/client.xml【包括客戶端&服務端】
- 此配置文件的作用是所有的客戶端都需要一個地址指向CAT的服務端,比如CAT服務端有三個IP,10.1.1.1,10.1.1.2,10.1.1.3,2280是默認的CAT服務端接受數據的端口,不允許修改,http-port是Tomcat啟動的端口,默認是8080,建議使用默認端口。
- 此文件可以通過運維統一進行部署和維護,比如使用puppert等運維工具。
- 不同環境這份文件不一樣,比如區分prod環境以及test環境,在美團點評內部一共是2套環境的CAT,一份是生產環境,一份是測試環境
<?xml version="1.0" encoding="utf-8"?> <config mode="client"> <servers> <server ip="10.1.1.1" port="2280" http-port="8080"/> <server ip="10.1.1.2" port="2280" http-port="8080"/> <server ip="10.1.1.3" port="2280" http-port="8080"/> </servers> </config>
安裝CAT的數據庫
數據庫的腳本文件 script/Cat.sql
MySQL的一個系統參數:max_allowed_packet,其默認值為1048576(1M),修改為1000M,修改完需要重啟mysql
注意:一套獨立的CAT集群只需要一個數據庫(之前碰到過個別同學在每台cat的服務端節點都安裝了一個數據庫)
配置/data/appdatas/cat/datasources.xml【服務端配置】
需要每台CAT集群10.1.1.1,10.1.1.2,10.1.1.3都進行部署
注意:此xml僅僅為模板,請根據自己實際的情況替換jdbc.url,jdbc.user,jdbc.password的實際值。 app數據庫和cat數據配置為一樣,app庫不起作用,為了運行時候代碼不報錯。
<?xml version="1.0" encoding="utf-8"?> <data-sources> <data-source id="cat"> <maximum-pool-size>3</maximum-pool-size> <connection-timeout>1s</connection-timeout> <idle-timeout>10m</idle-timeout> <statement-cache-size>1000</statement-cache-size> <properties> <driver>com.mysql.jdbc.Driver</driver> <url><![CDATA[${jdbc.url}]]></url> <user>${jdbc.user}</user> <password>${jdbc.password}</password> <connectionProperties><![CDATA[useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&socketTimeout=120000]]></connectionProperties> </properties> </data-source> <data-source id="app"> <maximum-pool-size>3</maximum-pool-size> <connection-timeout>1s</connection-timeout> <idle-timeout>10m</idle-timeout> <statement-cache-size>1000</statement-cache-size> <properties> <driver>com.mysql.jdbc.Driver</driver> <url><![CDATA[${jdbc.url}]]></url> <user>${jdbc.user}</user> <password>${jdbc.password}</password> <connectionProperties><![CDATA[useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&socketTimeout=120000]]></connectionProperties> </properties> </data-source> </data-sources>
配置/data/appdatas/cat/server.xml【服務端配置】
需要每台CAT集群10.1.1.1,10.1.1.2,10.1.1.3都進行部署
CAT節點一共有四個職責
控制台 - 提供給業務人員進行數據查看【默認所有的cat節點都可以作為控制台,不可配置】
消費機 - 實時接收業務數據,實時處理,提供實時分析報表【默認所有的cat節點都可以作為消費機,不可配置】
告警端 - 啟動告警線程,進行規則匹配,發送告警(目前僅支持單點部署)【可以配置】
任務機 - 做一些離線的任務,合並天、周、月等報表 【可以配置】
線上做多集群部署,比如說10.1.1.1,10.1.1.2,10.1.1.3這三台機器
建議選取一台10.1.1.1 負責角色有控制台、告警端、任務機,建議配置域名訪問CAT,就配置一台機器10.1.1.1一台機器掛在域名下面
10.1.1.2,10.1.1.3 負責消費機處理,這樣能做到有效隔離,任務機、告警等問題不影響實時數據處理
默認script下的server.xml為
<?xml version="1.0" encoding="utf-8"?> <config local-mode="false" hdfs-machine="false" job-machine="true" alert-machine="false"> <storage local-base-dir="/data/appdatas/cat/bucket/" max-hdfs-storage-time="15" local-report-storage-time="7" local-logivew-storage-time="7"> <hdfs id="logview" max-size="128M" server-uri="hdfs://10.1.77.86/user/cat" base-dir="logview"/> <hdfs id="dump" max-size="128M" server-uri="hdfs://10.1.77.86/user/cat" base-dir="dump"/> <hdfs id="remote" max-size="128M" server-uri="hdfs://10.1.77.86/user/cat" base-dir="remote"/> </storage> <console default-domain="Cat" show-cat-domain="true"> <remote-servers>127.0.0.1:8080</remote-servers> </console> </config>
配置說明:
local-mode : 建議在開發環境以及生產環境時,都設置為false
hdfs-machine : 定義是否啟用HDFS存儲方式,默認為 false
job-machine : 定義當前服務是否為報告工作機(開啟生成匯總報告和統計報告的任務,只需要一台服務機開啟此功能),默認為 false
alert-machine : 定義當前服務是否為報警機(開啟各類報警監聽,只需要一台服務機開啟此功能),默認為 false;
storage : 定義數據存儲配置信息
local-report-storage-time : 定義本地報告文件存放時長,單位為(天)
local-logivew-storage-time : 定義本地日志文件存放時長,單位為(天)
local-base-dir : 定義本地數據存儲目錄,建議直接使用/data/appdatas/cat/bucket目錄
hdfs : 定義HDFS配置信息
server-uri : 定義HDFS服務地址
console : 定義服務控制台信息
remote-servers : 定義HTTP服務列表,(遠程監聽端同步更新服務端信息即取此值)
ldap : 定義LDAP配置信息(這個可以忽略)
ldapUrl : 定義LDAP服務地址(這個可以忽略)
按照如上的說明,10.1.1.1 機器/data/appdatas/cat/serverm.xml配置,注意hdfs配置就隨便下了一個,請忽略
<?xml version="1.0" encoding="utf-8"?> <config local-mode="false" hdfs-machine="false" job-machine="true" alert-machine="true"> <storage local-base-dir="/data/appdatas/cat/bucket/" max-hdfs-storage-time="15" local-report-storage-time="7" local-logivew-storage-time="7"> <hdfs id="logview" max-size="128M" server-uri="hdfs://10.1.77.86/user/cat" base-dir="logview"/> <hdfs id="dump" max-size="128M" server-uri="hdfs://10.1.77.86/user/cat" base-dir="dump"/> <hdfs id="remote" max-size="128M" server-uri="hdfs://10.1.77.86/user/cat" base-dir="remote"/> </storage> <console default-domain="Cat" show-cat-domain="true"> <remote-servers>10.1.1.1:8080,10.1.1.2:8080,10.1.1.3:8080</remote-servers> </console> </config>
10.1.1.2,10.1.1.3 機器/data/appdatas/cat/serverm.xml配置如下,僅僅job-machine&alert-machine修改為false
<?xml version="1.0" encoding="utf-8"?> <config local-mode="false" hdfs-machine="false" job-machine="false" alert-machine="false"> <storage local-base-dir="/data/appdatas/cat/bucket/" max-hdfs-storage-time="15" local-report-storage-time="7" local-logivew-storage-time="7"> <hdfs id="logview" max-size="128M" server-uri="hdfs://10.1.77.86/user/cat" base-dir="logview"/> <hdfs id="dump" max-size="128M" server-uri="hdfs://10.1.77.86/user/cat" base-dir="dump"/> <hdfs id="remote" max-size="128M" server-uri="hdfs://10.1.77.86/user/cat" base-dir="remote"/> </storage> <console default-domain="Cat" show-cat-domain="true"> <remote-servers>10.1.1.1:8080,10.1.1.2:8080,10.1.1.3:8080</remote-servers> </console> </config>
war打包
- 在cat的源碼目錄,執行mvn clean install -DskipTests
- 如果發現cat的war打包不通過,CAT所需要依賴jar都部署在 http://unidal.org/nexus/
- 可以配置這個公有雲的倉庫地址到本地的settings路徑,理論上不需要配置即可,可以參考cat的pom.xml配置
- 如果自行打包仍然問題,請使用下面鏈接進行下載http://unidal.org/nexus/service/local/repositories/releases/content/com/dianping/cat/cat-home/2.0.0/cat-home-2.0.0.war
- 官方的cat的master版本,重命名為cat.war進行部署,注意此war是用jdk8,服務端請使用jdk8版本
- 如下是個人本機電腦的測試,下載的jar來自於repo1.maven.org 以及 unidal.org
Downloading: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus-utils/3.0.24/plexus-utils-3.0.24.jar
Downloaded: http://repo1.maven.org/maven2/org/apache/commons/commons-email/1.1/commons-email-1.1.jar (30 KB at 9.8 KB/sec)
Downloaded: http://repo1.maven.org/maven2/javax/servlet/jstl/1.2/jstl-1.2.jar (405 KB at 107.7 KB/sec)
Downloaded: http://repo1.maven.org/maven2/com/google/code/javaparser/javaparser/1.0.8/javaparser-1.0.8.jar (235 KB at 55.4 KB/sec)
Downloaded: http://repo1.maven.org/maven2/org/codehaus/plexus/plexus-utils/3.0.24/plexus-utils-3.0.24.jar (242 KB at 46.9 KB/sec)
Downloaded: http://repo1.maven.org/maven2/org/freemarker/freemarker/2.3.9/freemarker-2.3.9.jar (789 KB at 113.3 KB/sec)
Downloading: http://unidal.org/nexus/content/repositories/releases/org/unidal/webres/WebResServer/1.2.1/WebResServer-1.2.1.jar
Downloading: http://unidal.org/nexus/content/repositories/releases/org/unidal/webres/WebResTagLibrary/1.2.1/WebResTagLibrary-1.2.1.jar
Downloading: http://unidal.org/nexus/content/repositories/releases/org/unidal/webres/WebResTag/1.2.1/WebResTag-1.2.1.jar
Downloading: http://unidal.org/nexus/content/repositories/releases/org/unidal/webres/WebResRuntime/1.2.1/WebResRuntime-1.2.1.jar
Downloading: http://unidal.org/nexus/content/repositories/releases/org/unidal/webres/WebResApi/1.2.1/WebResApi-1.2.1.jar
Downloaded: http://unidal.org/nexus/content/repositories/releases/org/unidal/webres/WebResApi/1.2.1/WebResApi-1.2.1.jar (21 KB at 82.7 KB/sec)
Downloading: http://unidal.org/nexus/content/repositories/releases/org/unidal/webres/WebResBase/1.2.1/WebResBase-1.2.1.jar
```
[INFO] parent ............................................. SUCCESS [ 40.478 s]
[INFO] cat-client ......................................... SUCCESS [03:47 min]
[INFO] cat-core ........................................... SUCCESS [ 31.740 s]
[INFO] cat-hadoop ......................................... SUCCESS [02:50 min]
[INFO] cat-consumer ....................................... SUCCESS [ 3.197 s]
[INFO] cat-home ........................................... SUCCESS [ 58.964 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
```
war部署
1、將cat.war部署到10.1.1.1的tomcat的webapps下,啟動tomcat,注意webapps下只允許放一個war,僅僅為cat.war
2、如果發現重啟報錯,里面有NPE等特殊情況,可以檢查當前java進程,ps aux | grep java,可能存在之前的tomcat的進程沒有關閉,又新啟動了一個,導致出問題,建議kill -9 干掉所有的java進程
3、打開控制台的URL,http://10.1.1.1:8080/cat/s/config?op=routerConfigUpdate
4、注意10.1.1.1這個IP需要替換為自己實際的IP鏈接,修改路由配置只能修改一次即可
5、修改路由配置為如下,當為如下配置時,10.1.1.1 正常不起消費數據的作用,僅當10.1.1.2以及10.1.1.3都掛掉才會進行實時流量消費
<?xml version="1.0" encoding="utf-8"?> <router-config backup-server="10.1.1.1" backup-server-port="2280"> <default-server id="10.1.1.2" weight="1.0" port="2280" enable="true"/> <default-server id="10.1.1.3" weight="1.0" port="2280" enable="true"/> </router-config>
6、重啟10.1.1.1的機器的tomcat
7、將cat.war部署到10.1.1.2,10.1.1.3這兩台機器中,啟動tomcat
8、cat集群部署完畢,如果有問題,歡迎在微信群咨詢,如果文檔有誤差,歡迎指正以及提交pullrequest
重啟保證數據不丟
請在tomcat重啟之前調用當前tomcat的存儲數據的鏈接 http://${ip}:8080/cat/r/home?op=checkpoint,重啟之后數據會恢復。【注意重啟時間在每小時的整點10-55分鍾之間】
線上部署時候,建議把此鏈接調用存放於tomcat的stop腳本中,這樣不需要每次手工調用
開發環境CAT的部署
1、請按照如上部署/data/環境目錄,數據庫配置client.xml ,datasources.xml,server.xml這三個配置文件,注意server.xml里面的節點角色,job-machine&alert-machine都可以配置為true
2、在cat目錄中執行 mvn eclipse:eclipse,此步驟會生成一些代碼文件,直接導入到工程會發現找不到類
3、將源碼以普通項目到入eclipse中,注意不要以maven項目導入工程
4、運行com.dianping.cat.TestServer 這個類,即可啟動cat服務器
5、這里和集群版本唯一區別就是服務端部署單節點,client.xml server.xml以及路由地址配置為單台即可
CAT客戶端Demo
cat客戶端的配置:
cat客戶端也需要配置 /data 目錄,程序對於/data/目錄具體讀寫權限可以參考上一節,
然后通過maven引入cat客戶端包,在pom.xml 加入:
<dependency> <groupId>com.dianping.cat</groupId> <artifactId>cat-core</artifactId> <version>2.0.0</version> </dependency> <dependency> <groupId>com.dianping.cat</groupId> <artifactId>cat-client</artifactId> <version>2.0.0</version> </dependency>
引入之后:

以上配置會通過maven從網上引入 cat-core和cat-client 包,如果無法配置引入,也可以手動引入 cat-client.jar 包。
CAT默認會將/data/appdatas/cat 作為CAT Home目錄,這個目錄至關重要,CAT客戶端配置文件 client.xml 是在這個目錄內,當然,如果你是在 windows 下調試, 你也可以在src/main/resources/ 目錄下新建 META-INF/cat 目錄, 並將 client.xml 配置文件放入 src/main/resources/META-INF/cat 目錄里,你可以為你的監控配置domain,即項目名,如下配置 <domain id="translate"/> 。
<?xml version="1.0" encoding="utf-8"?> <config mode="client" xmlns:xsi="http://www.w3.org/2001/XMLSchema" xsi:noNamespaceSchemaLocation="config.xsd"> <domain id="translate"/> <servers> <!-- Local mode for development --> <server ip="127.0.0.1" port="2280" http-port="8080" /> <!-- 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>
第一個監控程序:
所有都配置好了,接下來我們來寫第一個監控程序,監控都是由用戶自己埋點,當然,在本書最后,我們將會講解CAT監控如何與各個流行框架之間更好的融合,以幫助用戶達到無侵入式的監控埋點,假設我們對用戶提供了一個翻譯服務,其中有個接口就是HelloWorld,在下面程序中,我們將以事務日志形式記錄用戶調用行為:
public Object helloWorld(HttpServletRequest request, HttpServletResponse response) {
MessageProducer cat = Cat.getProducer();
Transaction t = cat.newTransaction("URL", "Translate/HelloWorld"); //type=URL的事務記錄: 你的接口/方法名稱
try{
//do your business
t.setStatus(Message.SUCCESS);
} catch (Exception e) {
Cat.getProducer().logError(e);
t.setStatus(e);
} finally {
t.complete();
}
return null;
}
好了,我們再去管理平台去看看報表信息把,Transaction事務報表中,type=URL的事務有3條,我們通常用URL類型的事務消息標志着接口服務的開始,展開之后,我們看到這個里面該項目提供的3個服務被調用了,其中就有我們的 Translate/HelloWorld:


再點開某個接口的[::show::]進入詳細統計,包括耗時分布、每分鍾的數據、以及該服務集群下各個機器的統計情況,我們可以通過這個看出是不是某台機器出了問題:

點擊LogView進入最近一條事務的原始日志(problem報表才會記錄全部的原始日志):

接下來,我們來看一個更復雜的案例,涉及服務的調用以及數據庫、緩存的調用,如下:
@Controller public class Translate { public Map<String, String> maps = new HashMap<String, String>(); public Cat.Context context; @RequestMapping(value = "/translate/getWordMean", produces = "application/json") @ResponseBody //獲取翻譯釋義 public Object getWordMean(HttpServletRequest request, HttpServletResponse response) { context = new Cat.Context() { @Override public void addProperty(String key, String value) { maps.put(key, value); } @Override public String getProperty(String key) { return maps.get(key); } }; //服務調用消息上下文 MessageProducer cat = Cat.getProducer(); Transaction t = cat.newTransaction("URL", "translate/getWordMean"); //你的接口/方法名稱 cat.logEvent("ClientInfo", "RemoteIp=127.0.0.1&Referer=..."); //記錄遠程調用端信息 cat.logEvent("Payload", "HTTP/GET /translate/getWordMean?client=3&clientVersion=0&v=9.5&uid=3214567...."); //調用端參數 //用戶校驗 authCheck(); //先從緩存Redis獲取結果 Transaction cacheT = cat.newTransaction("Cache.memcached.redis", "translate_result:get"); cat.logEvent("Cache.memcached.redis.server", "127.0.0.1:6379"); //do your cache operation cacheT.setStatus(Message.SUCCESS); cacheT.complete(); //do your translate operation //記錄遠程語音服務調用 Transaction callT = cat.newTransaction("Call", "voice:getVoice"); Cat.logEvent("Call.server","localhost"); //遠程服務地址 Cat.logEvent("Call.app","voice"); //語音服務 Cat.logEvent("Call.port","8080"); //語音服務端口 Cat.logRemoteCallClient(context); //生成消息調用上下文,主要是幾個messageId的創建。 voiceService(context); callT.setStatus(Message.SUCCESS); callT.complete(); OutputData result = new OutputData(); result.setErrno(0); result.setErrmsg("success"); result.setTranslateResult("translate result ..."); t.setStatus(Message.SUCCESS); t.complete(); return result; } public boolean authCheck() { MessageProducer cat = Cat.getProducer(); Transaction checkUser = cat.newTransaction("Method", "checkAuth"); //從數據庫查詢用戶信息 Transaction sqlT = cat.newTransaction("SQL", "Select"); cat.logEvent("SQL.Database", "jdbc:mysql://127.0.0.1:3306/user"); cat.logEvent("SQL.Method", "select"); cat.logEvent("SQL.Statement", "SELECT", Message.SUCCESS, "select * from user_info"); //to do your SQL query sqlT.setStatus(Message.SUCCESS); sqlT.complete(); //to do your auth check checkUser.setStatus(Message.SUCCESS); checkUser.complete(); return true; } //線程模擬語音服務 protected void voiceService(final Cat.Context context) { Thread thread = new Thread() { @Override public void run() { //服務器埋點,Domain為 voice 提供語音服務 Cat.getManager().getThreadLocalMessageTree().setDomain("voice"); MessageProducer cat = Cat.getProducer(); Transaction voiceService = Cat.newTransaction("URL", "voice/getVoice"); //你的接口/方法名稱 cat.logEvent("ClientInfo", "RemoteIp=127.0.0.1&Referer=..."); //記錄遠程調用端信息 cat.logEvent("Payload", "HTTP/GET /voice/getVoice?client=3&clientVersion=0&v=9.5&uid=3214567...."); //調用端參數 //記錄服務信息 Transaction child = Cat.newTransaction("Service", "voice:getVoice"); Cat.logEvent("Service.client", "localhost"); //客戶端地址 Cat.logEvent("Service.app", "translate"); //客戶端domain Cat.logRemoteCallServer(context); //記錄消息上下文 //to do your business child.setStatus(Message.SUCCESS); child.complete(); voiceService.setStatus(Message.SUCCESS); voiceService.complete(); } }; thread.start(); // wait for it to complete try { thread.join(); } catch (InterruptedException e) { // ignore it } } }
這是我們一個對外提供的翻譯服務,getWordMean為服務控制器入口,我們將原始消息展開如下,整個服務處理耗時571ms,一進來我們會記錄URL類型事務,以及調用參數,然后記錄用戶校驗函數,在函數內部,有查詢用戶信息的數據庫操作,也會被記錄下,查詢耗時150ms,接下來我們會先從緩存獲取結果, 緩存查詢耗時 59ms,隨后我們翻譯內容,翻譯之后我們會調用語音服務提供的發音接口,voice/getVoice,發音接口調用一共耗時 361 ms。

