WSGI和ASGI

 

WSGI和ASGI,都是基于Python设计的网关接口(Gateway Interface,GI)

WSGI是基于http协议模式开发的,不支持websocket,而ASGI的诞生解决了python中的WSGI不仅支持当前的web开发中的一些新的协议标准,同时ASGI支持原有模式和Websocket的扩展,即ASGI是WSGI的扩展

网关接口

网关接口就是一种为了实现加载动态脚本而运行在WEB服务器和WEB应用程序中的通信接口,也可以理解为一种协议规范。只有web应用程序都实现了网关接口规范以后,双方的通信才能顺利完成。常见的网关接口协议

CGI,FASTAPI,WSGI,ASGI.

 

 

 CGI

公共网关接口(Common Gateway Interface,CGI)是最早的Web服务器主机提供的信息服务的标准接口规范。只要实现了CGI协议,web服务器就能获取并了解客户端提交的信息,转交给服务器端的web应用程序进行处理,最后返回结果给客户端

CGI程序是一种实现CGI协议的程序

快速通用网关接口(Fast Common Gateway Interface/ FastCGI)是一种让web应用程序与web服务器通信的协议。FastCGI是公共网关接口(CGI)的增强版

Web服务器

Web服务器(Web Server)是一种运行于网站后台(物理服务器)的软件。Web服务器主要是用于提供网页浏览或文件下载服务,他可以向浏览器等Web客户端提供HTML网页文档,也可以提供其他类型的展示文档,让客户端用户浏览;还可以提供数据文件下载等。目前世界上最主流的Web服务器有Nginx,Apache ,IIS ,tomcat。

Web应用程序

web应用程序(Web)是一种能完成web业务逻辑,能让用户基于web浏览器访问的应用程序,他可以是一个实现HTTP请求和响应功能的函数或者类,也可以是Django,Flash,sanic等这样的web框架,当然也可以是其他语言的web程序或web框架

问:web服务器和web应用程序的区别?
答:web应用程序主要是完成web应用的业务逻辑的处理,web服务器则朱亚市应对外部请求的接收,响应和转发
需要使用web服务器启动运行,web应用程序才能被用户访问到。
而django框架中,我们之所以只有一个web应用程序就跑起来了,是因为我们在终端执行了,python manage.py runserver
这个命令启动了django框架中内置提供测试的web服务器

WSGI

 

 

 web服务器网关接口,是Python为了解决web服务器与客户端之间的通信基于CGI标准而设计的。实现WSGI协议的服务器有uWSGI、uvicorn、gunicorn。想django框架一般在开发中就不会使用runserver来运行,而是采用上面实现的WSGI协议的web服务器来运行

django中运行润server命令事,其实内部就启动了wsgiref模块作为web服务器运行的。wsgiref是python内置的一个简单的遵循WSGI接口规范的web服务器程序。

from wsgiref.simple_server import make_server
# application 由wsgi服务器调用、函数对http请求与响应的封装、使得Python专注与HTML
# environ http 请求 (dist)
# start_response 响应 (function)
def application(environ, start_response):
    # 请求
    if environ['REQUEST_METHOD'] == 'GET' and environ['PATH_INFO'] == '/':
        # 响应
        start_response('200 OK', [('Content-Type', 'text/html')])
        return [b'<h1>hi, python!</h1>']

if __name__ == '__main__':
    # 启动服务器 | 这个服务器负责与 wsgi 接口的 application 函数对接数据
    httpd = make_server('127.0.0.1', 8000, application)

    # 监听请求
    httpd.serve_forever()
    
    
# 1. 监听8000端口,
# 2. 把http请求根据WSGI协议将其转换到applcation中的environ参数, 然后调用application函数.
# 3. wsgiref会把application函数提供的响应头设置转换为http协议的响应头,
# 4. 把application的返回(return)作为响应体, 根据http协议,生成响应, 返回给浏览器.

开发中我们一般使用uWSGI或者Gunicorn作为web服务器运行django

uWSGI是一个快速的,自我驱动的,对开发者和系统管理员非常友好的容器服务器,完全由C语言编写,实现WSGI协议,uWSGI,http等协议。注意uwsgi协议是一个uWSGi服务器自有的协议,用于定义传输信息的类型,常用语uWSGI服务器与其他网络服务器的数据通信中

可参考django官方文档:https://docs.djangoproject.com/zh-hans/3.2/howto/deployment/wsgi/uwsgi/

conda虚拟环境下安装uWSGI

conda config --add channels conda-forge
conda install uWSGI

