ASGI

2019/10/15

Tags: python ASGI

Django 3.0 要支持 ASGI 了,全称 Asynchronous Server Gateway Interface,使用了 coroutines 异步。WSGI 是目前普遍使用的,把进来的请求标准化处理之后,交给 WSGI 程序,这个支持的是请求来了,处理完毕,返回结果这样的模型。对于像 websockt 这样的就不太友好了,websockt 实际是建立连接之后会持续发送请求和接收结果,所以有了 ASGI。

ASGI 不同的地方在于,他有一个 scope,表示一个连接,这个会一直存在直到用户断开连接。然后通过一个一个的 event 和应用交互,event 是个字典,必定包含一个 type 指明类型。ASGI 还支持了 coroutine。

这样对于 http 请求,一个请求就一个 scope, 所需要的数据基本就全了,除了 body 对应一个 event。对于 websockt 请求,一个 scope 会持续到直到用户断开,期间通过 event 发送详细消息。Application 不能和客户端在 scope 阶段交互,必须等进入 event loop 才可以,而且取决于协议规定,可能还需要等一些初始化的 event 之后才可以。

然后目前实现的 ASGI server 我看有 daphne,简单看了一下源码。

这个一般是命令行执行 daphne 这个命令,入口是在 setup.py 里面指定的 daphne.cli:CommandLineInterface.entrypoin ,这里面实例化了一个 cli 然后执行了 run。run 里面主要是处理命令行参数,找到我们指定的 application 然后 import 。

这里我们的用法是通过 get_channel_layer() 初始化了一个,这里面主要是通过配置文件里面的配置,把 backend 和 routing 初始化了一下。

1
2
3
4
5
6
asgi_layer = backend_class(**config)
return ChannelLayerWrapper(
            channel_layer=asgi_layer,
            alias=name,
            routing=routing,
        )

cli.py 里面最终会组装一个 server.py:Server 对象,然后调用他的 run 方法。在这里面会初始化一下 http_factoryws_factory ,初始化的时候会把 self 传进去,这个操作值得学习下。在这两个 factory 初始化的时候,都会指定使用自己实现的 requestFactory 和 websockt.factory,分别在 http_protocol.py 和 ws_protocol.py 里面。

server.py 的 run 方法后面会设置监听的信息什么的,最后是通过 twisted 的 reactor 来启动的服务。

不管是 http 请求还是 websocket 请求,一开始进来都是 http 请求,所以先看看 http_protocol.py。里面的 process 应该是主要的方法。主要是看有没有 Upgrade 这个 header,如果没有,那就是普通的 http 请求,那会通过下面代码创建一个 application_queue, 最后是通过 application_queue.put_nowait 把响应发回去。

1
2
3
4
5
self.application_queue = yield maybeDeferred(
                    self.server.create_application,
                    self,
                    HEADERS
)

如果是 websocket 请求,那么会初始化一个 websocket 处理实例,然后把用户请求拼接成原始的 http 请求形式,调用 websocket 的处理程序。

1
2
3
4
protocol = self.server.ws_factory.buildProtocol(
                    self.transport.getPeer()
                )
protocol.dataReceived(data)

ws_protocol.py 里面的 WebSocketProtocol 是基于 autobahn.twisted.websocket.WebSocketServerProtocol 的,这个似乎已经包装好了,只需要自己实现 onConnect, onMessage 什么的就可以了。这样在 onConnect 里面初始化了 application ,然后后面 onMessage 的时候会调用。

参考

Comments