阻塞: 在单线程下,当要执行两个相同的操作时,必须等前一个操作完成后,后一个操作才能执行

非阻塞: 在单线程下,当要执行两个相同的操作时,不必等待前一个操作完成,直接继续执行下去,也就是可以并发

异步:当达到某种状态后自动调用回调函数,我们在向服务端发送请求时,给它传递一个函数,当它处理完请求时,执行这个函数,站在客户端的角度上就是异步

阻塞和非阻塞 关键看是否等待前一个操作完成

异步:看是否有回调函数

 

自定义异步非阻塞

这个是基于socket设置setblocking(ffalse)和io多路复用实现的

爬虫发送HTTP的本质就是在创建socket

io多路复用循环监听socket是否发生变化,一旦发生变化,可以触发我们自定义的操作

##实现单线程下可以发送很多请求
import socket
import select

class Request(object):
    def __init__(self,sk,callback):
        self.sk=sk
        self.callback=callback
    def fileno(self):
        #这个函数是为了多路复用用的
        return self.sk.fileno()
class Async:
    def __init__(self):
        self.fds=[]#监听是socket,
        self.conn=[]#监听连接成功

    def add(self,url,callback):
        """
        创建socket连接
        :param url:
        :return:
        """
        sk=socket.socket()
        sk.setblocking(False)#这样写就是非阻塞了
        # sk.callback=callback
        try:
            sk.connect((url,80))
        except BlockingIOError as e:#当没连接成功就会报错
            pass
        req=Request(sk,callback)#把socket和callback封装到Request的对象中
        self.fds.append(req)
        self.conn.append(req)

    def run(self):
        """
        '监听socket的状态变化'
        :return:
        """
        while True:
            #监听是否连接成功
            r,w,e=select.select(self.fds,self.conn,[],0.05)#一旦连接成功后w就会有值了,#w表示已经连接成功的socket,多路复用其实是监听的socket.fileno()函数
            # w表示已经连接成功的socket
            for req in w:
                #连接成功后发数据
                req.sk.sendall(
                    b'GET /wupeiqi HTTP/1.1\r\nUser-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                    b'Chrome/63.0.3239.132 Safari/537.36\r\n\r\n')
                #连接成功后,不用再监听了.
                self.conn.remove(req)
                #如果服务端给用户返回数据了, r里面有值表示服务端发回来数据了
            for req in r:
                #获取到响应数据
                data=req.sk.recv(8096)
                req.callback(data)#调用回调函数
                #获取完数据后,断开连接
                req.sk.close()
                #断开监听
                self.fds.remove(req)
            if not self.fds:
                break


ah=Async()

def callback1(data):
    print('callback1',data)
def callback2(data):
    print('callback2',data)
def callback3(data):
    print('callback3',data)


ah.add('www.cnblogs.com',callback1) # sk1
ah.add('www.baidu.com',callback2)   # sk2
ah.add('www.luffycity.com',callback3) # sk3

ah.run()

 

结果:

callback2 b'HTTP/1.1 302 Found\r\nCache-Control: max-age=86400\r\nConnection: Keep-Alive\r\nContent-Length: 222\r\nContent-Type: text/html; charset=iso-8859-1\r\nDate: Thu, 07 Jun 2018 08:20:48 GMT\r\nExpires: Fri, 08 Jun 2018 08:20:48 GMT\r\nLocation: https://www.baidu.com/search/error.html\r\nServer: Apache\r\n\r\n<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">\n<html><head>\n<title>302 Found</title>\n</head><body>\n<h1>Found</h1>\n<p>The document has moved <a href="http://www.baidu.com/search/error.html">here</a>.</p>\n</body></html>\n'
callback3 b'HTTP/1.1 400 Bad Request\r\nDate: Thu, 07 Jun 2018 08:20:48 GMT\r\nContent-Type: text/html\r\nContent-Length: 242\r\nConnection: close\r\n\r\n<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">\r\n<html>\r\n<head><title>400 Bad Request</title></head>\r\n<body bgcolor="white">\r\n<h1>400 Bad Request</h1>\r\n<p>Your browser sent a request that this server could not understand.</body>\r\n</html>\r\n'
callback1 b'HTTP/1.1 400 Bad Request\r\nDate: Thu, 07 Jun 2018 08:20:48 GMT\r\nContent-Type: text/html\r\nContent-Length: 242\r\nConnection: close\r\n\r\n<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">\r\n<html>\r\n<head><title>400 Bad Request</title></head>\r\n<body bgcolor="white">\r\n<h1>400 Bad Request</h1>\r\n<p>Your browser sent a request that this server could not understand.</body>\r\n</html>\r\n'
View Code

提高并发的方式

1.多进程

2.多线程

3.利用异步非阻塞模块来实现单线程的下的并发

 

posted on 2018-06-07 17:01  程序员一学徒  阅读(407)  评论(0)    收藏  举报