Go言語:6万の同時HTTPリクエスト処理
1. Go言語の能力
- コンパイル可能なGo言語の能力が確認できます。
- 同時HTTPリクエストと、最小限のRAM消費で本当に大量のリクエストを処理することができます。
- ここでは、60,000の処理を試行します。
2. 制限
- HTTP は TCP 上で動作するため、TCP ポートの範囲は 0 ~ 65535 です。つまり、2台のマシン間で最大65kの同時TCP接続を開くことができます。
- 60kの要求を同時に送信し(つまり、同時にアクティブな60kのHTTPセッションを持つことを意味します)、いくつか試行を行います。
3. 試行
- サーバーに要求を受信させ、DBからの応答からの待機をシミュレートします。
- “Hello” 文字列で応答します。
- まず第一に、RAMの使用状況と現在のHTTPセッション数に関する統計に興味があります。uilive libを使って端末のデータを更新します。
package main
import (
"fmt"
"github.com/gosuri/uilive"
"net/http"
"runtime"
"sync/atomic"
"time"
)
func hello(w http.ResponseWriter, req *http.Request) {
ch <- true
atomic.AddInt32(&count, 1)
time.Sleep(time.Second * 120)
_, _ = fmt.Fprintf(w, "hello\n")
atomic.AddInt32(&count, -1)
ch <- true
}
var count int32
var ch chan bool
func main() {
ch = make(chan bool)
go func() {
var m runtime.MemStats
var writer = uilive.New()
writer.Start()
for {
<-ch
_, _ = fmt.Fprintf(writer, "Current connections count: %d\n", atomic.LoadInt32(&count))
runtime.ReadMemStats(&m)
_, _ = fmt.Fprintf(writer, "Alloc = %v MiB\n", m.Alloc/1024/1024)
_, _ = fmt.Fprintf(writer, "TotalAlloc = %v MiB\n", m.TotalAlloc/1024/1024)
_, _ = fmt.Fprintf(writer, "Sys = %v MiB\n", m.Sys/1024/1024)
_, _ = fmt.Fprintf(writer, "NumGC = %v\n", m.NumGC)
}
}()
http.HandleFunc("/", hello)
http.ListenAndServe(":8080", nil)
}
- クライアント
package main
import (
"fmt"
"net/http"
"time"
)
func main() {
tr := &http.Transport{
ResponseHeaderTimeout: time.Hour,
MaxConnsPerHost: 99999,
DisableKeepAlives: true,
}
myClient := &http.Client{Transport: tr}
for i := 0; i < 60000; i++ {
go func(n int) {
_, err := myClient.Get("http://192.168.xxx.xx:8080")
if err != nil {
fmt.Printf("%d: %s\n", n, err.Error())
}
}(i)
time.Sleep(1 * time.Millisecond)
if i%5000 == 0 || i == 59998 {
fmt.Println("Sleeping for 1 sec")
time.Sleep(1 * time.Second)
}
}
time.Sleep(time.Hour)
}
- TCP接続がスタックしたり再利用されたりすることがないことを保証するためにKeepAliveを無効にします。
- プロセスが安定していることを確認するために、5kリクエストごとにクールダウンを導入しました。
- サーバー部分をRaspberryPiで、クライアント部分をPCで実行してみます。
- Linux にはハードとソフトの 2 つの制限がありますので、/etc/security/limits.confに次の行を追加します。
* soft nofile 1000000
* hard nofile 1000000
- 開いているTCPポートの実際の49152-65535の範囲がありますので開かれるポートの限界を大きくします。
sudo sysctl -w net.inet.ip.portrange.first=1024
sudo sysctl -w net.inet.ip.portrange.hifirst=1024
- 確認
sysctl -a | grep portrange
以上