用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,过程应该是这样的:

image

 

多进程

显然,每个请求对应一个进程的话,可以解决阻塞的问题,但是进程太耗资源,而且每个进程都是独立的,无法共享,进程切换成本也高。

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]
posted @ 2016-07-27 22:57  黄小墨  阅读(256)  评论(0)    收藏  举报