SO_REUSEPORT mengubah satu endpoint lokal dari binding satu socket menjadi sebuah grup socket. Di Linux, beberapa socket TCP atau UDP dapat melakukan bind ke alamat lokal yang sama ketika semua socket yang ikut mengaktifkan opsi ini sebelum bind() dan kredensial binding memenuhi aturan reuse dari kernel.

Perilaku ini berbeda dari sekadar melonggarkan pemeriksaan konflik alamat. Trafik yang masuk juga harus diarahkan ke salah satu anggota grup. Batas pemilihan ini memengaruhi arsitektur listener, isolasi antrean, restart proses, penempatan flow UDP, dan desain apa pun yang menganggap satu port selalu identik dengan satu socket.

Satu alamat dapat mewakili beberapa socket

Server TCP konvensional biasanya membuat satu listening socket:

int fd = socket(AF_INET, SOCK_STREAM, 0);
bind(fd, (struct sockaddr *)&addr, sizeof(addr));
listen(fd, 512);

Beberapa worker thread dapat sama-sama memanggil accept() pada descriptor tersebut, tetapi semuanya tetap bersaing pada satu listening socket dan antreannya.

Dengan SO_REUSEPORT, setiap worker dapat memiliki socket terpisah yang terikat ke alamat yang sama:

int one = 1;

int fd = socket(AF_INET, SOCK_STREAM, 0);
setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one));
bind(fd, (struct sockaddr *)&addr, sizeof(addr));
listen(fd, 512);

Mengulang urutan tersebut pada worker yang memenuhi syarat akan membuat beberapa listener untuk endpoint yang sama. Kernel memilih listener sebelum aplikasi memanggil accept(). Setiap koneksi yang terpilih kemudian masuk ke antrean milik listener tersebut.

Perbedaan ini memindahkan distribusi trafik ke bawah loop accept() aplikasi. Artinya, setiap listener memiliki state socket, kapasitas antrean, masa hidup file descriptor, dan konteks eksekusi pemiliknya masing-masing.

Setiap anggota harus mengaktifkan opsi sebelum bind

SO_REUSEPORT bukan flag yang bisa dipasang secara retroaktif pada satu socket agar dapat bergabung dengan listener lain yang sudah ada. Untuk shared bind yang valid, setiap socket harus mengaktifkan opsi sebelum bind().

Urutan ini penting dalam penggantian proses. Proses baru tidak dapat berasumsi bahwa menyalakan opsi tersebut akan membuatnya bisa bergabung ke port yang dipegang listener lama yang dibuat tanpa konfigurasi reuse yang kompatibel.

Linux juga menerapkan batasan effective user ID pada socket yang ingin bergabung ke reuse-port group. Aturan ini mencegah user lain yang tidak terkait membuka listener tambahan pada endpoint reusable yang sudah terisi dan menerima sebagian trafiknya.

Dengan demikian, keanggotaan reuse-port ditentukan saat setup socket, bukan izin umum untuk menempel pada port yang sudah dibind.

Pemilihan TCP terjadi sebelum accept

Untuk TCP, reuse-port group dapat menggantikan satu listener bersama dengan beberapa listener terpisah. Koneksi baru dibagikan ke socket-socket tersebut oleh logika pemilihan kernel. Worker aplikasi tidak lagi perlu berlomba pada listening file descriptor yang sama untuk membagi koneksi masuk.

Arsitektur ini mengubah cakupan tekanan antrean. Jika satu worker berhenti memanggil accept(), listener miliknya dapat menumpuk pekerjaan sendiri, terpisah dari listener lain yang masih diproses dengan lancar. Listener sehat tidak otomatis mengambil koneksi yang sudah diarahkan ke listener yang macet.

Mekanisme pemilihan kernel dapat memperbaiki distribusi, tetapi tidak membuat semua worker otomatis sama sehatnya. State backlog per listener dan scheduling tetap berpengaruh. Model satu listener per CPU atau worker memang memberi isolasi, tetapi juga menciptakan beberapa domain antrean yang perlu dipantau secara terpisah.

UDP mempertahankan batas datagram tetapi menambah pemilihan socket

Pada UDP tidak ada accept queue. Setiap datagram yang masuk tetap harus diarahkan ke satu socket dalam grup.

Karena itu, SO_REUSEPORT berguna untuk receive path paralel tanpa memaksa beberapa worker membaca dari descriptor yang sama. Setiap worker dapat menunggu pada socket miliknya sendiri, sementara kernel melakukan pemilihan anggota grup sebelum delivery.