项目根目录下创建uwsgi配置文件,uwsgi.ini,代码样本:

 1 [uwsgi]
 2 #使用nginx连接时使用,Django程序所在服务器地址
 3 socket=0.0.0.0:8000
 4 #直接做web服务器使用,Django程序所在服务器地址
 5 # http=0.0.0.0:8000
 6 #项目目录
 7 chdir=/home/项目根目录
 8 #项目中wsgi.py文件的目录,相对于项目目录
 9 wsgi-file=主应用目录/wsgi.py
10 # 进程数 CPU * 2 -1
11 processes=4
12 # 线程数
13 threads=2
14 # uwsgi服务器的角色
15 master=True
16 # 存放进程编号的文件
17 pidfile=uwsgi.pid
18 # 日志文件,因为uwsgi可以脱离终端在后台运行,日志看不见。我们以前的runserver是依赖终端的
19 daemonize=uwsgi.log
20 # 指定依赖的虚拟环境
21 virtualenv=/root/.virtualenvs/环境名称

 

具体配置实例过程

[uwsgi]
#使用nginx连接时使用,Django程序所在服务器地址
# socket=0.0.0.0:8088
#直接做web服务器使用,Django程序所在服务器地址
http=0.0.0.0:8088
#项目根目录[绝对路径]
chdir=/home/moluo/Desktop/demo
#项目中wsgi.py文件的目录,相对于项目目录
wsgi-file=demo/wsgi.py
# 进程数 CPU * 2 -1
processes=4
# 线程数
threads=2
# uwsgi服务器的角色
master=True
# 存放进程编号的文件
pidfile=uwsgi.pid
# 日志文件,因为uwsgi可以脱离终端在后台运行,日志看不见。我们以前的runserver是依赖终端的
daemonize=uwsgi.log
# 指定依赖的虚拟环境
virtualenv=/home/moluo/anaconda3/envs/djdemo

 项目根目录下,启动uwsgi服务器

# 启动项目
uwsgi --ini uwsgi.ini

# 停止运行
uwsgi --stop uwsgi.pid

# 查看当前系统中的指定名称的进程
ps aux | grep uwsgi

# 输出效果如下,则表示成功运行,接下来就可以根据配置中设置的地址访问项目了。
# (base) moluo@ubuntu:~$ ps aux | grep uwsgi
# moluo      12759  2.3  0.9  96944 37736 ?        S    11:52   0:00 uwsgi --ini uwsgi.ini
# moluo      13167  0.0  0.7 170676 29468 ?        Sl   11:52   0:00 uwsgi --ini uwsgi.ini
# moluo      13169  0.0  0.7 170676 29468 ?        Sl   11:52   0:00 uwsgi --ini uwsgi.ini
# moluo      13171  0.0  0.7 170676 29468 ?        Sl   11:52   0:00 uwsgi --ini uwsgi.ini
# moluo      13173  0.0  0.7 170676 29468 ?        Sl   11:52   0:00 uwsgi --ini uwsgi.ini
# moluo      13176  0.0  0.8 105140 35416 ?        S    11:52   0:00 uwsgi --ini uwsgi.ini
# moluo      18073  0.0  0.0  17688   740 pts/2    R+   11:52   0:00 grep --color=auto uwsgi

uwsgi具体访问过程如下图:

ASGI

ASGI,是构建于WSGI接口规范之上的异步服务器网关接口,是WSGI的延伸和扩展。

 A指的是Async,异步的意思。

 下面表格列举了不同协议对在不同框架及版本中的应用

协议,规范支持的请求协议(常见,未列全)同步/异步支持的框架
CGI HTTP   CGI程序
WSGI HTTP 同步 django3.0以前,Flask1.0
ASGI HTTP,HTTP2,WebSocket等 同步/异步 FastAPI,Quart,Sanic,Tornado,django3.0以后,flask2.0

 

在 Python3.5 之后增加 async/await 特性之后简化了协程操作以后,异步编程变得异常火爆,越来越多开发者投入异步的怀抱。

3.0版本以前,django所提供的所有内部功能都是基于同步编程的。所以,在以往django开发中,针对网络请求,数据库读取等IO操作形成的阻塞,往往会导致项目运行性能的下降。虽然等待I/O操作数微秒时,但是随着流量的增加和操作的频率上升,这一点点的阻塞就会导致整个项目运作的缓慢。而如果换成异步就不会有任何阻塞,还可以同时处理其他任务,从而以较低的延迟处理更多的请求。所以在目前python开发中,越来越多的框架开始支持了异步编程。所以,3.0版本以后,django开始支持异步编程,可以让开发者在django中使用python第三方异步模块,推出了asgi异步web服务器。3.1版本推出了异步视图,当然,目前django的异步编程还不够完善,django中只有极少的功能是支持了异步操作。

Uvicorn(独角兽)

Uvicorn是一个快速的ASGI服务器,Uvicorn是基于uvloop(可以理解为死循环)和HTTPtools构建的,实python一部开发生态中的重要一员

