5

我需要在发布者和订阅者之间建立双向通信。这是为了方便前端 MVC3 应用程序使用相关过滤器定义订阅,然后将消息放置到主题上。最后,MVC3 控制器在 SubscriptionClient 上调用 BeginReceive(),并等待响应。

问题似乎是这些订阅对象的创建和删除。开销是巨大的,它会减慢应用程序的速度。更不用说要解决的各种限制,例如一个主题不超过 2000 个订阅。

在发布者和订阅者之间建立这种双向通信的最佳实践是什么?我们希望 MVC3 应用程序发布一条消息,然后等待对该确切消息的响应(通过 CorrelationId 属性和 CorrelationFilter)。我们已经缓存了 NamespaceManager 和 MessagingFactory,因为它们在资源方面也非常昂贵,而且还因为我们被告知 Service Bus 使用显式配置模型,我们需要在角色启动期间预先创建大部分这些东西。

因此,这给我们带来了将请求与响应关联起来的挑战,以及创建和删除订阅的巨大开销。有什么更好的做法?我们是否应该保留 SubscriptionClients 的缓存,并每次交换过滤器?其他人都做什么?我需要通过 Web 角色集群获得每秒 5 到 10,000 个 MVC3 请求的请求吞吐量。我们已经在使用 AsyncController 并在 SubscriptionClient 上使用异步 BeginReceive()。在这一点上,似乎是成千上万的订阅的创建和删除正在阻塞系统。

更新1: 根据此处提供的重要建议,我们更新了此解决方案,以在每个 Web 角色实例上保留 SubscriptionClient 对象的缓存。此外,我们已迁移到面向 MessageSession 的方法。

但是,这仍然没有缩放。看来 AcceptMessageSession() 是一个非常昂贵的操作。MessageSession 对象是否也应该被缓存和重用?每个打开的 MessageSession 对象是否都使用到服务总线的连接?如果是这样,这是否计入订阅的并发连接配额?

非常感谢。我认为我们正在到达那里。Web 上的大多数示例代码显示:Create Topic(),然后 CreateSubscription(),然后 CreateSubscriptionClient(),然后在客户端上 BeginReceive(),然后拆除所有对象。我只能说,如果你在现实生活中这样做,你的服务器就会崩溃,你很快就会用尽连接。

我们需要通过这个东西每秒发出数千个请求,很明显这些对象必须被大量缓存和重用。那么,MessageSession 是另一个要缓存的项目吗?我会享受缓存的乐趣,因为我们必须实现一个引用计数机制,一次只能给出一个对 MessageSession 的引用,因为这是针对特定于 http 请求的请求/响应,我们不能有其他的订阅者同时使用 MessageSession 对象。

UPDATE2: 好的,缓存 MessageSession 以供重复使用是不可行的,因为它们的生存时间与订阅上的 LockDuration 一样长。这是一个无赖,因为最大 LockDuration 是 5 分钟。这些似乎是针对短期发布/订阅的,而不是针对长期运行的分布式进程。看起来我们需要回到轮询 Azure 表。

总结/评论 我们尝试在服务总线上构建,因为它具有规模潜力及其持久性和交付语义。但是,似乎有些情况(其中的大量请求/响应)不适合它。发布部分工作得很好,在后端有竞争的消费者很好,但是让前端请求等待一个定义的、单一的消费者响应,根本不能很好地扩展,因为 MessageSessions 需要太长时间才能完成通过 AcceptMessageSession() 或 BeginAcceptMessageSession() 创建,因为它们不适合缓存。

如果有人有另一种观点,我很想听听。

4

3 回答 3

5

这个场景是一个经典的请求/响应,是使用会话的好候选。这些是另一种相关机制。制作一个简单的请求队列和响应队列。每个 Web 角色线程都会为请求创建一个唯一的 sessionid,并将该值放入 brokeredmessage 的“ReplyToSessionID”属性中。此外,该线程使用 sessionid 值在响应队列上调用AcceptMessageSession,因此它会锁定它。代理消息被发送到请求队列,所有工作角色竞争消息。当工作角色收到请求时,它会对其进行处理,创建响应消息并在响应消息上设置 sessionid 属性=replytosessinid of request。然后将其发送到响应队列,并且只会传递到已锁定该会话 ID 的线程。详细样本使用会话在这里。这里还有 2 个额外的示例使用QueuesTopics来实现请求响应关联。

于 2012-09-27T05:19:27.873 回答
1

另一种方法是使用关联 ID 和回调委托的字典对 Web 角色实例执行消息关联。

每个 Web 角色(发布者)实例都有一个订阅,通过单个订阅 ID 过滤,以响应主题。

在发送消息之前,该消息的回调将在字典中注册,并以相关 id 作为键。相关 id 和订阅 id 与消息一起发送,以便可以将响应与相关 id 一起发送回正确的订阅。

每个 Web 角色实例监控单个订阅,并在收到响应时删除字典条目并调用回调。

于 2012-10-04T05:13:28.317 回答
0

对于单个发送者和接收者,您只需要一个主题/订阅。您可以在同一订阅上设置多个关联过滤器,从而使您需要关联的所有响应都显示在同一订阅中。目前,我们在单个订阅上支持多达100,000 个相关过滤器实例,并且可以添加/删除这些实例(如果需要,可以使用事务)以及对主题/订阅的消息发送/接收操作。

此外,如果需要,您可以使用带有操作的规则来为消息添加附加属性,具体取决于匹配的过滤器。

于 2012-09-26T23:16:06.400 回答