资讯专栏INFORMATION COLUMN

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

microelec / 2338人阅读

摘要:是用于服务监控以及服务降级基础组件,主要为了解决调用外部接口的时候进行成功率,响应时间,指标的监控,同时在成功率下降到预设的阈值以下的时候自动切断外部接口的调用,外部接口成功率恢复后自动恢复请求。

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

项目主页: https://github.com/ChanningBJ...

为什么要进行监控和熔断

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

meerkat主要功能

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

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

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

基本使用 maven

    com.github.channingbj
    meerkat
    1.2
定义受监控的操作

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

public class GetPlayCountCommand extends FusingCommand {

    private final Long videoID;

    public GetPlayCountCommand(Long videoID) {
        this.videoID = videoID;
    }
        
    protected Optional 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.test,那么在日志中的输出将会是这个样子:

type=GAUGE, name=com.test.GetPlayCountCommand.normal-rate, value=0.0
type=GAUGE, name=com.test.GetPlayCountCommand.success-rate, value=61.0
type=TIMER, name=com.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 {

    private final Long videoID;

    public GetPlayCountCommand(Long videoID) {
        super( APPFusingConfig.class);  //设定配置文件
        this.videoID = videoID;
    }
        
    protected Optional 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.test,那么在日志中的输出将会是这个样子:

type=GAUGE, name=com.test.GetPlayCountCommand.normal-rate, value=0.0
type=GAUGE, name=com.test.GetPlayCountCommand.success-rate, value=61.0
type=TIMER, name=com.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 {

    private final Long videoID;

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

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

public class GetPlayCountCommand extends FusingCommand {

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

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

type=GAUGE, name=com.test.GetPlayCountCommand.PGC.normal-rate, value=100.0
type=GAUGE, name=com.test.GetPlayCountCommand.PGC.success-rate, value=100.0
type=GAUGE, name=com.test.GetPlayCountCommand.UGC.normal-rate, value=100.0
type=GAUGE, name=com.test.GetPlayCountCommand.UGC.success-rate, value=60.0
type=TIMER, name=com.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.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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/71138.html

相关文章

  • 阿里百川码力APP监控 来了!

    摘要:的可用性检测方式阿里百川码力的监控体系对于加强应用的可用性,一般都采取应用监控结合服务监控的形式,使得开发者实现端到端的全链路性能管理。 阿里百川码力APP监控 来了!这个APP监控 和手淘一起成长历经千锤百炼 走过千BUG万坑如今百川起产品 为了让你的APP更好 用户更爽! 在移动互联网时代,一款应用是否成功,用户体验是一个关键的因素。APM的发展使得用户体验越来越完善,本文通...

    tuomao 评论0 收藏0
  • 阿里百川码力APP监控 来了!

    摘要:的可用性检测方式阿里百川码力的监控体系对于加强应用的可用性,一般都采取应用监控结合服务监控的形式,使得开发者实现端到端的全链路性能管理。 阿里百川码力APP监控 来了!这个APP监控 和手淘一起成长历经千锤百炼 走过千BUG万坑如今百川起产品 为了让你的APP更好 用户更爽! 在移动互联网时代,一款应用是否成功,用户体验是一个关键的因素。APM的发展使得用户体验越来越完善,本文通...

    snifes 评论0 收藏0
  • 为什么 kubernetes 天然适合微服务 (2)

    摘要:有了分布式数据库可以使数据库的性能可以随着节点增加线性地增加。分布式数据库最最下面是,是主备的,通过的内核开发能力,我们能够实现主备切换数据零丢失,所以数据落在这个里面,是非常放心的,哪怕是挂了一个节点,切换完了以后,你的数据也是不会丢的。 此文已由作者刘超授权网易云社区发布。 欢迎访问网易云社区,了解更多网易技术产品运营经验 三、微服务化的十个设计要点 微服务有哪些要点呢?第一张图是...

    lentrue 评论0 收藏0
  • 慕课网_《微服务架构在二手交易平台(转转)中的实践》学习总结

    时间:2017年07月06日星期四说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.com教学示例源码:无学习学习源码:无 第一章:微服务架构在二手交易平台(转转)中的实践 1-1 微服务架构特点 分享要点-微服务架构 特点 使用原因 演进 通信协议、服务注册与发现 柔性可用实践 服务治理 什么是微服务 微服务是一系列小服务的组合 微服务可以单独运行,独立的进程 微服务整...

    ckllj 评论0 收藏0
  • Spring Cloud Hystrix入门Hystrix命令原理分析

    摘要:系统需要支持命令的撤销。第步计算断路器的健康度会将成功失败拒绝超时等信息报告给断路器,断路器会维护一组计数器来统计这些数据。第步,当前命令的线程池请求队列或者信号量被占满的时候。 断路由器模式 在分布式架构中,当某个服务单元发生故障之后,通过断路由器的故障监控(类似熔断保险丝),向调用方返回一个错误响应,而不是长时间的等待。这样就不会使得线程因调用故障服务被长时间占用不释放,避免了故障...

    Betta 评论0 收藏0

发表评论

0条评论

microelec

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<