Pemilihan bukan berarti duplikasi. Datagram yang diarahkan ke satu anggota tidak dikirim ke semua anggota hanya karena mereka memakai alamat lokal yang sama. Aplikasi yang membutuhkan fan-out harus memakai mekanisme lain.

Penempatan yang stabil juga dapat penting untuk protokol di atas UDP. Jika datagram yang saling terkait diharapkan masuk ke worker yang sama, kebijakan pemilihan menjadi bagian dari perilaku aplikasi, bukan sekadar detail binding.

Reuse-port BPF dapat mengganti pemilihan default

Linux menyediakan SO_ATTACH_REUSEPORT_CBPF dan SO_ATTACH_REUSEPORT_EBPF untuk reuse-port group. Program yang dipasang melalui opsi ini dapat memengaruhi socket mana yang menerima trafik.

Pada classic BPF, program mengembalikan indeks socket. Extended BPF juga memiliki program khusus reuse-port dan helper terkait untuk pemilihan socket. Pemilihan classic BPF yang tidak valid akan fallback ke mekanisme reuse-port kernel biasa.

Fasilitas ini mengubah grup dari mekanisme distribusi kernel yang tetap menjadi batas dispatch yang dapat diprogram. Pemilihan dapat menggunakan konteks paket atau socket yang tersedia bagi program BPF, sesuai batasan verifier dan tipe program.

Keanggotaan grup bersifat dinamis. Socket diindeks mengikuti aturan urutan grup, dan penghapusan anggota dapat mengubah posisi indeks. Selector yang menganggap angka indeks sebagai identitas worker permanen dapat menjadi salah ketika anggota masuk atau keluar.

SO_REUSEPORT dan SO_REUSEADDR menyelesaikan masalah berbeda

SO_REUSEADDR dan SO_REUSEPORT sering dipasang berdekatan dalam kode setup socket, tetapi keduanya tidak memiliki kontrak yang sama.

SO_REUSEADDR mengubah aturan validasi alamat yang digunakan oleh bind(). Opsi ini umum dipakai ketika alamat lokal perlu dipakai kembali pada berbagai tahap siklus hidup socket. Namun, opsi tersebut tidak dengan sendirinya membuat grup beberapa listener TCP aktif yang menerima koneksi baru secara terdistribusi.

SO_REUSEPORT secara eksplisit memungkinkan socket yang memenuhi syarat mengikat alamat yang sama dan menyediakan mekanisme pemilihan penerima untuk grup tersebut.

Menganggap kedua opsi ini setara dapat menyembunyikan masalah deployment. Server mungkin dapat restart mulus dengan salah satunya, tetapi tetap tidak mendukung listener lama dan baru yang aktif bersamaan, atau mungkin berhasil bind tanpa mendapatkan model distribusi trafik yang dimaksud.

Penggantian proses mendapat jendela overlap

Desain reuse-port dapat memungkinkan generasi server lama dan baru memegang listener yang kompatibel pada endpoint yang sama dalam waktu bersamaan. Ini menciptakan jendela overlap yang berguna untuk process replacement.

Namun overlap tersebut bukan transaksi handoff. Selama kedua generasi masih berada dalam grup, trafik baru dapat diarahkan ke salah satunya sesuai kebijakan pemilihan aktif. Menutup listener lama menghapusnya dari pemilihan koneksi baru, tetapi koneksi yang sudah diterima tetap dimiliki proses lama hingga siklus hidupnya selesai.

Artinya, graceful replacement memiliki setidaknya dua batas terpisah: penghapusan listener lama dari pemilihan koneksi baru, dan penyelesaian atau penghentian koneksi yang sudah dimiliki generasi lama.

Sistem deployment yang menyamakan kedua batas ini dapat melaporkan transisi listener selesai padahal masih banyak state koneksi berada di proses yang sedang dipensiunkan.

Grup socket memindahkan balancing ke batas kernel

SO_REUSEPORT mengubah unit yang melekat pada satu alamat lokal. Endpoint dapat mewakili grup socket yang dimiliki secara terpisah, dengan kernel memilih penerima sebelum pemrosesan normal di level aplikasi.

Perubahan ini dapat mengurangi kontensi pada descriptor bersama dan menyelaraskan kepemilikan socket dengan kepemilikan worker. Di sisi lain, state antrean per socket, perubahan keanggotaan grup, batasan kredensial, dan kebijakan pemilihan menjadi bagian dari perilaku runtime server.

Karena itu, opsi ini lebih dari sekadar kemudahan saat bind. SO_REUSEPORT menentukan sebuah batas dispatch yang state-nya tetap berdiri secara independen pada setiap socket yang menjadi anggota grup.