python学习笔记-异步非阻塞web框架

一、异步非阻塞框架介绍

1、介绍

支持异步非阻塞web框架:tornado , node js

2、定义对比

异步IO模块:我们作为客户端向服务端“并发”请求

异步非阻塞web框架:针对服务端,希望一个线程处理更多的请求

二、tornado异步非阻塞

【要点提炼】

使用装饰器@gen.coroutine

模拟等待,使用特殊的形式等待5秒,写法

future=Future()
tornado.ioloop.IOLoop.current().add_timeout(time.time()+5,self.done)
yield future

1、场景实例1

程序1,之前写法,同步阻塞(先访问main会进入等待,此时访问index也到等main处理完了才有返回)

import tornado.ioloop
import tornado.web
import time

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        time.sleep(10)
        self.write("main")

class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("index")

application=tornado.web.Application([
    (r"/main",MainHandler),
    (r"/index", IndexHandler),])

if __name__=="__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

程序2,此时要用到future,实现异步不阻塞

from tornado import gen
from tornado.concurrent import Future

class MainHandler(tornado.web.RequestHandler):

    @gen.coroutine
    def get(self):
        future = Future()
        tornado.ioloop.IOLoop.current().add_timeout(time.time() + 5, self.done)
        yield future

    def done(self):
        self.write("Main")
        self.finish()

class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("index")

application=tornado.web.Application([
    (r"/main",MainHandler),
    (r"/index", IndexHandler),])

if __name__=="__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

  

2、场景实例2 (请求外部url时引起的阻塞)

【要点提炼】

通过tornado发请求,实现异步非阻塞

from tornado import httpclient
http=httpclient.AsyncHTTPClient()
yield http.fetch("http://www.google.com",self.done)

future的作用

1、挂起当前请求,线程可以处理其他请求

2、future设置值,当前挂起的请求返回

future.set_result(None)

程序3,同步阻塞场景

import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        import requests
        ret=requests.get("http://www.google.com")
        self.write("main")

class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("index")

application=tornado.web.Application([
    (r"/main",MainHandler),
    (r"/index", IndexHandler),])

if __name__=="__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

程序4、异步非阻塞

import tornado.ioloop
import tornado.web
from tornado import gen

class MainHandler(tornado.web.RequestHandler):

    @gen.coroutine
    def get(self):
        from tornado import httpclient
        http=httpclient.AsyncHTTPClient()
        yield http.fetch("http://www.google.com",self.done)

    def done(self,*args,**kwargs):
        self.write("Main")
        self.finish()

class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("index")

application=tornado.web.Application([
    (r"/main",MainHandler),
    (r"/index", IndexHandler),])

if __name__=="__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

  

3、实例3,(自己设置future的值,将挂起的请求释放)

future=None
class MainHandler(tornado.web.RequestHandler):
    @gen.coroutine
    def get(self):
        global future
        future=Future()
        future.add_done_callback(self.done)
        yield future

    def done(self,*args,**kwargs):
        self.write("Main")
        self.finish()

class IndexHandler(tornado.web.RequestHandler):
    def get(self):
        global future
        future.set_result(None) #设置值,将挂起的请求释放
        self.write("index")

application=tornado.web.Application([
    (r"/main",MainHandler),
    (r"/index", IndexHandler),])

if __name__=="__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

  

三、自定义web框架

1、使用socket实现同步的web框架

示例:

import socket
import select

class HttpRequest(object):
    """
    用来封装用户请求信息
    """
    def __init__(self, content):
        self.content = content #用户发来的请求数据,请求头和请求体
        self.header_bytes = bytes()
        self.header_dict = {}
        self.body_bytes = bytes()

        self.method = ""
        self.url = ""
        self.protocol = ""

        self.initialize()
        self.initialize_headers()

    def initialize(self):
        temp = self.content.split(b'\r\n\r\n', 1)
        if len(temp) == 1:
            self.header_bytes += temp
        else:
            h, b = temp
            self.header_bytes += h
            self.body_bytes += b
            header_flag = True

    @property
    def header_str(self):
        return str(self.header_bytes, encoding='utf-8')

    def initialize_headers(self):
        headers = self.header_str.split('\r\n')
        first_line = headers[0].split(' ')
        if len(first_line) == 3:
            self.method, self.url, self.protocol = headers[0].split(' ')
            for line in headers:
                kv = line.split(':')
                if len(kv) == 2:
                    k, v = kv
                    self.header_dict[k] = v

