Some time ago 2014 Beijing Pycon Conference Spit A lot, so I went to Infoq looking for 2013 of the Assembly video, on the network shooter high Noon 2 Python-based server architecture video is very interested, especially the game server 0 downtime, Principle they are not native sockets at the bottom,
It is based on the ZEROMQ socket, because ZEROMQ's short-term automatic re-connection can meet the hot start of the game server, does not require a code-level hot-start, hot-Update, when the update code is complete after the direct restart of the server, the previously unhandled request will continue processing. Instantly feel very tall on, so the recent period of time to go home has been studying ZEROMQ, in the guide's LRU side stay for a long time, this is to talk about ZEROMQ LRU algorithm middleware.
For the normal request reply agent, that is, using the Router-dealer mode is better understood, but this way has a natural disadvantage, dealer will use load balancing method to forward the client request to the server side, because the server processing power varies, will cause some servers are busy, Some are busy, this is not what we want to see, we want to squeeze all the server capabilities, so there is a general-purpose LRU algorithm.
Using Router-router mode here, you may be surprised at first and listen to me slowly.
How can you make the most of each server's performance?
1. When Woker starts, tell backend that he is ready to join the worker in the Work_queue available queue
2. When the client requests, remove a work from the work_queue and hand over the request to the working
3. When work is finished, send the response to the client and add yourself to the Work_queue
That is, the work queue is used, and the working queue is logged, as long as it is idle, and once again, it is available.
So why do backend need to use router mode? The key point is that when I want to send the client request to a fixed work, and only the router mode has the route identification function, understand this, the code is easy.
"" "least-recently Used (LRU) queue device clients and workers is shown here in-process Author:guillaume Aubert (g Aubert) <guillaume (dot) aubert (at) gmail (dot) com> ' "" from __future__ import Print_functionimport Threadingimport Timeimport zmqnbr_clients = 10nbr_workers = 3def Worker_thread (worker_url, Context, I): "" "worker using REQ socket to The response message format for do LRU routing work is--------------------| frame0| Client-idntity--------------------| frame1| -------------------- | frame2| OK "" "Socket = Context.socket (ZMQ. REQ) # Set the worker identity socket.identity = (u "worker-%d"% (i)). Encode (' ASCII ') Socket.connect (Worker_url) # Tell the Borker we is ready to work Socket.send (b "Ready") try:while true:address = socket . recv () empty = SOCKET.RECV () request = SOCKET.RECV () print ("%s:%s\n"% (socket.identity . Decode (' ASCII '), Request.decode (' ASCII '), end= ') socket.send (addRess, ZMQ. Sndmore) Socket.send (b "", ZMQ. Sndmore) Socket.send (b "OK") except ZMQ. Contextterminated: # context terminated so quit silently returndef client_thread (client_url, Context, I): The message format for the Basic request-reply client using REQ socket client is--------------------| frame0| Client-1--------------------| frame1| -------------------- | frame2| HELLO "" "Socket = Context.socket (ZMQ. REQ) socket.identity = (u "client-%d"% (i)). Encode (' ASCII ') Socket.connect (client_url) # Send request, get reply Socket.send (b "HELLO") Reply = Socket.recv () print ("%s:%s\n"% (Socket.identity.decode (' ASCII '), Reply.decode (' A Scii '), end= ') def Main (): "" "" "" "" "" "" "Url_worker =" inproc://workers "url_client =" inproc://clients " CLIENT_NBR = nbr_clients # Prepare Our context and sockets context = ZMQ. Context () frontend = Context.socket (zmq. ROUTER) Frontend.bind (url_client) backend = context.Socket (ZMQ. ROUTER) Backend.bind (url_worker) # Create workers and clients threads for I in range (nbr_workers): Thread = Threading. Thread (Target=worker_thread, args= (url_worker, context, I,)) Thread.Start () for I in Range (nbr_clients): Thread_c = Threading. Thread (Target=client_thread, args= (url_client, context, I,)) Thread_c.start () # Logic of LRU Loop #-Poll b Ackend always, frontend only if 1+ worker ready #-If worker replies, queue worker as ready and forward reply # to Client if necessary #-if client requests, pop next worker and send request to it # Queue of available workers a Vailable_workers = 0 Workers_list = [] # init Poller poller = zmq. Poller () # always poll for worker activity on backend poller.register (backend, zmq. Pollin) # Poll Front-End only if we have available workers Poller.register (frontend, ZMQ. Pollin) While true:socks = Dict (Poller.poll ()) # Handle WorkerActivity on backend if (backend in socks and socks[backend] = = Zmq. Pollin): # Queue worker address for LRU Routing worker_addr = Backend.recv () Assert Avai Lable_workers < Nbr_workers # Add worker back to the list of workers available_workers + = 1 Workers_list.append (WORKER_ADDR) # Second frame is empty empty = Backend.recv () a Ssert empty = = B "" # Third frame is ready or else a client reply address client_addr = BACKEND.RECV ( # If client reply, send rest back to frontend If client_addr! = B "Ready": # Followi Ng frame is empty empty = Backend.recv () assert empty = = B "" Reply = backend. Recv () frontend.send (Client_addr, ZMQ. Sndmore) Frontend.send (b "", ZMQ. Sndmore) Frontend.send (reply) Client_nbr-= 1 ifCLIENT_NBR = = 0:break # Exit after N messages # poll on frontend only if workers is available If Available_workers > 0:if (frontend in socks and socks[frontend] = = Zmq. Pollin): # Now get next client request, route-to-LRU worker # Client request is [Address][em Pty][request] client_addr = frontend.recv () empty = Frontend.recv () Assert EM Pty = = B "request = FRONTEND.RECV () # Dequeue and drop the next worker address Available_workers-= 1 worker_id = Workers_list.pop () "" "worker_id is the identification of work, that is, to send to Worke R_ID, so backend need to use router mode between all messages ZMQ need an empty message as the identity, when work accepts the request will be read directly to the first empty message, that is the work accepted the first The message is CLIENT_ADDR, and then work to send the response to CLIENT_ADDR, when backend received the message, directly through the forentend to the CLIENT_ADDR, which is also forentend need to be RO Reason for Uter mode "" " Backend.send (worker_id, ZMQ. Sndmore) Backend.send (b "", ZMQ. Sndmore) Backend.send (client_addr, ZMQ. Sndmore) Backend.send (b "", ZMQ. Sndmore) Backend.send (Request) # Out of infinite loop:do some housekeeping frontend.close () Backe Nd.close () context.term () if __name__ = = "__main__": Main () "" "when the need for specific forwarding, is router when the" ""
ZeroMq LRU Algorithm Middleware