출처: 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하는 쪽도 적당히 잘 분산 되는 것 같다.