浅入浅出监控系统

本贴最后更新于 2371 天前,其中的信息可能已经时移世改

如何搭建一个监控系统

生产环境必须是可监控的,一个对开发者黑盒的线上应用无异于灾难。

  1. 采集数据
  2. 保存数据
  3. 可视化数据
  4. 产生告警

m0

从一个熟悉的画面开始:

m1
这是 javaer 每天都会看到的一个画面,当然为了减少 bug,有时候也需要借助一下来自东方的神秘力量
m2

大家注意看 console 的第一行,灰色字体&被折叠,看起来很不起眼,就被忽略了。

m3

/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/bin/java 
-XX:TieredStopAtLevel=1 
-noverify 
-Dspring.output.ansi.enabled=always 
-Dcom.sun.management.jmxremote 
-Dcom.sun.management.jmxremote.port=61764 
-Dcom.sun.management.jmxremote.authenticate=false 
-Dcom.sun.management.jmxremote.ssl=false 
-Djava.rmi.server.hostname=127.0.0.1 
-Dspring.liveBeansView.mbeanDomain 
-Dspring.application.admin.enabled=true 
"-javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=61765:/Applications/IntelliJ IDEA.app/Contents/bin" 
-Dfile.encoding=UTF-8 
-classpath ...

出现频率最高的单词是 jmxremote,这就是我给大家介绍的第一个概念 JMX

JMX

JMX(Java Management Extensions,即 Java 管理扩展)是 Java 平台上为应用程序、设备、系统等植入管理功能的框架。 --wikipedia

如何做到管理功能呢?

  1. 监控指标,包括业务监控&系统性能监控
  2. 执行方法

我们通过架构图来看一下,JMX 如何实现这两个功能。
m4

  1. 接入层,提供远程访问接口
  2. 适配层,对资源的管理和注册
  3. MBean,提供变量 or 函数

还是不够直观,我们来具体看一下 jmx 能做什么。

在控制台中输入 jconsole,你可以看到一个 java GUI 风格的工具窗口,这是 jdk 自带用于 jmx 连接&展示的工具。

m5

可以通过 JDK 提供的 MBean 查看线程、内存、CPU 占用,检测死锁、执行 GC。也可以通过三方按照 JMX 标准提供的 MBean,查看 or 执行封装的函数方法。

m6

SpringApplicationAdminMXBean 为例,声明了一个包含函数的 interface 作为 MBean,并将实现类注册到 MBeanServer 服务中。用到了一个委托模式。

public interface SpringApplicationAdminMXBean {

	boolean isReady();
	boolean isEmbeddedWebApplication();
	String getProperty(String key);
	void shutdown();
}
public class SpringApplicationAdminMXBeanRegistrar
		implements ApplicationContextAware, EnvironmentAware, InitializingBean,
		DisposableBean, ApplicationListener<ApplicationReadyEvent> {

	private static final Log logger = LogFactory.getLog(SpringApplicationAdmin.class);

	private ConfigurableApplicationContext applicationContext;

	private Environment environment = new StandardEnvironment();

	private final ObjectName objectName;

	private boolean ready = false;

	public SpringApplicationAdminMXBeanRegistrar(String name)
			throws MalformedObjectNameException {
		this.objectName = new ObjectName(name);
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext)
			throws BeansException {
		Assert.state(applicationContext instanceof ConfigurableApplicationContext,
				"ApplicationContext does not implement ConfigurableApplicationContext");
		this.applicationContext = (ConfigurableApplicationContext) applicationContext;
	}

	@Override
	public void setEnvironment(Environment environment) {
		this.environment = environment;
	}

	@Override
	public void onApplicationEvent(ApplicationReadyEvent event) {
		if (this.applicationContext.equals(event.getApplicationContext())) {
			this.ready = true;
		}
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		MBeanServer server = ManagementFactory.getPlatformMBeanServer();
		server.registerMBean(new SpringApplicationAdmin(), this.objectName);
		if (logger.isDebugEnabled()) {
			logger.debug("Application Admin MBean registered with name '"
					+ this.objectName + "'");
		}
	}

	@Override
	public void destroy() throws Exception {
		ManagementFactory.getPlatformMBeanServer().unregisterMBean(this.objectName);
	}

	private class SpringApplicationAdmin implements SpringApplicationAdminMXBean {

		@Override
		public boolean isReady() {
			return SpringApplicationAdminMXBeanRegistrar.this.ready;
		}

		@Override
		public boolean isEmbeddedWebApplication() {
			return (SpringApplicationAdminMXBeanRegistrar.this.applicationContext != null
					&& SpringApplicationAdminMXBeanRegistrar.this.applicationContext instanceof EmbeddedWebApplicationContext);
		}

		@Override
		public String getProperty(String key) {
			return SpringApplicationAdminMXBeanRegistrar.this.environment
					.getProperty(key);
		}

		@Override
		public void shutdown() {
			logger.info("Application shutdown requested.");
			SpringApplicationAdminMXBeanRegistrar.this.applicationContext.close();
		}

	}

}
  1. JConsole 会根据方法及返回值,判断是指标还是可执行函数。
  2. 除了指标和函数,还有通知。但是 JMX 并不保证所有通知都会被监听器接收

