• 使用wsgiref库diy简单web架构


    1. 了解CGI和WSGI

    (1)CGI

      CGI(Common Gateway Interface)通用网关接口,即接口协议,前端向服务器发送一个URL(携带请求类型、参数、cookie等信息)请求,服务器把这个请求的各种参数写进进程的环境变量,比如
    REQUEST_METHOD,PATH_INFO之类的,然后开启 cgi模块以后,将其发送给CGI程序,CGI程序(可以由各种语言编写,比如C、C ++、VB 和Delphi 等)从环境变量中解析出各种参数,然后向标准输出输出内容(比如cout了一段HTML代码),这些内容没有被打印到控制台上,而是最终响应给了你的浏览器,渲染出了网页。每一次向CGI发送请求,都会生成一个CGI进程,这就是所谓的fork-and-exec模式,这也通常是导致并发瓶颈的症结,反向代理加上大型的的分布式系统可以一定程度上减轻这些压力。

    (2)WSGI

      WSGI(Python Web Server Gateway Interface,缩写为WSGI)web服务器网关接口,也是接口协议,前端向服务器发送一个URL(携带请求类型、参数、cookie等信息)请求,服务器把这个请求的各种参数传给WSGI模块,wsgi将各种参数进行python化,封装为request对象传递给按照WSGI接口标准调用注册的WSGI Application,并返回response参数给客户端。

    2. wsgiref库简介

      wsgiref是python内置库,实现了一个简单的WSGI Server和WSGI Application,使用该库我们将很容易实现自定义的web架构而不用考虑TCP/HTTP层协议,库源码位于/django/lib/wsgiref文件夹,该库提供了5个模块:

    * util -- Miscellaneous useful functions and wrappers
    
    * headers -- Manage response headers
    
    * handlers -- base classes for server/gateway implementations
    
    * simple_server -- a simple BaseHTTPServer that supports WSGI
    
    * validate -- validation wrapper that sits between an app and a server
                      to detect errors in either
                                                

     下面主要对simple_server模块的重点函数和使用方法进行说明

    3. wsgiref.simple_server类使用及部分源码分析

    (1)先上一段代码

      该代码来自simple_server.py的最后7行

    if __name__ == '__main__':
        with make_server('', 8000, demo_app) as httpd:
            sa = httpd.socket.getsockname()
            print("Serving HTTP on", sa[0], "port", sa[1], "...")
            import webbrowser
            webbrowser.open('http://localhost:8000/xyz?abc')
            httpd.handle_request()  # serve one request, then exit

      这段代码表达两个意思:启动服务---->处理'http://localhost:8000/xyz?abc'请求

    (2)分析make_server启动服务过程

      

      server_class传入两个参数,第一个元组(host,ip),第二个WSGIRequestHandler类,第一个参数用于socketserver.TCPServer启动服务(图中标记1),第二个参数WSGIRequestHandler用于BaseServer类初始化self.RequestHandlerClass属性(图中标记2),用于finish_request()函数进行对象初始化(图中的标记3),主要目的实现后面回调函数调用

    (3)分析handle_request()函数处理过程

      该函数的实现过程在基类BaseServer中,该函数主要实现以下功能(这里不讨论epoll异步并发,相关epoll的内容可以看我之前的博客):

      get_request()---->verify_request()---->process_request()---->shutdown_request()

      单看函数名就应该明白整个流程了

    (4)现在到重点了,看demo_app回调是如何实现的

      接下来主要分析下make_server()函数的第三个参数demo_app是如何实现调用的

      通过源码我们看到,finish_request()函数中实现了RequestHandlerClass初始化,也就是WSGIRequestHandler初始化

    def finish_request(self, request, client_address):
            """Finish one request by instantiating RequestHandlerClass."""
            self.RequestHandlerClass(request, client_address, self)

      来张图一看什么都清楚了

      

      a. 服务启动后,WSGIServer通过set_app将回调函数保存起来

      b. finish_request()对WSGIRequestHandler进行实例化,调用其基类BaseRequestHandle的构造函数,构造函数中又调用了handle处理函数,由于派生类WSGIRequestHandler重写了handle方法,实则调用的是WSGIRequestHandler类的handle函数,如上图标记3,得到回调函数对象,并执行。

    def run(self, application):
            try:
                self.setup_environ()
                self.result = application(self.environ, self.start_response)
                self.finish_response()
                。。。。。。。。。。。。。。

      到现在为止一切就都清楚了!接下来就可以自由的对回调函数进行处理了!

    4. DIY web架构

      模仿django架构的model---->route---->view--->template模型实现http请求,显示网页

    (1)models.py

      使用redis的创建两个字符串结构name和url

    import redis
    
    class Model:
        def __init__(self):
            conn = redis.Redis()
            self.name = conn.get('name')
            self.url = conn.get('url')

    (2)urls.py

    import views
    
    urlpattern = ((r'/fate0729/', views.login),)

    (3)views.py

      实现通过model对模板进行渲染

    from models import Model
    import re
    
    def render(html_path, **wargvs):
        with open(html_path, 'r') as pf:
            data = ''.join(pf.readlines())
            print(data)
    
        if wargvs is not None:
            # 为了测试这里使用了固定格式obj.attr
            results = re.findall(r'{{(.*)}}', data)
            for result in results:
                obj_attr_list = result.partition('.')
                obj = wargvs.get(obj_attr_list[0])
                attr = obj_attr_list[-1]
                regex = r'{{.*.%s}}' %(attr)
                value = getattr(obj,attr).decode('utf-8')
                print(attr,value)
                data = re.sub(regex,str(value), data)
        return data.encode('utf-8')
    
    def login(request):
        model = Model()
        return render('web.html', **{'model':model})

    (4)main.py

    from wsgiref.simple_server import make_server
    import urls
    
    def routers():
        urlpattern=urls.urlpattern
        return urlpattern
    
    def applications(environ,start_response):
        path=environ.get("PATH_INFO")
        start_response('200 OK', [('Content-Type', 'text/html'),('Charset', 'utf8')])
    
        urlpattern=routers()
        func=None
        for item in urlpattern:
            if path==item[0]:
                func=item[1]
                break
        if func:
            return [func(environ)]
        else:
            return [b"<h1>404!<h1>"]
    
    if __name__ == '__main__':
        conn = make_server('127.0.0.1', 8001, applications)
        print('server is working...')
        conn.serve_forever()

    (5)测试

      python main.py

      打开浏览器输入:127.0.0.1/fate0729/

      

  • 相关阅读:
    Leetcode86.分隔链表
    Leetcode39.组合总和
    Leetcode31.下一个排列
    剑指Offer35.复杂链表复制
    剑指Offer14-I.剪绳子
    剑指Offer38.字符串的排序
    Leetcode29.两数相除
    232. Implement Queue using Stacks
    程序员跳槽指南
    226. Invert Binary Tree
  • 原文地址:https://www.cnblogs.com/xiaobingqianrui/p/10082391.html
Copyright © 2020-2023  润新知