背景
SO_REUSEPORTってなんだろ。https://t.co/0YDyRDKrI2
— Ryuichi@k8s再入門中 (@ryuichi_1208) 2021年6月26日
so_reuseportってなんやねんと思って調べた
SO_REUSEPORTとは
ローカルアドレスの再使用を有効にするソケットオプションのSO_REUSEADDRは使ったことがあったがportは使ったことがなかった。manを参照すると「重複したアドレスとポートのバインドを有効にします」と書いてある。そのままだった。
これの何が嬉しいのかというと例えば複数プロセスのソケットプログラミングを行う際にマルチプロセスモデルで書く場合にこのオプションを指定しない場合は親プロセスでListenして子プロセスでそのsocketをaccept(2)する必要がある。このオプションを使うと各子プロセスでbindすることが可能となる。
Nginxでの使い方
Socket Sharding in NGINX OSS Release 1.9.1
Nginxの場合のわかりやすい図。使わない場合は各workerがリクエストごとに接続を取得しようとする。
サンプルソース
#include <sys/types.h> #include <sys/socket.h> #include <sys/wait.h> #include <netdb.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <netinet/in.h> void child(int idx) { int S, C; const int on = 1; struct addrinfo *res; struct addrinfo hints; struct sockaddr_in sin4; socklen_t slen; memset(&hints, sizeof(hints), 0); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV; getaddrinfo("127.0.0.1", "10000", &hints, &res); S = socket(res->ai_family, res->ai_socktype, res->ai_protocol); setsockopt(S, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)); bind(S, res->ai_addr, res->ai_addrlen); freeaddrinfo(res); listen(S, 100); slen = sizeof(sin4); while ((C = accept(S, (struct sockaddr *)&sin4, &slen)) >= 0) { close(C); printf("accepted by child #%d\n", idx); } } int main(void) { int status; int i; for (i = 0; i < 4; i++) { pid_t pid = fork(); if (pid == 0) { child(i); return 0; } else if (pid < 0) { perror("fork"); return 1; } } wait(&status); return 0; }