用python理解web并发模型
最简单的并发
import socket response = 'HTTP/1.1 200 OK\r\nConnection:Close\r\nContent-Length:11\r\n\r\nHello World' server = socket.socket() server.bind(('0.0.0.0', 9527)) server.listen(1024) while True: client, clientaddr = server.accept() #等待客户端连接,阻塞 request = client.recv(1024) #等待客户端面消息,阻塞 client.send(response) #可能会阻塞 client.close()
虽然可以处理很多请求,但它依然是一个请求一个请求执行的,而且有两个阻塞的地方,还有一个可能会阻塞的地方,为什么会是可能阻塞,在内核的socket的实现中,有一个read buffer,还有一个write buffer,过程应该是这样的:
多进程
显然,每个请求对应一个进程的话,可以解决阻塞的问题,但是进程太耗资源,而且每个进程都是独立的,无法共享,进程切换成本也高。
import socket import signal import multiprocessing response = 'HTTP/1.1 200 OK\r\nConnection:Close\r\nContent-Length:11\r\n\r\nHello World' server = socket.socket() server.setsockopt(socket.SOL.SOCKET, socket.SO_REUSEADDR, 1) server.bind(('0.0.0.0', 9527)) server.listen(1024) def handler(client): request = client.recv(1024) client.send(response) client.close() signal.signal(signal.SIGCHLD, signal.SIG_IGN) #多进程里的子进程执行完后并不会死掉,会变成僵尸进程,必须等到主进程挂掉后,才会死掉,这句可以解决这个问题 while True: client, addr = server.accetp() process = multiprocessing.Process(target=handler, args=(client,)) process.start()
Prefork
如上面的多进程,如果请求数多,资源占用无法控制;可以分配和cpu核数一样的进程数来有效控制资源占用
import socket import multiprocessing response = 'HTTP/1.1 200 OK\r\nConnection:Close\r\nContent-Length:11\r\n\r\nHello World' server = socket.socket() server.bind(('0.0.0.0', 9527)) server.listen(1024) def handler(): while True: client, addr = server.accept() request = client.recv(1024) client.send(response) client.close() processors = 8 for i in range(0, processors): process = multiprocessing.Process(target=handler, args=()) process.start()
线程池
import queue import socket import threading response = 'HTTP/1.1 200 OK\r\nConnection:Close\r\nContent-Length:11\r\n\r\nHello World' server = socket.socket() server.bind(('0.0.0.0', 9527)) server.listen(1024) def handler(queue): while True: client = queue.get() request = client.recv(1024) client.send(response) client.close() queue_list = queue.Queue() processors = 8 for i in range(0, processors): thread = threading.Thread(target=handler, args=(queue_list,)) thread.daemon = True thread.start() while True: client, clientaddr = server.accept() queue_list.put(client)
线程占用的资源比较少,因为资源可以共享, 但是要考虑线程安全问题和锁的性能, 而且python的解释器有GIL, 导致不能有效利用多核CPU。
epoll
import select import select response = 'HTTP/1.1 200 OK\r\nConnection:Close\r\nContent-Length:11\r\n\r\nHello World' server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setblocking(False) server_address = ('localhost', 9527) server.bind(server_address) server.listen(1024) READ_ONLY = select.EPOLLIN | select.EPOLLPRI epoll = select.epoll() epoll.register(server, READ_ONLY) timeout = 60 fd_to_socket = {server.fileno(): server} while True: events = epoll.poll(timeout) for fd, flag in events: sock = fd_to_socket[fd] if flag and READ_ONLY: if sock is server: conn, client_address = sock.accept() conn.setblocking(False) fd_to_socket[conn.fileno()] = conn epoll.register(conn, READ_ONLY) else: request = sock.recv(1024) sock.send(response) sock.close() del fd_to_socket[fd]

浙公网安备 33010602011771号