Java 乐观缓存并发设计模式

Java 乐观缓存并发设计模式,java,design-patterns,concurrency,Java,Design Patterns,Concurrency,我有一个在服务器集群上运行的web服务。此web服务执行一些内部处理,然后可能会调用外部服务,从而产生费用 我想加入一些缓存,这样如果我收到对服务的相同请求(这是肯定会发生的),那么我就不必重复处理,既节省了处理时间/精力,也节省了服务调用外部部分的成本 但是,当我遇到以下限制时,我很难弄清楚如何管理这个缓存 该服务在多个web服务器上运行,以实现高可用性和可扩展性 请求可能需要5秒钟才能响应,但在此期间,我可能会收到2或3个其他相同的请求 在分布式环境中工作时,如何推迟执行其他服务调用,直

我有一个在服务器集群上运行的web服务。此web服务执行一些内部处理,然后可能会调用外部服务,从而产生费用

我想加入一些缓存,这样如果我收到对服务的相同请求(这是肯定会发生的),那么我就不必重复处理,既节省了处理时间/精力,也节省了服务调用外部部分的成本

但是,当我遇到以下限制时,我很难弄清楚如何管理这个缓存

  • 该服务在多个web服务器上运行,以实现高可用性和可扩展性
  • 请求可能需要5秒钟才能响应,但在此期间,我可能会收到2或3个其他相同的请求
在分布式环境中工作时,如何推迟执行其他服务调用,直到第一个服务调用响应为止(因此在缓存中可用)

我曾经考虑过引入前端代理模式,并在代理中建立一个相同请求的队列,这样当第一个请求返回时,它也可以向其他请求返回相同的响应。这是正确的模式,还是有更好的并发模式来处理这个场景

1.)该服务在多个web服务器上运行,以实现高可用性 和可扩展性

将其简单地视为设计约束。这意味着不要在缓存查找方法中包含主机名。只要结果不依赖于主机,就应该没有问题。如果在同一服务和相同参数的HA环境中,HESA返回与STOB不同的东西,那么我认为这是一个设计缺陷。 如果要保持系统冗余,则不应使用中央缓存。因为大多数时候,“中心”解决方案是“单点故障”解决方案的同义词。如果跨应用程序服务器同步,锁定也会更加复杂

引入多少缓存有点取决于缓存命中率和系统上可用的资源。最简单的解决方案是在每个服务实例级别进行缓存

2.)响应请求可能需要5秒钟,但同时, 我可能收到了2或3个其他相同的请求

这也是一个设计约束。您只需将缓存分为两个不同的步骤

  • 如果第一个线程进入缓存例程并锁定对其值的访问,则首先为相同的请求插入一个键
  • 处理完成后插入值并释放锁
  • 不过,您还需要处理异常处理

    锁定
    连接
    机制可以用不同的策略实现

    • 同步-您只需创建一个互斥/信号量或任何东西,并锁定对关键部分的访问。这可能会导致一些请求处于等待状态,直到锁消失
    • 异步-实现某种轮询机制,如果线程遇到锁定的关键部分(如在同步处理中),将产生一条消息,表明数据未就绪。这不会导致许多打开的连接,但会引入更多的复杂性
    互斥锁/信号量或用于锁定对关键部分的访问的任何结构可能取决于为相同请求计算的唯一密钥(只要您不想序列化对服务的访问)。

    您可以

  • 计算请求的加密哈希
  • 查看此哈希的结果是否已存在于数据库中,如果已存在,则返回它
  • 在数据库中以“结果挂起”状态存储哈希
  • 调用web服务并使用结果更新数据库中的行
  • 在步骤2,如果散列已经在数据库中,并且状态为“result pending”,则可以每X毫秒轮询一次数据库,最后在数据库中返回结果

    当然,问题出在细节上,因为如果发生错误,您必须决定怎么做:

    • 是否为所有后续相同的请求返回错误
    • 是否导致等待的线程重试调用web服务
    • 是否返回错误,但仅返回一段时间,然后重试
    当然,所有主机都将返回相同的结果。为了HA/可伸缩性,它们只在不同的节点上分开。但它产生的限制是,我们不再处于串行域中,而是处于并行处理域中。因此,您建议使用一个中央缓存,所有使用锁定机制的节点都可以访问该中央缓存。缓存代理应该位于负载平衡器之前;如果你让一个请求通过,你最好让它运行,而不是让它等待一个请求node@Codemwnci我调整了我的帖子来回答这个问题。@guido如果你把缓存机制放在负载平衡器之前,你会不会失去高可用性?@Codemwnci负载平衡器也是如此;任何角色都应支持HA的故障转移。-外部ws也是集群的吗?您控制了它吗?这似乎是一个很好的开始,您可以在ITIT前面编写自己的包装缓存web服务,而不是在我的控制之下。这是我们在每个缓存场景中从供应商处调用的服务:您真的确定该服务是无状态的吗?例如,“FetchCustomerDetailById”服务是不可缓存的,因为中间的“ChangeCustomerName”必须使您的缓存无效。啊,不,它确实是无状态的。如果给出相同的输入,每个外部呼叫请求都应保证相同的响应。我还想了解否决票。两张赞成票(一张矿井票),两张反对票,但没有解释?你的解决方案在@fyr上有什么不好的地方吗?IIUC,fyr的解决方案是