スキップしてメイン コンテンツに移動

Goでリアルタイムチャット実装

Goでリアルタイムチャット実装

Go実践でのWebsocket導入

Go言語でリアルタイム通信を実現する際、Websocketは欠かせない技術です。WebsocketはHTTPのハンドシェイクを経て、双方向の持続的な接続を確立します。これにより、サーバーとクライアント間でメッセージを即時にやり取りでき、チャットや通知、プッシュ機能をシームレスに構築できます。

まずは、Goの標準ライブラリだけでWebsocketを扱うことも可能ですが、実務では gorilla/websocket パッケージが広く採用されています。理由は、ハンドシェイクの実装が簡潔で、エラーハンドリングやフレーム制御が充実している点です。

gorilla/websocketの基本

gorilla/websocketを使う際の基本的な流れは次の通りです。

  1. HTTPハンドラ内で websocket.Upgrader を作成し、クライアントからの接続要求をWebsocketにアップグレード。
  2. アップグレード後、Conn オブジェクトを取得し、ReadMessageWriteMessage を使ってメッセージの送受信を行う。
  3. 接続が切断されたら Close を呼び出し、リソースを解放。
var upgrader = websocket.Upgrader{
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
    CheckOrigin: func(r *http.Request) bool {
        return true // 本番ではオリジンチェックを実装
    },
}

func wsHandler(w http.ResponseWriter, r *http.Request) {
    conn, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Println("upgrade error:", err)
        return
    }
    defer conn.Close()

    for {
        mt, message, err := conn.ReadMessage()
        if err != nil {
            log.Println("read error:", err)
            break
        }
        log.Printf("recv: %s", message)
        if err := conn.WriteMessage(mt, message); err != nil {
            log.Println("write error:", err)
            break
        }
    }
}

上記の例では、クライアントから受信したメッセージをそのまま返すエコーサーバーを実装しています。実際のチャットアプリでは、メッセージをパースしてデータベースに保存したり、他のクライアントへブロードキャストしたりします。

リアルタイムチャットの実装

チャット機能を構築する際のポイントは、複数クライアント間でメッセージを共有するためのブロードキャストロジックです。以下は、簡易的なチャットサーバーの構成例です。

  • 全クライアントを管理する Hub 構造体を作成。
  • クライアントが接続すると Hub に登録し、切断時に解除。
  • 受信したメッセージを Hub が保持し、全登録クライアントへ送信。
type Client struct {
    conn *websocket.Conn
    send chan []byte
}

type Hub struct {
    clients    map[*Client]bool
    broadcast  chan []byte
    register   chan *Client
    unregister chan *Client
}

func newHub() *Hub {
    return &Hub{
        clients:    make(map[*Client]bool),
        broadcast:  make(chan []byte),
        register:   make(chan *Client),
        unregister: make(chan *Client),
    }
}

func (h *Hub) run() {
    for {
        select {
        case client := <-h.register:
            h.clients[client] = true
        case client := <-h.unregister:
            if _, ok := h.clients[client]; ok {
                delete(h.clients, client)
                close(client.send)
            }
        case message := <-h.broadcast:
            for client := range h.clients {
                select {
                case client.send <- message:
                default:
                    close(client.send)
                    delete(h.clients, client)
                }
            }
        }
    }
}

クライアント側では、send チャネルからメッセージを受け取り、Websocketへ書き込みます。これにより、リアルタイムで全員に通知が届きます。

通知とプッシュの拡張

チャット以外の用途として、サーバーからクライアントへ通知やプッシュを送るケースがあります。Websocketはそのままプッシュ通知の代替としても機能しますが、モバイルデバイス向けには FCM(Firebase Cloud Messaging)や APNs(Apple Push Notification Service)と組み合わせることが一般的です。

Go側では、Websocketで「通知用チャネル」を設け、特定のイベント(例:新規メッセージ到着、システムメンテナンス)を検知した際にクライアントへメッセージを送信します。クライアントは受信したメッセージをブラウザ通知 API と連携させることで、ユーザーに即時に知らせることができます。

実装例として、以下のようにメッセージ構造体を定義し、JSONで送信します。

type Notification struct {
    Type    string `json:"type"`
    Message string `json:"message"`
    Time    int64  `json:"time"`
}

func sendNotification(hub *Hub, n Notification) {
    data, _ := json.Marshal(n)
    hub.broadcast <- data
}

このように、Go実践でWebsocketを活用すれば、双方向通信をベースにしたリアルタイムチャットや通知、プッシュ機能を柔軟に構築できます。

この記事はAIによって作成されました。

コメント