출처: http://nigaky.hatenablog.com/entry/2013/03/12/234954

Linux kernel 3.9에서 SO_REUSEPORT 라는 소켓 옵션이 병합 되었다.

Merge branch 'soreuseport'· c617f39 · torvalds / linux · GitHub

이것은 동일한 포트에 여러 리스너(listen 소켓)를 bind 할 수 있게 된다는 것으로, Web 서버 등 단일 포트에 많은 연결이 올 것 같은 워크로드에서 여러 프로세스로 부하 분산을 할 수 있게 된다.

(지금까지라면 하나가 accept()하고 각 worker에 전달하는 모델이있었지만 이렇게 하면 accept() 하는 부분이 병목이 되어 버리기 십상이었다.)

BSD는 원래 SO_REUSEPORT 옵션은 있었지만, 멀티 캐스트 통신에 사용 되는 것 같다. Linux 의 이 구현은 목적이 다르기 때문에 약간 혼란 스러울 수 있다.

우선 재미있을 것 같은 기능이므로 실제로 시험해 보았다.

준비

커널은 최신의 것을 git에서 가지고 왔다.

# uname -r

3.9.0-rc2 +

테스트 프로그램

간단하게 시험하기 Python 으로 썼다.

10 프로세스가 동일한 포트에 bind하고, 누가 accept했는지를 표시하는 것이다.

#!/usr/bin/env python

import sys, socket, time

from multiprocessing import Process

PORT = 8000

NR_LISTENERS = 10

SO_REUSEPORT = 15

def listener_work(num):

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    s.setsockopt(socket.SOL_SOCKET, SO_REUSEPORT, 1)   # set SO_REUSEPORT

    s.bind(("", PORT))

    s.listen(5)

    while True:

        conn, addr = s.accept()

        print '%2d: accepted!' % num

        ret = conn.recv(16)

        conn.close()

def server():

    processes = []

    for i in xrange(NR_LISTENERS):

        p = Process(target=listener_work, args=(i,))

        p.start()

        processes.append(p)

    for p in processes:

        p.join()

def client():

    while True:

        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        s.connect((sys.argv[1], PORT))

        s.sendall("a" * 16)

        s.close()

        time.sleep(1)

def main():

    if '-s' in sys.argv:

        server()

    else:

        client()

if __name__ == '__main__':

    main()

실행 결과

클라이언트 측

SO_REUSEPORT를 사용하면 10 프로세스가 동일한 포트에 bind 할 수 있다.

또한 accept하는 쪽도 적당히 잘 분산 되는 것 같다.