You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
6.4 KiB
6.4 KiB
LINGYUN.Abp.DistributedLocking.Dapr
基于Dapr分布式锁API的ABP分布式锁实现。该模块提供了与Dapr分布式锁服务的无缝集成,支持跨服务、跨实例的分布式锁定功能。
参考文档: Dapr Distributed Lock API
功能特性
- 与ABP分布式锁系统集成
- 支持自定义锁资源拥有者标识
- 支持可配置的锁定超时时间
- 支持锁资源自动释放
- 支持多种锁存储组件
- 支持锁获取和释放的事件通知
- 支持分布式锁的健康检查
配置选项
{
"DistributedLocking": {
"Dapr": {
"StoreName": "lockstore", // Dapr组件中定义的存储名称
"DefaultIdentifier": "dapr-lock-owner", // 默认锁资源拥有者标识
"DefaultTimeout": "00:00:30" // 默认锁定超时时间(30秒)
}
}
}
基础使用
1. 模块配置
[DependsOn(typeof(AbpDistributedLockingDaprModule))]
public class YourProjectModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
// 基础配置
Configure<AbpDistributedLockingDaprOptions>(options =>
{
options.StoreName = "redis-lock"; // 使用Redis作为锁存储
options.DefaultIdentifier = "my-service"; // 自定义锁拥有者标识
options.DefaultTimeout = TimeSpan.FromMinutes(1); // 设置默认超时时间为1分钟
});
}
}
2. 基本用法
public class OrderService
{
private readonly IDistributedLockProvider _lockProvider;
public OrderService(IDistributedLockProvider lockProvider)
{
_lockProvider = lockProvider;
}
public async Task ProcessOrderAsync(string orderId)
{
// 尝试获取锁
using (var handle = await _lockProvider.TryAcquireAsync($"order:{orderId}"))
{
if (handle != null)
{
try
{
// 执行需要加锁的业务逻辑
await ProcessOrderInternalAsync(orderId);
}
catch (Exception ex)
{
// 处理异常
_logger.LogError(ex, "处理订单时发生错误");
throw;
}
}
else
{
throw new ConcurrencyException("订单正在被其他进程处理");
}
}
}
}
3. 高级用法
public class InventoryService
{
private readonly IDistributedLockProvider _lockProvider;
private readonly ILogger<InventoryService> _logger;
public InventoryService(
IDistributedLockProvider lockProvider,
ILogger<InventoryService> logger)
{
_lockProvider = lockProvider;
_logger = logger;
}
public async Task UpdateInventoryAsync(string productId, int quantity)
{
// 自定义锁配置
var lockOptions = new DistributedLockOptions
{
Timeout = TimeSpan.FromSeconds(10), // 自定义超时时间
RetryDelay = TimeSpan.FromMilliseconds(100) // 重试延迟
};
try
{
using (var handle = await _lockProvider.TryAcquireAsync(
$"inventory:{productId}",
lockOptions))
{
if (handle == null)
{
_logger.LogWarning("无法获取库存锁,产品ID: {ProductId}", productId);
throw new ConcurrencyException("无法获取库存锁");
}
// 执行库存更新操作
await UpdateInventoryInternalAsync(productId, quantity);
}
}
catch (Exception ex) when (ex is not ConcurrencyException)
{
_logger.LogError(ex, "更新库存时发生错误");
throw;
}
}
}
组件配置
Redis锁存储配置示例
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: redis-lock # 对应StoreName配置
spec:
type: lock.redis
version: v1
metadata:
- name: redisHost
value: localhost:6379
- name: redisPassword
value: ""
- name: enableTLS
value: false
- name: maxRetries
value: 5
- name: maxRetryBackoff
value: 5s
Consul锁存储配置示例
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: consul-lock
spec:
type: lock.consul
version: v1
metadata:
- name: host
value: localhost:8500
- name: sessionTTL
value: 10
- name: scheme
value: http
核心接口
ILockOwnerFinder
提供锁资源持有者标识的接口。
public interface ILockOwnerFinder
{
string GetOwner();
}
默认实现 LockOwnerFinder 会:
- 优先使用当前用户ID作为锁拥有者标识
- 如果用户未登录,则使用配置的
DefaultIdentifier
自定义锁拥有者标识实现
public class CustomLockOwnerFinder : ILockOwnerFinder
{
private readonly ICurrentTenant _currentTenant;
public CustomLockOwnerFinder(ICurrentTenant currentTenant)
{
_currentTenant = currentTenant;
}
public string GetOwner()
{
// 使用租户ID和机器名称组合作为锁拥有者标识
return $"{_currentTenant.Id ?? "host"}-{Environment.MachineName}";
}
}
// 注册自定义实现
context.Services.AddTransient<ILockOwnerFinder, CustomLockOwnerFinder>();
最佳实践
-
合理设置超时时间
- 根据业务操作的预期执行时间设置合适的超时时间
- 避免设置过长的超时时间,以防止死锁
-
正确的锁粒度
- 锁的范围应该尽可能小,只锁定必要的资源
- 避免长时间持有锁,及时释放
-
异常处理
- 始终在 using 块中使用锁
- 妥善处理锁获取失败的情况
- 记录关键的锁操作日志
-
性能优化
- 使用合适的存储组件
- 配置适当的重试策略
- 监控锁的使用情况
注意事项
- 确保Dapr Sidecar已正确配置并运行
- 分布式锁组件需要在Dapr配置中正确定义
- 合理设置锁的超时时间,避免死锁
- 正确处理锁获取失败的情况
- 在高并发场景下注意性能影响
- 建议配置锁组件的健康检查
- 重要操作建议添加日志记录