WSGI服务器

WSGI

转:https://www.jianshu.com/p/29f66eb4e55a

Web Server Gateway Interface 的缩写,即 Web 服务器网关接口。

Python web开发中,服务端程序分为两个部分

  1. 服务器程序(用来接收、整理客户端发送的请求)

  2. 应用程序(处理服务器程序传递过来的请求)

    其实我们使用的Flask,Django,Tornado等框架其实相当于开发服务端的应用程序,处理后台逻辑

但是,服务器程序和应用程序互相配合才能给用户提供服务,而不同应用程序(不同框架)会有不同的函数、功能。 此时,我们就需要一个标准,让服务器程序和应用程序都支持这个标准,那么,二者就能很好的配合了

WSGI:wsgi是python web开发的标准,类似于协议。它是服务器程序和应用程序的一个约定,规定了各自使用的接口和功能,以便二和互相配合

WSGI 规范了些什么,下图能很直观的说明。
在这里插入图片描述

首先,我们对WSGI有个简单的理解,下面我将通过一个实例来帮助大家理解

首先假设我们有这么一个服务器

wsgi/server.py

# coding=utf-8

import socket

# 建立TCP套接字
listener = socket.socket()
# 设置端口可复用
listener.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1)
# 绑定IP和端口
listener.bind(('0.0.0.0', 8080))
# 设置监听队列,并创建监听套接字
listener.listen(5)
print('Serving HTTP on 0.0.0.0 port 8080 ...')

while True:
    # 等待客户端连接
    client_connection, client_address = listener.accept()
    print(f'Server received connection'
          f' from {client_address}')
    request = client_connection.recv(1024)
    print(f'request we received: {request}')

   	headers_set = None
	
    # 服务器要传递给应用程序的start_response函数,应用程序在返回数据前需要先调用
    def start_response(status, headers):
        """
        paramer status:状态码
        paramer headers:是由 (header_name, header_value) 这样的元祖组成的列表
        [('Content-type', 'text/plain')]
        """
        global headers_set
        headers_set = [status, headers]
	
    # 获取请求类型和请求地址
    method, path, _ = request.split(b' ', 2)
    environ = {'REQUEST_METHOD': method.decode('utf-8'),
               'PATH_INFO': path.decode('utf-8')}
    
    # 调用应用程序
    from wsgi_app import simple_app
    app_result = simple_app(environ, start_response)
	
    # 组织reponse并返回,要符合http协议的规范,响应行 相应头 空行 相应体
    response_status, response_headers = headers_set
    response = f'HTTP/1.1 {response_status}\r\n'
    for header in response_headers:
        response += f'{header[0]}: {header[1]}\r\n'
    response += '\r\n'
    response = response.encode('utf-8')
    for data in app_result:
        response += data

    client_connection.sendall(response)
    client_connection.close()

通过上面的图,我们可以清晰的知道服务器每接收到一个请求,就会通过 application_callable(environ, start_response) 调用应用程序。

而应用程序在处理完请求准备返回数据的时候,会先调用服务传给它的函数 start_response(status, headers, exec_info),最后再返回可迭代对象作为数据

另外我们在创建一个Web应用

wsgi/app.py

def simple_app(environ, start_response):
    """
    paramer environ:
    {
      AUTH_TYPE
      CONTENT_LENGTH        #HTTP请求中Content-Length的部分
      CONTENT_TYPE          #HTTP请求中Content-Tpye的部分
      GATEWAY_INTERFACE
      HTTP_*                    #包含一系列变量, 如HTTP_HOST,HTTP_ACCEPT等
      PATH_INFO             #URL路径除了起始部分后的剩余部分,用于找到相应的应用程序对象,如果请求的路								径就是根路径,这个值为空字符串
      PATH_TRANSLATED
      QUERY_STRING          #URL路径中?后面的部分
      REMOTE_ADDR
      REMOTE_HOST
      REMOTE_IDENT
      REMOTE_USER
      REQUEST_METHOD        #HTTP 请求方法,例如 "GET", "POST"
      SCRIPT_NAME           #URL路径的起始部分对应的应用程序对象,如果应用程序对象对应服务器的根,那么								这个值可以为空字符串
      SERVER_NAME
      SERVER_PORT
      SERVER_PROTOCOL       #客户端请求的协议(HTTP/1.1 HTTP/1.0)
      SERVER_SOFTWARE
    }
    paramer start_reponse:应用程序在返回数据之前会先调用,用于告诉服务设置响应的头部信息或错误处理等	 
    """
    status = '200 OK'
    response_headers = [('Content-type', 'text/plain')]
    start_response(status, response_headers)
    return [f'Request {environ["REQUEST_METHOD"]}'
            f' {environ["PATH_INFO"]} has been'
            f' processed\r\n'.encode('utf-8')]

好了,启动服务器后(即执行服务器代码,和之前的类似,这里不赘述),然后请求看看结果

~ curl 127.0.0.1:8080/user/1
Request GET /user/1 has been processed