def index(requet):
    return "index"

def main(request):
    return 'main'

routers=[('/index',index),
         ('/main',main),
         ]

def run():
    sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    sock.bind(('127.0.0.1',8002,))
    sock.listen(128)

    inputs=[]
    inputs.append(sock)

    while True:
        rlist,wlist,elist=select.select(inputs,[],[],0.05)
        for r in rlist:
            if r==sock:
                '''新请求到来'''
                conn,addr=sock.accept()
                conn.setblocking(False)
                inputs.append(conn)
            else:
                '''客户端发来数据'''
                data=b""
                while True:
                    try:
                        chunk=r.recv(1024)
                        data=data+chunk
                    except Exception as e:
                        chunk=None
                    if not chunk:
                        break
                #dada进行处理:请求头和请求体
                #1、请求头中获取url
                #2、去路由中匹配,获取指定的函数
                #3、执行函数,获取返回值
                #4、发送给客户端
                request=HttpRequest(data)
                import re
                flag=False
                func=None
                for route in routers:
                    if re.match(route[0],request.url):
                        flag=True
                        func=route[1]
                        break
                if flag:
                    result=func(request)
                    r.sendall(bytes("HTTP/1.1 201 OK\r\n\r\n", 'utf8'))
                    r.sendall(bytes(result, 'utf8'))
                else:
                    r.sendall(bytes("HTTP/1.1 201 OK\r\n\r\n", 'utf8'))
                    r.sendall(b'404')

                inputs.remove(r)
                r.close()

if __name__=="__main__":
    run()

  

2、自定义web框架支持异步

【要点提炼】

编写实现future功能,如访问的main,不执行操作

》示例2

import socket
import select

class HttpRequest(object):
    """
    用来封装用户请求信息
    """
    def __init__(self, content):
        self.content = content #用户发来的请求数据,请求头和请求体
        self.header_bytes = bytes()
        self.header_dict = {}
        self.body_bytes = bytes()

        self.method = ""
        self.url = ""
        self.protocol = ""

        self.initialize()
        self.initialize_headers()

    def initialize(self):
        temp = self.content.split(b'\r\n\r\n', 1)
        if len(temp) == 1:
            self.header_bytes += temp
        else:
            h, b = temp
            self.header_bytes += h
            self.body_bytes += b
            header_flag = True

    @property
    def header_str(self):
        return str(self.header_bytes, encoding='utf-8')

    def initialize_headers(self):
        headers = self.header_str.split('\r\n')
        first_line = headers[0].split(' ')
        if len(first_line) == 3:
            self.method, self.url, self.protocol = headers[0].split(' ')
            for line in headers:
                kv = line.split(':')
                if len(kv) == 2:
                    k, v = kv
                    self.header_dict[k] = v

class Future(object): def __init__(self): self.result=None F=None def stop(request): global F F.result=b'future-xxxxx' return 'stop' def index(requet): return "index" def main(request): global F F=Future() return F routers=[('/index',index), ('/main',main), ('/stop',stop), ] def run(): sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) sock.bind(('127.0.0.1',8002,)) sock.listen(128) inputs=[] inputs.append(sock) async_request_dict={} while True: rlist,wlist,elist=select.select(inputs,[],[],0.05) for r in rlist: if r==sock: '''新请求到来''' conn,addr=sock.accept() conn.setblocking(False) inputs.append(conn) else: '''客户端发来数据''' data=b"" while True: try: chunk=r.recv(1024) data=data+chunk except Exception as e: chunk=None if not chunk: break request=HttpRequest(data) import re flag=False func=None for route in routers: if re.match(route[0],request.url): flag=True func=route[1] break if flag: result=func(request) if isinstance(result,Future): async_request_dict[r]=result else: r.sendall(bytes("HTTP/1.1 201 OK\r\n\r\n", 'utf8')) r.sendall(bytes(result,encoding='utf8')) inputs.remove(r) r.close() else: r.sendall(bytes("HTTP/1.1 201 OK\r\n\r\n", 'utf8')) r.sendall(b'404') inputs.remove(r) r.close() for conn in list(async_request_dict.keys()): future=async_request_dict[conn] if future.result: conn.sendall(bytes("HTTP/1.1 201 OK\r\n\r\n", 'utf8')) conn.sendall(future.result) conn.close() del async_request_dict[conn] inputs.remove(conn) if __name__=="__main__": run()

 

