在 Azure Service Fabric (API) 中从一个微服务向另一个微服务发送消息
Posted
技术标签:
【中文标题】在 Azure Service Fabric (API) 中从一个微服务向另一个微服务发送消息【英文标题】:Send a message from one microservice to another in Azure Service Fabric (APIs) 【发布时间】:2017-06-12 07:46:16 【问题描述】:使用 Service Fabric 来保证我需要从服务 1(主要是 API)发送到服务 2(主要是 API)的消息不会丢失(黑色箭头)的最佳架构是什么?
想法:
1
1.a.使服务 1 和 2 有状态服务。拥有一个有状态的 Web API 是不是一个糟糕的调用?
1.b。使用 Reliable Collections 将消息从 API 代码发送到服务 2。
2
2.a.使服务 1 和 2 无状态服务
2.b。添加第三个服务
2.c。从服务 1 通过排队系统(即:服务总线)发送消息
2.d。由第三次服务接机。注意:这第三个服务也可以访问服务 2 (API) 可以访问的数据库。不是微服务架构的理想解决方案,对吧?
3
3.a.还有其他想法吗?
请记住,目标是永远不会丢失消息,即使在服务 2 完全关闭或临时移除时也不会丢失……所以不要直接调用。
谢谢
【问题讨论】:
【参考方案1】:我将介绍第三个(有状态)服务,它包含一个队列,“服务 3”。 服务 1 会将消息排入队列。服务 3 将运行一个无限循环,试图将消息传递给服务 2。
您可以为此使用pub/sub 包。服务 1 是发布者,服务 2 是订阅者。
(如果您依赖服务总线等外部队列系统,则会降低系统的整体可用性。服务总线停机会导致消息无法传递。)
【讨论】:
【参考方案2】: 我认为从来没有完全任何解决方案可以 100% 确定永远在两方之间丢失消息。即使您在两个服务之间有一个服务总线,也总是有机会(可能非常小,但绝不会为空)服务总线出现故障,或者与服务总线的通信出现故障。话虽如此,当然有些模型不太可能很少丢失消息,但您无法完全解决您仍然必须在客户端处理错误的事实。
事实上,Service Fabric 故障处理主要是围绕客户端重试通信而设计的,而不是让服务或中介来做这件事。这有很多原因(我猜),但其中之一是分布式、复制、可靠服务的性质。如果主服务出现故障,副本将承担责任,但它不会知道主服务在它死亡的那一刻正在做什么(除非它复制了它的状态,但它可能在此之前就已经死亡)。在这种情况下,唯一真正知道自己想要做什么的是客户端。客户端知道它在做什么,并且可以对服务中的不同故障场景做出反应。在 Fabric Transport 中,大多数已知的可能“自然”发生的异常,例如服务中断或网络电缆被管理员切断,实际上都会自动重试。这包括重新解析地址,以防万一主服务被替换为辅助服务。
对于引入第三个服务或服务总线的场景实际上也是如此。如果在消息完全到达服务之前网络出现故障怎么办?在这种情况下,只有客户端知道出了什么问题以及它打算发送什么。如果它在到达服务之后但在响应发送之前发生了故障怎么办?在这种情况下,客户端必须假设消息从未到达并尝试重新发送它。这也是为什么建议服务方法是幂等的 - 同一个客户端可以多次调用同一个调用。
即使您要引入辅助部件(如服务总线),仍然存在服务总线出现故障的风险,或者更可能是连接到服务总线的网络出现故障。因此,客户端需要重试,并且当它重试多次时,它所能做的就是将消息放入失败消息的队列中,或者只是将其记录下来,或者将异常抛出回原始调用者(在您的场景中,浏览器)。
好吧,那是我很悲观。但它可能会发生。以上所有的事情,只是有些不太可能发生。但它们可能会发生。 关于您的问题:
1) 使无状态服务有状态的问题是您现在必须在调用者中处理分区。您可以为有状态服务设置 Http 侦听器,但您必须在 Uri 中包含分区和副本信息,这不适用于负载均衡器,因此在这种情况下浏览器在调用 API 时必须选择分区。不是一个理想的解决方案。
2) 是的,您可以这样做,即在为您排队消息之间引入 something else。没有什么可以说服务总线或数据库比具有可靠队列的有状态服务更可靠,这取决于您选择最适合的方式。我会选择有状态服务,这样我就可以轻松地将所有内容保存在我的 SF 应用程序中。但同样,这并不是 100% 保护心怀不满的看门人用剪刀,因为您仍然需要能够处理故障的客户端。
3) 确保您有一种方法来处理错误(重试)并使用客户端(服务 1)记录或存储失败的消息(重试后)。
3.a) 一种方法是让它本地存储在它正在运行的节点上,并定期(例如 RunAsync)尝试重新运行那些失败的消息。在运行它的节点完全被核破坏并丢失数据的情况下,这可能是危险的,但是该数据不会被复制。
3.b) 另一种方法是将语义日志与 ETW 结合使用,并在事件中包含足够的数据,以便能够从日志中重新创建消息并构建一些功能,即手动 UI也许,您可以从记录的信息中重新运行它。就像您在服务总线中的错误队列上重试失败的消息一样。
3.c) 将失败的消息存储到不会失败的任何其他地方(数据库、服务总线、队列),原因与与服务 2 的通信相同。
我在这里的主要观点是(我可能已经开始了)是有很多场景只有客户知道足以处理这种情况。因此,请确保您有处理客户端故障的策略。
【讨论】:
我也喜欢使用第三个有状态服务的想法,以便在 SF 中拥有一切。所以它会是:服务 1(无状态 API)将消息发送给服务 X(有状态),服务 X 负责在消息再次可用时将消息传递给服务 2(无状态 API)。这个与外部排队系统(即:ServiceBus)的一个缺点是,我负责始终保持服务 X 的每个分区保持活动状态。我不能只是将它们全部关闭以重新部署新的东西或出于任何其他我需要的原因把它们全部拿下来或用剪刀看门人。我说的对吗? 是的,没错,您的新服务 X 将是硬依赖。随着 SF 的滚动升级以及它被复制到至少 3 个节点的事实,它应该是稳定的。只要您的客户端可以重试,在升级期间它就会切换主节点,这可能会影响客户端在通话中强制它重新解析地址。 在您的第 1 点中,您说负载均衡器无法与有状态服务一起使用,但文档说:负载均衡器会自动将给定端口上的入站流量转发到具有同一个端口打开。 Azure 负载均衡器只知道节点上打开的端口,它不知道各个服务打开的端口。 docs.microsoft.com/en-us/azure/service-fabric/… 也许措辞不好,是的,它可以将流量重定向到节点,但是有状态服务必须在其地址中包含实例和分区,因为可以同时存在多个副本(实例)和每个节点上相同有状态服务的分区。这意味着为了通过 Http 从外部解决有状态服务,您需要知道您正在处理哪个副本和分区,并且这些的任何给定组合在所有节点上都不会有意义,只是该副本/分区所在的节点,因此,随机选择一个节点是没有意义的 +1,值得包括服务总线/队列可以提供开箱即用的额外功能、重试、死字等(如果需要)必须在 SF 实现中复制。以上是关于在 Azure Service Fabric (API) 中从一个微服务向另一个微服务发送消息的主要内容,如果未能解决你的问题,请参考以下文章
Azure Service Fabric:无法运行本地 Service Fabric 群集
在 Azure 中收集 Service Fabric 群集日志