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

时间:2016-02-24 21:48:15

标签: .net rest azure architecture microservices

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

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

我的主要API(公共)只会执行身份验证,然后调用我的一个微服务,它将位于我的私人网络中。

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

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

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

任何人都可以为此问题推荐任何其他解决方案吗? Azure中有什么适合这个吗?

非常感谢

4 个答案:

答案 0 :(得分:13)

首先 - 阐明你在"实时","同步/异步"之间的区别。 "单向/双向"。你排除的东西(队列和发布/订阅)当然可以用于双向请求/响应,但它们是异步的。

第二 - 澄清"效率" - 效率指标是多少?带宽?潜伏?开发时间?客户支持?

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

  

我可以在主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列表(要从趋势/最近观看过滤过来)
  6. 其中每一个都是由不同的微服务提供的(我们称之为M1-M5)。来自客户的每次通话 - >数据中心有100毫秒的预期延迟;服务之间的呼叫延迟为20ms。

    让我们比较一些方法:

    1:单片服务

    1. T0 + 100ms:客户端发送/ API // StartScreen的请求;收到回复
    2. 正如预期的那样,这是最低延迟选项 - 但需要整体服务中的所有内容,我们已经决定因运营问题而不愿意这样做。

      2:微服务

      1. T0 + 100ms:客户端向M1 / API / Trending发送请求
      2. T1 + 100ms:客户端向M2 / API //最近发送
      3. 发送请求
      4. T2 + 100ms:客户端向M3 / API //帐户
      5. 发送请求
      6. T3 + 100ms:客户端向M4 / API / Summary发送请求?movieids = []
      7. T4 + 100ms:客户端向M5 / API发送请求//不可用
      8. 那是500ms。使用具有此功能的代理无法提供帮助 - 它只会为每个请求添加20毫秒的延迟(使其达到600毫秒)。我们有1 + 2和4,以及3和5之间的依赖关系,但可以做一些异步。让我们看看它有何帮助。

        3:微服务 - 异步

        1. T0:客户向M1 / API /趋势
        2. 发送请求
        3. T0:客户端向M2 / API //最近发送
        4. 发送请求
        5. T0:客户端向M3 / API //帐户
        6. 发送请求
        7. T0 + 200ms :(响应1 + 2)客户端向M4 / API / Summary发送请求?movieids = []
        8. T0 + 200ms :(响应3)客户端向M5 / API发送请求//不可用
        9. 我们降到200毫秒;还不错 - 但我们的客户需要了解我们的微服务架构。如果我们用我们的代理抽象它,那么我们有:

          4:微服务 - 与网关的异步

          1. T0 + 100ms:客户端向G1 / API / StartScreen发送请求
          2. T1:G1向M1 / API /趋势
          3. 发送请求
          4. T1:G1向M2 / API // Recent
          5. 发送请求
          6. T1:G1向M3 / API //帐户
          7. 发送请求
          8. T1 + 40ms :(响应1 + 2)G1向M4 / API / Summary发送请求?movieids = []
          9. T1 + 40ms :(响应3)G1向M5 / API发送请求//不可用
          10. 低至140毫秒,因为我们正在利用减少的服务内延迟。

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

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

            当M4的延迟增加到200毫秒时会发生什么?好吧,在客户端 - >异步微服务路由(#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 Recent / 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
            8. 我们的客户,订阅了P2 - 接收到单个请求的部分结果,并从M1 + M2和M4以及M3和M5之间的工作流程中抽象出来。我们在最佳情况下的延迟是140ms,与#4相同,在最坏的情况下类似于直接客户端路由(#3)w /部分结果。

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

              6个微服务 - 带队列的网关

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

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

              例如,让我们说,如果M4(摘要服务)不可用 - 我们有一个M4b对缓存数据进行操作(例如,星级评定已过期) 。 M4b可以立即应答R4a和R4b,然后我们的网关可以确定是否应该等待M4应答或者根据超时时间等待M4b。

              有关Netflix 实际如何解决此问题的详细信息,请查看以下资源:

              1. 他们的API Gateway
              2. fault-tolerance layer
              3. 下的消息传递模式
              4. 更多about IPC in microservices

答案 1 :(得分:2)

Apache Thrift旨在高效沟通。它由Facebook(现在是开源)开发,实现是available for C#

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

答案 2 :(得分:2)

首先,我建议将单片REST服务分解为多个微服务,每个服务都针对单个数据实体或用例。例如:发送电子邮件将是一个用例,查询搜索页面的结果将是一个用例。

然后,当您推出每个服务时,您可以修改单片服务以成为新微服务的代理。这样,您现有的应用程序可以继续调用您的单一服务,新的应用程序更新可以适当地调用微服务。

基本上,逐步从一个单一的服务迁移到微服务,一次一个。

  

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

使用单片Web服务作为微服务的代理会增加一些延迟,但单个微服务不会增加任何延迟,您可以将这些扩展到Azure后面的尽可能多的托管实例根据需要加载均衡器。

如果您确实看到了查询延迟,那么您可以为任何显示查询性能问题的微服务实现类似Redis缓存的功能。

如果您确实看到写入延迟成为问题,那么我建议移动这些问题写入以使用某种消息队列,以允许在可能的情况下异步处理它们。

  

Azure中有什么适合这个吗?

您可能需要查看Azure API Apps,因为它们可能提供一些很好的功能,可以通过Azure更轻松地保护/验证您的微服务,而不是单独保护。

  

我可以在主API与其他微服务API之间进行通信的不同方式是什么?

听起来你需要服务之间的实时通信。因此,除非您有可以在后台执行的任务,否则消息队列是不可能的。您可以在服务之间使用简单的REST调用,如果使用WCF实现,甚至可以使用SOAP之类的调用。虽然,听起来你现在正在使用REST,所以保持它是一样的。

此外,请确保您的微服务不通过数据库相互通信,因为这可能会很快变得混乱。使用简单的REST端点进行通信,类似于客户端应用程序使用的REST端点。

我喜欢遵循KISS(Keep It Simple Stupid)原则,因此我建议您尽量不要过度设计解决方案,并尽量保持简单。

答案 3 :(得分:1)

  

Azure中有什么适合这个吗?

Azure中还有另一种解决微服务方法的解决方案,我认为这种解决方案会引起很大的关注,称为Service Fabric

这里的问题是你有一个现有的应用程序,并且使它适应Service Fabric可能要困难得多,但它当然值得一看。

Here you can find a Service Fabric example of a web service.

希望这有帮助!

祝你好运!