使用meerkat进行服务监控和服务降级

标签: metrics 监控 java | 发表时间:2017-06-10 09:38 | 作者:channingbj
出处:https://segmentfault.com/blogs

meerkat 是爱奇艺移动服务端团队开发的服务监控以及服务降级基础组件,主要为了解决调用外部接口的时候进行成功率,响应时间,QPS指标的监控,同时在成功率下降到预设的阈值以下的时候自动切断外部接口的调用,外部接口成功率恢复后自动恢复请求。本文将对使用方式以及进阶特性进行介绍。

项目主页: https://github.com/qiyimbd/me...

为什么要进行监控和熔断

在我们的Java服务中,经常会调用外部的一些接口进行数据的获取操作,当这些外部接口的成功率比较低的时候会直接影响到服务本身的成功率,因此我们添加了对外部接口调用的成功率和响应时间监控,这样可以在造成大量用户影响之前预先发现并解决问题。同时,对于接口中的非关键数据,我们采取了更具成功率判断进行触发熔断的方式,当成功率下降到预定的阀值以下的时候自动停止对这个外部接口的访问以便保证关键数据能够正常提供,当成功率恢复以后自动恢复请求。

meerkat主要功能

  • 监控:监控Java内部操作的成功率以及响应时间指标

  • 上报:log文件和Grafhite两种监控指标上报方式,支持扩展其他的上报方式

  • 熔断:(可选功能)成功率下降到预设的阈值以下触发熔断保护,暂定对外部接口的访问,成功率恢复以后自动恢复访问

基本使用

maven

  <dependency>
    <groupId>com.github.qiyimbd</groupId>
    <artifactId>meerkat</artifactId>
    <version>1.0</version>
</dependency>

定义受监控的操作

假设我们的服务中需要从HTTP接口查询一个节目的播放次数,为了防止这个HTTP接口大量超时影响我们自身服务的质量,可以定义一个查询Command:

  public class GetPlayCountCommand extends FusingCommand<Long> {

    private final Long videoID;

    public GetPlayCountCommand(Long videoID) {
        this.videoID = videoID;
    }
        
    protected Optional<Long> run() {
        Long result = 0l;
        // 调用HTTP接口获取视频的播放次数信息
        // 如果调用失败,返回 null 或者抛出异常,会将这次操作记录为失败
        // 如果ID非法,返回 Optional.absent(),会将这次操作记录为成功
        return Optional.fromNullable(result);
    }
}

执行查询:

  //获取视频ID为123的视频的播放次数
GetPlayCountCommand command = new GetPlayCountCommand(123l);
Long result = command.execute(); // 执行查询操作,如果执行失败或者处于熔断状态,返回 null 

配置监控上报

在服务初始化的时候需要对监控上报进行设置。下面的例子中开启了监控数据向日志文件的打印

  MeterCenter.INSTANCE
    .enableReporter(new EnablingLogReporter("org.apache.log4j.RollingFileAppender"))
    .init();

查看统计结果

统计结果会以熔断命令类名为进行分组。例如前面我们定义的 GetPlayCountCommand 类,package name 是 com.qiyi.mbd.test,那么在日志中的输出将会是这个样子:

  type=GAUGE, name=com.qiyi.mbd.test.GetPlayCountCommand.normal-rate, value=0.0
type=GAUGE, name=com.qiyi.mbd.test.GetPlayCountCommand.success-rate, value=61.0
type=TIMER, name=com.qiyi.mbd.test.GetPlayCountCommand.time, count=25866500, min=0.0, max=0.001, mean=3.963926781047921E-5, stddev=1.951102156677818E-4, median=0.0, p75=0.0, p95=0.0, p98=0.001, p99=0.001, p999=0.001, mean_rate=649806.0831335272, m1=1665370.7316699813, m5=2315813.300713087, m15=2446572.324069477, rate_unit=events/second, duration_unit=milliseconds
监控项 含义
[classname].success-rate 成功率
[classname].time.m1 QPS
[classname].time.mean 平均响应时间
[classname].normal-rate 过去1分钟内处于正常访问(非熔断)的时间比例

