记录Kafka producer中读写分离设计的CopyOnWriteMap。
public class CopyOnWriteMap<K, V> implements ConcurrentMap<K, V> {
//k:topic-partition信息
//v:DQueue,每一个topic的分区对应一个队列,队列里面存放batch
private volatile Map<K, V> map;
@Override
//读写分离保证了在读取this.map的时候不用加锁也是线程安全的
//这就保证了读取数据的时候效率非常高
public V get(Object k) {
return map.get(k);
}
@Override
public synchronized V put(K k, V v) {
//读写分离设计
//每一次put都是new一个新的map,然后将this.map指向这个地址,并且this.map是volatile的,其他线程可见
//读写分离保证了在读取this.map的时候不用加锁也是线程安全的,但是写数据的时候需要耗费额外的空间
Map<K, V> copy = new HashMap<K, V>(this.map);
V prev = copy.put(k, v);
this.map = Collections.unmodifiableMap(copy);
return prev;
}
}
读写分离的设计使得使用这个数据类型的时候,写数据时需要耗费更多的内存空间,但是在读取数据的时候不需要加锁,读取效率非常的高效。
Kafka Producer中的使用场景:
Kafka Producer可以批量发送数据,每一个批次是一个batch,这些ProducerBatch根据Topic-Partition不同分别放在各自的DQueue中,所以Kafka使用了上面的这个数据类型保存这些数据,可想而知,key就是Topic-Partition,value就是DQueue。
我们分析一下这个map的读写量:
写数据:
第一次发送一个topic的一个partition数据时需要些一个DQueue,也就是说一个topic-partition只需要写一次。
读数据:
每一条发送的消息需要封装到ProducerBatch去的时候,都需要根据topic-partition获取它的DQueue,这就有可能非常非常大。
所以Kafka利用这个数据结构大大提升了封装消息的效率。