influxdb

知道了数据如何产生,接下来需要考虑数据如何持久化。

InfluxDB 是一个由 InfluxData 开发的开源时序型数据库。它由 Go 写成,着力于高性能地查询与存储时序型数据。InfluxDB 被广泛应用于存储系统的监控数据,IoT 行业的实时数据等场景。

选型原因是 influxdb 有以下特点

  1. 可度量性:你可以实时对大量数据进行计算
  2. 无结构(无模式):可以是任意数量的列
  3. 原生的 HTTP 支持,内置 HTTP API
  4. 基于时间序列,支持与时间有关的相关函数,如 min, max, sum, count, mean, median 等一系列函数
  5. 强大的类 SQL 语法

强大的类 SQL 语法 & 无结构

    influx
    show databases;
    create database wyh_dev;
    use wyh_dev;
    show Measurements;

Measurement 等价于 mysql 中的 table,区别在于 mysql 表中存储字段,字段既可以作为展示也可以建立索引。但是 influxdb 存储的数据从逻辑上由 Measurementtag组field组 以及一个时间戳组成的。

  1. tag 信息是默认被索引的。
  2. Field 信息用于展示,是无法被索引的。
  3. time 表示该条记录的时间属性。

Line Protocol 语法
利用逗号和空格,简化插入语句, 如果插入数据时没有明确指定时间戳,则默认存储在数据库中的时间戳则为该条记录的入库时间。(纳秒)

weather,location=us-midwest temperature=82 1465839830100400200
  |    -------------------- --------------  |
  |             |             |             |
  |             |             |             |
+-----------+--------+-+---------+-+---------+
|measurement|,tag_set| |field_set| |timestamp|
+-----------+--------+-+---------+-+---------+
insert table1,tag1=a field1="fieldA" 
insert table1,tag1=tagB field1="fieldA",field2="fieldB" 
insert table1,tag1=a,tag2=b field1="fieldA",field2="fieldB",field3="fieldC" 
select * from table1

show series from table1
SHOW FIELD KEYS FROM table1
SHOW TAG KEYS FROM table1
drop measurement table1

insert table2,tagVal=tagA fieldVal="fieldA" 
insert table2,tagVal=tagB fieldVal="fieldA" 
insert table2,tagVal=tagA fieldVal="fieldB" 

select * from table2 where fieldVal='fieldA'
select * from table2 where tagVal='tagA'
select * from table2 group by fieldVal
select * from table2 group by tagVal

select * from table1 group by *
select count(*) from table2 group by time(2d)

  1. field 不可以作为 group by 条件,但是可以作为 where 条件
  2. 8083 端口停用,web 管理界面通过独立组件 chronograf 实现

原生的 HTTP 支持,内置 HTTP API

curl -i -X POST 'http://localhost:8086/write?db=wyh_dev' --data-binary 'table2,tagVal=tagA fieldVal="http" '

