本文由作者林洋港授权网易云社区发布。

作为服务端程序,我们总是需要向外界报告一些统计数据,以助于了解系统的运行情况,比如某个接口的调用时间、系统处理的请求数等等。当我们的程序以Storm Topology的形式运行时同样需要输出这些统计数据。Storm为我们提供了Metric接口,可以方便的把一些统计指标输出到指定的地方。Storm Metric的统计方式为每隔指定的时间间隔输出统计内容。本文首先介绍Storm Metric相关的接口以及它们之间的关系,然后以实际应用中的一个例子来说明如何使用Metric接口。本文使用的Storm版本为0.9.1-incubating。

IMetric是Storm用于保存统计数据的接口

public interface IMetric {

public Object getValueAndReset();

}

接口只有一个getValueAndReset方法,当需要输出统计内容时,Storm就会调用这个方法。值得注意的是getValueAndReset方法返回的是Object类型,这为统计内容的形式提供了灵活性,我们可以返回任意的类型作为统计信息,这一点在后面的例子中我们会再提到。另一个引起我们注意的地方是IMetric接口并没有声明更新统计数据的方法,这样当我们实现IMetric接口的时候就更加灵活了——参数类型、参数个数都没有限制。Storm自身提供了6个IMetric实现:AssignableMetric、CombinedMetric、CountMetric、MultiCountMetric、ReducedMetric、StateMetric。这里只介绍CountMetric和MultiCountMetric的使用方式,以印证前面说的IMetric接口统计数据更新方式的灵活性以及getValueAndReset返回Object类型的灵活性。CountMetric就是一个简单的计数器,有两个方法incr()和incrBy(long incrementBy),其getValueAndReset方法返回一个long类型的值:

public Object getValueAndReset() {

long ret = _value;

_value = 0;

return ret;

}

MultiCountMetric,顾名思义,就是多个指标的计数器,维护着一个Map,只有一个方法CountMetric scope(String key)。因此MultiCountMetric的更新方式为MultiCountMetric.scope(key).incr()或MultiCountMetric.scope(key).incrBy(long incrementBy)。它的getValueAndReset返回的是一个Map:

public Object getValueAndReset() {

Map ret = new HashMap();

for(Map.Entry e : _value.entrySet()) {

ret.put(e.getKey(), e.getValue().getValueAndReset());

}

return ret;

}

除了IMetric接口,还有另外一个接口IMetricsConsumer,它负责向外输出统计信息,即把IMetric getValueAndReset方法返回的数据输出到外面。IMetricsConsumer有三个方法

void prepare(Map stormConf, Object registrationArgument, TopologyContext context, IErrorReporter errorReporter);

void handleDataPoints(TaskInfo taskInfo, Collection dataPoints);

void cleanup();

其中prepare是初始化,cleanup是生命周期结束时的清理工作,handleDataPoints才是真正的统计信息输出方法,taskInfo参数存储当前task的信息(host、port、component id、task id等等),dataPoints存储的是IMetric返回的统计信息,可能是出于性能考虑,dataPoints是一个集合,包含了多个IMetric返回的数据。让我们来具体看看DataPoint这个类:

public static class DataPoint {

@Override

public String toString() {

return "[" + name + " = " + value + "]";

}

public String name;

public Object value;

}

name是IMetric注册时的名字,value就是IMetric getValueAndReset返回的那个Object。

Storm只提供了一个IMetricsConsumer实现——LoggingMetricsConsumer。LoggingMetricsConsumer做的事情很简单,就是把dataPoints输出到日志文件metrics.log,下面是其handleDataPoints方法的部分代码:

for (DataPoint p : dataPoints) {

sb.delete(header.length(), sb.length());

sb.append(p.name)

.append(padding).delete(header.length()+23,sb.length()).append("\t")

.append(p.value);

LOG.info(sb.toString());

}

可以看到它通过调用DataPoint的value的toString方法把统计信息输出到日志里面的,所以如果你的IMetric实现返回的是自己定义的类型,记得重载toString()方法,让统计信息以可读的格式输出。

到这里Storm的Metric接口和自带的实现基本介绍完了,接下来我们来看看怎么使用Storm自带的这些实现。首先,Storm默认的配置是关掉Metric功能的,可以有两种方式开启Metric功能:

1)在storm.yaml里面配置,这种是集群级别的设置,个人不建议这么做,所以就不多介绍了

