GinでUnix Domain Socket
GinでUnix Domain Socketを使うには、net.Listen("unix", "/tmp/demo.sock")を使用します。
main.go
package main
import (
"fmt"
"net"
"net/http"
"runtime"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
router.GET("/ram", func(c *gin.Context) {
var m runtime.MemStats
runtime.ReadMemStats(&m)
// Convert bytes to megabytes
mb := m.Alloc / 1024 / 1024
c.String(http.StatusOK, fmt.Sprintf("Current RAM usage: %v MB", mb))
})
listener, err := net.Listen("unix", "/tmp/demo.sock")
if err != nil {
panic(err)
}
http.Serve(listener, router)
}
Nginxの設定ファイル
$ brew install nginx
設定ファイルは、/opt/homebrew/etc/nginx/にあると思います。
demo.conf
upstream demo {
server unix:/tmp/demo.sock;
}
# the nginx server instance
server {
listen 8081;
server_name localhost;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_http_version 1.1; # for keep-alive
proxy_pass http://demo/;
proxy_redirect off;
}
}
Graceful Shutdown版
GinでUnix Domain Socketを使用してGraceful Shutdownをするコードを示します。サーバー起動時の.sockファイルの削除(サーバークラッシュをケア)とサーバー停止時の.sockファイルの削除処理を追加しています。
router.SetTrustedProxies([]string{"127.0.0.1"})も大事なのでお忘れなく。
main.go
package main
import (
"context"
"errors"
"fmt"
"log"
"net"
"net/http"
"os"
"os/signal"
"runtime"
"syscall"
"time"
"github.com/gin-gonic/gin"
)
var socketPath = "/tmp/demo.sock"
func main() {
// 1. Remove existing socket file if it exists from a previous crash
if _, err := os.Stat(socketPath); err == nil {
_ = os.Remove(socketPath)
}
router := gin.Default()
// Trust NGINX proxy headers (highly recommended)
router.SetTrustedProxies([]string{"127.0.0.1"})
router.GET("/ram", func(c *gin.Context) {
var m runtime.MemStats
runtime.ReadMemStats(&m)
// Convert bytes to megabytes
mb := m.Alloc / 1024 / 1024
c.String(http.StatusOK, fmt.Sprintf("Current RAM usage: %v MB", mb))
})
listener, err := net.Listen("unix", socketPath)
if err != nil {
panic(err)
}
server := &http.Server{
Handler: router,
}
// 2. Run the server in a goroutine
go func() {
log.Printf("Starting Gin server on UNIX socket: %s", socketPath)
if err := server.Serve(listener); err != nil && !errors.Is(err, http.ErrServerClosed) {
log.Fatalf("Failed to run server: %v", err)
}
}()
// 3. Fix permissions so NGINX can access the socket file
// Give the socket file read/write permissions (adjust as needed for security)
time.Sleep(100 * time.Millisecond) // Wait a brief moment for the file to be created
if err := os.Chmod(socketPath, 0666); err != nil {
log.Fatalf("Failed to set socket permissions: %v", err)
}
// 4. Graceful Shutdown handling to clean up the socket file
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("Shutting down server...")
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
log.Fatal("Server forced to shutdown:", err)
}
// Always delete the socket file on exit
_ = os.Remove(socketPath)
log.Println("Server exiting, socket file removed.")
}
動作確認
Nginx側
demo.confをnginx-conf-directory/servers/demo.confに配置して、nginxを起動。
$ pbpaste > nginx-conf-directory/servers/demo.conf # macOSの場合
$ brew services reload nginx
Server側
$ mkdir workspace
$ cd workspace
$ go mod init example
$ go get github.com/gin-gonic/gin
$ pbpaste > main.go # macOSの場合
$ go run main.go
Client側
$ curl http://localhost:8081/ram
Current RAM usage: 0 MB
参考
- Blazingly Fast Interprocess communication in Go using UNIX Sockets!!
- Googleで検索:🔍
nginx unix domain socket to gin in golang