python下高性能的网络模式: 多进程+多协程

     人生苦短,我用python。众所周知,python由于全局锁的存在,使用多线程模型并不能提高程序性能。使用协程是较好的选择。但这仍不能发挥机器多cpu的性能。于是乎,有了下文的 多进程+多协程的网络模型。大体思路是使用gevent的StreamServer监听一个端口。然后使用multiprocessing.Process启动多进程。当有客户端连接到来时,会随机分配到某个进程的ClientHandler实例中去处理,只需要继承自该实例,处理你的业务即可。如果没有重载该类的函数,默认会把所有的新连接到来,数据到达,连接关闭事件通过multiprocessing.Queue传递给主进程(即开始启动监听端口的进程),然后由主程从队列中取出事件处理即可。需要特别说明的是window不支持该模型,为了方便在windows下调试业务代码,windos下实现为普通的单程gevent多协程模式。

具体使用例子看代码下面__main__部份,用python tcpserver.py启动即可测试。

------------------------------------------------------------------代码下如-------------------------------------------------------------------------------

import gevent
from gevent import monkey
monkey.patch_all(thread=False)
from gevent.server import StreamServer
import gevent.queue
import multiprocessing
import os

EVENT_CONNECT = 0
EVENT_RECV = 1
EVENT_SEND = 2
EVENT_CLOSE = 3
EVENT_STOP  = 4


class ClientHandler(object):
    def __init__(self,socket, address,clientprocess):
        self.socket = socket
        self.address = address
        self._sock_id = 0
        self._is_close = False
        self._buffer = ""
        self._clientprocess = clientprocess
        self._send_queues = gevent.queue.Queue() 
        self._send_spawn = gevent.spawn(self._run_send)

    def set_socket_id(self,socket_id):
        self._sock_id = socket_id

    def on_connect(self):
        self._clientprocess.on_connect(self._sock_id,self.address)
        return True

    def on_recv(self,data):
        self._clientprocess.on_recv(self._sock_id ,data)
        return True

    def on_close(self):
        self._clientprocess.on_close(self._sock_id)

    def send(self,data):
        self._send_queues.put((EVENT_SEND,data))

    def _run_send(self):
        combine_message = b""
        while self._is_close == False:
            msg = self._send_queues.get()
            if msg is None:
                continue
            (event,data) = msg
            if event == EVENT_SEND:
                try:
                    combine_message += data
                except:
                    pass
                if self._send_queues.qsize() > 0:
                    continue
                try:
                    self.socket.sendall(combine_message)
                    combine_message = b""
                except:
                    if self._is_close == False:
                       self._clientprocess.on_close(self._sock_id)
                    break
            elif event == EVENT_STOP:
                if combine_message != b"":
                    self.socket.sendall(combine_message)
                break
            else:
                break

    def close(self):
        if self._is_close:
            return
        self._is_close = True
        self._send_queues.put((EVENT_STOP,0))
        self.socket.close()
        gevent.joinall([self._send_spawn])


class ClientProcess:
    def __init__(self,server,index,queue_cls,hanlder_cls):                   
          self._index = index                 
          self._send_queues = queue_cls()
          self._hanlder_cls = hanlder_cls
          self.server = server
          self._is_stop = False
          
    def run(self):
        self._clients = {}                
        self._send_spawn = gevent.spawn(self._run_send)

    def stop(self):
        self.put((EVENT_STOP,0,0))

    def get(self,block=True,outtime=None):
        return self._send_queues.get(block,outtime)
    
    def put(self,data,block=True,outtime=None):
        self._send_queues.put(data,block,outtime)

    def on_new_connect(self,socket, address):
        client = self._hanlder_cls(socket, address,self)
        sock_id = id(client)*100+self._index
        self._clients[sock_id] = client       
        client.set_socket_id(sock_id)        
        if client.on_connect() == False:
          return

        while True:
            try:
               data = socket.recv(1024)            
               if data is not None and len(data)>0 and client.on_recv(data):                
                  continue
               else: 
                  client.on_close()               
                  break
            except:
                client.on_close()
                break


    def on_connect(self,sock_id,address):
        self.server.put((EVENT_CONNECT,sock_id,address))


    def on_close(self,sock_id):
        self.server.put((EVENT_CLOSE,sock_id,0))

    def on_recv(self,sock_id,data):
        self.server.put((EVENT_RECV,sock_id,data))

    def send(self,sock_id,data):
        try:
           self.put((EVENT_SEND,sock_id,data),True,0.001)
        except:
            print("send put timeout sock_id:%d"%(sock_id)) 
            return False
        return True

    def close_socket(self,sock_id):
        self.put((EVENT_CLOSE,sock_id,0))

    def __send__(self,sock_id,data):
        client = self._clients.get(sock_id,None)
        if client is not None:
           client.send(data)

    def __close_socket__(self,sock_id):
        client = self._clients.get(sock_id,None)
        if client is not None:
           client.close()
           self._clients.pop(sock_id)

    def _run_send(self):        
        msg = None
        print("_run_send pid:%d" %(os.getpid()))
        while True:
            try:
               msg = self.get(False)
            except :
                gevent.sleep(0.01)
                continue

            if msg is None: 
                continue
            (event,socket_id,data) = msg
            if event == EVENT_SEND:
                self.__send__(socket_id,data)
            elif event == EVENT_CLOSE:
                if socket_id != 0:
                   self.__close_socket__(socket_id)
            elif event == EVENT_STOP:
                for sock_id,client in self._clients.items():
                       self.__close_socket__(sock_id)
                break


