一. 并发、并行、同步、异步、阻塞、非阻塞

  1.并发:是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机(CPU)上运行,但任一个时刻点上只有一个程序在处理机上运行。

  2.并行:是指任何时间点,有多个程序运行在多个CPU上(最多和CPU数量一致)。

  3.并发和并行的区别:

    并发和并行是即相似又有区别的两个概念,并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔内发生。在多道程序环境下,并发性是指在一段时间内宏观上有多个程序在同时运行,但在单处理机系统中,每一时刻却仅能有一道程序执行,故微观上这些程序只能是分时地交替执行。倘若在计算机系统中有多个处理机,则这些可以并发执行的程序便可被分配到多个处理机上,实现并行执行,即利用每个处理机来处理一个可并发执行的程序,这样,多个程序便可以同时执行。

  4.同步:是指代码调用IO操作时,必须等待IO操作完成才能返回的调用方式。

  5.异步:是指代码调用IO操作时,不必等待IO操作完成就能返回的调用方式。

  6.阻塞:是指调用函数的时候当前线程被挂起。

  7.非阻塞:是指调用函数的时候当前线程不会被挂起,而是立即返回。

二. C10K问题和io多路复用(select、poll、epoll)

  1.C10K问题:

    谓c10k问题,指的是服务器同时支持成千上万个客户端的问题,也就是concurrent 10 000 connection(这也是c10k这个名字的由来)。由于硬件成本的大幅度降低和硬件技术的进步,如果一台服务器同时能够服务更多的客户端,那么也就意味着服务每一个客户端的成本大幅度降低,从这个角度来看,问题显得非常有意义。

  2.五种I/O模型(详情:https://www.cnblogs.com/findumars/p/6361627.html):

协程和异步io

    协程和异步io

 

    5.1阻塞I式/O:系统调用不会立即返回结果,当前线程会阻塞,等到获得结果或报错时在返回(问题:如在调用send()的同时,线程将被阻塞,在此期间,线程将无法执行任何运算或响应任何的网络请求。)

协程和异步io

 

    5.2非阻塞式I/O:调用后立即返回结果(问题:不一定三次握手成功,recv() 会被循环调用,循环调用recv()将大幅度推高CPU 占用率),做计算任务或者再次发起其他连接就较有优势

协程和异步io

协程和异步io

 

 

    5.3I/O复用:它的基本原理就是select/epoll这个function会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程。(阻塞式的方法,可以监听多个socket状态)(问题:将数据从内核复制到用户空间的时间不能省)

协程和异步io

 

    5.4信号驱动式I/O:运用较少

协程和异步io

 

    5.5异步I/O:它就像是用户进程将整个IO操作交给了他人(kernel)完成,然后他人做完后发信号通知。在此期间,用户进程不需要去检查IO操作的状态,也不需要主动的去拷贝数据。

  3.解决方法(参照:https://blog.csdn.net/wangtaomtk/article/details/51811011):

    3.1每个线程/进程处理一个连接:

      但是由于申请进程/线程会占用相当可观的系统资源,同时对于多进程/线程的管理会对系统造成压力,因此这种方案不具备良好的可扩展性。因此,这一思路在服务器资源还没有富裕到足够程度的时候,是不可行的;即便资源足够富裕,效率也不够高。

      问题:资源占用过多,可扩展性差。

    3.2每个进程/线程同时处理多个连接(IO多路复用):

      3.2.1传统思路

        最简单的方法是循环挨个处理各个连接,每个连接对应一个 socket当所有 socket 都有数据的时候,这种方法是可行的。但是当应用读取某个 socket 的文件数据不 ready 的时候,整个应用会阻塞在这里等待该文件句柄,即使别的文件句柄 ready,也无法往下处理。    

        思路:直接循环处理多个连接。问题:任一文件句柄的不成功会阻塞住整个应用。

      3.2.2select:

协程和异步io

 

       

        思路:有连接请求抵达了再检查处理。

        问题:句柄上限+重复初始化+逐个排查所有文件句柄状态效率不高。

      3.2.3poll

协程和异步io

 

        思路:设计新的数据结构提供使用效率。

        问题:逐个排查所有文件句柄状态效率不高。

      3.2.4epoll(nginx使用的是epoll)

协程和异步io

 

        思路:只返回状态变化的文件句柄。

        问题:依赖特定平台(Linux)。

    注:epoll不一定比select好(在高并发的情况下,连接活跃度不是很高,epoll比select好;在并发性不高,同时连接很活跃select比epoll好(游戏))

三. epoll+回调+事件循环方式url

  1. 通过非阻塞I/O实现http请求:

 1 import socket
 2 from urllib.parse import urlparse
 3 
 4 def get_url(url):
 5     #通过socket请求html
 6     url=urlparse(url)
 7     host=url.netloc
 8     path=url.path
 9     if path=="":
10         path="/"
11     #建立socket连接
12     client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
13     #设置成非阻塞(抛异常:BlockingIOError: [WinError 10035] 无法立即完成一个非阻止性套接字操作。)
14     client.setblocking(False)
15     try:
16         client.connect((host,80))
17     except BlockingIOError as e:
18         pass
19     #向服务器发送数据(还未连接会抛异常)
20     while True:
21         try:
22             client.send("GET {} HTTP/1.1\r\nHost:{}\r\nConnection:close\r\n\r\n".format(path, host).encode("utf8"))
23             break
24         except OSError as e:
25             pass
26     #将数据读取完
27     data=b""
28     while True:
29         try:
30             d=client.recv(1024)
31         except BlockingIOError as e:
32             continue
33         if d:
34             data+=d
35         else:
36             break
37     #会将header信息作为返回字符串
38     data=data.decode('utf8')
39     print(data.split('\r\n\r\n')[1])
40     client.close()
41 
42 if __name__=='__main__':
43     get_url('http://www.baidu.com')
View Code

相关文章:

  • 2021-05-18
  • 2022-01-12
  • 2021-10-05
  • 2021-12-11
  • 2021-08-02
  • 2022-12-23
猜你喜欢
  • 2021-09-22
  • 2022-12-23
  • 2021-05-19
  • 2021-08-26
  • 2022-12-23
  • 2022-12-23
相关资源
相似解决方案