最近在做一个监控系统,用来监控网站上各个业务功能的调用量以及处理时间,以便及时发现问题,及时处理。做这种实时统计处理系统,自然首先想到了storm,于是现学现用,自然遇到了一些坑,而且不少是网上也难以找到的问题。在这里就做个记录,记录下这个最让我苦恼的错误。

  首先我的业务逻辑是按分钟统计一分钟中的调用次数的数据,所以我在bolt里跑了一个定时器,定时将统计数据发到下一个bolt入库。所在我在定时器执行的代码里调用了OutputCollector发射到下一个bolt。本地调试没啥问题,就部署到外网环境测试。通常也没发现问题,但是偶尔会出现这种错误,作为开发人员最讨厌的就是这种可复现率很低的错误 。

  这里是错误日志:

5675 [Thread-7-disruptor-executor[2 2]-send-queue] ERROR backtype.storm.daemon.executor -
java.lang.RuntimeException: java.lang.NullPointerException
at backtype.storm.utils.DisruptorQueue.consumeBatchToCursor(DisruptorQueue.java:128) ~[storm-core-0.9.3.jar:0.9.3]
at backtype.storm.utils.DisruptorQueue.consumeBatchWhenAvailable(DisruptorQueue.java:99) ~[storm-core-0.9.3.jar:0.9.3]
at backtype.storm.disruptor$consume_batch_when_available.invoke(disruptor.clj:80) ~[storm-core-0.9.3.jar:0.9.3]
at backtype.storm.disruptor$consume_loop_STAR_$fn__1460.invoke(disruptor.clj:94) ~[storm-core-0.9.3.jar:0.9.3]
at backtype.storm.util$async_loop$fn__464.invoke(util.clj:463) ~[storm-core-0.9.3.jar:0.9.3]
at clojure.lang.AFn.run(AFn.java:24) [clojure-1.5.1.jar:na]
at java.lang.Thread.run(Thread.java:722) [na:1.7.0_15]
Caused by: java.lang.NullPointerException: null
at clojure.lang.RT.intCast(RT.java:1087) ~[clojure-1.5.1.jar:na]
at backtype.storm.daemon.worker$mk_transfer_fn$fn__3549.invoke(worker.clj:129) ~[storm-core-0.9.3.jar:0.9.3]
at backtype.storm.daemon.executor$start_batch_transfer__GT_worker_handler_BANG_$fn__3283.invoke(executor.clj:258) ~[storm-core-0.9.3.jar:0.9.3]
at backtype.storm.disruptor$clojure_handler$reify__1447.onEvent(disruptor.clj:58) ~[storm-core-0.9.3.jar:0.9.3]
at backtype.storm.utils.DisruptorQueue.consumeBatchToCursor(DisruptorQueue.java:125) ~[storm-core-0.9.3.jar:0.9.3]
... 6 common frames omitted 5697 [Thread-7-disruptor-executor[2 2]-send-queue] ERROR backtype.storm.util - Halting process: ("Worker died")
java.lang.RuntimeException: ("Worker died")
at backtype.storm.util$exit_process_BANG_.doInvoke(util.clj:325) [storm-core-0.9.3.jar:0.9.3]
at clojure.lang.RestFn.invoke(RestFn.java:423) [clojure-1.5.1.jar:na]
at backtype.storm.daemon.worker$fn__3808$fn__3809.invoke(worker.clj:452) [storm-core-0.9.3.jar:0.9.3]
at backtype.storm.daemon.executor$mk_executor_data$fn__3274$fn__3275.invoke(executor.clj:240) [storm-core-0.9.3.jar:0.9.3]
at backtype.storm.util$async_loop$fn__464.invoke(util.clj:473) [storm-core-0.9.3.jar:0.9.3]
at clojure.lang.AFn.run(AFn.java:24) [clojure-1.5.1.jar:na]
at java.lang.Thread.run(Thread.java:722) [na:1.7.0_15]

  如果你也遇到这个问题,相信你第一次看到这个错误一定很痛苦,因为错误日志中没有任何与自己的业务代码相关的记录。所以实在是无从定位问题的所在。痛苦至极的是复现还不那么容易。

  经过我多次猜测尝试,终于测出了问题的所在。下面我先贴出一个会报这个错误的例子代码:

public class Main {

	public static void main(String[] args) {
TopologyBuilder builder = new TopologyBuilder();
builder.setSpout("spout",new TestWordSpout()); builder.setBolt("dispatch", new WordDispatchBolt()).shuffleGrouping("spout");
builder.setBolt("print",new PrintBolt()).fieldsGrouping("dispatch", new Fields("word")); Config conf = new Config(); conf.setDebug(false);
conf.setNumWorkers(1);
//conf.put(Config.TOPOLOGY_MAX_SPOUT_PENDING, 1);
LocalCluster cluster = new LocalCluster();
cluster.submitTopology("test-kafka-1", conf, builder.createTopology());
} }

  

