在分布式集群环境中,最容易导致资损的并发问题莫过于“重复扣款”或“重复生成账单”。 例如:操作员快速连点了两次“生成结算单”,两台不同的 API 服务器同时收到了请求并并发执行,如果没有跨进程的互斥锁,就会导致双倍出账!
BitzOrcas.Modern 提供了强类型的 IDistributedLock 抽象:基于 Redis 原子指令(SET NX PX)与 Lua 脚本实现安全加锁与所有者校验释放,杜绝死锁与误解锁。
分布式锁安全流转模型
第一步:正确获取与释放分布式锁句柄
在关键业务(如账单生成、额度扣减)中使用 await using 确保锁句柄百分之百安全释放:
using BitzOrcas.Infrastructure.Locking;using BitzOrcas.Domain.Results;
public static class BillingErrors{ public static readonly Error InvoiceBusy = Error.Conflict("Billing.InvoiceBusy", "当前账单正在结算中,请勿重复提交。");}
public sealed class GenerateInvoiceCommandHandler( IDistributedLock distributedLock, IInvoiceService invoiceService){ public async ValueTask<Result> Handle(GenerateInvoiceCommand command, CancellationToken ct) { // ① 资源键必须包含租户 ID 与业务唯一标识,严禁不同租户争抢同一把公共锁 var resourceKey = $"billing:{command.TenantId}:invoice:{command.InvoiceId}";
// ② 尝试获取锁:设置 30 秒自动租约,最多等待 2 秒(避免请求在网关层堆积挂起) await using var handle = await distributedLock.AcquireAsync( resourceKey, expiry: TimeSpan.FromSeconds(30), waitTimeout: TimeSpan.FromSeconds(2), cancellationToken: ct);
// ③ 未能抢占锁时立即短路返回 409 Conflict,告知客户端“当前正在处理中” if (handle is null || !handle.IsHeld) { return Result.Failure(BillingErrors.InvoiceBusy); }
// ④ 处于受保护的关键区:执行幂等结算操作 return await invoiceService.ProcessInvoiceAsync(command.InvoiceId, ct); }}第二步:分布式锁核心安全铁律
总结
BitzOrcas 的分布式锁为集群互斥提供了简单安全的底座:
await using自动释放:杜绝因未捕获异常导致锁永久泄露;- 毫秒级有界等待:防止集群线程池被慢请求耗尽;
- 租户空间隔离:保障多租户并发安全。
续期与键规范
续期调用 ILockHandle.RenewAsync(expiry, ct)(返回 ValueTask<bool>),用于持有方仍在临界区而租期将到的场景。Redis 实现的锁键统一带 bitzorcas:lock: 前缀;无法取得锁时的行为由 DistributedLockOptions.FallbackMode 控制,默认 Deny——调用方拿到 null 而不是排队阻塞。