分布式调用链--Cat
<h2>1、什么是调用链</h2>
<p>一个业务功能可能需要多个服务协作才能实现,一个请求到达服务A,服务A需要依赖服务B,服务B又依赖服务C,甚至C仍需依赖其他服务,形成一个调用链条,即调用链。</p>
<h2>2、为什么微服务需要调用链?</h2>
<p>当我们开始微服务架构之后,我们的很多服务变成分布式的了,并且我们对服务进行了拆分,拆分之后,用户的一个请求进来,会依次经过不同的服务节点进行处理,处理完成后再返回结果给用户。那么在整个处理的链条中,如果有任何一个节点出现了延迟或者问题,都有可能导致最终的结果出现异常,有的时候不同的服务节点甚至是由不同的团队开发的、部署在不同的服务器上,那么在这么错综复杂的环境下,我们想要排查出是链条中的具体哪个服务节点出了问题,其实并不容易。而使用调用链则可以有效的解决这些问题</p>
<h2>3、调用链监控原理</h2>
<p>2010年的时候,谷歌发布过Dapper的论文,可以读一下论文
<a href="https://ai.google/research/pubs/pub36356" title="论文地址">论文地址</a>
<img src="https://www.showdoc.cc/server/api/common/visitfile/sign/9593e90a865b6768d3aa20436ff4de19?showdoc=.jpg" alt="" /></p>
<ul>
<li>
<p><strong>traceid</strong>:有的会命名为requestNo,整个调用链路中的traceid是相同的,这样可以通过一个traceid找到系统间所有交互过的请求和响应</p>
</li>
<li><strong>spanid</strong>:仅仅有traceid,无法精确的得知到底是哪个服务先被调用的,哪个服务后被调用的,spanid和parentSpanid组合起来就可以表示成一个树形的调用关系。</li>
</ul>
<p>当系统出错的时候:</p>
<ol>
<li>把traceid收集到一个集合中,包含请求与响应</li>
<li>通过spanid与parentSpanId恢复成树形调用</li>
<li>识别超时与出错的节点,进行标记</li>
<li>把上面的信息与出错节点信息展示出来</li>
</ol>
<h2>4、分布式调用链CAT</h2>
<h3>4.1 CAT简介</h3>
<p>CAT 是一个实时和接近全量的监控系统,它侧重于对Java应用的监控,基本接入了美团上海所有核心应用。目前在中间件(MVC、RPC、数据库、缓存等)框架中得到广泛应用,为美团各业务线提供系统的性能指标、健康状况、监控告警等</p>
<h3>4.2 CAT优势</h3>
<ul>
<li>实时处理:信息的价值会随时间锐减,尤其是事故处理过程中</li>
<li>全量数据:全量采集指标数据,便于深度分析故障案例</li>
<li>高可用:故障的还原与问题定位,需要高可用监控来支撑</li>
<li>故障容忍:故障不影响业务正常运转、对业务透明</li>
<li>高吞吐:海量监控数据的收集,需要高吞吐能力做保证</li>
<li>可扩展:支持分布式、跨 IDC 部署,横向扩展的监控系统</li>
</ul>
<h3>4.3 CAT整体架构</h3>
<p>整个CAT主要分为三个模块,cat-client,cat-consumer,cat-home。</p>
<ul>
<li>cat-client 提供给业务以及中间层埋点的底层sdk。</li>
<li>cat-consumer 用于实时分析从客户端的提供的数据。</li>
<li>cat-home 作为用户提供给用户的展示的控制端。</li>
</ul>
<p>在实际开发和部署中,cat-consumer和cat-home是部署在一个jvm内部,每个CAT服务端都可以作为consumer也可以作为home,这样既能减少整个CAT层级结构,也可以增加整个系统稳定性</p>
<p><img src="https://www.showdoc.cc/server/api/common/visitfile/sign/13d3f29a9196f9c6abf2700db6796a5c?showdoc=.jpg" alt="" /></p>
<h3>4.4 CAT报表简介</h3>
<ul>
<li>
<p><strong>Transaction报表</strong> 一段代码运行时间、次数,比如URL、Cache、SQL执行次数和响应时间</p>
</li>
<li>
<p><strong>Event报表</strong> 一行代码运行次数,比如出现一个异常</p>
</li>
<li>
<p><strong>Problem报表</strong> 根据Transaction/Event数据分析出来系统可能出现的异常,包括访问较慢的程序等</p>
</li>
<li>
<p><strong>Heartbeat报表</strong> JVM内部一些状态信息,比如Memory,Thread等</p>
</li>
<li><strong> Business报表 </strong>业务监控报表,比如订单指标,支付等业务指标</li>
</ul>
<h3>4.5 CAT服务端环境搭建</h3>
<blockquote>
<p>步骤1: 部署tomcat</p>
</blockquote>
<p>调整启动参数,修改 catalina.sh文件。配置如下内容</p>
<pre><code class="language-shell">export CAT_HOME=/data/appdatas/cat/
CATALINA_OPTS="$CATALINA_OPTS -server -DCAT_HOME=$CAT_HOME -Djava.awt.headless=true -Xms2G -Xmx2G -XX:PermSize=256m -XX:MaxPermSize=256m -XX:NewSize=1024m -XX:MaxNewSize=1024m -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 -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"</code></pre>
<p>修改中文乱码 tomcat conf 目录下 server.xml</p>
<pre><code class="language-shell"><Connector port="8080" protocol="HTTP/1.1"
URIEncoding="utf-8" connectionTimeout="20000"
redirectPort="8443" /> <!-- 增加 URIEncoding="utf-8" --> </code></pre>
<blockquote>
<p>步骤2: 程序对于/data/目录具体读写权限<strong>(重要)</strong></p>
</blockquote>
<p><strong>注</strong>:此目录会存一些CAT必要的配置文件以及运行时候的数据存储目录</p>
<pre><code class="language-shell">mkdir /data
chmod -R 777 /data/</code></pre>
<blockquote>
<p>步骤3: 配置/data/appdatas/cat/client.xml</p>
</blockquote>
<p><strong>注</strong>:此配置文件的作用是所有的客户端都需要一个地址指向CAT的服务端。当路由错误,且该文件夹下存在client_cache.xml,请删除client_cache.xml,再重启服务</p>
<p>client.xml配置内容如下</p>
<pre><code class="language-shell"><?xml version="1.0" encoding="utf-8"?>
<config mode="client">
<servers>
<server ip="10.1.4.70" port="2280" http-port="8082"/>
<server ip="10.1.4.71" port="2280" http-port="8082"/>
<server ip="10.1.4.72" port="2280" http-port="8082"/>
</servers>
</config></code></pre>
<p>2280是默认的CAT服务端接受数据的端口,<strong>不允许修改</strong>,http-port是Tomcat启动的端口,默认是8080,建议使用默认端口</p>
<blockquote>
<p>步骤4: 安装CAT的数据库</p>
</blockquote>
<p>将数据库的脚本文件script/CatApplication.sql导入数据库</p>
<p><strong>注</strong>:一套独立的CAT集群只需要一个数据库,数据库编码使用utf8mb4,否则可能造成中文乱码等问题</p>
<blockquote>
<p>步骤5: 配置/data/appdatas/cat/datasources.xml</p>
</blockquote>
<pre><code class="language-shell"><?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:mysql://127.0.0.1:3306/cat]]></url> <!-- 请替换为真实数据库URL及Port -->
<user>root</user> <!-- 请替换为真实数据库用户名 -->
<password>root</password> <!-- 请替换为真实数据库密码 -->
<connectionProperties><![CDATA[useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&socketTimeout=120000]]></connectionProperties>
</properties>
</data-source>
</data-sources></code></pre>
<blockquote>
<p>步骤6: war打包</p>
</blockquote>
<p>源码构建,在cat的源码目录,执行</p>
<pre><code class="language-shell">mvn clean install -DskipTests</code></pre>
<blockquote>
<p>步骤7: war部署</p>
</blockquote>
<ol>
<li>将步骤6的war包部署到本机tomcat的webapps下,启动tomcat。</li>
<li>Cat服务需要部署在tomcat下的/cat路径下</li>
<li>打开控制台的URL,<a href="http://10.1.4.72:8082/cat/s/config?op=routerConfigUpdate">http://10.1.4.72:8082/cat/s/config?op=routerConfigUpdate</a></li>
</ol>
<p>更新配置示例如下</p>
<pre><code class="language-shell"><?xml version="1.0" encoding="utf-8"?>
<router-config backup-server="10.1.4.70" backup-server-port="2280">
<default-server id="10.1.4.70" weight="1.0" port="2280" enable="false"/>
<default-server id="10.1.4.71" weight="1.0" port="2280" enable="true"/>
<default-server id="10.1.4.72" weight="1.0" port="2280" enable="true"/>
<network-policy id="default" title="default" block="false" server-group="default_group">
</network-policy>
<server-group id="default_group" title="default-group">
<group-server id="10.1.4.71"/>
<group-server id="10.1.4.72"/>
</server-group>
<domain id="cat">
<group id="default">
<server id="10.1.4.71" port="2280" weight="1.0"/>
<server id="10.1.4.72" port="2280" weight="1.0"/>
</group>
</domain>
</router-config></code></pre>
<p>重启10.1.4.70的机器的tomcat,将cat.war部署到10.1.4.71,10.1.4.72这两台机器中,启动tomcat</p>
<blockquote>
<p>步骤8: 修改服务端配置</p>
</blockquote>
<p>配置链接:<a href="http://{ip:port}/cat/s/config?op=serverConfigUpdate">http://{ip:port}/cat/s/config?op=serverConfigUpdate</a></p>
<p>修改内容为</p>
<pre><code class="language-shell"><?xml version="1.0" encoding="utf-8"?>
<server-config>
<server id="default">
<properties>
<property name="local-mode" value="false"/>
<property name="job-machine" value="false"/>
<property name="send-machine" value="false"/>
<property name="alarm-machine" value="false"/>
<property name="hdfs-enabled" value="false"/>
<property name="remote-servers" value="10.1.4.70:8082,10.1.4.71:8082,10.1.4.72:8082"/>
</properties>
<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/" base-dir="user/cat/logview"/>
<hdfs id="dump" max-size="128M" server-uri="hdfs://10.1.77.86/" base-dir="user/cat/dump"/>
<hdfs id="remote" max-size="128M" server-uri="hdfs://10.1.77.86/" base-dir="user/cat/remote"/>
</storage>
<consumer>
<long-config default-url-threshold="1000" default-sql-threshold="100" default-service-threshold="50">
<domain name="cat" url-threshold="500" sql-threshold="500"/>
<domain name="OpenPlatformWeb" url-threshold="100" sql-threshold="500"/>
</long-config>
</consumer>
</server>
<server id="10.1.4.70">
<properties>
<property name="job-machine" value="true"/>
<property name="alarm-machine" value="true"/>
<property name="send-machine" value="true"/>
</properties>
</server>
</server-config>
</code></pre>
<blockquote>
<p>步骤9: 访问cat服务端地址/cat</p>
</blockquote>
<p><img src="https://www.showdoc.cc/server/api/common/visitfile/sign/b3c1dd9c033eab73e25e575b3e42e646?showdoc=.jpg" alt="" /></p>
<h3>4.6 场景预览</h3>
<blockquote>
<p>cat埋点</p>
</blockquote>
<p><img src="https://www.showdoc.cc/server/api/common/visitfile/sign/0e65c5d2164e7df0f440e0c95e3faf27?showdoc=.jpg" alt="" /></p>
<h3>4.7 快速入门</h3>
<p>参考<a href="https://www.showdoc.cc/chlingm?page_id=4696834824378815" title="分布式调用链客户端--nisbos-common-trace">分布式调用链客户端--nisbos-common-trace</a></p>