嗯,程序是正常的。

上面为了说明,代码耦合性较大,如果服务器需要更换应用的话,还得修改服务器代码,这显然是有问题的。现在原理差不多说清楚了,我们把代码优化下

wsgig/wsgi_server_oop.py

# coding=utf-8

import socket
import sys


class WSGIServer:
    def __init__(self):
        self.listener = socket.socket()
        self.listener.setsockopt(socket.SOL_SOCKET,
                                 socket.SO_REUSEADDR, 1)
        self.listener.bind(('0.0.0.0', 8080))
        self.listener.listen(1)
        print('Serving HTTP on 0.0.0.0'
              ' port 8080 ...')
        self.app = None
        self.headers_set = None

    def set_app(self, application):
        self.app = application

    def start_response(self, status, headers):
        self.headers_set = [status, headers]

    def serve_forever(self):
        while True:
            listener = self.listener
            client_connection, client_address = \
                listener.accept()
            print(f'Server received connection'
                  f' from {client_address}')
            request = client_connection.recv(1024)
            print(f'request we received: {request}')

            method, path, _ = request.split(b' ', 2)
            # 为简洁的说明问题,这里填充的内容有些随意
            # 如果有需要,可以自行完善
            environ = {
                'wsgi.version': (1, 0),
                'wsgi.url_scheme': 'http',
                'wsgi.input': request,
                'wsgi.errors': sys.stderr,
                'wsgi.multithread': False,
                'wsgi.multiprocess': False,
                'wsgi.run_once': False,
                'REQUEST_METHOD': method.decode('utf-8'),
                'PATH_INFO': path.decode('utf-8'),
                'SERVER_NAME': '127.0.0.1',
                'SERVER_PORT': '8080',
            }
            app_result = self.app(environ, self.start_response)

            response_status, response_headers = self.headers_set
            response = f'HTTP/1.1 {response_status}\r\n'
            for header in response_headers:
                response += f'{header[0]}: {header[1]}\r\n'
            response += '\r\n'
            response = response.encode('utf-8')
            for data in app_result:
                response += data

            client_connection.sendall(response)
            client_connection.close()


if __name__ == '__main__':
    if len(sys.argv) < 2:
        sys.exit('Argv Error')
    app_path = sys.argv[1]
    module, app = app_path.split(':')
    # 导入wsgi_app
    module = __import__(module)
    app = getattr(module, app)

    server = WSGIServer()
    server.set_app(app)
    server.serve_forever()

基本原理没变,只是使用了面向对象的方式修改了下原来的代码,同时 environ 添加了一些必要的环境信息。

可以使用以前的应用测试一下

➜  wsgi python wsgi_server_oop.py wsgi_app:simple_app
Serving HTTP on 0.0.0.0 port 8080 ...

然后发送请求

~ curl 127.0.0.1:8080/user/1
Request GET /user/1 has been processed

也可以用flask框架创建一个应用试试

wsgi/flask_app.py

# coding=utf-8

from flask import Flask
from flask import Response

flask_app = Flask(__name__)


@flask_app.route('/user/<int:user_id>',
                 methods=['GET'])
def hello_world(user_id):
    return Response(
        f'Get /user/{user_id} has been'
        f' processed in flask app\r\n',
        mimetype='text/plain'
    )

重新启动服务器

➜  wsgi python wsgi_server_oop.py flask_app:flask_app
Serving HTTP on 0.0.0.0 port 8080 ...

然后发送请求

~ curl 127.0.0.1:8080/user/1
Get /user/1 has been processed in flask app

因为 Flask 也是遵守 WSGI 规范的,所以执行也没有问题。

再来谈谈中间件Middleware

middleware是介于服务器程序和应用程序中间的部分,middleware对服务器程序和应用程序是透明的

对于服务器程序来说,middleware就是应用程序,middleware需要伪装成应用程序,传递给服务器程序
对于应用程序来说,middleware就是服务器程序,middleware需要伪装成服务器程序,接受并调用应用程序

服务器程序获取到了客户端请求的URL,需要把URL交给不同的函数处理,这个功能可以使用middleware实现:

# URL Routing middleware
def urlrouting(url_app_mapping):   
     def midware_app(environ, start_response):       #函数可调用,包含2个参数,返回可迭代的值
          url = environ['PATH_INFO']       
          app = url_app_mapping[url]  #获得对应url的应用程序
          start_response(status, response_headers)
          result = app(environ, start_response)       #调用应用程序
          return result       
     return midware_app

函数midware_app就是middleware:
一方面,midware_app函数设置了应用程序所需要的变量,并调用了应用程序。所以对于应用程序来说,它是一个服务器程序
另一方面,midware_app函数是一个可调用的对象,接收两个参数,同时可调用对象返回了一个可迭代的值。所以对于服务器程序来说,它是一个应用程序

