问题:
最近项目中遇到要在Bolt A 中添加缓存cache,在Bolt B中删除缓存cache的需求。
思路:
两个思路
- 在拓扑(Topology)中创建一个缓存,将创建的缓存以构造函数传参的形式传递进去。
- 在上游的Bolt,也就是Bolt A中创建一个缓存,并在该类中添加get方法。
第一种思路(构造函数传参:实验证明不可用)
原因:storm的prepare中会对对象进行序列化和反序列化,之后形成一个新的对象。传递进来的对象并没有使用。
以下代码是一个简写,主函数中会创建一个set对象,通过构造函数传递到bolt中。
此时set的对象地址传递到了boltA和boltB中。
在storm继续运行启动prepare的时候,storm框架会对你创建的对象进行序列化和反序列化,之后重新创建一个对象。
此时,主函数一个对象、BoltA一个对象、BoltB一个对象。一共有三个对象,不存在共用缓存的说法。
// 主函数
public static void main(String[] args){
Set<String> set = new HashSet<>();
TopologyBuilder builder = new TopologyBuilder();
builder.setSpout("Spout", new Spout());
builder.setBolt("BoltA", new BoltA(set)).globalGrouping("Spout");
builder.setBolt("BoltB", new BoltB(set)).globalGrouping("Spout");
}
// Bolt A
private static HashSet<String> setA;
public BoltA(HashSet<String> set) {
this.setA = set;
}
public void prepare(Map conf, TopologyContext context, OutputCollector collector) {
}
// Bolt B
private static HashSet<String> setB;
public BoltB(HashSet<String> set) {
this.setB = set;
}
public void prepare(Map conf, TopologyContext context, OutputCollector collector) {
}
更为详细的解释
private Map<String, Integer> counts = null;
/**
* 注意: 所有的序列化操作最好都在prepare方法中进行 原因:
* Storm在工作时会将所有的bolt和spout组件先进行序列化,然后发送到集群中,
* 如果在序列化之前创建过任何无法序列化的对象都会造成序列化时抛出NotSerializableException。
* 此处的HashMap本身是可以序列化的所以不会有这个问题,但是有必要养成这样的习惯 。
*/
@Override
public void prepare(Map stormConf, TopologyContext context, OutputCollector collector) {
this.collector = collector;
this.counts = new HashMap<>();
}
第二种思路(使用get方法,实验可以使用)
原因:在storm的prepare中初始化对象,通过get方法获取到的是同一个对象引用。
在BoltA中创建一个缓存,并添加一个get方法,在BoltB中调用。
此时传递的是同一个对象引用。
// Bolt A
private static ConcurrentHashSet<String> setA;
public void prepare(Map conf, TopologyContext context, OutputCollector collector) {
setA = new ConcurrentHashSet<>();
}
public static ConcurrentHashSet<String> getSetA() {
return setA;
}
// Bolt B
public void execute(Tuple tuple) {
BoltA.getSetA.remove();
}