多种.NET应用程序之间通信的最有效方式

RPM*_*984 5 .net architecture rest azure microservices

目前我有一个设置,我的客户(网络应用程序,iOS应用程序等)通过REST调用与我的后端API .NET Web应用程序(Nancy)对话.没什么特别的.

我现在需要将此API拆分为微服务,其中每个服务都可以单独升级/部署.

我的主API(公共)将只执行身份验证,然后调用我的一个微服务,它将在我的专用网络中.

我可以在主API和其他微服务API之间进行通信的不同方式是什么?每种方法的优缺点?

通信需要是实时的 - 例如,请求来自浏览器/设备,主API执行auth,然后调用微服务API然后返回响应.所以我不能使用像队列或pub/sub这样的东西.它不一定需要使用HTTP,但它需要是实时通信(请求/响应).我还有其他需要与这些微服务交谈的服务(WebJobs,云服务等)(它们也在私有网络中).

想到的唯一方法是简单的基于REST的调用.完全没问题,但延迟是这里的主要问题.

谁能推荐任何其他解决方案来解决这个问题?Azure中有什么适合这个吗?

非常感谢

Mar*_*ett 14

首先 - 澄清你在"实时","同步/异步"和"单向/双向"之间的区别.你排除的东西(队列和pub/sub)当然可以用于双向请求/响应,但它们是异步的.

第二 - 澄清"效率" - 衡量标准的效率如何?带宽?潜伏?开发时间?客户支持?

第三 - 意识到微服务的成本(其中之一)是延迟.如果您在第一次整合时遇到问题,那么您可能需要走很长的路.

我可以在主API和其他微服务API之间进行通信的不同方式是什么?每种方法的优缺点?

脱离我的头顶:

  • 主机在单个节点和IPC上:优点:性能; 缺点:紧密耦合到部署拓扑; 到其他节点的可达性; 客户支持; 失败模式
  • REST/SOAP /等.端点:优点:广泛的客户端和服务器支持; 调试; 网络缓存,缺点:性能; 失败模式
  • 二进制协议:优点:性能; 缺点:版本控制; 客户端和服务器支持; 调试; 失败模式
  • 消息队列:专业人士:异步性; 缺点:复杂性
  • ESB:专业人士:异步性; 客户支持; 缺点:复杂性
  • 平面文件:专业人士:带宽; 简单; 客户支持; 缺点:延迟,一般PITA

你会注意到,当我们将多个应用程序绑在一起时,这是相同的列表...因为这就是你正在做的事情.只是因为你使应用程序更小并没有真正改变太多,只是让你的系统更加分散.期望解决"普通"分布式系统所有相同的问题,然后再解决一些与部署和版本控制相关的问题.

考虑来自用户的幂等GET请求,例如"给我问题1".该客户端期望问题1的JSON响应.简单.在我预期的架构中,客户端会点击api.myapp.com,然后它会通过REST将调用代理发送到question-api.myapp.com(微服务)以获取数据,然后返回给用户.我们怎么能在这里使用pub/sub?谁是发布者,谁是订阅者?这里没有事件要提出来.我对队列的理解:一个出版商,一个消费者.发布/子主题:一个发布者,许多消费者.谁是谁?

好的 - 首先,如果我们谈论微服务和延迟 - 我们将需要一个更具代表性的例子.假设我们的客户端是Netflix移动应用程序,要显示开始屏幕,它需要以下信息:

  1. 趋势电影ID列表
  2. 最近观看的电影ID列表
  3. 帐户状态
  4. 对于引用的每个电影ID:名称,星号,摘要文本
  5. 您所在地区不提供的电影ID列表(要过滤趋势/最近观看过的)

其中每一个都由不同的微服务提供(我们称之为M1-M5).来自客户端 - >数据中心的每次呼叫都有100ms的预期延迟; 服务之间的呼叫有20ms的延迟.

让我们比较一些方法:

1:单片服务

  1. T0 + 100ms:客户端发送/ API // StartScreen的请求; 收到回应

正如预期的那样,这是最低延迟选项 - 但需要整体服务中的所有内容,我们认为由于操作问题我们不希望这样做.

2:微服务

  1. T0 + 100ms:客户端向M1/API /趋势发送请求
  2. T1 + 100ms:客户端向M2/API //最近发送请求
  3. T2 + 100ms:客户端向M3/API //帐户发送请求
  4. T3 + 100ms:客户端向M4/API/Summary发送请求?movieids = []
  5. T4 + 100ms:客户端向M5/API发送请求//不可用

那是500ms.使用代理w/this不会有帮助 - 它只会为每个请求增加20ms的延迟(使其达到600ms).我们有1 + 2和4,以及3和5之间的依赖关系,但可以做一些异步.让我们看看这有多大帮助.

