【发布时间】:2016-12-20 02:33:18
【问题描述】:
假设我在 1..n 个 VertX (V) 实例前面有一个负载均衡器 (LB),每个 VertX 实例都连接到一个队列 (Q),并且我有 1..m 个后端 (BE)。
用户单击一个按钮,该按钮发出一个发布请求,甚至打开一个 Web 套接字,负载均衡器将请求转发到其中一个 VertX 实例,该实例向队列触发一个请求,其中一个后端使用该消息并发回响应;如果正确的 VertX 实例使用它,它可以查找响应处理程序并向用户写入响应,如果错误的 VertX 实例使用它,则不会有响应处理程序来写入响应,用户将无限期等待回复。
看这个草图:
或者,V2 死机并且负载平衡器将用户重新连接到 V1,这意味着即使我可以将其发送回发出请求的完全相同的用户,但不能保证在响应返回后仍然存在,但是用户可能仍在等待来自另一个 VertX 实例的响应。
我目前正在做的是为每个新连接生成一个 GUID,然后一旦 websocket 连接,将 websocket 处理程序存储在针对 GUID 的 hashmap 中,然后当 BE 想要响应时,它会进行扇出对于所有 1..n VertX 实例,当前在其哈希图中具有正确 GUID 的实例可以向用户写入响应。 以这种方式处理 POST / GET 也是如此。
伪代码:
queue.handler { q ->
q.handler {
val handler = someMap.get(q.guid)
// only respond if handler exists
if (handler != null){
handler.writeResponse(someresponsemessagehere)
}
}
}
vertx.createHttpServer().websocketHandler { ws ->
val guid = generateGUID()
someMap.put(guid, ws)
ws.writeFinalTextFrame("guid=${guid}")
ws.handler {
val guid = extractGuid(it)
// send request to BE including generated GUID
sendMessageToBE(guid, "blahblah")
}
}.requestHandler { router.accept(it) }.listen(port)
但这确实意味着,如果我有 1000 个 VertX 应用程序正在运行,那么后端需要将其消息扇出到 1000 个前端实例,其中只有一个会使用该消息。
VertX 似乎已经很好地处理了异步操作,VertX 中有没有一种方法可以识别每个 websocket 连接,而不必维护映射到 websocket 处理程序/post 处理程序的 GUID 映射?
另外,参考图片,有没有办法让 V3 消费消息,但仍然能够向当前连接到 V2 的 websocket 处理程序写回响应?
【问题讨论】:
-
我对 VertX 不熟悉,所以我可能会遗漏一些原本显而易见的东西,但为什么不使用 1..n 个队列(每个 VertX (V) 实例一个)或使用一个队列支持选择器以便客户端只能为它们消费消息(例如activemq.apache.org/how-do-i-consume-a-specific-message.html)?
-
我们使用的是 RabbitMQ,我不认为 RabbitMQ 可以做到这一点 - 换句话说,专门从单个队列中消费特定消息。为了路由到 1000 个队列,您需要设置一个主题交换,如果我没记错的话,一个主题交换限制为 255 个主题。实际上,我在 2 年前就在 RabbitMQ 中寻找类似的功能:stackoverflow.com/questions/25489301/… 如果我们使用 ActiveMQ,也许这会是一个解决方案。
-
@JanVladimirMostert 作为一个附带问题,为什么您的“公式”中需要队列? Vert.x 默认情况下是异步的,并且假设您的所有操作都是非阻塞和异步的,我认为您实际上并不需要队列……而且“后端”也可以是(任何类型的)verticles。
-
@ɐuıɥɔɐɯ 这是一个多语言系统,Java、Kotlin、C#、VB、perl、Python、PHP 甚至一些 bash 脚本,并且有近 200 个应用程序坐在队列中,已经很高兴地互相聊天 -我正在将它们从 RPC 样式(阻塞)转换为异步。对于大多数应用程序,异步很容易,对于前端应用程序或集成,这很棘手,因为有一个物理连接等待响应写入它,所以它需要由保持连接打开的应用程序处理.
标签: java architecture kotlin vert.x