# Python Tornado集成JSON Web Token方式登录

By [kk](https://paragraph.com/@kk-24) · 2023-03-29

---

IO multiplexing(IO多路复用)
-----------------------

![](https://storage.googleapis.com/papyrus_images/433057598e3e8671a6792d13040112e7565c0c2139de75b4f1f63207e5701250.png)

### select虽然是阻塞的，但是它的优势在于它可以用一个进程处理多个连接，这个利用非阻塞的轮询方式是无法实现的，当连接数增多时优势就明显，而对于单个连接则跟同步IO区别不大甚至性能还要更低。

### select,poll,epoll都是IO多路复用的机制，IO多路复用就是通过机制用一个进程监视多个描述符，一旦某个描述符就绪（可读或者可写或者异常），能够通知进程进行响应的操作。但是select，poll，epoll本质上是同步IO，因为他们都需要在读写事件就绪后自己负责读写，这个过程是阻塞的。

下面用Python的socket编程模拟IO多路复用（IO多路复用+回调+事件循环）
------------------------------------------

    class Fetcher:
        def connected(self, key):
            selector.unregister(key.fd)
            self.con.send('GET {} HTTP/1.1\r\nHost:{}\r\nConnection:close\r\n\r\n'.format(self.path,self.host).encode('utf-8'))
            selector.register(self.con.fileno(), EVENT_READ, self.read)
    
        def read(self, key):
            d = self.con.recv(1024)
            if d:
                print(d)
                self.data += d
            else:
                selector.unregister(key.fd)
                self.data = self.data.decode('utf-8')
                html_data = self.data.split('\r\n\r\n')[1]
                print(html_data)
                self.con.close()
    
        def get_url(self, url):
            ...
            self.con = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.con.setblocking(False)
            #设置非阻塞
            try:
                self.con.connect((self.host, 80))
            except BlockingIOError as e:
                pass
            selector.register(self.con.fileno(), EVENT_WRITE, self.connected)
    

过程：发送一个socket请求设置为非阻塞，在select函数中注册事件，self.con.fileno表示当前连接在进程中的描述符，EVENT\_WRITE表示socket准备是否就绪，self.connected为回调函数，准备完成后就调用。selector.unregister(key.fd)取消注册，发送HTTP请求，再调用selector.register(self.con.fileno(), EVENT\_READ, self.read)注册，若当前请求内容可读，则调用read回调函数读取出响应内容。

注明：在windows下会调用select函数，而在linux/unix下则会调用epoll函数

完整代码如下：

    import socket
    from urllib.parse import urlparse
    from selectors import DefaultSelector, EVENT_READ, EVENT_WRITE
    selector = DefaultSelector()
    
    class Fetcher:
        def connected(self, key):
            selector.unregister(key.fd)
            self.con.send('GET {} HTTP/1.1\r\nHost:{}\r\nConnection:close\r\n\r\n'.format(self.path,self.host).encode('utf-8'))
            selector.register(self.con.fileno(), EVENT_READ, self.read)
    
        def read(self, key):
            d = self.con.recv(1024)
            if d:
                print(d)
                self.data += d
            else:
                selector.unregister(key.fd)
                self.data = self.data.decode('utf-8')
                html_data = self.data.split('\r\n\r\n')[1]
                print(html_data)
                self.con.close()
    
        def get_url(self, url):
            url = urlparse(url)
            self.host = url.netloc
            self.path = url.path
            self.data = b''
            if self.path == "":
                self.path = '/'
    
            self.con = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.con.setblocking(False)
    
            try:
                self.con.connect((self.host, 80))
            except BlockingIOError as e:
                pass
            #注册
            selector.register(self.con.fileno(), EVENT_WRITE, self.connected)
    
    def loop():
        while True:
            ready = selector.select()
            for key, mask in ready:
                callback = key.data
                callback(key)
    
    if __name__ == '__main__':
        fetcher = Fetcher()
        fetcher.get_url('http://www.baidu.com')
        loop()

---

*Originally published on [kk](https://paragraph.com/@kk-24/python-tornado-json-web-token)*
