SO_REUSEPORT memungkinkan beberapa socket Linux AF_INET atau AF_INET6 melakukan bind pada alamat dan port lokal yang sama selama setiap anggota memenuhi aturan reuse-port. Pada listener TCP, distribusi koneksi masuk berpindah ke tahap sebelum accept(): kernel memilih satu listener dari grup reuse-port, lalu listener tersebut menerima koneksi pada accept queue miliknya. Pada UDP, seleksi menentukan socket yang menerima datagram masuk.
Batas concurrency ini berbeda dari beberapa thread yang berbagi satu listening file description. Setiap anggota reuse-port adalah socket terpisah dengan descriptor, queue, status polling, dan lifecycle sendiri.
Keanggotaan dimulai sebelum bind
SO_REUSEPORT harus diaktifkan pada setiap socket peserta sebelum bind(). Linux juga mensyaratkan proses yang melakukan bind alamat sama ke dalam grup reuse-port memiliki effective UID yang sama. Pembatasan ini mencegah proses yang tidak terkait mengambil traffic untuk endpoint yang sudah ada.
Satu worker TCP dapat memiliki seluruh lifecycle listener:
int fd = socket(AF_INET6, SOCK_STREAM | SOCK_NONBLOCK, 0);
int one = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one));
bind(fd, (struct sockaddr *)&addr, sizeof(addr));
listen(fd, backlog);Pengulangan rangkaian tersebut untuk beberapa worker menghasilkan beberapa listener socket yang terikat pada endpoint sama. Kernel memperlakukannya sebagai grup reuse-port, bukan menolak bind berikutnya sebagai konflik alamat.
Opsi ini tidak setara dengan SO_REUSEADDR. Opsi tersebut mengubah pemeriksaan penggunaan ulang alamat di sekitar bind(), sedangkan SO_REUSEPORT secara eksplisit mendukung beberapa socket yang terikat secara bersamaan pada alamat identik untuk distribusi traffic.
Seleksi terjadi sebelum penjadwalan aplikasi
Pada satu listener yang dipakai banyak worker, concurrency dimulai setelah readiness atau setelah percobaan accept(). Beberapa worker dapat berebut socket listener yang sama, sedangkan mekanisme seperti EPOLLEXCLUSIVE dapat mengurangi wakeup berlebih tanpa membuat accept queue terpisah.
Grup reuse-port memindahkan batas tersebut. Traffic masuk terlebih dahulu melewati seleksi socket di kernel:
paket atau koneksi
|
v
seleksi reuse-port
/ | \
v v v
L0 L1 L2
| | |
W0 W1 W2Pada TCP, listening socket yang terpilih menjadi endpoint yang queue-nya menerima koneksi baru. Worker yang menunggu atau melakukan polling pada listener lain tidak ikut bersaing untuk menerima koneksi yang sudah masuk queue tersebut. Pemisahan ini dapat mengurangi contention pada desain server yang memberi setiap worker satu listener dan satu event loop.
Mekanisme ini tidak menjamin pembagian kerja aplikasi yang sama rata. Koneksi dapat memiliki durasi, request rate, biaya CPU, atau ukuran respons yang sangat berbeda. Distribusi koneksi yang seimbang tetap dapat menghasilkan utilisasi worker yang timpang.
Distribusi default merupakan kebijakan kernel
SO_REUSEPORT tanpa tambahan menyerahkan seleksi kepada logika reuse-port kernel. Aplikasi sebaiknya memperlakukan algoritma distribusi persisnya sebagai perilaku implementasi Linux, bukan jaminan API yang portabel. Jaminan antarmuka yang stabil adalah socket yang memenuhi syarat dapat berbagi endpoint dan traffic masuk dialokasikan di antara anggota melalui jalur seleksi kernel.
Perbedaan ini penting saat aplikasi bergantung pada affinity. Desain yang memerlukan kelas flow tertentu, lokalitas CPU, atau hubungan dengan hardware receive queue tidak dapat menyimpulkan penempatan tersebut hanya dari penggunaan SO_REUSEPORT.
Linux menyediakan hook BPF eksplisit ketika aplikasi memerlukan seleksi yang dapat diprogram. SO_ATTACH_REUSEPORT_CBPF dan SO_ATTACH_REUSEPORT_EBPF memasang program pada grup reuse-port. Program classic dan extended bergaya socket filter memilih indeks dari 0 sampai N-1; hasil yang tidak valid membuat kernel kembali ke seleksi reuse-port normal. BPF_PROG_TYPE_SK_REUSEPORT dapat memakai bpf_sk_select_reuseport() untuk memilih socket.
Indeks grup adalah status operasional
Seleksi BPF reuse-port memakai keanggotaan grup, sehingga indeks socket bukan identitas aplikasi yang permanen. Linux memberi nomor anggota UDP berdasarkan urutan bind() dan anggota TCP berdasarkan urutan listen(). Saat satu anggota ditutup, kernel dapat memindahkan socket terakhir dalam grup ke posisi anggota yang dihapus.
Perilaku tersebut membentuk batas penting bagi kebijakan BPF. Program yang memetakan traffic ke posisi numerik perlu memperhitungkan perubahan grup dan tidak boleh menganggap indeks 2 selalu merujuk worker yang sama.
Socket baru yang bergabung ke grup reuse-port mewarisi program BPF milik grup. Program juga dapat diganti dengan mengatur kembali opsi BPF reuse-port pada salah satu anggota. Dengan demikian, kebijakan seleksi merupakan status grup, bukan sekadar konfigurasi yang berdiri sendiri pada setiap worker.
TCP dan UDP memiliki unit alokasi berbeda
Pada TCP, seleksi reuse-port berlangsung di jalur yang memilih listening socket untuk koneksi masuk. Setelah koneksi terbentuk, connected socket yang dihasilkan menjadi milik koneksi tersebut; segmen TCP berikutnya tidak didistribusikan ulang secara independen ke berbagai listener.
Pada UDP, penerimaan datagram normal tidak memerlukan connected socket hasil accept(). Jalur seleksi memilih anggota reuse-port untuk paket masuk. Akibatnya, kebijakan packet steering berhubungan langsung dengan penempatan worker pada sisi penerima.
Perbedaannya bersifat struktural. Server TCP biasanya mengelola kepemilikan koneksi setelah seleksi listener. Layanan UDP mungkin perlu mengelola konsistensi flow dan penempatan paket langsung di dalam kebijakan reuse-port.
Kepemilikan queue mengubah perilaku kegagalan
Listener terpisah juga berarti status queue yang terpisah. Jika satu worker TCP berhenti menguras listener-nya sementara proses tetap hidup, koneksi yang sudah diarahkan ke listener tersebut tidak otomatis berubah menjadi koneksi pending pada listener lain hanya karena worker lain masih memiliki kapasitas.
Sifat ini menjadikan kondisi worker sebagai bagian dari pengelolaan listener. Server dengan satu listener reuse-port per worker memerlukan strategi lifecycle untuk mengeluarkan anggota yang tidak sehat, menutup listener saat shutdown, dan mengoordinasikan worker pengganti. Distribusi kernel menghilangkan satu titik accept bersama; mekanisme itu tidak membentuk sistem work stealing pada tingkat aplikasi.
Pemisahan yang sama memengaruhi observability. Traffic endpoint agregat dapat terlihat normal sementara satu anggota mengalami tekanan. Accept rate per worker, tekanan queue, durasi koneksi, dan processing latency memberikan gambaran yang lebih tepat dibanding satu counter koneksi untuk seluruh proses.
Reuse-port dan kontrol wakeup menangani masalah berbeda
SO_REUSEPORT dan EPOLLEXCLUSIVE sama-sama dapat muncul pada server jaringan dengan concurrency tinggi, tetapi bekerja pada lapisan berbeda. EPOLLEXCLUSIVE membatasi wakeup ketika beberapa instance epoll memantau target bersama. SO_REUSEPORT membuat beberapa target socket dan meminta networking stack memilih salah satunya sebelum penerimaan di user space.
Perbedaannya dapat dilihat sebagai kepemilikan versus notifikasi. Reuse-port mengubah socket yang memiliki pekerjaan masuk. Registrasi epoll eksklusif mengubah waiter yang menerima notifikasi readiness pada sebuah socket. Pemilihan atau penggabungan keduanya bergantung pada topologi queue server, kepemilikan event loop, kebutuhan affinity, dan lifecycle worker.
SO_REUSEPORT dengan demikian bukan sekadar kemudahan saat bind. Mekanisme ini memindahkan keputusan concurrency ke socket lookup dan selection, membentuk listener atau receive queue independen dengan kebijakan distribusi yang dapat tetap ditentukan kernel atau diprogram secara eksplisit melalui BPF.
Referensi
- Linux
socket(7):SO_REUSEPORT,SO_ATTACH_REUSEPORT_CBPF, danSO_ATTACH_REUSEPORT_EBPF. - Dokumentasi NAPI kernel Linux: BPF reuse-port sebagai mekanisme penempatan worker pada sisi penerima.