美文网首页程序员
浅入浅出监控系统

浅入浅出监控系统

作者: Crick | 来源:发表于2018-06-30 10:33 被阅读34次

    如何搭建一个监控系统

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

    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

    相关文章

      网友评论

        本文标题:浅入浅出监控系统

        本文链接:https://www.haomeiwen.com/subject/ltktuftx.html