阶段内容回顾:
1. socket
2. 浏览器/爬虫等都是socket客户端
3. 到底谁疼?
- 客户端向服务端发起连接时,服务端疼
- 客户端向服务端发送数据时,客户端疼(为客户端创建的socket对象)
conn,addr = server.accept()
conn.recv()
4. 如果你想要提高并发?
- 多进程:计算
- 多线程:IO
本节内容:
1. IO多路复用
2. 基于IO多路复用+socket实现并发请求(一个线程100个请求)
3. 协程
内容详细:
1. IO多路复用
IO多路复用作用:检测多个socket是否已经发生变化(是否已经连接成功/是否已经获取数据)(可读/可写)
2. 基于IO多路复用+socket实现并发请求(一个线程100个请求)
IO多路复用
socket非阻塞
基于事件循环实现的异步非阻塞框架:lzl
非阻塞:不等待
异步:执行完某个人物后自动调用我给他的函数。
Python中开源 基于事件循环实现的异步非阻塞框架 Twisted (单线程完成并发)
总结:
1. socket默认是否是阻塞的?阻塞体现在哪里?
是,content、recv
2. 如何让socket编程非阻塞?
socket.setblocking(false)
3. IO多路复用作用?
检测多个socket是否发生变化。
操作系统检测socket是否发生变化,有三种模式:
select:最多1024个socket;循环去检测。
poll:不限制监听socket个数;循环去检测(水平触发)。
epoll:不限制监听socket个数;回调方式(边缘触发)。
Python模块:
select.select
select.epoll
4. 提高并发方案:
- 多进程
- 多线程
- 异步非阻塞模块(Twisted) scrapy框架(单线程完成并发)
5. 什么是异步非阻塞?
- 非阻塞,不等待。
比如创建socket对某个地址进行connect、获取接收数据recv时默认都会等待(连接成功或接收到数据),才执行后续操作。
如果设置setblocking(False),以上两个过程就不再等待,但是会报BlockingIOError的错误,只要捕获即可。
- 异步,通知,执行完成之后自动执行回调函数或自动执行某些操作(通知)。
比如做爬虫中向某个地址baidu.com发送请求,当请求执行完成之后自执行回调函数。
6. 什么是同步阻塞?
- 阻塞:等
- 同步:按照顺序逐步执行
key_list = ['alex','db','sb']
for item in key_list:
ret = requests.get('https://www.baidu.com/s?wd=%s' %item)
print(ret.text)
7. 概念 (用封装的方法传输多个数据)
之前:
# 你写的代码:7000w
v = [
[11,22], # 每个都有一个append方法
[22,33], # 每个都有一个append方法
[33,44], # 每个都有一个append方法
]
# 王思聪
for item in v:
print(item.append)
之后:
class Foo(object):
def __init__(self,data,girl):
self.row = data
self.girl = girl
def append(self,item):
self.row.append(item)
v = [
Foo([11,22],'雪梨'), # 每个都有一个append方法
Foo([22,33],'冰糖'), # 每个都有一个append方法
Foo([33,44],'糖宝'), # 每个都有一个append方法
]
for item in v:
print(item.append)
item.girl
3. 协程
概念:
进程,操作系统中存在;
线程,操作系统中存在;
协程,是由程序员创造出来的一个不是真实存在的东西;
协程:是微线程,对一个线程进程分片,使得线程在代码块之间进行来回切换执行,而不是在原来逐行执行。
import greenlet def f1(): print(11) gr2.switch() print(22) gr2.switch() def f2(): print(33) gr1.switch() print(44) # 协程 gr1 gr1 = greenlet.greenlet(f1) # 协程 gr2 gr2 = greenlet.greenlet(f2) gr1.switch()
注意:单纯的协程无用
def f1(): print(11) print(33) def f2(): print(22) print(44) f1() f2()
协程存在的意义
协程 + 遇到IO就切换 => 牛逼起来了 pip3 install gevent
from gevent import monkey monkey.patch_all() # 以后代码中遇到IO都会自动执行greenlet的switch进行切换 import requests import gevent def get_page1(url): ret = requests.get(url) print(url,ret.content) def get_page2(url): ret = requests.get(url) print(url,ret.content) def get_page3(url): ret = requests.get(url) print(url,ret.content) gevent.joinall([ gevent.spawn(get_page1, 'https://www.python.org/'), # 协程1 gevent.spawn(get_page2, 'https://www.yahoo.com/'), # 协程2 gevent.spawn(get_page3, 'https://github.com/'), # 协程3 ])
总结:
1. 什么是协程?
协程也可以称为“微线程”,就是开发者控制线程执行流程,控制先执行某段代码然后再切换到另外函执行代码...来回切换。
2. 协程可以提高并发吗?
协程自己本身无法实现并发(甚至性能会降低)。
协程+IO切换性能提高。
3. 进程、线程、协程的区别?
4. 单线程提供并发:
- 协程+IO切换:gevent
- 基于事件循环的异步非阻塞框架:Twisted
手动实现协程:yield关键字生成器
def f1(): print(11) yield print(22) yield print(33) def f2(): print(55) yield print(66) yield print(77) v1 = f1() v2 = f2() next(v1) # v1.send(None) next(v2) # v1.send(None) next(v1) # v1.send(None) next(v2) # v1.send(None) next(v1) # v1.send(None) next(v2) # v1.send(None)
其他:
def f1(): print(11) x1 = yield 1 print(x1,22) x2 = yield 2 print(33) def f2(): print(55) yield print(66) yield print(77) v1 = f1() v2 = f2() ret = v1.send(None) print(ret) r2 = v1.send(999) print(r2)
重点总结:
1. 进程、线程、协程的区别? **********
2. 写代码:gevent *****
from gevent import monkey monkey.patch_all() # 以后代码中遇到IO都会自动执行greenlet的switch进行切换 import requests import gevent def get_page1(url): ret = requests.get(url) print(url,ret.content) def get_page2(url): ret = requests.get(url) print(url,ret.content) def get_page3(url): ret = requests.get(url) print(url,ret.content) gevent.joinall([ gevent.spawn(get_page1, 'https://www.python.org/'), # 协程1 gevent.spawn(get_page2, 'https://www.yahoo.com/'), # 协程2 gevent.spawn(get_page3, 'https://github.com/'), # 协程3 ])
3. 写代码:twisted *****
from twisted.web.client import getPage, defer from twisted.internet import reactor def all_done(arg): reactor.stop() def callback(contents): print(contents) deferred_list = [] url_list = ['http://www.bing.com', 'http://www.baidu.com', ] for url in url_list: deferred = getPage(bytes(url, encoding='utf8')) deferred.addCallback(callback) deferred_list.append(deferred) dlist = defer.DeferredList(deferred_list) dlist.addBoth(all_done) reactor.run()
4. 异步非阻塞
5. IO多路复用
作用:可以监听所有的IO请求的状态。
- socket
I,input
o,output
三种模式:
- select
- poll
- epoll
两周总结:
网络编程:
1. 网络基础
- 网卡
- IP
- ...
2. OSI 7层
3. 三次握手四次挥手
4. BS和CS架构?
5. socket基本代码
6. 黏包 (不用看代码)
出现的原因,解决方法
7. 断点续传
8. 协议
自定义协议:{'code':10001,data:{...}}
Http协议:GET /s?wd=alex HTTP/1.0
host:www.baidu.com
9. 面向对象+高级作业:反射/面向对象
并发编程:
1. 进程、线程、协程的区别?
2. 线程
- 基本写法
- 实例化
- 继承
- 锁
- RLock
...
- 线程池
3. 进程
- 基本写法
- 实例化
- 继承
- 锁
- RLock
...
- 线程池
- 进程数据共享
4. 协程
- 协程
- 协程+IO:gevent
5. IO多路复用
6. 异步/同步 阻塞/非阻塞