4、まとめ
インタビューは尋ねられました:メッセージを受け取った後、ブローカーはどのように持続しますか?
回答者:同期と非同期の2つの方法があります。通常、非同期を選択します。同期効率は低くなりますが、信頼性は高くなります。メッセージストレージの一般的な原則は次のとおりです。
コアクラスのMappedFileは、各commitlogファイルに対応します。MappedFileQueueはフォルダーに相当し、すべてのファイルを管理します。一部の操作の提供を担当するマネージャーのCommitLogオブジェクトもあります。具体的には、ブローカーはメッセージを取得した後、最初にメッセージ、トピック、キューなどをByteBufferに格納し、次にそれをcommitlogファイルに保持します。commitlogファイルのサイズは1Gです。サイズを超えると、nioメソッドを使用して、ストレージ用に新しいcommitlogファイルが作成されます。
5.補足:同期/非同期ブラッシング
1.主なカテゴリ
クラス名 | 説明 | ブラシ性能 |
---|---|---|
CommitRealTimeService | 非同期フラッシュディスク&&オープンバイトバッファ | 最高 |
FlushRealTimeService | 非同期フラッシュディスク&&メモリバイトバッファを閉じる | より高い |
GroupCommitService | 同期フラッシュ。ディスクが正常にフラッシュされた後、メッセージが返されます。 | 最低 |
2.グラフィック
3.同期ブラッシング
3.1、ソースコード
// {@link org.apache.rocketmq.store.CommitLog#submitFlushRequest()}
// Synchronization flush
if (FlushDiskType.SYNC_FLUSH == this.defaultMessageStore.getMessageStoreConfig().getFlushDiskType()) {
// 同步刷盘service -> GroupCommitService
final GroupCommitService service = (GroupCommitService) this.flushCommitLogService;
if (messageExt.isWaitStoreMsgOK()) {
// 数据准备
GroupCommitRequest request = new GroupCommitRequest(result.getWroteOffset() + result.getWroteBytes(),
this.defaultMessageStore.getMessageStoreConfig().getSyncFlushTimeout());
// 将数据对象放到requestsWrite里
service.putRequest(request);
return request.future();
} else {
service.wakeup();
return CompletableFuture.completedFuture(PutMessageStatus.PUT_OK);
}
}
putRequest
public synchronized void putRequest(final GroupCommitRequest request) {
synchronized (this.requestsWrite) {
this.requestsWrite.add(request);
}
// 这里很关键!!!,给他设置成true。然后计数器-1。下面run方法的时候才会进行交换数据且return
if (hasNotified.compareAndSet(false, true)) {
waitPoint.countDown(); // notify
}
}
実行
public void run() {
while (!this.isStopped()) {
try {
// 是同步还是异步的关键方法,也就是说组不阻塞全看这里。
this.waitForRunning(10);
// 真正的刷盘逻辑
this.doCommit();
} catch (Exception e) {
CommitLog.log.warn(this.getServiceName() + " service has exception. ", e);
}
}
}
waitForRunning
protected volatile AtomicBoolean hasNotified = new AtomicBoolean(false);
// 其实就是CountDownLatch
protected final CountDownLatch2 waitPoint = new CountDownLatch2(1);
protected void waitForRunning(long interval) {
// 如果是true,且给他改成false成功的话,则onWaitEnd()且return,但是默认是false,也就是默认情况下这个if不会进。
if (hasNotified.compareAndSet(true, false)) {
this.onWaitEnd();
return;
}
//entry to wait
waitPoint.reset();
try {
// 等待,默认值是1,也就是waitPoint.countDown()一次后就会激活这里。
waitPoint.await(interval, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
log.error("Interrupted", e);
} finally {
// 给状态值设置成false
hasNotified.set(false);
this.onWaitEnd();
}
}
3.2。まとめ
同期ブラッシングの主なプロセスを要約します。
コアクラスはGroupCommitServiceであり、コアメソッドはwaitForRunningです。
-
最初にputRequestメソッドを呼び出してhasNotifiedをtrueに変更し、通知します
waitPoint.countDown()
。つまり、です。 -
runメソッドに続く
waitForRunning()
、waitForRunning()
hasNotifiedが真でないかを決定するために、それはそれはない直接的なリターンを遮断待つために、である、交換データへのtrueの場合、リターン・スワップです。 -
最後に、前のステップに戻り、ブロッキングがない場合は、doCommitを呼び出して実際の更新を実行するのが論理的です。
4.非同期ブラッシング
4.1、ソースコード
コアクラスは次のとおりです。FlushRealTimeService
// {@link org.apache.rocketmq.store.CommitLog#submitFlushRequest()}
// Asynchronous flush
if (!this.defaultMessageStore.getMessageStoreConfig().isTransientStorePoolEnable()) {
flushCommitLogService.wakeup();
} else {
commitLogService.wakeup();
}
return CompletableFuture.completedFuture(PutMessageStatus.PUT_OK);
実行
// {@link org.apache.rocketmq.store.CommitLog.FlushRealTimeService#run()}
class FlushRealTimeService extends FlushCommitLogService {
@Override
public void run() {
while (!this.isStopped()) {
try {
// 每隔500ms刷一次盘
if (flushCommitLogTimed) {
Thread.sleep(500);
} else {
// 根上面同步刷盘调用的是同一个方法,区别在于这里没有将hasNotified变为true,也就是还是默认的false,那么waitForRunning方法内部的第一个判断就不会走,就不会return掉,就会进行下面的await方法阻塞,默认阻塞时间是500毫秒。也就是默认500ms刷一次盘。
this.waitForRunning(500);
}
// 调用mappedFileQueue的flush方法
CommitLog.this.mappedFileQueue.flush(flushPhysicQueueLeastPages);
} catch (Throwable e) {
}
}
}
}
4.2。まとめ
コアクラス#メソッド:FlushRealTimeService#run()
-
flushCommitLogTimed
trueかどうかを判断し、デフォルトはfalseです。trueの場合は、直接スリープ(500ms)してからmappedFileQueue.flush()
、ディスクをフラッシュします。 -
falseの場合は、次のように入力し
waitForRunning(500)
ます。これが同期フラッシュとの違いの鍵です。同期フラッシュの前に、hasNotifiedがtrueに変更されるため、一連の小さなトリックが直接行われます。はいreturn+doCommit
、非同期はここで直接呼び出されます。waitForRunning(500)
この直前には何もありません。hasNotifiedの操作は戻らないwaitPoint.await(500, TimeUnit.MILLISECONDS);
ため、500ミリ秒の間ブロックを続け、500ミリ秒後に自動的にウェイクアップしてからディスクをフラッシュします。つまり、ディスクが非同期で更新される場合、デフォルトでは500msでディスクを1回更新します。