2)conf.registerMetricsConsumer(Class klass, long parallelismHint);这是topology级别的,klass是IMetricsConsumer的实现类,parallelismHint这个参数Storm代码里面没注释我也没深入看底层的实现,这里结合自己的实验谈谈它的意义:topology是在1个或多个worker上面以多个task的方式跑的嘛,parallelismHint就是指定多少个并发来输出统计信息。这里我也不知道parallelismHint指的是多个task、worker还是supervisor,反正parallelismHint=1的时候只在特定的一个supervisor下面的metrics.log有统计信息,parallelismHint>1时可能取决于worker的数量,我测试的时候由于是在多个supervisor上跑的,因此观察到多个supervisor都有metrics.log的输出。个人经验是parallelismHint设为1,这样可以在一个supervisor下面的metrics.log就能看到所有task的统计信息。

由于我建议采用第二种方法,所以示例代码为:

//客户端注册IMetricsConsumer

conf.registerMetricsConsumer(LoggingMetricsConsumer.class);

StormSubmitter.submitTopology(name, conf, builder.createTopology());

//我们假设要统计spout某段代码的调用次数

//注册IMetric

@Override

public void open(Map conf, TopologyContext context, SpoutOutputCollector collector) {

...

metric=new CountMetric();

context.registerMetric("spout time cost", metric, 60);  //因此DataPoint的name为spout time cost,60表示1分钟统计一次

...

}

//更新统计数据

@Override

public void nextTuple() {

if(...)...

else{

...

metric.incr();

}

}

这样就可以了,然后你就能在metrics.log看到统计数据了。

现在,假设我们的需求跟上面不太一样:1)metrics.log只打印我们自己维护的统计信息,屏蔽__system、__fail-count这种系统自己的统计信息;2)不只统计代码的调用次数,还要统计调用时间——最小时间、最大时间、平均时间。

第一点可以通过重载LoggingMetricsConsumer的方法来实现:

public class AppLoggingMetricsConsumer extends LoggingMetricsConsumer {

@Override

public void handleDataPoints(TaskInfo taskInfo, Collection<DataPoint> dataPoints) {

if (taskInfo.srcComponentId != null && taskInfo.srcComponentId.startsWith("__")) return;

if (dataPoints == null || dataPoints.isEmpty()) return;

List<DataPoint> list = new ArrayList<DataPoint>();

for (DataPoint p : dataPoints) {

if (p.name == null || p.name.startsWith("__")) continue;

list.add(p);

}

if (list.isEmpty()) return;

super.handleDataPoints(taskInfo, list);

}

}

第二点需要开发我们自己的IMetric接口实现类TimeCostMetric,以下是其主要代码:

@Override

public Object getValueAndReset() {

TimeCost timeCost=new TimeCost();

timeCost.count=count;

if(timeCost.count>0){

timeCost.min=min;

timeCost.max=max;

timeCost.mean=all*1.0/timeCost.count;

}

init();

return timeCost;

}

public void update(long time){

count++;

all+=time;

if(min>time)min=time;

if(max<time)max=time;

}

public static class TimeCost implements Serializable{

private static final long serialVersionUID = 8355726599226036228L;

int count;

long min;

long max;

double mean;

public String toString(){

return "count: "+count+", min: "+min+", max:"+max+", mean: "+mean;

}

}

TimeCostMetric的getValueAndReset方法返回的是一个TimeCost 对象,日志中最终打印的就是其toString方法的内容。然后把前面红色部分的代码改成下面的内容:

① conf.registerMetricsConsumer(AppLoggingMetricsConsumer .class);

② metric=new TimeCostMetric();

context.registerMetric("MQ spout time cost", metric, 60);

③ metric.incr();

再来看看metrics.log

本文中是直接把统计信息打到日志中,你也可以自己实现IMetricsConsumer接口,把统计信息保存到指定的地方,如数据库、监控平台等等。

免费领取验证码、内容安全、短信发送、直播点播体验包及云服务器等套餐

更多网易技术、产品、运营经验分享请访问网易云社区

相关文章:
【推荐】 NOS跨分区灾备设计与实现