单独使用监控功能

如果不想使用熔断功能,只是想监控Java方法调用的耗时和成功率,可以直接使用 OperationMeter 进行实现,只需要在函数调用的前后添加开始和结束的调用即可:

  //创建一个操作的计数器
    OperationMeter meter = MeterCenter.INSTANCE.getOrCreateMeter(OperationMeterTest.class, OperationMeter.class);

    //模拟成功率60%
    for(int k=0; k<100; k++){
        Timer.Context context = meter.startOperation();
        if(k%10<6){
            meter.endOperation(context, OperationMeter.Result.SUCCESS);
        } else {
            meter.endOperation(context, OperationMeter.Result.FAILURE);
        }
    }

# 开启熔断并配置阀值和持续时间

首先创建一个接口,继承自FusingConfig,用于指定配置文件的加载路径,同时还可以设定配置文件的刷新时间,具体定义方法请参照 owner 文档

  @Config.Sources("classpath:app_config.properties")
@Config.HotReload(
        value = 1, unit = java.util.concurrent.TimeUnit.MINUTES,
        type = Config.HotReloadType.ASYNC)
public interface APPFusingConfig extends FusingConfig {
}

创建查询Command的时候在构造函数中传入

  public class GetPlayCountCommand extends FusingCommand<Long> {

    private final Long videoID;

    public GetPlayCountCommand(Long videoID) {
        super( APPFusingConfig.class);  //设定配置文件
        this.videoID = videoID;
    }
        
    protected Optional<Long> run() {
        Long result = 0l;
        // 调用HTTP接口获取视频的播放次数信息
        // 如果调用失败,返回 null 或者抛出异常,会将这次操作记录为失败
        // 如果ID非法,返回 Optional.absent(),会将这次操作记录为成功
        return Optional.fromNullable(result);
    }
}

配置文件内容如下:

监控项 含义 默认值
fusing.[CommandClassName].mode 熔断模式:
FORCE_NORMAL-关闭熔断功能;
AUTO_FUSING-自动进入熔断模式;
FORCE_NORMAL-强制进行熔断
FORCE_NORMAL
fusing.[CommandClassName].duration 触发一次熔断以后持续的时间,支持ms,sec,min 单位。例如 10sec 50sec
fusing.[CommandClassName].success_rate_threshold 触发熔断的成功率阀值,降低到这个成功率以下将触发熔断,例如0.9表示成功率90% 0.9

配置文件中的 CommandClassName 是每个操作类的名称,可以为每个操作单独设置上述参数。同时,这个配置文件支持动态加载,乐意通过修改fusing.[CommandClassName].mode 手工触发或者关闭熔断。

监控指标上报Graphite

我们的服务中使用的是Metric+Graphite+Gafana进行监控数据的采集存储和展现,下面将介绍如何配置监控数据上报Grafana,关于Graphite+Grafana的配置,可以参考文章: 使用graphite和grafana进行应用程序监控

定义配置文件

首先定义一个接口,继承自GraphiteReporterConfig,通过这个接口定义配置文件的加载路径。配置文件路径的定义方法请参照 owner 文档, 下面是一个例子:

  @Config.Sources("classpath:config.properties")
public interface MyConfig extends GraphiteReporterConfig {
}

配置文件中需要定义下列内容:

配置项 含义
meter.reporter.enabled.hosts 开启监控上报的服务器列表
meter.reporter.perfix 上报使用的前缀
meter.reporter.carbon.host grafana(carbon-cache) 的 IP 地址,用于存储监控数据
meter.reporter.carbon.port grafana(carbon-cache) 的端口

下面这个例子是在192.168.0.0.1和192.168.0.0.2两台服务器上开启监控数据上报,上报监控指标的前缀是project_name.dc:

  meter.reporter.enabled.hosts = 192.168.0.0.1,192.168.0.0.2
