WebSocket

对于WebSocket的支持是内建到Sanic的路由系统中的,与URL路由一样都采用修饰器和绑定方法来完成绑定。修饰器.websocket()和绑定方法.add_websocket_route()的用法与URL路由是一样的,但处理函数除接受Request类型参数以外,还需要接受一个WebSocketConnection类型的参数来控制WebSocket通讯。

以下是一个简单的WebSocket通讯的示例,可以实现回声传送。

@app.websocket("/echo")
async def echo_feed(request, ws):
	data = "greeting"
	while True:
		print(f"Sending: {data}")
		await ws.send(data)
		data = await ws.recv()
		print(f"Received: {data}")

这里需要注意的是,WebSocketConnection类型参数不能保证始终存在并且是已连接状态,所以不要将其放置到处理函数以外的变量中以方便服务器推送的实现,此外提升局部变量的作用域范围还会对服务本身造成较大的GC压力。对于服务器推送的实现,可以在处理函数内部定义局域函数,并使用观察者模式来将其注册到推送广播源来实现对服务器推送信息的响应和处理。

以下给出一个简单的示意示例。

# 首先定义一个广播源
class EmitSource:
	handlers = dict()
	
	def register(self, key, handler):
		self.handlers[key] = handler
	
	def unregister(self, key):
		self.handlers.pop(key)
	
	async def publish(self, data):
		for h in range self.handlers.values():
			await h(data)

source = EmitSource()

# 在WebSocket处理函数中可以这样来用
@app.websocket("/message")
async def process_message(request, ws):
	async def server_pushing(data):
		await ws.send(data)
	
	# 注册服务广播处理
	source.register(request.ctx.user, server_pushing)
	
	while True:
		# 这里处理从客户端发来的信息
	
	# 连接即将结束,注销服务广播处理
	source.unregister(request.ctx.user)