我有一个托管在Google Cloud Run上的NodeJS服务,它使用Socket IO在服务实例运行时与浏览器客户端进行通信。
然而,我注意到一些奇怪的事情。
奇怪的是,有时当服务器向客户端发出套接字事件时,客户端会立即获得事件,但在其他一些情况下,事件永远不会到达客户端。这是随机发生的,所以很难重现断开连接是从哪里来的。
下面是我的客户端代码:client_socket.js
import io from "socket.io-client";
const socketUrl = EndPoints.SOCKET_IO_BASE;
let socketOptions = { transports: ["websocket"] }
let socket;
if (!socket) {
socket = io(socketUrl, socketOptions);
socket.on('connect', () => {
console.log(`Connected to Server`);
})
socket.on('disconnect', () => {
console.log(`Disconnected from Server`); //This never gets called when the Cloud Run service instance is running, so I can assume a disconnect never happened.
})
}
export default socket;
有趣的是,当Cloud Run服务实例正在运行时,断开连接事件从未被触发回客户端,这意味着客户端仍然连接到服务。所以,在某些情况下,即使已经连接,它也不会从服务器获取事件,这真的很奇怪。
请注意,在Google Cloud Run服务端,我已将服务的超时设置为3600秒,这足以确保服务运行足够长的时间以保持套接字连接。
基于本文档的最佳实践:
在Cloud Run上创建WebSockets服务最困难的部分是在多个Cloud Run容器实例之间同步数据。这很困难,因为容器实例的自动伸缩和无状态特性,以及并发性和请求超时的限制。
一个建议是使用会话关联。如果启用,Cloud Run会将给定客户机的顺序请求路由到相同的容器实例,并使用TTL为30天的会话关联cookie。它还将检查该值以识别来自同一客户端的请求,并将请求定向到同一实例。但是,不能保证它将由同一个实例提供服务。
同时,此功能仍处于预览阶段,在开发过程中可能会发生变化。
建议使用外部数据存储,如数据库(Cloud SQL)或外部消息队列(Redis Pub/Sub/Memorystore/Firestore实时更新),可以通过容器实例发起的连接向所有实例传递更新。