meter.reporter.perfix = project_name.dc
meter.reporter.carbon.host = hostname.graphite

由于相同机房的不同服务器对外部接口的访问情况一般比较类似,所以仅选取部分机器上报,也是为了节省资源。仅选择部分机器上报不影响熔断效果。

初始化配置上报

在服务初始化的时候需要对监控上报进行设置。下面的例子中开启了监控数据向日志文件的打印,同时通过MyConfig指定的配置文件加载Graphite配置信息。

  MeterCenter.INSTANCE
    .enableReporter(new EnablingLogReporter("org.apache.log4j.RollingFileAppender"))
    .enableReporter(new EnablingGraphiteReporter(MyConfig.class))   //监控数据上报Grafana
    .init();

查看统计结果

统计结果会以熔断命令类名为进行分组。例如前面我们定义的 GetPlayCountCommand 类,package name 是 com.qiyi.mbd.test,那么在日志中的输出将会是这个样子:

  type=GAUGE, name=com.qiyi.mbd.test.GetPlayCountCommand.normal-rate, value=0.0
type=GAUGE, name=com.qiyi.mbd.test.GetPlayCountCommand.success-rate, value=61.0
type=TIMER, name=com.qiyi.mbd.test.GetPlayCountCommand.time, count=25866500, min=0.0, max=0.001, mean=3.963926781047921E-5, stddev=1.951102156677818E-4, median=0.0, p75=0.0, p95=0.0, p98=0.001, p99=0.001, p999=0.001, mean_rate=649806.0831335272, m1=1665370.7316699813, m5=2315813.300713087, m15=2446572.324069477, rate_unit=events/second, duration_unit=milliseconds
监控项 含义
[classname].success-rate 成功率
[classname].time.m1 QPS
[classname].time.mean 平均响应时间
[classname].normal-rate 过去1分钟内处于正常访问(非熔断)的时间比例

在Grafanna中可以看到下面的监控图:

图片描述

自定义监控上报

meerkat使用 Metrics进行监控数据的统计,因此可以使用Metrics支持的所有reporter进行上报。添加一种上报的时候,只需要实现 EnablingReporter 并在 MeterCenter 初始化之前进行调用即可。下面是log reporter的实现,可以作为参考

  public class EnablingLogReporter implements EnablingReporter {
    private String loggername;

    public EnablingLogReporter(String loggername) {
        this.loggername = loggername;
    }

    @Override
    public void invoke(MetricRegistry metricRegistry, long period, TimeUnit timeUnit) {
        Slf4jReporter.forRegistry(metricRegistry)
                .outputTo(LoggerFactory.getLogger(loggername))
                .convertRatesTo(java.util.concurrent.TimeUnit.SECONDS)
                .convertDurationsTo(java.util.concurrent.TimeUnit.MILLISECONDS)
                .build().start(period, timeUnit);
    }
}

MeterCenter 初始化的时候开启reporter

  MeterCenter.INSTANCE
    .enableReporter(new EnablingLogReporter("org.apache.log4j.RollingFileAppender"))
    .init();

多实例监控

多实例监控主要是为了解决一个被监控操作的实现类需要根据输入参数的不同分别进行监控和熔断的情况,通过定义实例的名称进行实现。例如获取视频播放次数的例子,获取视频播放次数的接口对于不同的视频类型而言请求逻辑是一样的,所以使用同一个类进行实现;但是对于不同的视频类型,接口实现的复杂程度不同导致成功率不同,当用户上传的视频的播次接口大量失败的时候我们不希望同时熔断电影电视剧这类视频的播放次数获取,这时就需要使用多实例这种特性进行监控和熔断。

