以上的this.syncRunners就是SyncRunner線程池??梢钥吹?,通過計算syncRunnerIndex,采用了簡單的輪循提交算法。
另外,WAL日志消費線程,會嘗試收集一批SyncFuture對象(即sync操作),一次提交給SyncRunner。
所以,在以上代碼中,可以看到傳入offer()方法的,是this.syncFutures這一SyncFutures[]數(shù)組,而不是單個SyncFuture對象。
收集一批次再提交,性能比較好。但是單個批次需要積攢的SyncFuture對象越多,則Sync的及時性越差,會導(dǎo)致前臺Region Server RPC服務(wù)線程阻塞在SyncFuture.get()上的時間就越長。
因此,這里存在吞吐量和及時性之間的平衡。HBase為了支持海量數(shù)據(jù)的寫入,在這里更傾向于 高吞吐量 ,體現(xiàn)在了以下注釋中。具體多少個SyncFuture構(gòu)成一個批次,有一定的策略,在此不再累述。

SyncRunner線程
1. 從隊列中獲取一個由WAL日志消費線程提交的SyncFuture(下圖紅框中的代碼)。
2. 調(diào)用文件系統(tǒng)API,執(zhí)行sync()操作(下圖藍(lán)框中的代碼)
合并多次頻繁的sync()操作,提高性能。
上文提到,WAL日志消費線程一次會提交多個SyncFuture。對此,SyncRunner線程只會落實執(zhí)行其中最新的SyncFuture(也就是Sequence ID最大的那個)所代表的Sync操作。而忽略之前的SyncFuture。
這就是下圖綠框中的代碼。

3. 如果sync()完成,或者因為上面提到的合并忽略了某一個SyncFuture,那么會調(diào)用releaseSyncFuture() ==> Object.notify()來通知SyncFuture阻塞退出。
之前阻塞在SyncFuture.get()上的Region Server RPC服務(wù)線程就可以繼續(xù)往下執(zhí)行了。

至此,整個WAL寫入流程完成。
總結(jié)
我覺得對線程并發(fā)寫入文件時,用隊列來協(xié)調(diào),保證日志寫入的順序,這還是比較容易想到的。
但是,提供Sync() API確保日志寫入的可靠性,同時避免頻繁的Sync()操作影響性能。——這是HBase WAL實現(xiàn)的一大亮點。