CopyOnWrite 思想在 Kafka 源碼中的運用服務器
在 Kafka 的內核源碼中,有這麼一個場景,客戶端在向 Kafka 寫數據的時候,會把消息先寫入客戶端本地的內存緩衝,而後在內存緩衝裏造成一個 Batch 以後再一次性發送到 Kafka 服務器上去,這樣有助於提高吞吐量。
請看下圖:數據結構
這個時候 Kafka 的內存緩衝用的是什麼數據結構呢?
請看源碼:併發
private final ConcurrentMap<TopicPartition, Deque<RecordBatch>> batches = new CopyOnWriteMap<TopicPartition, Deque<RecordBatch>>();
這個數據結構就是核心的用來存放寫入內存緩衝中的消息的數據結構,要看懂這個數據結構須要對不少 Kafka 內核源碼裏的概念進行解釋。Kafka 是本身實現了一個 CopyOnWriteMap,這個CopyOnWriteMap 採用的就是 CopyOnWrite 思想。
咱們來看一下這個 CopyOnWriteMap 的源碼實現:ide
// 典型的volatile修飾普通Map private volatile Map<K, V> map; @Override public synchronized V put(K k, V v) { // 更新的時候先建立副本,更新副本,而後對volatile變量賦值寫回去 Map<K, V> copy = new HashMap<K, V>(this.map); V prev = copy.put(k, v); this.map = Collections.unmodifiableMap(copy); return prev; } @Override public V get(Object k) { // 讀取的時候直接讀volatile變量引用的map數據結構,無需鎖 return map.get(k); }
Kafka 這個核心數據結構在這裏之因此採用 CopyOnWriteMap 思想來實現,就是由於這個 Map 的 Key-Value 對,其實沒那麼頻繁更新。性能
也就是 TopicPartition-Deque 這個 Key-Value 對,更新頻率很低。可是它的 Get 操做倒是高頻的讀取請求,由於會高頻的讀取出來一個 TopicPartition 對應的 Deque 數據結構,來對這個隊列進行入隊出隊等操做,因此對於這個 Map 而言,高頻的是其 Get 操做。這個時候,Kafka 就採用了 CopyOnWrite 思想來實現這個 Map,避免更新 Key-Value 的時候阻塞住高頻的讀操做,實現無鎖的效果,優化線程併發的性能。優化
相信看完這個文章,對於 CopyOnWrite 思想以及適用場景,包括 JDK 中的實現,以及在 Kafka 源碼中的運用,都有了一個切身的體會了。this