示例2是手动写的方法停止阻塞,可以实现利用超时时间自动停止

》示例3

import socket
import select
import time

class HttpRequest(object):
    """
    用来封装用户请求信息
    """
    def __init__(self, content):
        self.content = content #用户发来的请求数据,请求头和请求体
        self.header_bytes = bytes()
        self.header_dict = {}
        self.body_bytes = bytes()

        self.method = ""
        self.url = ""
        self.protocol = ""

        self.initialize()
        self.initialize_headers()

    def initialize(self):
        temp = self.content.split(b'\r\n\r\n', 1)
        if len(temp) == 1:
            self.header_bytes += temp
        else:
            h, b = temp
            self.header_bytes += h
            self.body_bytes += b
            header_flag = True

    @property
    def header_str(self):
        return str(self.header_bytes, encoding='utf-8')

    def initialize_headers(self):
        headers = self.header_str.split('\r\n')
        first_line = headers[0].split(' ')
        if len(first_line) == 3:
            self.method, self.url, self.protocol = headers[0].split(' ')
            for line in headers:
                kv = line.split(':')
                if len(kv) == 2:
                    k, v = kv
                    self.header_dict[k] = v

class Future(object):
    def __init__(self,timeout=0):
        self.result=None
        self.timeout=timeout
        self.start=time.time()
F=None
def index(requet):
    return "index"

def main(request):
    global F
    F=Future(5)
    return F

routers=[('/index',index),
         ('/main',main),
         ]

def run():
    sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    sock.bind(('127.0.0.1',8002,))
    sock.listen(128)

    inputs=[]
    inputs.append(sock)

    async_request_dict={}

    while True:
        rlist,wlist,elist=select.select(inputs,[],[],0.05)
        for r in rlist:
            if r==sock:
                '''新请求到来'''
                conn,addr=sock.accept()
                conn.setblocking(False)
                inputs.append(conn)
            else:
                '''客户端发来数据'''
                data=b""
                while True:
                    try:
                        chunk=r.recv(1024)
                        data=data+chunk
                    except Exception as e:
                        chunk=None
                    if not chunk:
                        break
                request=HttpRequest(data)
                import re
                flag=False
                func=None
                for route in routers:
                    if re.match(route[0],request.url):
                        flag=True
                        func=route[1]
                        break
                if flag:
                    result=func(request)
                    if isinstance(result,Future):
                        async_request_dict[r]=result
                    else:
                        r.sendall(bytes("HTTP/1.1 201 OK\r\n\r\n", 'utf8'))
                        r.sendall(bytes(result,encoding='utf8'))
                        inputs.remove(r)
                        r.close()
                else:
                    r.sendall(bytes("HTTP/1.1 201 OK\r\n\r\n", 'utf8'))
                    r.sendall(b'404')

                    inputs.remove(r)
                    r.close()

        for conn in list(async_request_dict.keys()):
            future=async_request_dict[conn]
            start=future.start
            timeout=future.timeout
            ctime=time.time()
            if (start+timeout)<=ctime:
                future.result=b'timeout'
            if future.result:
                conn.sendall(bytes("HTTP/1.1 201 OK\r\n\r\n", 'utf8'))
                conn.sendall(future.result)
                conn.close()
                del async_request_dict[conn]
                inputs.remove(conn)


if __name__=="__main__":
    run()

  

 

posted @ 2023-10-19 19:04  子不语332  阅读(36)  评论(0编辑  收藏  举报