美文网首页程序员
python 基于wsgi写一个web服务器

python 基于wsgi写一个web服务器

作者: ilyq | 来源:发表于2018-10-24 18:59 被阅读78次

环境说明

  • 虚拟机 双核 4G
  • ubuntu16.04
  • python3.6.6

什么是WSGI

WSGI是Web服务器网关接口。它是一个规范,描述了Web服务器如何与Web应用程序通信,以及Web应用程序如何链接在一起以处理一个请求。
WSGI是一个详细描述的Python标准 PEP 3333

一个简单的WSGI例子

代码
from wsgiref.simple_server import make_server


def demo_app(environ, start_response):
    # 回调 状态码、响应头
    start_response("200 OK", [('Content-Type', 'text/plain; charset=utf-8')])
    # 返回内容,注意返回内容应该是一个可迭代的bytes, python2.x是str
    # 详情请看PEP-3333
    return [b"Hello World!"]


if __name__ == '__main__':
    with make_server('', 8000, demo_app) as httpd:
        httpd.serve_forever()
说明

这里有两个很重要的参数environ和start_response

  • environ: 字典类型的客户端请求信息
  • start_response: 回调函数,用于返回HTTP响应状态、返回头信息等
访问

http://localhost:8000/
如果看到Hello World! 就正常

封装成类

为了方便扩展我把上面的例子封装成类, 运行效果和上面一致

from wsgiref.simple_server import make_server


class WSGIApplication:
    def __call__(self, environ, start_response):
        start_response("200 OK", [('Content-Type', 'text/plain; charset=utf-8')])
        yield b"Hello World!"


if __name__ == '__main__':
    with make_server('', 8000, WSGIApplication) as httpd:
        httpd.serve_forever()

封装environ

为了方便请求我把environ封装成一个类

class HTTPRequest:
    def __init__(self, environ):
        # 可以通过environ.items()看下所有内容
        # 可以把url参数,from表单都写到这里
        self.method = environ['REQUEST_METHOD']     # 请求方法
        self.PATH_INFO = environ['PATH_INFO']       # 请求url
        self.status_code = 200

添加请求类

这里仿tornado写一个RequestHandler类, 把常用的GET POST PUT DELETE 都写在里面

class RequestHandler:
    def __init__(self, application, request, environ):
        self.application = application      # wsgi对象
        self.request = request              # environ封装后的实例对象
        self.environ = environ              # environ

    def get(self, *args, **Kwargs):
        """get 请求方法"""
        raise HTTPError(405)

添加路由

class WSGIApplication:
    def __init__(self, handlers):
        self.handlers = handlers    # 路由列表
        self.route_handlers = {}    # 路由字典,通过url查找调用类
        self.add_handlers()

    def add_handlers(self):
        for handler in self.handlers:
            self.route_handlers[handler[0]] = handler[1]

客户端对应写法

class IndexHandler(RequestHandler):
    def get(self):
        """重写get方法"""
        return b'Hello world!'


if __name__ == '__main__':
    # 添加路由
    app = WSGIApplication([('/', IndexHandler)])

添加WSGI适配器

class ServerAdapter:
    def __init__(self, host='127.0.0.1', port=9000, **kwargs):
        self.host = host
        self.port = int(port)
        self.options = kwargs

    def __repr__(self):
        return "%s (%s:%d)" % (self.__class__.__name__, self.host, self.port)

    def run(self, handler):
        pass


class WSGIRefServer(ServerAdapter):
    def run(self, handler):
        with make_server(self.host, self.port, handler) as httpd:
            httpd.serve_forever()


class BjoernServer(ServerAdapter):
    def run(self, handler):
        """
        bjoern是一个C语言编写高性能WSGI服务器
        https://github.com/jonashaag/bjoern
        :param handler:
        :return:
        """
        from bjoern import run
        run(handler, self.host, self.port)


def run(server=WSGIRefServer, host='localhost', port=9000, app=None):
    httpd = server(host=host, port=port)
    httpd.run(app)

测试代码

from apiserver import RequestHandler, WSGIApplication, run


class IndexHandler(RequestHandler):
    def get(self):
        print(self.get_argument('name'))
        return {'msg': 'get index'}

    def post(self):
        print(self.get_body_argument())
        return 'post index'

    def put(self):
        print(self.get_json_argument())
        return {'msg': 'put index'}


if __name__ == '__main__':
    app = WSGIApplication([('/', IndexHandler)])
    run(app=app)

测试

相比之下bjoern比自带的wsgiref快60多倍

# wsgiref
~$ wrk -t4 -c1000 -d30s http://127.0.0.1:9000/
Running 30s test @ http://127.0.0.1:9000/
  4 threads and 1000 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    13.79ms  105.37ms   1.80s    97.79%
    Req/Sec   503.30    327.59     1.12k    59.09%
  3440 requests in 30.09s, 540.86KB read
  Socket errors: connect 0, read 3440, write 0, timeout 0
Requests/sec:    114.32
Transfer/sec:     17.97KB

# bjoern
~$ wrk -t4 -c1000 -d30s http://127.0.0.1:9000/
Running 30s test @ http://127.0.0.1:9000/
  4 threads and 1000 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   137.30ms   33.55ms 462.43ms   75.77%
    Req/Sec     1.87k     0.87k    3.86k    75.34%
  213376 requests in 30.05s, 30.41MB read
Requests/sec:   7101.14
Transfer/sec:      1.01MB

完整代码

https://github.com/ilyq/wsgi-server-py

参考

http://wsgi.tutorial.codepoint.net/#
https://wsgi.readthedocs.io/en/latest/
https://www.letiantian.me/2015-09-10-understand-python-wsgi/

相关文章

  • werkzeug: 使用笔记(一)

    WSGI WSGI,Python Web Server Gate Interface, Python 服务器网关接...

  • WSGI--python web服务器接口

    WSGI简介 WSGI:Web Server Gateway Interface是python web服务器网关接...

  • 南大慕课《用Python玩转数据》-01走近Python

    Python的应用 WEB开发,定了WSGI标准应用接口来协调http服务器与基于python的web程序之间的沟...

  • 1. Flask基础知识

    1. Flask的web服务器 1)WSGI Python Web服务器网关接口(Python Web Serve...

  • WSGI规范

    1.WSGI协议 什么是WSGI(1)、WSGI(Web 服务器网关接口)是python中所定义的Web Serv...

  • Apache Python WSGI服务

    一、什么是WSGI? WSGI是指web服务器和python web应用或web框架之间的标准接口。以提高web...

  • wsgi 与 asgi

    什么是wsgi Web服务器网关接口(Python Web Server Gateway Interface,缩写...

  • WSGI

    简介 Web服务器网关接口(WSGI)是用于Python编程语言的Web服务器(Web Server)和Web应用...

  • 用 Python 写一个简单的Web框架

    一、概述 在Python中,WSGI(Web Server Gateway Interface)定义了Web服务器...

  • wsgi application

    WSGI 是一个规范(协议),定义了 Web服务器 如何与 Python应用程序 进行交互。WSGI 协议约定的 ...

网友评论

    本文标题:python 基于wsgi写一个web服务器

    本文链接:https://www.haomeiwen.com/subject/nupxtqtx.html