微服务环境下如何不长时间锁定资源进行写操作?

Posted

技术标签:

【中文标题】微服务环境下如何不长时间锁定资源进行写操作?【英文标题】:How not to lock a resource for long for write operation in a micro-service environment? 【发布时间】:2021-10-25 22:51:33 【问题描述】:

我有一个微服务 A,它基本上支持对资源的 CRUD 操作(想象它们就像数据存储一样)。我有一个微服务 B,它使用 A 将某些对象写入资源。这个写作过程包括两个步骤。

    B 通过 HTTP 请求调用 A 以创建一个名为 writeObject 的对象,其中包含一些附加细节(比如 B 想要复制其对象的资源)。微服务 A 创建这个 writeobject 并将其添加到队列中。 B 轮询此队列并获取这些写入对象并对其自身进行一些验证。但在开始验证过程之前,它会在资源上设置一个锁,这样就不会处理其他 writeObject 并将其复制到同一资源。

    验证成功后,B再次通过HTTP请求调用A,最终将对象复制到资源中。 A处理请求并将writeObject指定的对象复制到资源并释放锁。

所以我的问题是,如果 B 进行验证的时间过长,队列中尝试写入同一资源的其他 writeObjects 将不得不等待很长时间。如何减轻或避免这一过程?可以采取哪些步骤?

【问题讨论】:

【参考方案1】:

对于分布式锁,让它们在一段时间后过期通常是个好主意。很难保证服务 B 将始终完成每个请求。它可能会崩溃并忘记它正在处理事务,或者遭受存储故障等。这些是缓慢事务的特殊情况(可能永远不会完成)。对锁设置过期时间会为任何其他请求在存在慢速事务时必须等待的时间设置一个上限。

这样的工作方式是:

    B 调用 A 创建 writeObject 请求并申请锁。 A 将根据验证和复制请求的预期最大持续时间设置该锁的到期时间。 C 尝试调用 A 来声明自己的锁。 A 会检查:B 要求的锁是否过期?如果没有,C 会一直等到锁被释放或过期。 如果 B 在锁过期之前发出复制请求,它会正常完成并将锁释放给 C。 如果 B 在锁到期前没有发出复制请求,A 将其交给 C。 如果 B 在锁过期后发出复制请求,则此请求失败。 B 可以选择从新的验证过程重新开始,一旦锁再次可用就收回锁,或者接受失败并以另一种方式处理它(记录它,将它传播给它自己的调用者等)。

如果服务 B 始终比过期时间慢,它可能永远无法完成其事务。这被称为“资源匮乏”。如果没有其他服务试图声明它,您可以通过允许它保持其过期锁定来改进这一点,但是如果资源处于高度争用状态,则速度较慢的服务可能总是会丢失。如果有很多缓慢的事务,这种方法仍然会导致瓶颈。设置过期时间可能是不希望有高过期率和不希望有很长等待时间之间的一个棘手的平衡。

您可以采取其他一些方法来权衡不同的情况。

乐观锁

乐观锁定使用资源上的版本跟踪来最小化锁定的持续时间:

    当 B 从队列中获取 writeObject 数据时,消息包括它正在写入的资源的当前版本。 B 执行其验证而不声明对资源的锁定。 在提交最终复制请求时,会在请求中包含版本号。 A 只需要在此复制请求期间锁定资源。它检查资源的当前版本是否与请求中的版本相同。如果是这样,它会成功完成并增加版本。

在有两个并发写入者的场景中,假设版本字段以 1 开头:

    B 创建一个 writeObject 请求。 C 为同一资源创建 writeObject 请求。 B 从队列中读取其 writeObject,包含版本 1。 C 从队列中读取它的 writeObject。它还包含版本 1。 B & C 同时进行验证。 C 先完成验证,提交版本 1 的复制请求。 A 完成复制并将资源的版本增加到 2。 B 提交版本 1 的复制请求。 A看到资源版本大于请求中的版本,拒绝请求。

这种方法的好处是快速事务根本不必等待慢速事务。它简化了锁的处理。缺点是它会使资源匮乏变得更糟。一个较慢的服务可能永远无法在一个较快的服务出现并抢占它之前完成其写入。它还增加了资源在高争用下失败的普遍性。应用程序需要设计为将这些作为常见事件而不是异常情况来处理。

合并

更好的解决方案是通过合并并发请求的结果来完全避免锁定。这非常依赖于用例和数据模型,因此并不总是可行的,但是当它可行时,它可以提供一个非常可扩展的解决方案,而无需等待。在这种情况下,除非 A 检测到冲突,否则来自 B 和 C 的请求都将被允许。这可能意味着分解对资源的请求以查看更新了哪些特定字段,并允许对不同字段集的并发请求。在最好的情况下,您可以使用像 Conflict-Free Replicated Data Type (CRDT) 这样的数据结构,它允许一致地合并并发更新。

【讨论】:

以上是关于微服务环境下如何不长时间锁定资源进行写操作?的主要内容,如果未能解决你的问题,请参考以下文章

DevOps架构下如何进行微服务性能测试

springcloud-hystrix断路器对微服务的容错处理

微服务架构 | 怎样解决超大附件分片上传?

Spring Cloud构建微服务架构断路器

Spring Cloud Contract 微服务契约测试框架

微服务架构 | 怎样解决超大附件分片上传?