写中间件(middleware)的逻辑:
1. middleware需要伪装成应用程序—> WSGI应用程序的要求 —> 1. 可调用  2. 两个参数  3. 返回可迭代的值
2. middleware需要伪装成服务器程序 —> WSGI服务器程序的要求 —> 调用应用程序
<think>好的,我现在需要解决用户的问题:“如何在Flask中配置WSGI服务器”。首先,我要明确用户的需求。他们可能已经知道Flask内置了一个开发服务器,但该服务器不适合生产环境,所以想了解如何配置生产级的WSGI服务器,比如Gunicorn或uWSGI。 首先,我应该回忆Flask的部署相关知识。记得Flask应用通过WSGI接口与服务器通信,而常见的WSGI服务器有Gunicorn、uWSGI、Waitress等。用户可能需要具体的配置步骤和示例。 接下来,我需要组织回答的结构。通常,步骤式的教程比较清晰。可能需要分几个部分:为什么需要配置WSGI服务器、如何安装服务器、基本配置、高级配置示例、结合Docker部署等。同时,用户提供的参考引用中有提到Gunicorn和Docker,所以应该重点涵盖这些内容。 然后,检查用户提供的参考内容。引用[2]提到基于Gunicorn、Flask和Docker的高并发部署教程,里面提到了安装步骤和基本命令,比如用pip安装Gunicorn,运行命令`gunicorn -w 4 -b 0.0.0.0:5000 app:app`。这些信息需要整合到回答中,确保正确引用。 另外,需要考虑不同WSGI服务器的配置差异。比如Gunicorn相对简单,而uWSGI配置更复杂但功能更多。用户可能需要对比,或者分步骤说明不同服务器的配置方法。但用户的问题比较广泛,可能先以Gunicorn为例,再提及其他选项。 还要注意LaTeX格式的要求,比如行内公式用$...$,独立公式用$$...$$。但在这个问题中可能数学公式较少,但如果有参数说明,比如进程数、线程数,可能需要用变量表示,比如$N$个worker进程。 另外,用户要求生成相关问题,所以需要根据回答内容提出几个相关的问题,比如性能调优、配置文件使用、Nginx整合等。 需要确保回答中的代码示例正确,例如Gunicorn的命令行参数,或者uWSGI的配置文件。同时,引用标记要正确添加,比如在提到高并发部署时引用[^2]。 最后,检查是否符合所有系统级指令:使用正确的中文,LaTeX格式正确,结构清晰,引用标识自然。可能还需要验证提供的步骤是否准确,比如安装命令、运行命令是否正确,避免技术错误。</think>### 如何在Flask中配置WSGI服务器 Flask内置的开发服务器(如`app.run()`)仅适用于开发环境,生产环境中需通过WSGI服务器部署以提高性能和稳定性。以下是配置WSGI服务器的详细步骤: --- #### 一、选择并安装WSGI服务器 常见的WSGI服务器包括**Gunicorn**(简单易用)和**uWSGI**(高性能)。以Gunicorn为例: 1. **安装Gunicorn** 使用pip安装: ```bash pip install gunicorn ``` 2. **验证安装** 运行`gunicorn --version`,输出版本号即表示成功。 --- #### 二、基本配置与启动 假设Flask应用入口文件为`app.py`,应用实例名为`app`。 1. **启动命令** 通过命令行启动Gunicorn: ```bash gunicorn -w 4 -b 0.0.0.0:5000 app:app ``` - `-w 4`: 指定4个worker进程(根据CPU核心数调整,通常为$2N+1$,$N$为CPU数) - `-b 0.0.0.0:5000`: 绑定IP和端口 - `app:app`: 模块名和应用实例名 2. **添加配置文件(可选)** 创建`gunicorn.conf.py`文件,定义更复杂的参数: ```python workers = 4 bind = "0.0.0.0:5000" timeout = 120 # 请求超时时间(秒) ``` 启动时指定配置文件: ```bash gunicorn -c gunicorn.conf.py app:app ``` --- #### 三、结合Nginx反向代理(推荐) 为提高并发能力,可通过Nginx转发请求至Gunicorn: 1. **Nginx配置示例** 在`/etc/nginx/sites-available/flask_app`中添加: ```nginx server { listen 80; server_name your_domain.com; location / { proxy_pass http://127.0.0.1:5000; # 转发到Gunicorn proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } } ``` 2. **重启Nginx** ```bash sudo systemctl restart nginx ``` --- #### 四、使用Docker容器化部署 结合Docker可简化环境依赖管理[^2]: 1. **编写Dockerfile** ```dockerfile FROM python:3.9 WORKDIR /app COPY requirements.txt . RUN pip install -r requirements.txt COPY . . CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:5000", "app:app"] ``` 2. **构建并运行容器** ```bash docker build -t flask-app . docker run -d -p 5000:5000 flask-app ``` --- ### 其他WSGI服务器配置 - **uWSGI**: 安装:`pip install uWSGI` 启动命令: ```bash uwsgi --http 0.0.0.0:5000 --module app:app --processes 4 ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值