Using SO_REUSEPORT with Python on Ubuntu 13.04

Post to Twitter

The Linux Kernel 3.9 introduced a new feature that allows you to bind multiple socket listeners (servers) to the same port on the same host. What this means is you can build servers that all listen on the same port and can handle incoming traffic very efficiently. For example, you wouldn’t need to spin up a master process that then spins up child processes (fork) to handle the load. Instead, you can just spin up multiple processes that all listen on the same port. Let’s do a simple example to demonstrate this.


Since this is unique to the Linux Kernel 3.9 you will need to have that version of the Linux Kernel (or newer) installed first. I’m using Ubuntu 13.04 and it does not come with the required Linux Kernel. Ubuntu 13.10 will come with an appropriate Linux Kernel though to support this but doesn’t release until Oct. 2013. You could use alpha or beta releases if you wish.

I’m going to first upgrade my Ubuntu 13.04 system to the 3.9 version of the Kernel. I’m using a 64-bit version of Ubuntu so make sure to make the appropriate changes for a 32-bit system. The downloads I’m using are taken from here: http://kernel.ubuntu.com/~kernel-ppa/mainline/v3.9-raring/

I’m going to borrow a lot of the commands below from here.

$ sudo apt-get update
$ sudo apt-get upgrade
$ sudo apt-get dist-upgrade
$ sudo apt-get autoremove
$ cd /tmp
$ wget http://kernel.ubuntu.com/~kernel-ppa/mainline/v3.9-raring/linux-headers-3.9.0-030900-generic_3.9.0-030900.201304291257_amd64.deb
$ wget http://kernel.ubuntu.com/~kernel-ppa/mainline/v3.9-raring/linux-headers-3.9.0-030900_3.9.0-030900.201304291257_all.deb
$ wget http://kernel.ubuntu.com/~kernel-ppa/mainline/v3.9-raring/linux-image-3.9.0-030900-generic_3.9.0-030900.201304291257_amd64.deb
$ sudo dpkg -i *.deb
$ sudo update-grub2

Reboot your system and then run the following to verify your kernel version:

$ uname -r

Result:

3.9.0-030900-generic

You should now be on a 3.9 version of the Linux Kernel. I’m using Python 2.7.5 so socket.SO_REUSEPORT is already supported.

Time to test out a simple Python server. Open a text editor and add the following code (copied from here and modified slightly):

File Name: server.py

import socket
import os

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
s.bind(('0.0.0.0', 8080))
s.listen(1)

while True:
    conn, addr = s.accept()
    print('Connected to {}'.format(os.getpid()))
    data = conn.recv(1024)
    conn.send(data)
    conn.close()

Now start two or three of these servers by running the following:

$ python server.py &
[1] 5480
$ python server.py &
[2] 5482

With a couple servers running (and listening on the same port) you can send the following commands at them:

$ echo data | nc localhost 8080

Send the above code several times. You should see results like the following:

chad-ubuntu@ubuntu:~/PythonProjects$ echo data | nc localhost 8080
Connected to 5482
data
chad-ubuntu@ubuntu:~/PythonProjects$ echo data | nc localhost 8080
Connected to 5480
data
chad-ubuntu@ubuntu:~/PythonProjects$ echo data | nc localhost 8080
Connected to 5482
data

Notice how you hit each of the process under the PIDs you started earlier? You’ve now got a system that is capable of having multiple processes bind to the same port.

Credit to these articles for the information provided in this post:

http://freeprogrammersblog.vhex.net/post/linux-39-introdued-new-way-of-writing-socket-servers/2
http://www.liberiangeek.net/2013/06/linux-kernel-3-9-8-released-how-to-upgrade-in-ubuntu-13-04-raring-ringtail/

Post to Twitter

This entry was posted in Open Source, Python, Socket Server, Ubuntu. Bookmark the permalink.