I’m currently working on a websocket implementation that allows multiprocessing over the same listening socket.
I’m able to achieve an amazing performance with 4 processes on a quad core machine.
When I go upper, like 8 processes, after 4 request, the epoll.poll don’t fire any event anymore. Interestingly, I tried running the same program , with 2 listener on 2 different ports. With 4 processes per listener, it blocks after 2 requests per socket. With 2 processes per listener, il all go fine through it.
Any thought?
main.py (extract)
#create the WSServer
wsserver = WSServer(s.bind_ip, s.bind_port, s.max_connections)
# specify on how many process we'll run
wsserver.num_process = s.num_process
Process(target=wsserver.run,args=()).start()
wsserver.py (extract)
def serve_forever_epoll(wsserver):
log(current_process())
epoll = select.epoll()
epoll.register(wsserver.socket.fileno(), select.EPOLLIN)
try:
client_map = {}
while wsserver.run:
events = epoll.poll(1)
for fileno, event in events:
if fileno == wsserver.socket.fileno():
channel, details = wsserver.socket.accept()
channel.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
aclient = wsclient.WSClient(channel, wsserver, process_server.client_manager)
client_map[channel.fileno()] = aclient
epoll.register(channel.fileno(), select.EPOLLIN )
log('Accepting client on %s' % current_process())
aclient.do_handshake()
elif event & select.EPOLLIN:
aclient = client_map[fileno]
threading.Thread(target=aclient.interact).start()
except Exception, e:
log(e)
finally:
epoll.unregister(wsserver.socket.fileno())
epoll.close()
wsserver.socket.close()
class WSServer():
def __init__(self, address, port, connections):
self.address = address
self.port = port
self.connections = connections
self.onopen = onopen
self.onclose = onclose
log('server init')
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
#self.socket.setblocking(0)
self.socket.bind((self.address, int(self.port)))
self.socket.listen(self.connections)
def run(self, *args):
multiprocessing.log_to_stderr(logging.DEBUG)
log("Run server")
try:
log("Starting Server")
self.run = True
serve_forever = serve_forever_epoll
for i in range(self.num_process-1):
log('Starting Process')
Process(target=serve_forever,args=(self,)).start()
serve_forever(self)
except Exception as e:
log("Exception-- %s " % e)
pass
OK so finally this weird case was caused by another module I was using.
I am using Pyro4 as a manager for keeping track of which process holds what client. This simplifies greately the IPC and also permits me for some client filtering based on some user_data.
The problem was the Pyro4 daemon was running on the MainProcess but not on the Main Thread!…
As long as I had less that 4 processes, all was OK (don’t ask me why).
Moving Pyro in the main-process + thread event loop, it was working perfectly!
So now, i’m able to achieve 8, 16 or 32 processes for the same listening port, as well as spawning new configuration to replicate it or expose a new endpoint for the websocket server!
Thanks for your contributions, and sorry for your time…