public class TestWordSpout extends BaseRichSpout {

  private static final long serialVersionUID = 1L;
boolean _isDistributed;
SpoutOutputCollector _collector;
String[] words = new String[] {"nathan", "mike", "jackson", "golda", "bertels"};
public TestWordSpout() {
this(true);
} public TestWordSpout(boolean isDistributed) {
_isDistributed = isDistributed;
} public void open(Map conf, TopologyContext context, SpoutOutputCollector collector) {
_collector = collector;
} public void close() { } public void nextTuple() {
Utils.sleep(1000);
final Random rand = new Random();
final String word = words[rand.nextInt(words.length)];
_collector.emit(new Values(word), word+new Random().nextDouble());
} public void ack(Object msgId) {
System.out.println("### ack:"+msgId);
} public void fail(Object msgId) {
System.out.println("### fail:"+msgId);
} public void declareOutputFields(OutputFieldsDeclarer declarer) {
declarer.declare(new Fields("word"));
}

  

public class WordDispatchBolt extends BaseRichBolt{

	private OutputCollector collector;

	@Override
public void prepare(Map stormConf, TopologyContext context,
OutputCollector collector) {
this.collector = collector; new Thread(new Runnable() { @Override
public void run() {
while(true){
send();//不做sleep休眠,否则抛出此异常的几率太小,不容易观察到
}
}
}).start();
} public void send(){
this.collector.emit(new Values(new Random().nextDouble()));
}
@Override
public void execute(Tuple input) {
String word = input.getStringByField("word");
this.collector.emit(new Values(word));
this.collector.ack(input);
} @Override
public void declareOutputFields(OutputFieldsDeclarer declarer) {
declarer.declare(new Fields("word"));
} }

  

public class PrintBolt extends BaseRichBolt {

	private static final long serialVersionUID = 1L;

	@Override
public void prepare(Map stormConf, TopologyContext context,
OutputCollector collector) {
} @Override
public void execute(Tuple input) {
System.out.println(input.getValue(0));
} @Override
public void declareOutputFields(OutputFieldsDeclarer declarer) { } }

  这个代码很简单,就不做详细介绍了。在WordDispatchBolt类里我启动了另一个线程来发射数据到下一个bolt。我的业务代码中与此类似,是通过Timer定时发送数据的(Timer底层其实也是线程,就不多说了)。但是Timer是按分钟调用的,所以出现问题的几率小的可怜,这里我故意零停顿的调用,让此异常发生的几率更大一些。

  如果运行以上例子代码,你也肯定遇到前边贴出的错误异常。如果不知道是OutputCollector的同步问题,相信解决起来绝对让人痛不欲生。既然知道了是同步问题,要么避免在别的线程里调用collector,要么改成同步的。以下是我简单想到的解决方案。(如果有大神还有更好的,希望留言指教)

  对WordDispatchBolt类做如下修改:

public class WordDispatchBolt extends BaseRichBolt{

	private OutputCollector collector;

	@Override
public void prepare(Map stormConf, TopologyContext context,
OutputCollector collector) {
this.collector = collector; new Thread(new Runnable() { @Override
public void run() {
while(true){
send(new Values(new Random().nextDouble()));//不做sleep休眠,否则抛出此异常的几率太小,不容易观察到
}
}
}).start();
} public synchronized void send(List<Object> tuple){
this.collector.emit(tuple);
}
@Override
public void execute(Tuple input) {
String word = input.getStringByField("word");
send(new Values(word));
this.collector.ack(input);
} @Override
public void declareOutputFields(OutputFieldsDeclarer declarer) {
declarer.declare(new Fields("word"));
} }

  到这里,这个坑算是基本得到解决了。之后可能还要大量使用到storm,遇到坑是再做记录。

  ”把遇到的坑记录下来,让后遇到者可以有更多的网络资源查询,以减少排查问题的时间和纠结“

  

storm坑之---同步问题的更多相关文章

  1. storm坑之---传递对象

    继之前遇到的那个同步问题的坑之后(storm坑之---同步问题),最近对代码又做了调整和重构,并且又遇到了另一个storm开发中应该值得警惕的坑.接下来说说这个坑的大体情况. 在我的storm程序中, ...

  2. 【Storm篇】--Storm中的同步服务DRPC

    一.前述 Drpc(分布式远程过程调用)是一种同步服务实现的机制,在Storm中客户端提交数据请求之后,立刻取得计算结果并返回给客户端.同时充分利用Storm的计算能力实现高密度的并行实时计算. 二. ...

  3. vscode sync插件 在不同设备 同步的坑

