GoでWebサーバーをゼロから構築する
Daniel Hayes
Full-Stack Engineer · Leapcell

GoでWebサーバーをゼロから構築する
GoでWebサーバーを記述するいくつかの方法
I. WebサーバーとHTTPサーバーの違い
HTTPサーバーとは、その名前が示すように、HTTPプロトコルをサポートするサーバーです。一方、Webサーバーは、HTTPプロトコルをサポートすることに加えて、他のネットワークプロトコルもサポートする場合があります。この記事では、Golangの公式パッケージを使用してWebサーバーを作成するいくつかの一般的な方法を紹介することに焦点を当てます。
II. 最もシンプルなHTTPサーバー
これは、Webサーバーを実装する最も簡単な方法です。以下にサンプルコードを示します。
package main import ( "fmt" "log" "net/http" ) func myHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello there!\n") } func main() { http.HandleFunc("/", myHandler) // アクセスルートを設定 log.Fatal(http.ListenAndServe(":8080", nil)) }
このプログラムを開始した後、別のターミナルを開いてコマンド curl localhost:8080
を入力するか、ブラウザで直接 localhost:8080
を開くと、出力結果 Hello there!
が表示されます。
ListenAndServe
関数の機能は、接続をリッスンして処理することです。その内部処理方法は、接続ごとにgoroutineを開始して処理することです。ただし、これは理想的な処理方法ではありません。オペレーティングシステムを学んだことがある人は、プロセスまたはスレッドの切り替えのコストが非常に大きいことを知っています。goroutineはユーザーレベルの軽量スレッドであり、それらを切り替えてもユーザーモードとカーネルモードの切り替えは発生しませんが、goroutineの数が多い場合、切り替えのコストは無視できません。より良い方法はgoroutineプールを使用することですが、この記事では今のところ詳しく説明しません。
III. Handlerインターフェースの使用
前の方法では、スケーラビリティが低くなります。たとえば、サーバーのタイムアウトを設定することはできません。このとき、カスタムサーバーを使用できます。
Server
構造体は次のように定義されます。
type Server struct { Addr string // リッスンするTCPアドレス Handler Handler // 呼び出すハンドラー ReadTimeout time.Duration // リクエストの読み取りがタイムアウトするまでの最大期間 WriteTimeout time.Duration // レスポンスの書き込みがタイムアウトするまでの最大期間 TLSConfig *tls.Config // ... }
Handler
はインターフェースであり、次のように定義されます。
type Handler interface { ServeHTTP(ResponseWriter, *Request) }
したがって、Handler
インターフェースの ServeHTTP
メソッドを実装する限り、サーバーをカスタマイズできます。サンプルコードは次のとおりです。
package main import ( "log" "net/http" "time" ) type myHandler struct{} func (this myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // 特定の処理ロジック } func main() { server := http.Server{ Addr: ":8080", Handler: &myHandler{}, ReadTimeout: 3 * time.Second, // ... } log.Fatal(server.ListenAndServe()) }
IV. connを直接処理する
場合によっては、より基本的なレベルで接続を処理する必要がある場合があります。このとき、net
パッケージを使用できます。サーバー側のコードは、次のように簡単に実装されます。
package main import ( "log" "net" ) func handleConn(conn net.Conn) { // 接続を処理するための特定のロジック } func main() { listener, err := net.Listen("tcp", ":8080") if err != nil { log.Fatal(err) } for { conn, err := listener.Accept() if err != nil { // エラーを処理します } go handleConn(conn) } }
例を簡単にするために、ここでは接続ごとにgoroutineを開始して処理します。実際の使用では、通常、goroutineプールを使用する方が良い選択です。クライアントへのリターン情報については、それを conn
に書き込むだけです。
V. プロキシ
前のセクションでは、Webサーバーを実装するいくつかの方法を紹介しました。次に、HTTPプロキシを簡単に実装しましょう。Golangでプロキシを作成するのは非常に簡単です。接続を転送するだけです。
package main import ( "io" "log" "net" ) func handleConn(from net.Conn) { to, err := net.Dial("tcp", ":8001") // ターゲットサーバーとの接続を確立します if err != nil { // エラーを処理します } done := make(chan struct{}) go func() { defer from.Close() defer to.Close() io.Copy(from, to) close(done) }() go func() { defer from.Close() defer to.Close() io.Copy(to, from) close(done) }() <-done <-done } func main() { listener, err := net.Listen("tcp", ":8080") if err != nil { log.Fatal(err) } for { conn, err := listener.Accept() if err != nil { // エラーを処理します } go handleConn(conn) } }
プロキシを少し強化することで、より多くの機能を実装できます。
Leapcell: Golangアプリのホスティングのための次世代サーバーレスプラットフォーム
最後に、Goサービスのデプロイに最適なプラットフォームである Leapcell をお勧めします。
1. 多言語サポート
- JavaScript、Python、Go、またはRustで開発します。
2. 無制限のプロジェクトを無料でデプロイする
- 使用量に対してのみ支払い - リクエストも料金も発生しません。
3. 無敵のコスト効率
- アイドル料金なしの従量課金制。
- 例:25ドルで、平均応答時間60msで694万リクエストをサポートします。
4. 合理化された開発者エクスペリエンス
- 簡単な設定のための直感的なUI。
- 完全に自動化されたCI/CDパイプラインとGitOps統合。
- 実用的な洞察を得るためのリアルタイムのメトリックとロギング。
5. 簡単なスケーラビリティと高いパフォーマンス
- 高い同時実行性を簡単に処理するための自動スケーリング。
- 運用オーバーヘッドはゼロ - 構築に集中するだけです。
Leapcell Twitter: https://x.com/LeapcellHQ