3:微服务 - 异步

  1. T0:客户端向M1/API /趋势发送请求
  2. T0:客户端向M2/API //最近发送请求
  3. T0:客户端向M3/API //帐户发送请求
  4. T0 + 200ms :(响应1 + 2)客户端向M4/API/Summary发送请求?movieids = []
  5. T0 + 200ms :(响应3)客户端向M5/API发送请求//不可用

我们降到200毫秒; 还不错 - 但我们的客户需要了解我们的微服务架构.如果我们用我们的代理抽象它,那么我们有:

4:微服务 - 与网关异步

  1. T0 + 100ms:客户端向G1/API/StartScreen发送请求
  2. T1:G1向M1/API /趋势发送请求
  3. T1:G1向M2/API // Recent发送请求
  4. T1:G1向M3/API //帐户发送请求
  5. T1 + 40ms :(响应1 + 2)G1向M4/API/Summary发送请求?movieids = []
  6. T1 + 40ms :(响应3)G1向M5/API发送请求//不可用

低至140ms,因为我们正在利用减少的服务内延迟.

很棒 - 当事情顺利进行时,与单片(#1)相比,我们的延迟仅增加了40%.

但是,与任何分布式系统一样,我们也不得不担心事情进展顺利.

当M4的延迟增加到200ms时会发生什么?那么,在客户端 - >异步微服务路由(#3)中,我们在100ms(第一批请求)中有部分页面结果,在200ms内不可用,在400ms内有摘要.在代理案例(#4)中,我们没有任何东西,直到340ms.如果微服务完全不可用,则考虑类似因素.

队列是一种在空间和时间中抽象生产者/消费者的方式.让我们看看如果我们介绍一个会发生什么:

5:使用async pub/sub的微服务

  1. T0 + 100ms:客户端将请求Q0发布到P1 StartScreen,回复通道为P2
  2. T1 + 20ms:M1看到Q0,将响应R1置于P2趋势/ Q0
  3. T1 + 20ms:M2看到Q0,将响应R2置于P2最近/ Q0
  4. T1 + 20ms:M3看到Q0,将响应R3置于P2 Account/Q0
  5. T2 + 40ms:M4看到R1,将响应R4a置于P2 Summary/Q0
  6. T2 + 40ms:M4看到R2,将响应R4b置于P2 Summary/Q0
  7. T2 + 40ms:M5看到R3,将响应R5置于P2不可用/ Q0

我们的客户,订阅了P2 - 接收到单个请求的部分结果,并从M1 + M2和M4以及M3和M5之间的工作流程中抽象出来.我们在最佳情况下的延迟是140ms,与#4相同,在最坏的情况下类似于直接客户端路由(#3)w /部分结果.

我们有一个更复杂的内部路由系统,但已经获得了微服务的灵活性,同时最大限度地减少了不可避免的延迟.我们的客户端代码也更复杂 - 因为它必须处理部分结果 - 但类似于异步微服务路由.我们的微服务通常彼此独立 - 它们可以独立扩展,并且没有中央协调权限(如代理案例中).我们可以根据需要添加新服务,只需订阅适当的渠道,让客户知道如何处理我们生成的响应(如果我们为客户端消费生成一个当然).

6微服务 - 带队列的网关

您可以使用网关进行变体聚合响应,同时仍在内部使用队列.外观看起来很像#4,内部看起来很#5.添加一个队列(是的,我一直在使用队列,发布/订阅,主题等可互换)仍然将网关与各个微服务分离,但从客户端抽象出部分结果问题(沿着它/但是好处.

但是,添加网关确实可以集中处理部分结果问题 - 如果它在多个平台上复杂,不断变化和/或重新实现,则非常有用.

例如,假设在M4(摘要服务)不可用的情况下 - 我们有一个M4b对缓存数据进行操作(例如,星级评定已过期).M4b可以立即应答R4a和R4b,然后我们的网关可以确定它是否应该等待M4应答或者仅仅基于超时来使用M4b.

有关如何Netflix的进一步信息实际上解决了这个问题,看看以下资源:

  1. 他们的API网关
  2. 它们的容错层下面的消息传递模式
  3. 更多关于微服务中的IPC


Eri*_* J. 2

Apache Thrift旨在实现高效通信。它由 Facebook 开发(现已开源),并且可用于 C# 的实现。

Google 开发了高效的协议缓冲区序列化机制。协议缓冲区不包括 RPC(远程过程调用),但可用于通过各种传输机制发送数据。它也可用于 C#(由 SO 自己的 Marc Gravell 项目)。