curl -G 'http://localhost:8086/query?pretty=true' --data-urlencode "db=wyh_dev" --data-urlencode "q=SELECT * FROM table2 "

基于时间序列,支持与时间有关的相关函数,如 min, max, sum, count, mean, median 等一系列函数

每次 insert 记录,如果没有指定,默认会保存数据库当前时间(单位纳秒)。复杂的函数计算不符合浅入浅出的定位,我们换一种直观的角度。

Grafana

The open platform for beautiful
analytics and monitoring

很炫很好很强大,没什么好讲的。

配置数据源

m8

支持多种数据源。Access 两种形式

  1. Server 服务器请求后渲染
  2. Browser 浏览器直接请求

时间函数

m9
where 不能选择 field,只能选择 tag

画图参考官方文档

配置告警

配置告警通道,支持 email、钉钉,支持 webhook=anything,e.g. 企业微信

m10

设定告警规则,包括统计方法、安全阈值。

m11

jmxtrans

上面介绍完 JMX 之后,其实缺少了一个通道,将 JMX 指标输出给 influxdb。放到后面介绍的原因是因为独立组件,不依赖 JMX,数据来源可以是 http、日志、kafka

This is effectively the missing connector between speaking to a JVM via JMX on one end and whatever logging / monitoring / graphing package that you can dream up on the other end.

m12

可以通过 JSOM | YAML 配置读取地址、查询线程数、采集指标以及持久化方式。

{
    "servers": [
        {
            "port": "12000",
            "host": "users",
            "numQueryThreads" : 2, 
            "queries": [
                {
                    "obj": "java.lang:type=Memory",
                    "attr": [
                        "HeapMemoryUsage",
                        "NonHeapMemoryUsage"
                    ],
                    "resultAlias": "MemoryUsage",
                    "outputWriters": [
                        {
                            "@class": "com.googlecode.jmxtrans.model.output.InfluxDbWriterFactory",
                            "url": "http://influxdb:8086/",
                            "username": "wyh",
                            "password": "wyh",
                            "database": "wyh",
                            "tags": {
                                "application": "MemoryUsage"
                            }
                        }
                    ]
                },
                {
                    "obj": "kafka.consumer:type=consumer-metrics,client-id=*",
                    "attr": [
                        "connection-close-rate",
                        "connection-creation-rate",
                        "network-io-rate",
                        "outgoing-byte-rate",
                        "request-rate",
                        "request-size-avg",
                        "request-size-max",
                        "incoming-byte-rate",
                        "response-rate",
                        "select-rate",
                        "io-wait-time-ns-avg",
                        "io-wait-ratio",
                        "io-time-ns-avg",
                        "io-ratio",
                        "connection-count",
                        "successful-authentication-rate",
                        "failed-authentication-rate"
                    ],
                    "resultAlias": "ConsumerMetrics",
                    "outputWriters": [
                        {
                            "@class": "com.googlecode.jmxtrans.model.output.InfluxDbWriterFactory",
                            "url": "http://influxdb:8086/",
                            "username": "wyh",
                            "password": "wyh",
                            "database": "consumer",
                            "tags": {
                                "application": "ConsumerMetrics"
                            }
                        }
                    ]
                }
            ]
        }
    ]
}

也可以通过 java 程序引入依赖包,增加扩展。

public class MemoryPool {

	public static void main(String[] args) throws Exception {
		Injector injector = JmxTransModule.createInjector(new JmxTransConfiguration());
		ProcessConfigUtils processConfigUtils = injector.getInstance(ProcessConfigUtils.class);
		JmxProcess process = processConfigUtils.parseProcess(new File("memorypool.json"));
		new JsonPrinter(System.out).print(process);
		JmxTransformer transformer = injector.getInstance(JmxTransformer.class);
		transformer.executeStandalone(process);
	}
}

总结

m13

  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3190 引用 • 8214 回帖
  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    943 引用 • 1460 回帖 • 1 关注
  • 监控
    26 引用 • 33 回帖 • 1 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...