Python と Go を使うことで Server-Sent Events (SSE) を習得:リアルタイムデータのストリーミング 🚀

Server-Sent Events (SSE) に理解:サーバーからクライアントへのリアルタイムデータのストリーミング 🕒📡

今日のインタラクティブなウェブアプリケーションでは、リアルタイムでのデータ更新がユーザー体験の向上において重要な役割を果たしています。株価のライブ更新、即時チャットメッセージ、またはコメントのストリーミングなど、リアルタイムデータストリーミングは欠かせません。リアルタイム通信のために利用できる技術は多くありますが、Server-Sent Events (SSE) は広く使われ、効果的なソリューションとして注目されています。SSEは、サーバーがクライアントにリアルタイム更新をHTTPでてプッシュできる仕組みを提供し、軽量で効率的な方法です。

なぜ Server-Sent Events (SSE) を選ぶべきか? 🤔

Server-Sent Events はHTML5仕様の一部であり、サーバーからクライアントにイベントをプッシュするために特別に設計されています。そのシンプルさ、再接続機能、およびイベントトラッキング機能により、SSEは継続的なデータストリーミングが必要なシナリオに最適です。片方向のデータフローが求められる状況で、SSE は特に優れています。

概要 📚

Server-Sent Events (SSE) は、サーバーがブラウザにリアルタイムで更新をプッシュできる技術です。HTML5仕様の一部で、主に以下のことを含みます:

  1. 通信プロトコル: HTTPを使用

  2. EventSourceオブジェクト: ブラウザ側のJavaScriptで利用可能

SSEとWebSocketsは両方ともサーバーからクライアントへのリアルタイム通信を提供しますが、いくつかの違いがあります:

サーバー実装 🌐

プロトコル実装

基本的に、ブラウザはHTTPリクエストを開始し、サーバーはHTTPステータスとデータを含むレスポンスを返します。このレスポンスには以下のヘッダーが含まれます:

Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive

SSEでは、イベントストリームのMIMEタイプをtext/event-streamにし、ブラウザはデータをキャッシュせず、接続は持続的(keep-alive)である必要があります。

メッセージ形式

EventStreamsはUTF-8でエンコードされたテキストまたは、Base64でエンコードされたバイナリメッセージで、gzipで圧縮されることがあります。各メッセージは1つ以上のフィールドで構成され、形式はfield-name: field-valueとなり、各フィールドは\nで終了します。コロンで始まる行はコメントとして無視されます。各プッシュは空行(\n\n)で区切られた複数のメッセージを含むことができます。

主なフィールド:

  • event: イベントの種類が指定されています。

  • id: イベントID、再接続時に最後に受信したイベントをブラウザが追跡するために使用されます。

  • retry: 接続失敗後にブラウザが再接続を試みる前に待機する時間(ミリ秒単位)

  • data: メッセージデータ

例:Pythonサーバー(SSE)

from flask import Flask, Response

app = Flask(__name__)

@app.route('/events')
def sse_handler():
    def generate():
        paragraph = [
            "こんにちは、これは連続的なテキスト出力の例です。",
            "複数の文が含まれており、各文はイベントとしてクライアントに送信されます。",
            "これはServer-Sent Events (SSE) の機能をシミュレートしています。",
            "この方法を使ってリアルタイム更新をプッシュできます。",
            "サンプルテキストの終わりです、ありがとうございます!",
        ]

        for sentence in paragraph:
            yield f"data: {sentence}\n\n"

            import time
            time.sleep(1)

    return Response(generate(), mimetype='text/event-stream')


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8081, debug=True)

例:Goサーバー(SSE)

package main

import (
    "fmt"
    "log"
    "net/http"
    "time"
)

func main() {
    http.HandleFunc("/events", sseHandler)

    fmt.Println("サーバーをポート8080で開始します")
    if err := http.ListenAndServe(":8080", nil); err != nil {
       log.Fatalf("サーバーエラー: %v", err)
    }
}

func sseHandler(w http.ResponseWriter, r *http.Request) {
    flusher, ok := w.(http.Flusher)
    if !ok {
       http.Error(w, "ストリーミングはサポートされていません!", http.StatusInternalServerError)
       return
    }

    w.Header().Set("Content-Type", "text/event-stream")
    w.Header().Set("Cache-Control", "no-cache")
    w.Header().Set("Connection", "keep-alive")

    // 出力を特定のテキストに変更
    paragraph := []string{
       "こんにちは、これは連続的なテキスト出力の例です。",
       "複数の文が含まれており、各文はイベントとしてクライアントに送信されます。",
       "これはServer-Sent Events (SSE) の機能をシミュレートしています。",
       "この方法を使ってリアルタイム更新をプッシュできます。",
       "サンプルテキストの終わりです、ありがとうございます!",
    }

    for _, sentence := range paragraph {
       _, err := fmt.Fprintf(w, "data: %s\n\n", sentence)
       if err != nil {
          return
       }
       flusher.Flush()
       time.Sleep(1 * time.Second) // 次のテキストを送信する前に1秒待機
    }
}

ブラウザAPI 🖥️

クライアント側では、JavaScriptのEventSource APIを使用して、サーバーから送信されたイベントをリッスンするEventSourceオブジェクトを作成できます。接続後、サーバーはtext/event-streamコンテンツタイプのHTTPレスポンスを通じてイベントメッセージをブラウザに送信します。ブラウザは、EventSourceオブジェクトのonmessage、onopen、onerrorイベントをリッスンして、これらのメッセージを処理します。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>SSEの例 🌟</title>
</head>
<body>
    <h1>Server-Sent Eventsの例 🚀</h1>
    <div id="messages"></div>
    <script>
        window.onload = function() {
            if (typeof(EventSource


いいなと思ったら応援しよう!