Goでリアルタイムチャット実装
Go実践でのWebsocket導入
Go言語でリアルタイム通信を実現する際、Websocketは欠かせない技術です。WebsocketはHTTPのハンドシェイクを経て、双方向の持続的な接続を確立します。これにより、サーバーとクライアント間でメッセージを即時にやり取りでき、チャットや通知、プッシュ機能をシームレスに構築できます。
まずは、Goの標準ライブラリだけでWebsocketを扱うことも可能ですが、実務では gorilla/websocket パッケージが広く採用されています。理由は、ハンドシェイクの実装が簡潔で、エラーハンドリングやフレーム制御が充実している点です。
gorilla/websocketの基本
gorilla/websocketを使う際の基本的な流れは次の通りです。
- HTTPハンドラ内で
websocket.Upgraderを作成し、クライアントからの接続要求をWebsocketにアップグレード。 - アップグレード後、
Connオブジェクトを取得し、ReadMessageとWriteMessageを使ってメッセージの送受信を行う。 - 接続が切断されたら
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を活用すれば、双方向通信をベースにしたリアルタイムチャットや通知、プッシュ機能を柔軟に構築できます。
コメント
コメントを投稿