配信保証
メッセージの順序
Socket.IO は、どの低レベルトランスポートが使用されていても(HTTP ロングポーリングから WebSocket へのアップグレード中でも)、メッセージの順序を保証します。
これは以下のことによって実現されます。
- 基盤となる TCP 接続によって提供される保証
- アップグレードメカニズムの注意深い設計
例
socket.emit("event1");
socket.emit("event2");
socket.emit("event3");
上記の例では、イベントは常に相手側で同じ順序で受信されます(実際に到着する場合、下記参照)。
メッセージの到着
最大1回
デフォルトでは、Socket.IO は 最大1回 の配信保証を提供します。
- イベントの送信中に接続が切断された場合、相手側がそれを受信した保証はなく、再接続時に再試行は行われません。
- 切断されたクライアントは、再接続までイベントをバッファリングします(ただし、前の点は依然として適用されます)。
- サーバーにはそのようなバッファがないため、切断されたクライアントが見逃したイベントは、再接続時にそのクライアントに送信されません。
情報
現時点では、追加の配信保証はアプリケーションで実装する必要があります。
少なくとも1回
クライアントからサーバーへ
クライアント側からは、確認応答とタイムアウトを使用して、少なくとも1回の保証を実現できます。
function emit(socket, event, arg) {
socket.timeout(2000).emit(event, arg, (err) => {
if (err) {
// no ack from the server, let's retry
emit(socket, event, arg);
}
});
}
emit(socket, "foo", "bar");
上記の例では、クライアントは指定された遅延の後、イベントの送信を再試行するため、サーバーは同じイベントを複数回受信する可能性があります。
注意
その場合でも、ユーザーがタブを更新すると、保留中のイベントは失われます。
サーバーからクライアントへ
サーバーから送信されたイベントの場合、追加の配信保証は、次のように実装できます。
- 各イベントに一意のIDを割り当てる
- イベントをデータベースに永続化する
- クライアント側で最後に受信したイベントのオフセットを保存し、再接続時に送信する
例
クライアント
const socket = io({
auth: {
offset: undefined
}
});
socket.on("my-event", ({ id, data }) => {
// do something with the data, and then update the offset
socket.auth.offset = id;
});
サーバー
io.on("connection", async (socket) => {
const offset = socket.handshake.auth.offset;
if (offset) {
// this is a reconnection
for (const event of await fetchMissedEventsFromDatabase(offset)) {
socket.emit("my-event", event);
}
} else {
// this is a first connection
}
});
setInterval(async () => {
const event = {
id: generateUniqueId(),
data: new Date().toISOString()
}
await persistEventToDatabase(event);
io.emit("my-event", event);
}, 1000);
不足しているメソッド(fetchMissedEventsFromDatabase()
、generateUniqueId()
、persistEventToDatabase()
)の実装はデータベース固有であり、読者の課題として残されます。
参考資料
socket.auth
(クライアント)socket.handshake
(サーバー)