📝

サクッとロングポーリングを試す

2022/07/30に公開

お知らせのようなリアルタイム性が求められる(こともある)機能実装をしたい場合、
websocketgRPCだと工数やアプリケーションの構成的にヘビーな場合があります
代替のシンプルな方法としてロングポーリングも頭の片隅に入れておきたいです

お知らせを例にすると
1, clientserverへ"お知らせ"を取得するためのhttpリクエストを行う
2, serverは新規の"お知らせ"があるまでレスポンスを返さず保留する
3, serverは新規の"お知らせ"を検知するとレスポンスを返す
4, clientserverから"お知らせ"を受け取る
5, 1~4をループ
というのがロングポーリングです

メリットは、
・ポーリングと比べてリアルタイム性が高いかつ、サーバーへの負荷が低い
websocketgRPCと比べると前提条件があまり無く、実装が容易

デメリットは、
・HTTPコネクションを1つ占領する
・データのやりとりは結局個別のHTTP通信なので、websocketgRPCに比べヘッダーや認証のオーバーヘッドがある
・上記の理由から頻繁に配信されるデータを扱う場合にはリアルタイム性を担保しずらい
あたりだと私は理解してます
他にもこんなのあるよというのがあれば教えてもらえると嬉しいです

試す

せっかくなのでGoでサクッと試してみます

main.go
package main

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

func main() {
	http.HandleFunc("/long_polling", longPolling)
	http.HandleFunc("/send", send)

	port := "80"
	log.Printf("Listening on port %s", port)
	if err := http.ListenAndServe(fmt.Sprintf(":%v", port), nil); err != nil {
		log.Panicln("Serve Error:", err)
	}
}

var msgCh = make(chan string)

func send(w http.ResponseWriter, r *http.Request) {
	msgCh <- "hello"

	w.Write([]byte("ok"))
}

func longPolling(w http.ResponseWriter, r *http.Request) {
	// msgChへ値が送信されるまで処理をブロック
	msg := <-msgCh

	w.Write([]byte(msg))
}
// consoleA
% curl http://localhost:80/long_polling
// "/send"へリクエストが来るまでレスポンスは保留される
=> hello

// consoleB
% curl http://localhost:80/send
=> ok

Goの場合channelを利用すると簡単に実装できますね
本番ではRedisのPub/Subなど使うといいと思います

Discussion