下面是一个单实例的实现:

  public class GetPlayCountCommand extends FusingCommand<Long> {

    private final Long videoID;

    public GetPlayCountCommand(Long videoID) {
        super( APPFusingConfig.class);
        this.videoID = videoID;
    }
        
    protected Optional<Long> run() {
        Long result = 0l;
        // 调用HTTP接口获取视频的播放次数信息
        // 如果调用失败,返回 null 或者抛出异常,会将这次操作记录为失败
        // 如果ID非法,返回 Optional.absent(),会将这次操作记录为成功
        return Optional.fromNullable(result);
    }
}

假设业务上我们可以根据视频ID判断视频类型,可以在类初始化的时候根据类型创建多种监控实例,添加了多实例支持的实现如下:

  public class GetPlayCountCommand extends FusingCommand<Long> {

    private final Long videoID;

    public GetPlayCountCommand(Long videoID) {
        super( getVideoType(videoID), APPFusingConfig.class);
        this.videoID = videoID;
    }

    private static String getVideoType(Long videoID){
        return "PGC"; //根据videoID进行判断,返回 "PGC" 或者 "UGC" 这两个类别
    }

    protected Optional<Long> run() {
        Long result = 0l;
        // 调用HTTP接口获取视频的播放次数信息
        // 如果调用失败,返回 null 或者抛出异常,会将这次操作记录为失败
        // 如果ID非法,返回 Optional.absent(),会将这次操作记录为成功
        return Optional.fromNullable(result);
    }

由于每个实例独享一个监控指标,日志中的监控个结果是这个样子:

  type=GAUGE, name=com.qiyi.mbd.test.GetPlayCountCommand.PGC.normal-rate, value=100.0
type=GAUGE, name=com.qiyi.mbd.test.GetPlayCountCommand.PGC.success-rate, value=100.0
type=GAUGE, name=com.qiyi.mbd.test.GetPlayCountCommand.UGC.normal-rate, value=100.0
type=GAUGE, name=com.qiyi.mbd.test.GetPlayCountCommand.UGC.success-rate, value=60.0
type=TIMER, name=com.qiyi.mbd.test.GetPlayCountCommand.PGC.time, count=100, min=0.0, max=0.509, mean=0.00635, stddev=0.05052135687013958, median=0.001, p75=0.002, p95=0.002, p98=0.003, p99=0.003, p999=0.509, mean_rate=1.6680162586215173, m1=8.691964170141569, m5=16.929634497812284, m15=18.919189378135307, rate_unit=events/second, duration_unit=milliseconds
type=TIMER, name=com.qiyi.mbd.test.GetPlayCountCommand.UGC.time, count=100, min=0.0, max=0.027, mean=0.00132, stddev=0.0026939933184772376, median=0.001, p75=0.001, p95=0.002, p98=0.005, p99=0.006, p999=0.027, mean_rate=1.6715904477699361, m1=8.691964170141569, m5=16.929634497812284, m15=18.919189378135307, rate_unit=events/second, duration_unit=milliseconds

相应的,对熔断阀值以及持续时间的泪痣也需要明确指出实例的名字:

  fusing.GetPlayCountCommand.UGC.mode = AUTO_FUSING
fusing.GetPlayCountCommand.UGC.duration = 50sec
fusing.GetPlayCountCommand.UGC.success_rate_threshold = 0.9

fusing.GetPlayCountCommand.PGC.mode = AUTO_FUSING
fusing.GetPlayCountCommand.PGC.duration = 50sec
fusing.GetPlayCountCommand.PGC.success_rate_threshold = 0.9

相关 [meerkat 服务 监控] 推荐:

使用meerkat进行服务监控和服务降级

- - SegmentFault 最新的文章
meerkat 是爱奇艺移动服务端团队开发的服务监控以及服务降级基础组件,主要为了解决调用外部接口的时候进行成功率,响应时间,QPS指标的监控,同时在成功率下降到预设的阈值以下的时候自动切断外部接口的调用,外部接口成功率恢复后自动恢复请求. 本文将对使用方式以及进阶特性进行介绍. 项目主页: https://github.com/qiyimbd/me....

服务监控脚本

- - Linux - 操作系统 - ITeye博客
已有 0 人发表留言,猛击->> 这里<<-参与讨论. —软件人才免语言低担保 赴美带薪读研.

Redis服务器监控工具redis-live

- - 企业架构 - ITeye博客
Redis服务器监控工具redis-live. 413 views     comments 暂无评论. 目前来说,越来越多的使用多了NOSQL的业务,但是这方面的监控缺不多. 今天给大家介绍几个专业监控redis服务的工具,便于大家进行redis性能分析. 这个工具是用ruby语言写的,ruby是小鬼子弄出来的,个人真心觉得比较难用.

监控Tomcat方案调研(监控应用服务器系列文章一)

- - 博客园_首页
前言: 最近在做一个监控应用服务器(Tocmat、WebSphere、WebLogic)的项目,目前已小有规模,回头看看,一路走来,也算是磕磕绊绊,遇到过种种问题,走过不少弯路,不过程序员最不怕的就是遇到问题——有什么问题就解决什么问题. 为了给后来人留下点经验之谈,助之少走弯路,特意把这些经验整理出来,与大家分享.

Linux服务器的16个监控命令

- - inJava
想不想知道你的服务器到底在干什么?那么你要知道本文介绍的这些基本命令. 一旦你熟悉掌握了这些命令,就为成为专业的 Linux系统管理员打下了基础. 你可以通过图形化用户界面(GUI)程序来获取这些外壳命令提供的大量信息,具体取决于使用哪一种Linux发行版. 比如说,SUSE Linux就有一款出色的、图形化配置和管理工具YaST,KDE的KDE System Guard同样很出色.

你需要知道的 16 个 Linux 服务器监控命令

- - 水煮沉浮
如果你想知道你的服务器正在做干什么,你就需要了解一些基本的命令,一旦你精通了这些命令,那你就是一个 专业的 Linux 系统管理员. 有些 Linux 发行版会提供 GUI 程序来进行系统的监控,例如 SUSE Linux 就有一个非常棒而且专业的工具 YaST,KDE 的 KDE System Guard 同样很出色.

用Nagios监控Dell服务器硬件状况

- - 阿辉的空间
Dell有一套监控硬件的软件,Linux/windows都可以监控. 官方网址: http://linux.dell.com/repo/hardware/. 安装方法(centos linux 5.7 x64):. 1) 增加dell的yum库. 2) 安装srvadmin. 3)安装firmware-tools,这个也可以不装,升级bios这类的用的.

【监控应用服务器】系列博文目录

- - CSDN博客架构设计推荐文章
前言:做了一个监控应用服务器(Tocmat、WebSphere、WebLogic)的项目,为了留下点印记,给后来人留下点经验之谈,助之少走弯路,特意把这些经验整理出来,与大家分享. 如有疑问,可以Q我:562116039. 监控Tomcat解决方案(监控应用服务器系列文章一). 讲解两种监控Tomcat的解决方案:一是通过Tomcat提供的manager应用,二是通过JMX的方式.

如何使用Spring开发和监控线程池服务

- - ImportNew
线程池对执行同步或异步的任务很重要. 本文展示如何利用Spring开发并监控线程池服务. 创建线程池的其他两种方法已讲解过. 第1步:创建Maven工程. (可以使用Maven或IDE的插件创建). 将Spring的依赖添加到Maven的pom.xml文件中. 使用下面的插件创建可执行jar包. 创建一个实现Runnable接口的新TestTask类.

你应该知道的16个Linux服务器监控命令

- - linux大棚-roclinux.cn
本原创文章属于 《Linux大棚》博客. 博客地址为 http://roclinux.cn. 原文: 16 Linux Server Monitoring Commands You Really Need To Know. 为了让大家能更直观的学习这些命令,添加了更多的解释内容、实例和外部链接.