Apache Storm Spout向Bolt传String ArrayList收空列表排查
Storm Spout向Bolt传递String类型ArrayList为空的问题排查
问题复现
参考Bolt间传递数组的实现方案编码后,ArrayList<Integer>可正常从Spout传递到Bolt,但ArrayList<String>传递后Bolt端接收到空列表,复现信息如下:
- Spout端两类列表声明(不同运行批次分别启用,注释掉另一组):
ArrayList<String> stringbuckets = new ArrayList<>(); ArrayList<Integer> integerbuckets = new ArrayList<>();
- 发射前控制台打印的列表实际取值:
- stringbuckets:
[Airdrop, BabyDogeArmy, BEOMGYU, CorbynWasRight, MOVEZ, TOMORROW_X_TOGETHER] - integerbuckets:
[1, 2, 3]
- stringbuckets:
- Spout端发射语句(不同运行批次分别启用,注释掉另一组):
this.collector.emit(new Values(stringbuckets)); this.collector.emit(new Values(integerbuckets));
- Bolt端接收到的tuple打印结果:
发射String ArrayList时的tuple:
tuple source: twitterSpout:3, stream: default, id: {}, [[]] PROC_START_TIME(sampled): null EXEC_START_TIME(sampled): null发射Integer ArrayList时的tuple:
tuple source: twitterSpout:3, stream: default, id: {}, [[1, 2, 3]] PROC_START_TIME(sampled): null EXEC_START_TIME(sampled): null
故障原因
这个问题是Storm默认序列化机制+Java泛型擦除共同导致的:
- Storm内置序列化仅对基本类型、基本类型包装类(包含Integer)、String、字节数组做了针对性适配,带泛型的集合类不在默认的精准序列化支持范围内。
- 传递
ArrayList<Integer>时,Integer属于内置序列化支持的类型,Storm可以直接识别集合内元素类型,把整个列表作为单个tuple字段完成序列化传输。 - 传递
ArrayList<String>时,受Java泛型擦除影响,序列化阶段无法直接获取集合内的String泛型类型,会触发默认的集合拆分逻辑:把集合内的每个String元素当成独立的tuple字段处理,而不是把整个列表作为单个字段。如果declareOutputFields方法中只声明了1个输出字段,拆分后的多元素和声明字段数不匹配,就会出现字段解析异常,最终拿到空列表。
解决方案
- 方案1(最稳妥,无额外配置):不要直接发射ArrayList,发射前将
ArrayList<String>转为Storm原生支持的String[]数组类型即可,数组类型不会触发错误的拆分逻辑:// 转换为String数组后再发射 this.collector.emit(new Values(stringbuckets.toArray(new String[0]))); - 方案2:显式注册集合类的Kryo序列化器,在拓扑配置中添加如下规则,让Storm可以正确识别泛型集合类型:
Config conf = new Config(); conf.registerSerialization(ArrayList.class); - 方案3:检查
declareOutputFields方法的输出字段声明,确保声明的字段数量、类型和发射的Values结构完全匹配,避免字段数不匹配导致的解析截断。
内容的提问来源于stack exchange,提问作者jmuth
相关产品推荐
相关产品推荐

