zoukankan      html  css  js  c++  java
  • Python层面中的高并发

    一、传输模型

    (一).基本模型

    (二).层次划分

    七层模型与四层模型

    作为Python开发,都是在应用层的HTTP协议之上进行开发的。HTTP协议是基于TCP之上的,也就是Python开发需要关心的是传输层。

     

    二、TCP连接

    (一).建立连接(三次握手)

    第一次,只是客户端告诉服务端。

    第二次,客户端才知道服务端收到了。

    第三次,服务端才知道客户端收到了。

    (二).传输数据

    客户端向服务端请求,服务端向客户端响应。

    一发一收,一收一发。

    (三).断开连接(四次挥手)

    因为服务端可能还有数据要发给客户端,所以在断开时就多了一次挥手。

     

    三、IP地址与端口

    (一).IP

    127.0.0.1(localhost):本地回环

    0.0.0.0:任意IP都可以访问

    (二).端口

    端口是用来区分我们的应用程序的

    端口的范围:0-65535,其中0-1023多为知名端口,1024-65535多为动态端口

     

    四、套接字编程

    (一).套接字基本概念

    (二).三种套接字

    (三).套接字编程的常用方法

    绑定套接字:bind()

    服务端监听套接字:listen()

    客户端连接服务端套接字:connect()

    对等连接套接字:accept()

    发送数据套接字:send()

    接收数据套接字:recv()

    关闭连接套接字:close()

    (四).示例

    (1).服务端代码

    # 服务端
    
    import socket
    from datetime import datetime
    
    server = socket.socket()  # 实例化
    server.bind(("127.0.0.1", 3333))  # 绑定
    server.listen(16)  # 监听
    print(server)
    
    while 1:
        conn, addr = server.accept()  # 生成对等连接套接字
        print(f"time:{datetime.now()}")
        msg = conn.recv(1024)  # 接收来自客户端的数据
        print(f"got a message from client:{msg}")
        conn.send(msg)  # 把数据发送回去给客户端
        conn.close()  # 把对等连接套接字关了。服务不要关,不然监听不到了
    View Code

    (2).客户端代码

    # 客户端
    
    import socket
    
    while 1:
        client = socket.socket()
        client.connect(("127.0.0.1", 3333))#连接
        sentence = input("enter your message:")
        msg = bytes(sentence, "utf-8")
        client.send(msg)
        response = client.recv(1024)
        print(f"have received response from server:{response}")
        client.close()
    View Code

    (3).先启动服务端

    不启动服务器,客户端怎么连过去?比如某服务器崩了,还能访问吗?

     

    五、非阻塞套接字

    第四部分中的示例是阻塞套接字,一次只能服务一个客户端,在accept()和recv()这两处会发生阻塞。

    (一).accept()阻塞

    在没有新的套接字来之前,不能处理已经建立连接的套接字的请求。

    (二).recv()阻塞

    在没有接受到客户端请求数据之前,不能与其他客户端建立连接。

    (三).普通服务器的IO模型

    (四).非阻塞IO模型

     

    六、IO多路复用

    第五部分的非阻塞套接字,有着非常严重的资源浪费,CPU吃满,无用处理很多。

    那么就需要考虑用更合适的技术来解决这个问题,这里使用Linux上的epoll,来解决这个问题。

    epoll是目前Linux上,效率最高的IO多路复用技术!

    (一).IO多路复用技术

    把socket交给操作系统去监控。

    (二).epoll是惰性的事件回调

    惰性事件回调是由用户进程自己调用的操作系统只起到通知的作用

     

    七、进程

    (一).计算机执行指令示意图

    (二).轮询调度实现并发执行

    并发:看上去一起执行,同时在发生

    并行:真正一起执行,同时在进行

    调度算法:时间片轮转;优先级调度

    前提:一个CPU

    (三).并行需要的核心条件

    并行真正的核心条件是有多个CPU

    (四).多进程实现并行

    (1).进程的概念

    计算机程序是存储在磁盘上的可执行二进制(或其他类型)文件。

    只有把它们加载到内存中,并被操作系统调用,它们才会拥有其自己的生命周期。

    进程则是表示的一个正在执行的程序。

    每个进程都拥有自己的地址空间、内存、数据栈,以及其他用于跟踪执行的辅助数据。

    操作系统负责其上所有进程的执行。操作系统会为这些进程合理地分配执行时间。

    (2).Python进程的使用流程

    (3).多进程并行的必要条件

    总进程数量不多于CPU核心数量!

    运行的程序都是轮询调度产生的并行假象。但是在Python层面的确获得了并行!

     

    八、线程

    (一).线程的概念

    线程被称作轻量级进程。与进程类似,不过它们是在同一个进程下执行的。并且它们会共享相同的上下文。

    当其他线程运行时,它可以被抢占(中断)和临时挂起(也成为睡眠),也被成为:让步。

    线程的轮询调度机制类似于进程的轮询调度。只不过这个调度不是由操作系统来负责,而是由Python解释器来负责。

    (二).Python线程的使用流程

    (三).GIL锁

    全局解释器锁

    Python在设计的时候,还没有多核处理器的概念。因此,为了设计方便与线程安全,直接设计了一个锁。这个锁要求,任何进程中,一次只能有一个线程在执行。

    因此,并不能为多个线程分配多个CPU。所以Python中的线程只能实现并发,而不能实现真正的并行。

    但是Python3中的GIL锁有一个很棒的设计,在遇到阻塞(不是耗时)的时候,会自动切换线程。因此我们可以利用这种机制来有效的避开阻塞,充分利用CPU

    Django、Flask、Web2py,都是使用多线程来做。

     

    九、并发通信

    (一).进程间的通信

    进程是互不干扰的独立内存空间

    进程间通信的解决方案:

    1.管理器负责与公共进程通信2.代理负责操作共享的空间

    (二).线程间的通信

    线程属于同一个进程,是共享内存空间的。会发生资源竞争的问题,需要用互斥锁来解决这个问题。

    互斥锁:控制共享资源的访问

    (三).线程与进程安全的队列

    队列的基本概念:

    一个入口,一个出口,先入先出,First input first output(FIFO)

     

    十、进程池与线程池

    (一).池的概念

    主线程: 相当于生产者,只管向线程池提交任务。并不关心线程池是如何执行任务的。因此,并不关心是哪一个线程执行的这个任务。

    线程池: 相当于消费者,负责接收任务,并将任务分配到一个空闲的线程中去执行。

    十一、greenlet

    (一).什么是greenlet

    虽然CPython(标准Python)能够通过生成器来实现协程,但使用起来还并不是很方便。

    与此同时,Python的一个衍生版 Stackless Python 实现了原生的协程,它更利于使用。

    于是,大家开始将 Stackless 中关于协程的代码单独拿出来做成了CPython的扩展包。

    这就是greenlet的由来,因此 greenlet 是底层实现了原生协程的C扩展库

    (二).安装包

    需要额外安装依赖包:sudo pip3 install greenlet

    (三).greenlet的价值

    (1).高性能的原生协程

    (2).语义更加明确的显式切换

    (3).直接将函数包装成协程,保持原有代码风格

    十二、gevent协程

    (一).什么事gevent协程

    虽然,我们有了 基于 epoll 的回调式编程模式,但是却难以使用。

    即使我们可以通过配合 生成器协程 进行复杂的封装,以简化编程难度。

    但是仍然有一个大的问题: 封装难度大,现有代码几乎完全要重写。

    gevent,通过封装了 libev(基于epoll) 和 greenlet 两个库。

    帮我们做好封装,允许我们以类似于线程的方式使用协程。

    以至于我们几乎不用重写原来的代码就能充分利用 epoll 和 协程 威力。

    (二).安装包

    sudo pip3 install gevent

    (三).gevent的价值

    遇到阻塞就切换到另一个协程继续执行 !

    (1).使用基于epoll的libev来避开阻塞

    (2).使用基于gevent的高效协程来切换执行

    (3).只在遇到阻塞的时候切换,没有轮询的开销,也没有线程的开销

  • 相关阅读:
    什么是webview
    juqery.fn.extend和jquery.extend
    LeetCode
    5. Longest Palindromic Substring
    42. Trapping Rain Water
    11. Container With Most Water
    621. Task Scheduler
    49. Group Anagrams
    739. Daily Temperatures
    3. Longest Substring Without Repeating Characters
  • 原文地址:https://www.cnblogs.com/quanquan616/p/10022625.html
Copyright © 2011-2022 走看看