Python及项目实现的高并发(一)

  1. 高并发的瓶颈在哪?
    根据大多数人对高并发的经验得知,大多数时候在IO上面,这里说得是大多数,不是说绝对。
    因为大多数时候业务本质上都是从数据库或者其他存储上读取内容,然后根据一定的逻辑,将数据返回给用户,比如大多数web内容。而大多数逻辑的交互都算不上计算量多大的逻辑,CPU的速度要远远高于内存IO,磁盘IO,网络IO, 而这些IO中网络IO最慢。
    当并发高到一定的程度,根据业务的不同,比如计算密集,IO密集,或两者皆有,因此瓶颈可能出在计算上面或者IO上面,又或两者兼有。
    解释:IO密集型:简单的可以理解为对某些应用的输入输出,最常见web应用
    CPU密集型: 要进行大量的计算,消耗CPU资源
  2. Python怎么处理高并发?
    使用协程,事件循环,高效IO模型(比如多路复用),三者缺一不可
    很多文章都是说协程,最后告诉我一些协程库或者asyncio用来说明协程的威力,但是一切还是得从生成器说起,因为asyncio或者大多数协程库内部也是通过生成器实现的。

如果对事件循环或者说多路复用的经验,也许能够隐隐察觉到微妙的感觉。 这个微妙的感觉是,是否可以将IO操作yield出来?由事件循环调度, 如果能get到这个微妙的感觉,那么你已经知道协程高并发的秘密了.

'''
说到协程之前得先明白生成器,生成器的定义很抽象,百度百科: 生成器是一次生成一个值的特殊类型函数。可以将其视为可恢复函数。
生成器的关键字在yield 
'''
def gen_func():
    yield 1
    yield 2
    yield 3
if __name__ == '__main__':
    gen = gen_func()
    print(next(gen))
    print(next(gen))
    print(next(gen))
    
返回结果为:
1
2
3
'''
每次执行到yield时,会跳出去并将yield关键字后面的内容返回给调用者。下一次有别的调用者再次调用生成器时,会先恢复生成器上次的机器状态,再接着执行指导遇到yield或者元素迭代完毕,这就是生成器的实现原理。
'''

那么协程是怎么回事呢,协程就是多个生成器函数之间的调用,这就是协程在底层实现的方法

  1. io模型
    Linux平台一共有五大IO模型,每个模型有自己的优点与确定。根据应用场景的不同可以使用不同的IO模型。
    1.同步IO,是效率最低的模型了,处理完一个连接才能处理下一个。
    2.非阻塞式IO,不会阻塞后面的代码,一般通过while循环,耗费大量的CPU
    3.多路复用,当前最流行,使用最广泛的高并发方案。
    4.信号驱动式IO,很偏门的一个IO模型,不曾遇见过使用案例。
    5.异步非阻塞IO,理论上比多路复用更快,因为少了一次调用,但是实际使用并没有比多路复用快非常多
  2. 事件循环
    上面的IO模型能够解决IO的效率问题,但是实际使用起来需要一个事件循环驱动协程去处理IO。
# 创建一个selctor对象
# 在不同的平台会使用不同的IO模型,比如Linux使用epoll
# 使用select调度IO
sel = selectors.DefaultSelector()
# 回调函数,用于接收新连接
def accept(sock, mask):
    conn, addr = sock.accept()  # Should be ready
    print('accepted', conn, 'from', addr)
    conn.setblocking(False) #设置socket的阻塞或非阻塞模式
    sel.register(conn, selectors.EVENT_READ, read)
# 回调函数,用户读取client用户数据
def read(conn, mask):
    data = conn.recv(1000)  # Should be ready
    if data:
        print('echoing', repr(data), 'to', conn)
        conn.send(data)  
    else:
        print('closing', conn)
        sel.unregister(conn)  # 注销描述符
        conn.close()
# 创建一个非堵塞的socket
sock = socket.socket()
sock.bind(('localhost', 1234))
sock.listen(100)
sock.setblocking(False)
sel.register(sock, selectors.EVENT_READ, accept)
# 一个事件循环,用于IO调度
# 当IO可读或者可写的时候, 执行事件所对应的回调函数
def loop():
    while True:
        events = sel.select()
        for key, mask in events:
            callback = key.data
            callback(key.fileobj, mask)
if __name__ == '__main__':
    loop()
# 上面代码中loop函数对应事件循环,它要做的就是一遍一遍的等待IO,然后调用事件的回调函数.
  1. 小结
    Python也实现了优秀高并发异步IO框架,比如tornado,gevent等等
    Python之所以能够处理网络IO高并发,是因为借助了高效的IO模型,能够最大限度的调度IO,然后事件循环使用协程处理IO,协程遇到IO操作就将控制权抛出,那么在IO准备好之前的这段事件,事件循环就可以使用其他的协程处理其他事情,然后协程在用户空间,并且是单线程的,所以不会像多线程,多进程那样频繁的上下文切换,因而能够节省大量的不必要性能损失。

你可能感兴趣的:(高并发)