Uvicorn官方文档:https://www.uvicorn.org/

uvicorn的安装

pip install uvicorn

项目根目录下,运行django项目

# uvicorn 主应用目录名.asgi:application --reload
uvicorn homework.asgi:application --reload

开发中一般使用gunicorn来管理uvicorn。所以可以一并安装

pip install gunicorn

运行

# gunicorn -w 4 主应用目录名.asgi:application -k uvicorn.workers.UvicornWorker --reload
gunicorn -w 4 homework.asgi:application -k uvicorn.workers.UvicornWorker --reload

异步视图

文档:https://docs.djangoproject.com/zh-hans/3.2/topics/async/

函数视图

在Django3.1后的版本中,我们可以通过async def语法,将任何函数视图定义为异步视图。

"""同步视图"""
import time
def home1(request):
    time.sleep(5)
    return HttpResponse('Hello, sync view!')

"""异步视图"""
import asyncio
async def home2(request):
    # asyncio.sleep(5)
    await asyncio.sleep(5)
    return HttpResponse('Hello, async view!')

类视图

 类视图,django内部是将它的__call__()方法定义为async def,成为异步视图。

class Home3View(View):
    async def __call__(self, *args, **kwargs):
        return super().__call__(*args, **kwargs)

    def get(self, request):
        return HttpResponse("ok, get")

模型操作

django目前最新版本3.2中的ORM对数据库的访问这块还没有实现异步处理,还是同步的。当我们需要在项目中调用模型等同步操作时,django有提供了2个适配器函数,可从asgiref.sync包中获取:async_to_sync()sync_to_async() 。它们用于同步和异步之间调用风格的转换,同时保持兼容性。适配器函数既可以当包装器函数使用,也可以作为装饰器使用。

async_to_sync():异步转同步,参数就是同步函数

sync_to_async():同步转异步,参数就是异步函数

from asgiref.sync import async_to_sync
# 用法1
sync_function = async_to_sync(async_function)

# 用法2
@async_to_sync
async def async_function(...):
    pass

实例代码:

from asgiref.sync import sync_to_async,async_to_sync, SyncToAsync
from django.http.response import HttpResponse
from .models import Student
from django.views import View

# 直接在异步视图中进行数据库操作的同步转异步
async def home4(request):
    ret = await sync_to_async(Student.objects.get, thread_sensitive=True)(pk=10)
    print(ret.name)
    return HttpResponse('Hello, async world!')


# 先声明数据库同步转异步
@sync_to_async(thread_sensitive=True)
def get_student_by_id(id):
    return Student.objects.get(pk=id)

# 异步视图,直接调用异步操作
async def home5(request):
    ret = await get_student_by_id(id=10)
    print(ret.name)
    return HttpResponse('Hello, async world!')

class Home(View):
    async def __call__(self, *args, **kwargs):
        return super().__call__(*args, **kwargs)

    def get(self,request):
        print("hello")

        # 添加一条数据
        ret = sync_to_async(Student.objects.create, thread_sensitive=False)(name="刘德华",age=19,sex=True,class_number="305")
        print(ret)

        # 获取一条数据
        # ret = sync_to_async(Student.objects.get, thread_sensitive=True)(pk=10)
        # print(ret)

        return HttpResponse('Hello, async base view!')

 

redis异步库:aioredis

mysql异步库:aiomysql

mongoDB异步库:motor

http网络请求异步库:httpx

异步HTTP请求

import asyncio
from time import sleep
 
import httpx
from django.http import HttpResponse
 

# 异步任务
import httpx
async def async_task():
    for num in range(1, 6):
        await asyncio.sleep(1)
        print(num)

    async with httpx.AsyncClient() as client: # 打开异步http请求客户端
        r = await client.get("https://httpbin.org/")
        print(r)

# 异步视图 - 调用异步任务
# aio, bio, nio
async def async_view(request):
    loop = asyncio.get_event_loop()
    loop.create_task(async_task())
    return HttpResponse("Async Non-Blocking HTTP request")

异步中间件(尚不成熟不推荐使用)

3.1版本以后,django还提供了中间件的异步支持。

import asyncio
from django.utils.decorators import sync_and_async_middleware

@sync_and_async_middleware
def simple_middleware(get_response):
    # One-time configuration and initialization goes here.
    if asyncio.iscoroutinefunction(get_response): # 判断是否是协程异步
        async def middleware(request):
            # Do something here!
            response = await get_response(request)
            return response

    else:
        def middleware(request):
            # Do something here!
            response = get_response(request)
            return response

    return middleware

 

ps:与戴王冠,必受其重。

posted @ 2021-06-09 17:52  Fleeting__Time  阅读(637)  评论(0编辑  收藏  举报