代码之家  ›  专栏  ›  技术社区  ›  Anmol Singh Jaggi Luke Mclaren

为什么不在微服务之间使用异步消息

  •  1
  • Anmol Singh Jaggi Luke Mclaren  · 技术社区  · 5 年前

    为什么我们不把异步消息/事件作为微服务架构中唯一的通信模式,并删除所有的sync/http调用呢?
    我无法想象同步在任何情况下都比异步更有优势。

    想象一下3个服务A、B、C之间的以下呼叫链:
    UI->A->B->C

    • UI调用a请求一些数据。
    • A需要调用B来获取一些数据,在返回UI之前,它将对这些数据进行一些处理。
    • 同样,B需要调用C来获取一些数据。

    想象一下,所有服务都有(有限的)线程池来处理传入的请求。
    A中已接受来自UI的连接请求的线程将向B发出HTTP请求。
    由于对B的请求是IO的一种形式,因此该线程将被抢占,直到数据从a->B->C->B->A、 从而长时间浪费线程。
    其他服务中也会发生类似的线程抢占。


    如果我们有一个消息驱动的设计,我们可以在每个服务中拥有以下线程池:

    • 一个线程池,它只是运行kafka消费者来读取下游服务的响应。
    • 一个线程池,用于接受来自上游服务的新请求。

    考虑以下事件顺序:

    • A: :Thread1从UI获取请求,并生成消息' MsgAB '将请求id与核心数据一起封装到消息队列中。
    • A: :Thread1将请求连接对象插入到以下对象的全局映射中 requestId -> requestObject .
    • A: :Thread1现在可以免费为更多请求提供服务。
    • 同样,B会为C生成msg。
    • C将创建一个响应消息供B读取,这反过来将产生 MsgBA .
    • 现在A::Thread2运行一个卡夫卡消费者将消费 MsgBA 封装原始请求id。
    • A: :Thread2将使用全局映射中的请求对象回复UI。

    这种设计的优点是,线程是免费的,可以立即提高服务的吞吐量,除了消息驱动架构的通常好处,如可扩展性和可扩展性(消息也可以通过日志记录、跟踪、数据分析或任何其他服务读取,而生产服务甚至不知道)。

    为了处理超时,我们可以采用以下策略:

    • 我们可以将所有请求ID保存在任何时间敏感的数据结构中,这允许快速访问和删除最旧的条目。 堆或LRU缓存(使用标准DLL+hashmap实现)可能是这种数据结构的不错选择。
    • 我们可以让一个专用线程运行一个调度器,该调度器遍历此数据结构,并对所有过期/超时的请求回复超时错误。

    那么,为什么基于HTTP的微服务架构如此流行呢?

    0 回复  |  直到 5 年前