从支持协议和支持的工作模式来解析【python】web框架第二部分

先上第一部分:
https://blog.csdn.net/csdniter/article/details/98500422

第一部分是从支持协议出发,接着第二部分

从工作模式出发:

这里指的是单线程下的工作模式,分为同步模式和异步非阻塞模式,

两者的公共点是都能同时接受多个请求并处理,只是处理方式不同

Django只支持同步模式
Tornado两种都支持

先说什么是同步模式:

指的是一个请求到达之后,服务器会开始处理,只有处理完成之后,才能处理下一个请求。也就是说一个一个顺序地处理请求。我们知道,一般的请求都属于IO密集型,不占用CPU,导致服务器CPU闲置,也会造成请求缓慢的结果

那什么是异步非阻塞模式呢?

异步非阻塞模式就是为了解决同步模式性能问题而产生的。一个请求到达之后,如果是IO请求,服务器不阻塞,能够立即处理下一个请求。在上一个请求有结果之后再去处理,如此性能上有很大的提升

同步模式的实现原理

主要是socket非阻塞加上事件循环(select/epoll)
这里不再详细阐述,可参考
https://blog.csdn.net/csdniter/article/details/97709752 中的第二中方法

异步非阻塞模式的实现原理

在说实现原理之前,先看看在tornado中是如何用的和原理

Tornado中的异步非阻塞

以超时型为例做个简单例子,详见注释
首先需要导入Future和gen

from tornado.concurrent import Future 
from tornado import gen 
class MainHandler(tornado.web.RequestHandler):
    '第一次请求'
    @gen.coroutine
    def get(self):
        future = Future()
        tornado.ioloop.IOLoop.current().add_timeout(time.time() + 10, self.doing) #模拟等待10秒,生产环境不会用
        yield future

    def doing(self, *args, **kwargs):
        self.write('Hello, world')
        self.finish()
class IndexHandler(tornado.web.RequestHandler):
    '第二次请求,紧接着第一次请求,不需要等待10s'
    def get(self):
        self.write("Index")

一共有三种应用方法:

  • 超时型,请求来了超过时间,自动处理返回
  • 再请求型:请求来了之后,服务器有向别处发送请求,请求回来之后再处理返回
  • 手动返回型:请求来了之后,一直阻塞,知道手动填充内容,再返回处理

在异步非阻塞模式中最重要的就是Future对象(),httpclient实现异步也是因为里面封装了Future

Future实现异步的原理:

‘’'请求到达,创建一个future对象,当futrue有内容时,则执行回调函数,如设置超时,时间过后会自动
在future中添加一个值,返回执行回调函数,或者是发送http请求,请求回来结果放入feture,返回执行,再或者是手动向future中添加内容

future对象的作用:
1.挂起当前请求,线程可以处理其他请求
2.future设置了值,当前挂起的请求返回,执行回调函数

标识作用,如果没有值,连接不断开不返回,有值代表请求完成,返回,断开请求

总结就是,torado会通过feture有无内容,判断是否执行回调函数
‘’’

在Tornado中是通过非阻塞socket和IO多路复用epoll和Future对象实现的异步非阻塞

知道了以上,可自己实现一个异步非阻塞框架(返回信息没有封装处理):

import socket
import select
import re
import time


class Future():
    def __init__(self, timeout):
        self.result = None  # future对象内容,有无决定是否返回
        self.timeout = timeout  # 超时终止挂起时间
        self.start = time.time()  # 创建future时间


def index(request):  # 同步请求
    return "index"


F = None


def main(request):  # 异步请求
    global F
    F = Future(5)  # 设置超时挂起时间

    return F


def stop(request):  # 终止异步请求挂起
    global F
    F.result = b'xxxxx'
    return "stop"


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


class HttpRequest(object):
    """
    封装处理用户请求信息
    """

    def __init__(self, content):
        self.content = 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

    @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 run():
    """
    事件循环
    :param host:
    :param port:
    :return:
    """
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind(('127.0.0.1', 8080,))
    sock.setblocking(False)
    sock.listen(128)
    inputs = []
    inputs.append(sock)

    async_request_dicy = {}

    while True:
        readable_list, writeable_list, error_list = select.select(inputs, [], [], 0.05)
        for conn in readable_list:
            if sock == conn:
                '''新请求'''
                print("xxxxxx")
                client, address = sock.accept()
                print("pk")
                client.setblocking(False)
                inputs.append(client)
            else:
                '''客户端发来数据'''
                recv_data = b""  # 空字节变量
                while True:
                    try:
                        chunk = conn.recv(8096)
                        recv_data += chunk
                    except Exception as e:
                        break
                request = HttpRequest(recv_data)
                print(request.url)
                print(request.method)
                print(request.header_dict)
                print(request.body_bytes)
                # recv_data进行处理,请求头和请求体
                # 1.请求头中获取url
                # 2.取路由中匹配,获取指定函数

                flag = False
                func = None
                for route in routers:
                    if re.match(route[0], request.url):  # 匹配路由成功
                        flag = True
                        func = route[1]
                        break
                if flag:

                    reslute = func(recv_data)
                    if isinstance(reslute, Future):
                        async_request_dicy[conn] = reslute
                    else:
                        conn.sendall(bytes(reslute, encoding='utf-8'))
                        inputs.remove(conn)
                        conn.close()

                else:
                    print(404)
                    conn.send(b'404')
                # 3.执行函数,获取返回值
                # 4,send返回值 conn.send(b'fsdf')

                inputs.remove(conn)
                conn.close()

        for ck in async_request_dicy:
            future = async_request_dicy[ck]
            start = future.start
            timeout = future.timeout
            ctime = time.time()
            if start + timeout <= ctime:  # 超时
                future.result = b'timeout'
            if future.result:  # 有内容
                ck.sendall(future.result)
                ck.close()
                del async_request_dicy[ck]
                inputs.remove(ck)
            else:
                pass


if __name__ == '__main__':
    run()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值