SO_REUSEPORT ソケットオプション

Linux Kernel3.9(BSDでは割と以前から実装されていたけど)で取り込まれたSO_REUSEPORTソケットオプションは、異なるプロセスで同一のポートにbindできるようになるので、いろいろおもしろいことができそう。

https://lwn.net/Articles/542629/

これがmerge commit

https://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/commit/?id=c617f398edd4db2b8567a28e899a88f8f574798d

試しにRubySO_REUSEPORTを使うサーバを書いてみた。 もちろんKernelを3.9以降にアップデートしないと動かない。 Ubuntuだと、script書いてくれてる人がいるので楽にUbuntu Kernelをアップデートできる。

http://dl.dropboxusercontent.com/u/47950494/upubuntu/kernel-3.10.9

require 'socket'

SO_REUSEPORT = 15

s = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
s.setsockopt(Socket::SOL_SOCKET, SO_REUSEPORT, 1)
s.bind(Addrinfo.tcp("127.0.0.1", 10080))
s.listen(5)

while true do
  conn, addr = s.accept
  puts "Connected to #{Process.pid}"
  data = conn.recv(1024)
  conn.send("HTTP/1.1 200 OK", 0)
  conn.close
end

で、これを10プロセスほど起動して、apache benchでつないでみる。

$ for i in `seq 10`
do
  ruby so_reuseport_opt.rb &
done
$ ab -n 10000 -c 10 localhost:10080/
This is ApacheBench, Version 2.3 <$Revision: 1430300 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)...Connected to 3476
Connected to 3334
Connected to 4875
Connected to 3531
Connected to 3531
Connected to 4879
Connected to 4869
Connected to 2891
Connected to 4870
Connected to 4868
Connected to 4866
...

カーネル側でバランシングしてくれていることがわかる。 使いどころとして、

  • 単純なpreforkモデルでパフォーマンスを出したいアプリケーション
  • システム管理系のアプリケーションで、プロセスを落とせないけど、新しいversionを使いたくて、同時に新しいものも起動させてから、古いversionのプロセスを落とす(hotswapしたい)

などが作れそう。