Storm的Metric接口简介的更多相关文章

  1. Linux中的IO复用接口简介(文件监视?)

    I/O复用是Linux中的I/O模型之一.所谓I/O复用,指的是进程预先告诉内核,使得内核一旦发现进程指定的一个或多个I/O条件就绪,就通知进程进行处理,从而不会在单个I/O上导致阻塞. 在Linux ...

  2. java多线程Lock接口简介使用与synchronized对比 多线程下篇(三)

    前面的介绍中,对于显式锁的概念进行了简单介绍 显式锁的概念,是基于JDK层面的实现,是接口,通过这个接口可以实现同步访问 而不同于synchronized关键字,他是Java的内置特性,是基于JVM的 ...

  3. SoapUI SoapUI测试WebService协议接口简介

    SoapUI测试WebService协议接口简介 by:授客 QQ:1033553122 1. 创建项目,入口:File -> New SOAP Project,或者右键默认项目Project- ...

  4. SQLite3 C/C++ 开发接口简介

    SQLite3 C/C++ 开发接口简介 1.0 总览 SQLite3是SQLite一个全新的版本,它虽然是在SQLite 2.8.13的代码基础之上开发的,但是使用了和之前的版本不兼容的数据库格式和 ...

  5. soapUI 使用soapUI测试http+json协议接口简介

    使用soapUI测试http+json协议接口简介 by:授客 QQ:1033553122 SoapUI-Pro-x64-5.1.2_576025(含破解文件),软件下载地址: http://pan. ...

  6. ADI高速信号采集芯片与JESD204B接口简介

    ADI高速信号采集芯片与JESD204B接口简介 JESD204B接口 介绍: JEDEC Standard No. 204B (JESD204B)—A standardized serial int ...

  7. Lock接口简介

    在Java多线程编程中,我们经常使用synchronized关键字来实现同步,控制多线程对变量的访问,来避免并发问题. 但是有的时候,synchronized关键字会显得过于沉重,不够灵活.synch ...

  8. Windows数据库编程接口简介

    数据库是计算机中一种专门管理数据资源的系统,目前几乎所有软件都需要与数据库打交道(包括操作系统,比如Windows上的注册表其实也是一种数据库),有些软件更是以数据库为核心因此掌握数据库系统的使用方法 ...

  9. Storm 学习之路(一)—— Storm和流处理简介

    一.Storm 1.1 简介 Storm 是一个开源的分布式实时计算框架,可以以简单.可靠的方式进行大数据流的处理.通常用于实时分析,在线机器学习.持续计算.分布式RPC.ETL等场景.Storm具有 ...

随机推荐

  1. php SESSON共享 (mysql方式)

    为什么要进行session共享? 因为一些大型网站,通常会有很多服务器,每个服务器运行不同的业务模块,并使用二级域名(或是完全不同的域名),而用户系统是统一的,通过登陆名.密码来登陆各模块.用户数据放 ...

  2. spring框架之依赖注入(DI)

    1. IOC和DI的概念 * IOC -- Inverse of Control,控制反转,将对象的创建权反转给Spring!! * DI -- Dependency Injection,依赖注入,在 ...

  3. Parallel.Foreach的基础知识

    微软的并行运算平台(Microsoft’s Parallel Computing Platform (PCP))提供了这样一个工具,让软件开发人员可以有效的使用多核提供的性能. Visual Stud ...

  4. Package gtk+-3.0 was not found in the pkg-config search path

    问题描述: 在fedora21系统上通过rpmbuild构建fcitx的二进制包时出现以上错误,经老程序员指点:“是相应的开发包没有安装” 解决办法: yum installl gtk3-devel  ...

  5. 对FPKM/RPKM以及TPM的理解

    对FPKM/RPKM以及TPM的理解 2018年07月03日 16:05:53 sixu_9days 阅读数:559 标签: FPKM/RPKMTPMRNA-Seq 更多 个人分类: RNA-Seq ...

  6. BZOJ2424 [HAOI2010]订货 - 费用流

    题解 (非常裸的费用流 题意有一点表明不清: 该月卖出的商品可以不用算进仓库里面. 然后套上费用流模板 代码 #include<cstring> #include<queue> ...

  7. Yandex插件使用说明——Slager_Z

    Yandex插件使用说明——Slager_Z     操作步骤:   1.1使用Chrome浏览器安装插件 / 1.2使用crx格式文件 2.  改装成Yandex可使用文件 3.  通过Yandex ...

  8. 还不好好读书吗?清华3D录取通知书出炉,还能动!

    近日,清华大学2018录取通知书“亮相”!看完后,网友直呼:哪里可以买到? 打开录取通知书 3D“二校门”跃然纸上 由清华师生共同打造.手工定制.独一无二的2018新版录取通知书来了!在新版录取通知书 ...

  9. 教你用PS修复老照片

    原图素材虽然很旧,不过人物部分并没有怎么损坏,只是有一些色块和杂色.修复的工程相对来说也少很多.只需要给人物磨好皮,然后把暗调和高光部分调出来即可.原图     一.打开原图素材,按Ctrl + J ...

  10. tomcat 启动报 找不到 StrutsPrepareAndExecuteFilter。。

    <?xml version="1.0" encoding="UTF-8"?><web-app xmlns:xsi="http://w ...