    sync的好处不言而喻,在不同的设备都可以同步自己的插件和所有配置: 但是有时有总是会有坑, 现在把我遇到的坑记录下来,以防再次踩坑 VSCode 同步方案 VSCode 的插件 Setting Sy ...

  4. 使用JDK的同步容器时,应该避免那些坑?

    摘要:在使用JDK中的同步容器时,应该尽量避免哪些坑 本文分享自华为云社区<[高并发]亿级流量高并发秒杀系统商品"超卖"了,只因使用的JDK同步容器中存在这两个巨大的坑!!( ...

  5. UiAutomator2.0升级填坑记

    UiAutomator2.0升级填坑记 SkySeraph May. 28th 2017 Email:skyseraph00@163.com 更多精彩请直接访问SkySeraph个人站点:www.sk ...

  6. HBase 入门笔记-数据落地篇

    一.前言 关于数据落地方面,HBase官网也有相关介绍.本文主要介绍一下实际工作中涉及的数据存储方面的一些经验和技巧,主要涉及表rowkey设计.数据落地方案 二.表设计 相对于MySQL等关系型数据 ...

  7. 使用ambari搭建Hadoop平台

    1.操作系统 CentoOS Server with GUI(有GUI,有浏览器*ambari基于浏览器*推荐latest stable version)2.分区 默认 + /hadoop3.网络设置 ...

  8. 测试开发【Mock平台】04实战:前后端项目初始化与登录鉴权实现

    [Mock平台]为系列测试开发教程,从0到1编码带你一步步使用Spring Boot 和 Antd React 框架完成搭建一个测试工具平台,希望作为一个实战项目能为你的测试开发学习有帮助. 一.后端 ...

  9. Storm同步调用之DRPC模型探讨

    摘要:Storm的编程模型是一个有向无环图,决定了storm的spout接收到外部系统的请求后,spout并不能得到bolt的处理结果并将结果返回给外部请求.所以也就决定了storm无法提供对外部系统 ...

随机推荐

  1. yuecheng 笑话

    http://115.28.189.219:9898/stock/manager_articles/fundamentals 要闻 http://115.28.189.219:9898/stock/m ...

  2. (function(){...}())与(function(){...})()

    (function(){         ......   }())  或   (function(){            ......   })()  匿名函数自调用,也就是说,定义一个匿名函数 ...

  3. bidi(双向文字)与RTL布局总结

    BIDI 双向文字就是一个字符串中包含了两种文字,既包含从左到右的文字又包含从右到左的文字. 大多数文字都是从左到右的书写习惯,比如拉丁文字(英文字母)和汉字,少数文字是从右到左的书写方式比如阿拉伯文 ...

  4. SpeechLib 语音播报

    SpeechLib这的dll专门用来播放语音,能够识别英语.简体和繁体.并且可以播放声音文件,支持WAV格式,但不支持MP3.在报警场合下已经够用了. 基本播放语音及文件.支持异步. using Sy ...

  5. 通过分析iframe和无阻塞脚本关系能让我们更懂iframe

    在我上篇文章里,我提到一种使用iframe完成无阻塞脚本加载的方式,因为我对iframe的偏见很大,所以上篇文章里我没有展开讨论这个问题. 文章发表后有位网友问了我这样一个问题,下面是他问题的原文,如 ...

  6. WaterfallTree(瀑布树) 详细技术分析系列

    前言 WaterfallTree(瀑布树) 是最强纯C#开源NoSQL和虚拟文件系统-STSdb专有的(版权所有/专利)算法/存储结构. 参考 关于STSdb,我之前写过几篇文章,譬如: STSdb, ...

  7. ios NSLog常见使用

    NSLog常见输出格式 Table 1  Format specifiers supported by the NSString formatting methods and CFString for ...

  8. 我心中的核心组件(可插拔的AOP)~消息组件~完善篇

    回到目录 为什么要有本篇文章 本篇文章主要实现了RTX消息生产者,并且完成了整体的设计方式,之前在设计时消息生产者全局使用单一的生产方式,即一个项目里使用了Email就不能使用SMS,这种设计方法和实 ...

  9. 大叔也说Xamarin~Android篇~Activity之间传递数组

    回到目录 我们在开发应用程序时,不可能只使用一个Layout或者一个Activity,比如你个管理系统,要求用户先登陆然后再使用,这时你至少要有两个activity吧,先登陆一个,然后成功后需要跳到别 ...

  10. [C#反射]C#中的反射解析及使用.

    1.对C#反射机制的理解2.概念理解后,必须找到方法去完成,给出管理的主要语法3.最终给出实用的例子,反射出来dll中的方法 参考: C#反射,MSDN编程指南 反射是一个程序集发现及运行的过程,通过 ...