目录
背景介绍
写作原因
本人由于暑假在某OTA公司实习,实习工作主要为将公司接入的一些监控系统编写一个通用的服务接口。因为在以前每次对业务代码埋点时,可能需要不止一个监控系统的指标,往往监控的埋点代码比实际的业务代码还要多。因此,需要一个监控代理服务端,用户可以通过该代理客户端进行埋点,而不用在意具体的各个监控系统。最终完成效果为暴露一个代理接口,用户通过该接口进行埋点,只需要关心需要哪些指标,而不用管理指标具体从哪个监控服务获取。
话不多说,我们还是回到今天的主题,CAT监控系统,这也是我进公司接触的第一个监控系统和开源框架。源码阅读过程非常痛苦,不过现在回想还是满满的收获。
初步认识
CAT(Central Application Tracking)是一个实时和接近全量的监控系统,它侧重于对Java应用的监控,基本接入了美团点评上海侧所有核心应用。目前在中间件(MVC、RPC、数据库、缓存等)框架中得到广泛应用,为美团点评各业务线提供系统的性能指标、健康状况、监控告警等。自2014年开源以来,除了美团点评之外,CAT还在携程、陆金所、猎聘网、找钢网等多家互联网公司生产环境应用,项目的开源地址是http://github.com/dianping/cat。
既然是监控系统,那么什么是监控?监控整体要求就是快速发现故障、快速定位故障以及辅助进行程序性能优化。为了做到这些,我们对监控系统的一些非功能做了如下的要求:
- 实时处理:信息的价值会随时间锐减,尤其是事故处理过程中。
- 全量数据:最开始的设计目标就是全量采集,全量的好处有很多。
- 高可用:所有应用都倒下了,需要监控还站着,并告诉工程师发生了什么,做到故障还原和问题定位。
故障容忍:CAT本身故障不应该影响业务正常运转,CAT挂了,应用不该受影响,只是监控能力暂时减弱。 - 高吞吐:要想还原真相,需要全方位地监控和度量,必须要有超强的处理吞吐能力。
- 可扩展:支持分布式、跨IDC部署,横向扩展的监控系统。
- 不保证可靠:允许消息丢失,这是一个很重要的trade-off,目前CAT服务端可以做到4个9的可靠性,可靠系统和不可靠性系统的设计差别非常大。
CAT从开发至今,一直秉承着简单的架构就是最好的架构原则,主要分为三个模块:CAT-client、CAT-consumer、CAT-home。
- Cat-client 提供给业务以及中间层埋点的底层SDK。
- Cat-consumer 用于实时分析从客户端提供的数据。
- Cat-home 作为用户给用户提供展示的控制端。
在实际开发和部署中,Cat-consumer和Cat-home是部署在一个JVM内部,每个CAT服务端都可以作为consumer也可以作为home,这样既能减少整个层级结构,也可以增加系统稳定性。
图中可见:
- 路由中心是根据应用所在机房信息来决定客户端上报的CAT服务端地址,目前美团点评有广州、北京、上海三地机房。
- 每个机房内部都有独立的原始信息存储集群HDFS。
- CAT-home可以部署在一个机房也可以部署在多个机房,在最后做展示的时候,home会从consumer中进行跨机房的调用,将所有的数据合并展示给用户。
- 实际过程中,consumer、home以及路由中心都是部署在一起的,每个服务端节点都可以充当任何一个角色。
部署安装
具体的部署安装,这里就不详细介绍了,配环境这种东西肯定会遇到不同的坑坑洼洼,通过网上查资料,大部分应该都能解决。这里简单说下部署安装过程,并贴下我当时参考的几篇比较好的博文。
-
安装相应软件:
• Windows环境作为开发环境
• Java8
• Maven
• MySQL 5.6,5.7
• Tomca9. -
下载源码https://github.com/dianping/cat
-
初始化Mysql数据库,一套CAT集群需要部署一个数据库,数据库脚本script/CatApplication.sql。
-
配置文件:
• 配置/data/appdatas/cat/client.xml文件
• 配置/data/appdatas/cat/datasources.xml文件
• 服务器配置 http://{ip:port}/cat/s/config?op=serverConfigUpdate (注意本地节点的角色,job-machine&alarm-machine都可以配置为true,以便于debug) -
启动
• Intellij IDEA 推荐使用tomcat插件启动cat-home模块,application context设置为/cat。
• Tomcat启动:打成war包,将war包部署在Tomcat后,启动Tomcat
• test case启动:运行com.dianping.cat.TestServer 这个类,即可启动cat服务器;注意:执行的是startWebApp()这个test case
推荐文档及博客:
首先肯定是官方的部署wiki,https://github.com/dianping/cat/wiki/readme_server
https://my.oschina.net/yanyimin/blog/1517724
windows安装教程https://blog.csdn.net/cd18333612683/article/details/82927196
Cat-Client
Context 线程本地变量
消息上下文 Context 采用的是线程本地变量。通过ThreadLocal存取Context数据。
高并发下日志的打印通常会采用这种方式,或者说一次事务的日志一起打印,因为一般默认一次事务都是由同一个线程执行的(如一次http请求),将事务的日志保存在线程局部变量当中,当事务执行完成的时候统一打印。
为什么需要用到线程本地变量?在低并发请求下,一条日志会很快被处理,普通变量即可满足需求,很少出现多个线程同时读写同一个变量,
然在高并发场景下,多个线程同时读写同一个变量会导致不可预知的结果,我们称这为线程非安全,比如线程A要写一大段日志,写到一半,线程B获得CPU执行时间片开始写日志,AB的日志就会交错混乱,有同学会问,为什么不用同步锁?这是一个方案,同步锁是一个相对较复杂的保证线程安全,保证同时只有一个线程可以读写变量,其它线程要读写变量就需要排队,这就必然会带来高延迟,
线程本地变量功用则非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是Java中一种较为特殊的线程绑定机制,JVM 为每个运行的线程,绑定了私有的本地实例存取空间,每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突,从而为多线程环境常出现的并发访问问题提供了一种隔离机制,但是会造成数据冗余,是一种用空间换时间的线程安全方案。
ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单,在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。大家看下面代码,核心在于ThreadLocal的get 和 set函数,函数首先会获取当前线程,然后从 Map 中获取或者设置该线程的Context
public class ThreadLocal<T> {
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
}
public class DefaultMessageManager extends ContainerHolder implements MessageManager, Initializable, LogEnabled {
private ThreadLocal<Context> m_context = new ThreadLocal<Context>();
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;
}
}
在Context构造函数里,我们看到了消息树MessageTree和Transaction栈被创建了,由于Context是线程本地变量,由此可以推断,每个线程都拥有各自的消息树和事务栈,这里所说的线程都是业务线程,Context属于MessageManager的内部类。可以认为MessageManager的其中一个功能是作为context的一个代理,MessageManager的start、add、end等方法,核心都是调用当前线程context的start、add、end方法。
合理的创建标题,有助于目录的生成
直接输入1次#,并按下space后,将生成1级标题。
输入2次#,并按下space后,将生成2级标题。
以此类推,我们支持6级标题。有助于使用TOC
语法后生成一个完美的目录。
如何改变文本的样式
强调文本 强调文本
加粗文本 加粗文本
标记文本
删除文本
引用文本
H2O is是液体。
210 运算结果是 1024.
插入链接与图片
链接: link.
图片:
带尺寸的图片:
居中的图片:
居中并且带尺寸的图片:
当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。
如何插入一段漂亮的代码片
去博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片
.
// An highlighted block
var foo = 'bar';
生成一个适合你的列表
- 项目
- 项目
- 项目
- 项目
- 项目1
- 项目2
- 项目3
- 计划任务
- 完成任务
创建一个表格
一个简单的表格是这么创建的:
项目 | Value |
---|---|
电脑 | $1600 |
手机 | $12 |
导管 | $1 |
设定内容居中、居左、居右
使用:---------:
居中
使用:----------
居左
使用----------:
居右
第一列 | 第二列 | 第三列 |
---|---|---|
第一列文本居中 | 第二列文本居右 | 第三列文本居左 |
SmartyPants
SmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:
TYPE | ASCII | HTML |
---|---|---|
Single backticks | 'Isn't this fun?' | ‘Isn’t this fun?’ |
Quotes | "Isn't this fun?" | “Isn’t this fun?” |
Dashes | -- is en-dash, --- is em-dash | – is en-dash, — is em-dash |
创建一个自定义列表
-
Markdown
- Text-to- HTML conversion tool Authors
- John
- Luke
如何创建一个注脚
一个具有注脚的文本。1
注释也是必不可少的
Markdown将文本转换为 HTML。
KaTeX数学公式
您可以使用渲染LaTeX数学表达式 KaTeX:
Gamma公式展示 Γ ( n ) = ( n − 1 ) ! ∀ n ∈ N \Gamma(n) = (n-1)!\quad\forall n\in\mathbb N Γ(n)=(n−1)!∀n∈N 是通过欧拉积分
Γ ( z ) = ∫ 0 ∞ t z − 1 e − t d t   . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)=∫0∞tz−1e−tdt.
你可以找到更多关于的信息 LaTeX 数学表达式here.
新的甘特图功能,丰富你的文章
- 关于 甘特图 语法,参考 这儿,
UML 图表
可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图::
这将产生一个流程图。:
- 关于 Mermaid 语法,参考 这儿,
FLowchart流程图
我们依旧会支持flowchart的流程图:
- 关于 Flowchart流程图 语法,参考 这儿.
导出与导入
导出
如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。
导入
如果你想加载一篇你写过的.md文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。
注脚的解释 ↩︎