异步服务器网关接口¶
This chapter contains information about using ASGI with Pyramid. 阅读有关 ASGI specification .
下面的示例应用程序使用来自 asgiref library 将普通的WSGI请求转换为ASGI响应。这允许应用程序与ASGI服务器一起运行,例如 uvicorn 或 daphne .
wsgi->asgi应用程序¶
此示例使用由提供的包装器 asgiref
将wsgi应用程序转换为asgi,允许由asgi服务器运行。
请注意,可能不支持wsgi的所有扩展功能,例如传入post主体的文件句柄。
1# app.py
2
3from asgiref.wsgi import WsgiToAsgi
4from pyramid.config import Configurator
5from pyramid.response import Response
6
7def hello_world(request):
8 return Response("Hello")
9
10# Configure a normal WSGI app then wrap it with WSGI -> ASGI class
11
12with Configurator() as config:
13 config.add_route("hello", "/")
14 config.add_view(hello_world, route_name="hello")
15 wsgi_app = config.make_wsgi_app()
16
17app = WsgiToAsgi(wsgi_app)
扩展的wsgi->asgi websocket应用程序¶
此示例扩展了 asgiref
支持将ASGi使用者与转换后的WSGi应用程序一起路由的包装器。这只是路由ASGi消费者的一个潜在解决方案。
1# app.py
2
3from asgiref.wsgi import WsgiToAsgi
4
5from pyramid.config import Configurator
6from pyramid.response import Response
7
8
9class ExtendedWsgiToAsgi(WsgiToAsgi):
10
11 """Extends the WsgiToAsgi wrapper to include an ASGI consumer protocol router"""
12
13 def __init__(self, *args, **kwargs):
14 super().__init__(*args, **kwargs)
15 self.protocol_router = {"http": {}, "websocket": {}}
16
17 async def __call__(self, scope, *args, **kwargs):
18 protocol = scope["type"]
19 path = scope["path"]
20 try:
21 consumer = self.protocol_router[protocol][path]
22 except KeyError:
23 consumer = None
24 if consumer is not None:
25 await consumer(scope, *args, **kwargs)
26 await super().__call__(scope, *args, **kwargs)
27
28 if consumer is not None:
29 await consumer(scope, *args, **kwargs)
30 try:
31 await super().__call__(scope, *args, **kwargs)
32 except ValueError as e:
33 # The developer may wish to improve handling of this exception.
34 # See https://github.com/Pylons/pyramid_cookbook/issues/225 and
35 # https://asgi.readthedocs.io/en/latest/specs/www.html#websocket
36 pass
37 except Exception as e:
38 raise e
39
40
41 def route(self, rule, *args, **kwargs):
42 try:
43 protocol = kwargs["protocol"]
44 except KeyError:
45 raise Exception("You must define a protocol type for an ASGI handler")
46
47 def _route(func):
48 self.protocol_router[protocol][rule] = func
49
50 return _route
51
52
53HTML_BODY = """<!DOCTYPE html>
54<html>
55 <head>
56 <title>ASGI WebSocket</title>
57 </head>
58 <body>
59 <h1>ASGI WebSocket Demo</h1>
60 <form action="" onsubmit="sendMessage(event)">
61 <input type="text" id="messageText" autocomplete="off"/>
62 <button>Send</button>
63 </form>
64 <ul id='messages'>
65 </ul>
66 <script>
67 var ws = new WebSocket("ws://127.0.0.1:8000/ws");
68 ws.onmessage = function(event) {
69 var messages = document.getElementById('messages')
70 var message = document.createElement('li')
71 var content = document.createTextNode(event.data)
72 message.appendChild(content)
73 messages.appendChild(message)
74 };
75 function sendMessage(event) {
76 var input = document.getElementById("messageText")
77 ws.send(input.value)
78 input.value = ''
79 event.preventDefault()
80 }
81 </script>
82 </body>
83</html>
84"""
85
86# Define normal WSGI views
87def hello_world(request):
88 return Response(HTML_BODY)
89
90# Configure a normal WSGI app then wrap it with WSGI -> ASGI class
91with Configurator() as config:
92 config.add_route("hello", "/")
93 config.add_view(hello_world, route_name="hello")
94 wsgi_app = config.make_wsgi_app()
95
96app = ExtendedWsgiToAsgi(wsgi_app)
97
98# Define ASGI consumers
99@app.route("/ws", protocol="websocket")
100async def hello_websocket(scope, receive, send):
101 while True:
102 message = await receive()
103 if message["type"] == "websocket.connect":
104 await send({"type": "websocket.accept"})
105 elif message["type"] == "websocket.receive":
106 text = message.get("text")
107 if text:
108 await send({"type": "websocket.send", "text": text})
109 else:
110 await send({"type": "websocket.send", "bytes": message.get("bytes")})
111 elif message["type"] == "websocket.disconnect":
112 break
运行与部署¶
可以使用asgi服务器运行应用程序:
$ uvicorn app:app
或
$ daphne app:app
有几个潜在的部署选项,一个例子是使用 nginx 和 supervisor . 下面是运行应用程序的示例配置文件 uvicorn
然而 daphne
也可以使用。
nginx配置示例¶
1upstream app {
2 server unix:/tmp/uvicorn.sock;
3}
4
5server {
6
7 listen 80;
8 server_name <server-name>;
9
10 location / {
11 proxy_pass http://app;
12 proxy_set_header Host $host;
13 proxy_set_header X-Real-IP $remote_addr;
14 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
15 proxy_set_header X-Forwarded-Proto $scheme;
16 proxy_buffering off;
17 proxy_http_version 1.1;
18 proxy_set_header Upgrade $http_upgrade;
19 proxy_set_header Connection "Upgrade";
20 proxy_redirect off;
21 }
22
23 location /static {
24 root </path-to-static>;
25 }
26}
Example Supervisor configuration¶
1[program:asgiapp]
2directory=/path/to/app/
3command=</path-to-virtualenv>/bin/uvicorn app:app --uds /tmp/uvicorn.sock --workers 2 --access-log --log-level error
4user=<app-user>
5autostart=true
6autorestart=true
7redirect_stderr=True
8
9[supervisord]