class TcpServer:

    def __init__(self):
        self._server = StreamServer(('0.0.0.0',0), self.on_new_connect,backlog=100000)
        self._server.reuse_addr = 1
        self._recv_queues = None
        self._clent_process = []
        self._process_num = 0

    def start(self,ip,port,process_count = 1,queue_cls = gevent.queue.Queue,hanlder_cls = ClientHandler):
        self._recv_queues = queue_cls()
        self._process_num = process_count        
        self._server.set_listener((ip,port))        
        self._server.start()
        for i in range(process_count):
           self._clent_process.append(ClientProcess(self,i,queue_cls,hanlder_cls))
        self.event_loop()

    def event_loop(self):
        self.serve_forever(0)
    
    def get_recv_queue(self):
        return self._recv_queues

    def serve_forever(self,i):
        print("server:%d,pid:%d,index = %d" %(id(self),os.getpid(),i))
        self._index = i
        self._clent_process[i].run()
        self._send_spawn = gevent.spawn(self._server.serve_forever)
        

    def stop_server(self):        
        for client in self._clent_process:
            client.stop()
        self._server.close()

    def send(self,sock_id,data):
        index = sock_id %100
        if index >= self._process_num:
            return False
        client = self._clent_process[index]
        if client is not None:
           client.send(sock_id,data)
        return True

    def close_socket(self,sock_id):
        index = sock_id %100
        if index >= self._process_num:
            return False
        client = self._clent_process[index]
        if client is not None:
           client.close_socket(sock_id)
        return True

    def get(self,block=True,outtime=None):
        return self._recv_queues.get(block,outtime)
    
    def put(self,data,block=True,outtime=None):
        self._recv_queues.put(data,block,outtime)

    def on_new_connect(self,sock, address):         
        print("on_new_connect server:%d,pid:%d" %(id(self),os.getpid()))
        self._clent_process[self._index].on_new_connect(sock,address) 

class MultiprocessingTcpServer(TcpServer):

    def start(self,ip,port,process_count = 1,queue_cls =multiprocessing.Queue,hanlder_cls = ClientHandler):
        self._hanlder_cls = hanlder_cls
        self._queue_cls = queue_cls
        TcpServer.start(self,ip,port,process_count,queue_cls,hanlder_cls)

    def event_loop(self):
        for i in range(self._process_num):
           multiprocessing.Process(target=self.serve_forever, args=(i,)).start()

    def serve_forever(self,i):
        TcpServer.serve_forever(self,i)
        gevent.joinall([self._send_spawn])

    def add_process(self):
        index = len(self._clent_process)
        process = ClientProcess(self,index,self._queue_cls,self._hanlder_cls)
        self._clent_process.append(process)
        self._process_num += 1
        multiprocessing.Process(target=self.serve_forever, args=(index,)).start()
        

if __name__ == "__main__":        
    import sys
    process_num = 2    
    server = None
    if sys.platform == 'win32':
       server = TcpServer()  #windows下为单程进程的gevent
    else:
       server = MultiprocessingTcpServer()   #其他平台是多进程+多协程

    server.start('0.0.0.0',8089,process_num)    #监听端口
    msg = None
    while True:
        try:
            msg = server.get(True)     #获取事件
        except KeyboardInterrupt:
            break
        except :
                gevent.sleep(0.1)
                continue
        if msg is not None:
            (event,socket_id,data) = msg
            if event == EVENT_RECV:   #有数据到来
               server.send(socket_id,data)   #数据原样返回
               print("socket_id:%d ,recv data:%d" %(socket_id,len(data)))
            elif event == EVENT_CONNECT:   #新连接到来
                print("new connect socket_id:%d,address:%s" %(socket_id,data))
            else:    #连接断开
               server.close_socket(socket_id)
               print("socket_id:%d close" %(socket_id))
    server.stop_server()

转载于:https://my.oschina.net/u/2504104/blog/3028719

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值