diff --git a/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr.Actors.AspNetCore.Wrapper/README.EN.md b/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr.Actors.AspNetCore.Wrapper/README.EN.md new file mode 100644 index 000000000..b144b172a --- /dev/null +++ b/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr.Actors.AspNetCore.Wrapper/README.EN.md @@ -0,0 +1,181 @@ +# LINGYUN.Abp.Dapr.Actors.AspNetCore.Wrapper + +Dapr Actors ASP.NET Core wrapper module for handling Actor method call response wrapping and unwrapping. + +## Features + +* Automatic Actor response result wrapping/unwrapping +* Integration with ABP's wrapper system +* Error handling for Actor method calls +* Support for success/error code configuration +* Integration with Dapr.Actors.AspNetCore +* Support for custom response wrapper format +* Flexible wrapping control options + +## Basic Usage + +Module reference as needed: + +```csharp +[DependsOn( + typeof(AbpDaprActorsAspNetCoreModule), + typeof(AbpWrapperModule) +)] +public class YouProjectModule : AbpModule +{ +} +``` + +## Configuration Options + +The module uses `AbpWrapperOptions` from the `LINGYUN.Abp.Wrapper` package for configuration: + +```json +{ + "Wrapper": { + "IsEnabled": true, // Enable/disable response wrapping + "CodeWithSuccess": "0", // Success code in wrapped response + "HttpStatusCode": 200, // Default HTTP status code for wrapped responses + "WrapOnError": true, // Whether to wrap error responses + "WrapOnSuccess": true // Whether to wrap success responses + } +} +``` + +## Implementation Example + +1. Actor Interface Definition + +```csharp +public interface ICounterActor : IActor +{ + Task GetCountAsync(); + Task IncrementCountAsync(); +} +``` + +2. Actor Implementation + +```csharp +public class CounterActor : Actor, ICounterActor +{ + private const string CountStateName = "count"; + + public CounterActor(ActorHost host) : base(host) + { + } + + public async Task GetCountAsync() + { + var count = await StateManager.TryGetStateAsync(CountStateName); + return count.HasValue ? count.Value : 0; + } + + public async Task IncrementCountAsync() + { + var currentCount = await GetCountAsync(); + await StateManager.SetStateAsync(CountStateName, currentCount + 1); + } +} +``` + +## Response Format + +When wrapping is enabled, Actor method responses will be in the following format: + +```json +{ + "code": "0", // Response code, "0" indicates success by default + "message": "Success", // Response message + "details": null, // Additional details (optional) + "result": { // Actual response data + // ... Actor method return value + } +} +``` + +## Error Handling + +The module automatically handles Actor method call errors: +* For wrapped responses: + * Unwraps the response and checks the code + * If code doesn't match `CodeWithSuccess`, throws `AbpRemoteCallException` + * Includes error message, details, and code in the exception + * Supports custom error code mapping +* For Actor runtime errors: + * Automatically wraps as standard error response + * Preserves original exception information + * Includes Actor-related context information + +### Error Response Example + +```json +{ + "code": "ERROR_001", + "message": "Actor method call failed", + "details": "Failed to access state for actor 'counter'", + "result": null +} +``` + +## Advanced Usage + +### 1. Controlling Response Wrapping + +Response wrapping can be controlled per Actor call using HTTP headers: + +```csharp +// Add to request headers +var headers = new Dictionary +{ + { "X-Abp-Wrap-Result", "true" }, // Force enable wrapping + // or + { "X-Abp-Dont-Wrap-Result", "true" } // Force disable wrapping +}; + +// Use in Actor method +public async Task GetCountAsync() +{ + var context = ActorContext.GetContext(); + context.Headers.Add("X-Abp-Wrap-Result", "true"); + + var count = await StateManager.TryGetStateAsync(CountStateName); + return count.HasValue ? count.Value : 0; +} +``` + +### 2. Custom Error Handling + +```csharp +public class CustomActorErrorHandler : IAbpWrapperErrorHandler +{ + public Task HandleAsync(AbpWrapperErrorContext context) + { + if (context.Exception is ActorMethodInvocationException actorException) + { + // Custom Actor error handling logic + context.Response = new WrapperResponse + { + Code = "ACTOR_ERROR", + Message = actorException.Message, + Details = actorException.ActorId + }; + } + return Task.CompletedTask; + } +} +``` + +## Important Notes + +* Response wrapping can be controlled through: + * Global settings in configuration + * HTTP headers for individual requests + * Dynamic control in Actor methods +* Error responses maintain original error structure for Actor methods +* The module integrates with ABP's remote service error handling system +* Recommended to use response wrapping consistently in microservices architecture +* Wrapper format can be customized by implementing `IAbpWrapperResponseBuilder` +* Actor state operation errors are properly wrapped and handled + +[查看中文](README.md) diff --git a/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr.Actors.AspNetCore.Wrapper/README.md b/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr.Actors.AspNetCore.Wrapper/README.md new file mode 100644 index 000000000..11b7ba6de --- /dev/null +++ b/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr.Actors.AspNetCore.Wrapper/README.md @@ -0,0 +1,181 @@ +# LINGYUN.Abp.Dapr.Actors.AspNetCore.Wrapper + +Dapr Actors ASP.NET Core响应包装模块,用于处理Actor方法调用的响应包装和解包。 + +## 功能特性 + +* Actor响应结果自动包装/解包 +* 与ABP包装系统集成 +* 支持Actor方法调用的错误处理 +* 支持成功/错误代码配置 +* 与Dapr.Actors.AspNetCore集成 +* 支持自定义响应包装格式 +* 灵活的包装控制选项 + +## 配置使用 + +模块按需引用: + +```csharp +[DependsOn( + typeof(AbpDaprActorsAspNetCoreModule), + typeof(AbpWrapperModule) +)] +public class YouProjectModule : AbpModule +{ +} +``` + +## 配置选项 + +模块使用来自`LINGYUN.Abp.Wrapper`包的`AbpWrapperOptions`进行配置: + +```json +{ + "Wrapper": { + "IsEnabled": true, // 启用/禁用响应包装 + "CodeWithSuccess": "0", // 包装响应中的成功代码 + "HttpStatusCode": 200, // 包装响应的默认HTTP状态码 + "WrapOnError": true, // 是否包装错误响应 + "WrapOnSuccess": true // 是否包装成功响应 + } +} +``` + +## 实现示例 + +1. Actor接口定义 + +```csharp +public interface ICounterActor : IActor +{ + Task GetCountAsync(); + Task IncrementCountAsync(); +} +``` + +2. Actor实现 + +```csharp +public class CounterActor : Actor, ICounterActor +{ + private const string CountStateName = "count"; + + public CounterActor(ActorHost host) : base(host) + { + } + + public async Task GetCountAsync() + { + var count = await StateManager.TryGetStateAsync(CountStateName); + return count.HasValue ? count.Value : 0; + } + + public async Task IncrementCountAsync() + { + var currentCount = await GetCountAsync(); + await StateManager.SetStateAsync(CountStateName, currentCount + 1); + } +} +``` + +## 响应格式 + +当启用包装时,Actor方法的响应将采用以下格式: + +```json +{ + "code": "0", // 响应代码,默认"0"表示成功 + "message": "Success", // 响应消息 + "details": null, // 附加详情(可选) + "result": { // 实际响应数据 + // ... Actor方法的返回值 + } +} +``` + +## 错误处理 + +模块自动处理Actor方法调用的错误: +* 对于包装的响应: + * 解包响应并检查代码 + * 如果代码与`CodeWithSuccess`不匹配,抛出`AbpRemoteCallException` + * 在异常中包含错误消息、详情和代码 + * 支持自定义错误代码映射 +* 对于Actor运行时错误: + * 自动包装为标准错误响应 + * 保留原始异常信息 + * 包含Actor相关的上下文信息 + +### 错误响应示例 + +```json +{ + "code": "ERROR_001", + "message": "Actor方法调用失败", + "details": "Actor 'counter' 的状态访问失败", + "result": null +} +``` + +## 高级用法 + +### 1. 控制响应包装 + +可以通过HTTP头控制单个Actor调用的响应包装: + +```csharp +// 在请求头中添加 +var headers = new Dictionary +{ + { "X-Abp-Wrap-Result", "true" }, // 强制启用包装 + // 或 + { "X-Abp-Dont-Wrap-Result", "true" } // 强制禁用包装 +}; + +// 在Actor方法中使用 +public async Task GetCountAsync() +{ + var context = ActorContext.GetContext(); + context.Headers.Add("X-Abp-Wrap-Result", "true"); + + var count = await StateManager.TryGetStateAsync(CountStateName); + return count.HasValue ? count.Value : 0; +} +``` + +### 2. 自定义错误处理 + +```csharp +public class CustomActorErrorHandler : IAbpWrapperErrorHandler +{ + public Task HandleAsync(AbpWrapperErrorContext context) + { + if (context.Exception is ActorMethodInvocationException actorException) + { + // 自定义Actor错误处理逻辑 + context.Response = new WrapperResponse + { + Code = "ACTOR_ERROR", + Message = actorException.Message, + Details = actorException.ActorId + }; + } + return Task.CompletedTask; + } +} +``` + +## 注意事项 + +* 响应包装可以通过以下方式控制: + * 配置文件中的全局设置 + * HTTP头控制单个请求 + * Actor方法中的动态控制 +* Actor方法的错误响应会保持原始错误结构 +* 模块与ABP的远程服务错误处理系统集成 +* 建议在微服务架构中统一使用响应包装 +* 包装格式可以通过继承`IAbpWrapperResponseBuilder`自定义 +* Actor状态操作的错误会被正确包装和处理 + +[查看英文](README.EN.md) diff --git a/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr.Actors.AspNetCore/README.EN.md b/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr.Actors.AspNetCore/README.EN.md new file mode 100644 index 000000000..9485b63b9 --- /dev/null +++ b/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr.Actors.AspNetCore/README.EN.md @@ -0,0 +1,123 @@ +# LINGYUN.Abp.Dapr.Actors.AspNetCore + +Integration of Dapr.Actors with ASP.NET Core in the ABP framework. This module automatically scans and registers Actor services defined within assemblies as Dapr.Actors. + +## Features + +* Automatic Actor service registration +* Integration with ABP's dependency injection system +* Support for custom Actor type names through `RemoteServiceAttribute` +* Actor runtime configuration through `ActorRuntimeOptions` +* Automatic Actor endpoint mapping +* Actor interface validation + +## Basic Usage + +Module reference as needed: + +```csharp +[DependsOn(typeof(AbpDaprActorsAspNetCoreModule))] +public class YourProjectModule : AbpModule +{ + public override void PreConfigureServices(ServiceConfigurationContext context) + { + // Configure Actor runtime options + PreConfigure(options => + { + options.ActorIdleTimeout = TimeSpan.FromMinutes(60); + options.ActorScanInterval = TimeSpan.FromSeconds(30); + options.DrainOngoingCallTimeout = TimeSpan.FromSeconds(30); + options.DrainRebalancedActors = true; + options.RemindersStoragePartitions = 7; + }); + } +} +``` + +## Implementation Example + +1. Define Actor Interface + +```csharp +[RemoteService("counter")] // Optional: customize Actor type name +public interface ICounterActor : IActor +{ + Task GetCountAsync(); + Task IncrementCountAsync(); +} +``` + +2. Implement Actor + +```csharp +public class CounterActor : Actor, ICounterActor +{ + private const string CountStateName = "count"; + + public CounterActor(ActorHost host) : base(host) + { + } + + public async Task GetCountAsync() + { + var count = await StateManager.TryGetStateAsync(CountStateName); + return count.HasValue ? count.Value : 0; + } + + public async Task IncrementCountAsync() + { + var currentCount = await GetCountAsync(); + await StateManager.SetStateAsync(CountStateName, currentCount + 1); + } +} +``` + +The module will automatically: +1. Detect the `CounterActor` implementation +2. Register it with Dapr.Actors +3. Configure the Actor runtime +4. Map the Actor endpoints + +## Actor Runtime Configuration + +The module supports all standard Dapr Actor runtime configurations through `ActorRuntimeOptions`: + +```csharp +PreConfigure(options => +{ + // Actor timeout settings + options.ActorIdleTimeout = TimeSpan.FromMinutes(60); + options.ActorScanInterval = TimeSpan.FromSeconds(30); + + // Draining settings + options.DrainOngoingCallTimeout = TimeSpan.FromSeconds(30); + options.DrainRebalancedActors = true; + + // Reminders settings + options.RemindersStoragePartitions = 7; + + // Custom serialization settings + options.JsonSerializerOptions = new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }; +}); +``` + +## Important Notes + +* Actor implementations must be registered in the dependency injection container +* Actor interfaces must inherit from `IActor` +* Actor type names can be customized using the `RemoteServiceAttribute` +* The module automatically maps Actor endpoints using ABP's endpoint routing system +* Actor runtime options should be configured in the `PreConfigureServices` phase + +## Endpoint Mapping + +The module automatically maps the following Actor endpoints: +* `/dapr/actors/{actorType}/{actorId}/method/{methodName}` +* `/dapr/actors/{actorType}/{actorId}/state` +* `/dapr/actors/{actorType}/{actorId}/reminders/{reminderName}` +* `/dapr/actors/{actorType}/{actorId}/timers/{timerName}` + +[查看中文](README.md) diff --git a/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr.Actors.AspNetCore/README.md b/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr.Actors.AspNetCore/README.md index f1e15535c..a80813802 100644 --- a/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr.Actors.AspNetCore/README.md +++ b/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr.Actors.AspNetCore/README.md @@ -2,6 +2,15 @@ Dapr.Asp.NetCore的Abp框架集成,扫描程序集内部实现的Actor服务列表,批量注册为Dapr.Actors +## 功能特性 + +* 自动Actor服务注册 +* 与ABP依赖注入系统集成 +* 通过`RemoteServiceAttribute`支持自定义Actor类型名称 +* 通过`ActorRuntimeOptions`配置Actor运行时 +* 自动Actor端点映射 +* Actor接口验证 + ## 配置使用 模块按需引用 @@ -10,10 +19,105 @@ Dapr.Asp.NetCore的Abp框架集成,扫描程序集内部实现的Actor服务列 [DependsOn(typeof(AbpDaprActorsAspNetCoreModule))] public class YouProjectModule : AbpModule { - // other + public override void PreConfigureServices(ServiceConfigurationContext context) + { + // 配置Actor运行时选项 + PreConfigure(options => + { + options.ActorIdleTimeout = TimeSpan.FromMinutes(60); + options.ActorScanInterval = TimeSpan.FromSeconds(30); + options.DrainOngoingCallTimeout = TimeSpan.FromSeconds(30); + options.DrainRebalancedActors = true; + options.RemindersStoragePartitions = 7; + }); + } +} +``` + +## 实现示例 + +1. 定义Actor接口 + +```csharp +[RemoteService("counter")] // 可选:自定义Actor类型名称 +public interface ICounterActor : IActor +{ + Task GetCountAsync(); + Task IncrementCountAsync(); +} +``` + +2. 实现Actor + +```csharp +public class CounterActor : Actor, ICounterActor +{ + private const string CountStateName = "count"; + + public CounterActor(ActorHost host) : base(host) + { + } + + public async Task GetCountAsync() + { + var count = await StateManager.TryGetStateAsync(CountStateName); + return count.HasValue ? count.Value : 0; + } + + public async Task IncrementCountAsync() + { + var currentCount = await GetCountAsync(); + await StateManager.SetStateAsync(CountStateName, currentCount + 1); + } } ``` -## 配置项说明 +模块将自动: +1. 检测`CounterActor`实现 +2. 将其注册到Dapr.Actors +3. 配置Actor运行时 +4. 映射Actor端点 + +## Actor运行时配置 + +模块通过`ActorRuntimeOptions`支持所有标准的Dapr Actor运行时配置: + +```csharp +PreConfigure(options => +{ + // Actor超时设置 + options.ActorIdleTimeout = TimeSpan.FromMinutes(60); + options.ActorScanInterval = TimeSpan.FromSeconds(30); + + // 清理设置 + options.DrainOngoingCallTimeout = TimeSpan.FromSeconds(30); + options.DrainRebalancedActors = true; + + // 提醒器设置 + options.RemindersStoragePartitions = 7; + + // 自定义序列化设置 + options.JsonSerializerOptions = new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }; +}); +``` + +## 重要说明 + +* Actor实现必须在依赖注入容器中注册 +* Actor接口必须继承自`IActor` +* Actor类型名称可以使用`RemoteServiceAttribute`自定义 +* 模块使用ABP的端点路由系统自动映射Actor端点 +* Actor运行时选项应在`PreConfigureServices`阶段配置 + +## 端点映射 + +模块自动映射以下Actor端点: +* `/dapr/actors/{actorType}/{actorId}/method/{methodName}` +* `/dapr/actors/{actorType}/{actorId}/state` +* `/dapr/actors/{actorType}/{actorId}/reminders/{reminderName}` +* `/dapr/actors/{actorType}/{actorId}/timers/{timerName}` -## 其他 +[View English](README.EN.md) diff --git a/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr.Actors/README.EN.md b/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr.Actors/README.EN.md new file mode 100644 index 000000000..7a58229b5 --- /dev/null +++ b/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr.Actors/README.EN.md @@ -0,0 +1,129 @@ +# LINGYUN.Abp.Dapr.Actors + +Dapr.IActor client proxy module + +## Features + +* Dynamic proxy generation for Dapr Actors +* Integration with ABP's remote service system +* Support for Actor authentication and authorization +* Multi-tenant support +* Automatic request/response handling +* Custom error handling +* Culture and language header support + +## Basic Usage + +Module reference as needed: + +```csharp +[DependsOn(typeof(AbpDaprActorsModule))] +public class YouProjectModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + // Register proxies similar to Volo.Abp.Http.Client module + context.Services.AddDaprActorProxies( + typeof(YouProjectActorInterfaceModule).Assembly, // Search for IActor definitions in YouProjectActorInterfaceModule + RemoteServiceName + ); + } +} +``` + +## Configuration Options + +```json +{ + "RemoteServices": { + "Default": { + "BaseUrl": "http://localhost:3500", // Required, Dapr HTTP endpoint + "DaprApiToken": "your-api-token", // Optional, Dapr API Token + "RequestTimeout": "30000" // Optional, request timeout in milliseconds (default: 30000) + } + } +} +``` + +## Implementation Example + +1. Actor Interface Definition + +```csharp +public interface ICounterActor : IActor +{ + Task GetCountAsync(); + Task IncrementCountAsync(); +} +``` + +2. Actor Implementation + +```csharp +public class CounterActor : Actor, ICounterActor +{ + private const string CountStateName = "count"; + + public CounterActor(ActorHost host) : base(host) + { + } + + public async Task GetCountAsync() + { + var count = await StateManager.TryGetStateAsync(CountStateName); + return count.HasValue ? count.Value : 0; + } + + public async Task IncrementCountAsync() + { + var currentCount = await GetCountAsync(); + await StateManager.SetStateAsync(CountStateName, currentCount + 1); + } +} +``` + +3. Client Usage + +```csharp +public class CounterService +{ + private readonly ICounterActor _counterActor; + + public CounterService(ICounterActor counterActor) + { + _counterActor = counterActor; + } + + public async Task GetAndIncrementCountAsync() + { + var count = await _counterActor.GetCountAsync(); + await _counterActor.IncrementCountAsync(); + return count; + } +} +``` + +## Important Notes + +* Actor methods must return `Task` or `Task` +* Actor methods can have at most one parameter +* Actor instances are single-threaded, processing one request at a time +* Actor state is managed by the Dapr runtime +* The module automatically handles: + * Authentication headers + * Tenant context + * Culture information + * Request timeouts + * Error handling + +## Error Handling + +The module provides custom error handling for Actor calls: +* `AbpDaprActorCallException`: Thrown when an Actor method call fails +* `ActorMethodInvocationException`: Contains detailed information about the failure +* Error responses include: + * Error message + * Error code + * Original exception type + +[查看中文](README.md) diff --git a/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr.Actors/README.md b/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr.Actors/README.md index 1baa45e79..e133373e9 100644 --- a/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr.Actors/README.md +++ b/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr.Actors/README.md @@ -1,6 +1,16 @@ # LINGYUN.Abp.Dapr.Actors -Dapr.IActor客户端代理 +Dapr.IActor客户端代理模块 + +## 功能特性 + +* Dapr Actors的动态代理生成 +* 与ABP远程服务系统集成 +* 支持Actor认证和授权 +* 多租户支持 +* 自动请求/响应处理 +* 自定义错误处理 +* 文化和语言标头支持 ## 配置使用 @@ -10,7 +20,7 @@ Dapr.IActor客户端代理 [DependsOn(typeof(AbpDaprActorsModule))] public class YouProjectModule : AbpModule { - public override void ConfigureServices(ServiceConfigurationContext context) + public override void ConfigureServices(ServiceConfigurationContext context) { // 注册代理类似于 Volo.Abp.Http.Client 模块 context.Services.AddDaprActorProxies( @@ -20,7 +30,100 @@ public class YouProjectModule : AbpModule } } ``` -## 配置项说明 +## 配置选项 + +```json +{ + "RemoteServices": { + "Default": { + "BaseUrl": "http://localhost:3500", // 必需,Dapr HTTP端点 + "DaprApiToken": "your-api-token", // 可选,Dapr API Token + "RequestTimeout": "30000" // 可选,请求超时时间(毫秒,默认:30000) + } + } +} +``` + +## 实现示例 + +1. Actor接口定义 + +```csharp +public interface ICounterActor : IActor +{ + Task GetCountAsync(); + Task IncrementCountAsync(); +} +``` + +2. Actor实现 + +```csharp +public class CounterActor : Actor, ICounterActor +{ + private const string CountStateName = "count"; + + public CounterActor(ActorHost host) : base(host) + { + } + + public async Task GetCountAsync() + { + var count = await StateManager.TryGetStateAsync(CountStateName); + return count.HasValue ? count.Value : 0; + } + + public async Task IncrementCountAsync() + { + var currentCount = await GetCountAsync(); + await StateManager.SetStateAsync(CountStateName, currentCount + 1); + } +} +``` + +3. 客户端使用 + +```csharp +public class CounterService +{ + private readonly ICounterActor _counterActor; + + public CounterService(ICounterActor counterActor) + { + _counterActor = counterActor; + } + + public async Task GetAndIncrementCountAsync() + { + var count = await _counterActor.GetCountAsync(); + await _counterActor.IncrementCountAsync(); + return count; + } +} +``` + +## 注意事项 + +* Actor方法必须返回`Task`或`Task` +* Actor方法最多只能有一个参数 +* Actor实例是单线程的,一次只能处理一个请求 +* Actor状态由Dapr运行时管理 +* 模块自动处理: + * 认证标头 + * 租户上下文 + * 文化信息 + * 请求超时 + * 错误处理 + +## 错误处理 + +模块为Actor调用提供自定义错误处理: +* `AbpDaprActorCallException`:当Actor方法调用失败时抛出 +* `ActorMethodInvocationException`:包含有关失败的详细信息 +* 错误响应包括: + * 错误消息 + * 错误代码 + * 原始异常类型 -## 其他 +[查看更多](README.EN.md) diff --git a/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr.Client.Wrapper/README.EN.md b/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr.Client.Wrapper/README.EN.md new file mode 100644 index 000000000..6feb2fc59 --- /dev/null +++ b/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr.Client.Wrapper/README.EN.md @@ -0,0 +1,189 @@ +# LINGYUN.Abp.Dapr.Client.Wrapper + +Dapr service-to-service invocation module that handles wrapped response result unpacking. + +## Features + +* Automatic response result wrapping/unwrapping +* Integration with ABP's wrapper system +* Custom error handling for wrapped responses +* Support for success/error code configuration +* HTTP status code mapping +* Support for custom response wrapper format +* Flexible wrapping control options + +## Basic Usage + +Module reference as needed: + +```csharp +[DependsOn(typeof(AbpDaprClientModule))] +public class AbpDaprClientWrapperModule : AbpModule +{ +} +``` + +## Configuration Options + +The module uses `AbpWrapperOptions` from the `LINGYUN.Abp.Wrapper` package for configuration: + +```json +{ + "Wrapper": { + "IsEnabled": true, // Enable/disable response wrapping + "CodeWithSuccess": "0", // Success code in wrapped response + "HttpStatusCode": 200, // Default HTTP status code for wrapped responses + "WrapOnError": true, // Whether to wrap error responses + "WrapOnSuccess": true // Whether to wrap success responses + } +} +``` + +## Implementation Example + +1. Service Definition + +```csharp +public interface IProductService +{ + Task GetAsync(string id); + Task> GetListAsync(); + Task CreateAsync(CreateProductDto input); +} +``` + +2. Service Implementation + +```csharp +public class ProductService : IProductService +{ + private readonly DaprClient _daprClient; + + public ProductService(DaprClient daprClient) + { + _daprClient = daprClient; + } + + public async Task GetAsync(string id) + { + // Response wrapping is handled automatically + return await _daprClient.InvokeMethodAsync( + "product-service", // Target service ID + $"api/products/{id}", // Method path + HttpMethod.Get + ); + } + + public async Task> GetListAsync() + { + return await _daprClient.InvokeMethodAsync>( + "product-service", + "api/products", + HttpMethod.Get + ); + } + + public async Task CreateAsync(CreateProductDto input) + { + return await _daprClient.InvokeMethodAsync( + "product-service", + "api/products", + HttpMethod.Post, + input + ); + } +} +``` + +## Response Format + +When wrapping is enabled, the response will be in the following format: + +```json +{ + "code": "0", // Response code, "0" indicates success by default + "message": "Success", // Response message + "details": null, // Additional details (optional) + "result": { // Actual response data + // ... response content + } +} +``` + +## Error Handling + +The module automatically handles error responses: +* For wrapped responses (with `AbpWrapResult` header): + * Unwraps the response and checks the code + * If code doesn't match `CodeWithSuccess`, throws `AbpRemoteCallException` + * Includes error message, details, and code in the exception + * Supports custom error code mapping +* For unwrapped responses: + * Passes through the original response + * Uses standard HTTP error handling + * Maintains original error information + +### Error Response Example + +```json +{ + "code": "ERROR_001", + "message": "Product not found", + "details": "Product with ID '123' does not exist", + "result": null +} +``` + +## Advanced Usage + +### 1. Controlling Response Wrapping + +Response wrapping can be controlled per request using HTTP headers: + +```csharp +// Add to request headers +var headers = new Dictionary +{ + { "X-Abp-Wrap-Result", "true" }, // Force enable wrapping + // or + { "X-Abp-Dont-Wrap-Result", "true" } // Force disable wrapping +}; + +await _daprClient.InvokeMethodAsync( + "product-service", + "api/products", + HttpMethod.Get, + null, + headers +); +``` + +### 2. Custom Error Handling + +```csharp +public class CustomErrorHandler : IAbpWrapperErrorHandler +{ + public Task HandleAsync(AbpWrapperErrorContext context) + { + // Custom error handling logic + if (context.Response.Code == "CUSTOM_ERROR") + { + // Special handling + } + return Task.CompletedTask; + } +} +``` + +## Important Notes + +* Response wrapping can be controlled through: + * Global settings in configuration + * HTTP headers for individual requests + * Dynamic control in code +* Error responses maintain original error structure when possible +* The module integrates with ABP's remote service error handling system +* Recommended to use response wrapping consistently in microservices architecture +* Wrapper format can be customized by implementing `IAbpWrapperResponseBuilder` + +[查看中文](README.md) diff --git a/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr.Client.Wrapper/README.md b/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr.Client.Wrapper/README.md index a5a138290..de9601245 100644 --- a/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr.Client.Wrapper/README.md +++ b/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr.Client.Wrapper/README.md @@ -2,6 +2,16 @@ Dapr服务间调用,对包装后的响应结果解包 +## 功能特性 + +* 自动响应结果包装/解包 +* 与ABP包装系统集成 +* 自定义包装响应的错误处理 +* 支持成功/错误代码配置 +* HTTP状态码映射 +* 支持自定义响应包装格式 +* 灵活的包装控制选项 + ## 配置使用 模块按需引用 @@ -13,4 +23,167 @@ public class AbpDaprClientWrapperModule : AbpModule } ``` -## 其他 +## 配置选项 + +模块使用来自`LINGYUN.Abp.Wrapper`包的`AbpWrapperOptions`进行配置: + +```json +{ + "Wrapper": { + "IsEnabled": true, // 启用/禁用响应包装 + "CodeWithSuccess": "0", // 包装响应中的成功代码 + "HttpStatusCode": 200, // 包装响应的默认HTTP状态码 + "WrapOnError": true, // 是否包装错误响应 + "WrapOnSuccess": true // 是否包装成功响应 + } +} +``` + +## 实现示例 + +1. 服务定义 + +```csharp +public interface IProductService +{ + Task GetAsync(string id); + Task> GetListAsync(); + Task CreateAsync(CreateProductDto input); +} +``` + +2. 服务实现 + +```csharp +public class ProductService : IProductService +{ + private readonly DaprClient _daprClient; + + public ProductService(DaprClient daprClient) + { + _daprClient = daprClient; + } + + public async Task GetAsync(string id) + { + // 调用会自动处理响应包装 + return await _daprClient.InvokeMethodAsync( + "product-service", // 目标服务ID + $"api/products/{id}", // 方法路径 + HttpMethod.Get + ); + } + + public async Task> GetListAsync() + { + return await _daprClient.InvokeMethodAsync>( + "product-service", + "api/products", + HttpMethod.Get + ); + } + + public async Task CreateAsync(CreateProductDto input) + { + return await _daprClient.InvokeMethodAsync( + "product-service", + "api/products", + HttpMethod.Post, + input + ); + } +} +``` + +## 响应格式 + +当启用包装时,响应将采用以下格式: + +```json +{ + "code": "0", // 响应代码,默认"0"表示成功 + "message": "Success", // 响应消息 + "details": null, // 附加详情(可选) + "result": { // 实际响应数据 + // ... 响应内容 + } +} +``` + +## 错误处理 + +模块自动处理错误响应: +* 对于包装的响应(带有`AbpWrapResult`头): + * 解包响应并检查代码 + * 如果代码与`CodeWithSuccess`不匹配,抛出`AbpRemoteCallException` + * 在异常中包含错误消息、详情和代码 + * 支持自定义错误代码映射 +* 对于未包装的响应: + * 传递原始响应 + * 使用标准HTTP错误处理 + * 保持原始错误信息 + +### 错误响应示例 + +```json +{ + "code": "ERROR_001", + "message": "产品未找到", + "details": "ID为'123'的产品不存在", + "result": null +} +``` + +## 高级用法 + +### 1. 控制响应包装 + +可以通过HTTP头控制单个请求的响应包装: + +```csharp +// 在请求头中添加 +var headers = new Dictionary +{ + { "X-Abp-Wrap-Result", "true" }, // 强制启用包装 + // 或 + { "X-Abp-Dont-Wrap-Result", "true" } // 强制禁用包装 +}; + +await _daprClient.InvokeMethodAsync( + "product-service", + "api/products", + HttpMethod.Get, + null, + headers +); +``` + +### 2. 自定义错误处理 + +```csharp +public class CustomErrorHandler : IAbpWrapperErrorHandler +{ + public Task HandleAsync(AbpWrapperErrorContext context) + { + // 自定义错误处理逻辑 + if (context.Response.Code == "CUSTOM_ERROR") + { + // 特殊处理 + } + return Task.CompletedTask; + } +} +``` + +## 注意事项 + +* 响应包装可以通过以下方式控制: + * 配置文件中的全局设置 + * HTTP头控制单个请求 + * 代码中的动态控制 +* 错误响应尽可能保持原始错误结构 +* 模块与ABP的远程服务错误处理系统集成 +* 建议在微服务架构中统一使用响应包装 +* 包装格式可以通过继承`IAbpWrapperResponseBuilder`自定义 + +[查看英文](README.EN.md) diff --git a/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr.Client/README.EN.md b/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr.Client/README.EN.md new file mode 100644 index 000000000..5b0e27f1c --- /dev/null +++ b/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr.Client/README.EN.md @@ -0,0 +1,277 @@ +[Actors](../README.md) | Dapr.Client Documentation + +# LINGYUN.Abp.Dapr.Client + +Implements service-to-service invocation as described in the Dapr documentation. The project design is consistent with Volo.Abp.Http.Client and can seamlessly replace Volo.Abp.Http.Client through configuration. + +For configuration reference, see [AbpRemoteServiceOptions](https://docs.abp.io/en/abp/latest/API/Dynamic-CSharp-API-Clients#abpremoteserviceoptions) + +## Features + +* Integration with ABP remote service system +* Dynamic proxy generation +* Service discovery and load balancing +* Custom request and response handling +* Error handling and formatting +* Multiple service endpoint configuration +* Request/response interceptors +* Custom DaprClient behavior support + +## Configuration Options + +```json +{ + "RemoteServices": { + "Default": { + "AppId": "default-app", // Dapr application ID + "BaseUrl": "http://localhost:3500", // Dapr HTTP endpoint + "HealthCheckUrl": "/health", // Health check endpoint + "RequestTimeout": 30000, // Request timeout in milliseconds + "RetryCount": 3, // Number of retry attempts + "RetryWaitTime": 1000 // Retry wait time in milliseconds + }, + "System": { + "AppId": "system-app", + "BaseUrl": "http://localhost:50000", + "Headers": { // Custom request headers + "Tenant": "Default", + "Culture": "en-US" + } + } + } +} +``` + +## Basic Usage + +Module reference as needed: + +```csharp +[DependsOn(typeof(AbpDaprClientModule))] +public class YourProjectModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + // Register proxies similar to Volo.Abp.Http.Client module + context.Services.AddDaprClientProxies( + typeof(YourProjectInterfaceModule).Assembly, // Search for remote service definitions + RemoteServiceName + ); + + // Configure proxy options + Configure(options => + { + // Configure request interceptor + options.ProxyRequestActions.Add((appId, request) => + { + request.Headers.Add("Custom-Header", "Value"); + }); + + // Configure response handling + options.OnResponse(async (response, serviceProvider) => + { + return await response.Content.ReadAsStringAsync(); + }); + + // Configure error handling + options.OnError(async (response, serviceProvider) => + { + var error = await response.Content.ReadAsStringAsync(); + return new RemoteServiceErrorInfo + { + Code = response.StatusCode.ToString(), + Message = error + }; + }); + }); + } +} +``` + +## Implementation Example + +### 1. Interface Definition + +```csharp +// IApplicationService implements IRemoteService +public interface ISystemAppService : IApplicationService +{ + Task GetAsync(); + Task CreateAsync(CreateSystemDto input); + Task> GetListAsync(); + Task DeleteAsync(string id); +} + +public class SystemInterfaceModule : AbpModule +{ +} +``` + +### 2. Server Implementation + +```csharp +[DependsOn( + typeof(SystemInterfaceModule), + typeof(AbpAspNetCoreMvcModule) +)] +public class SystemServerModule : AbpModule +{ + public override void PreConfigureServices(ServiceConfigurationContext context) + { + PreConfigure(mvcBuilder => + { + mvcBuilder.AddApplicationPartIfNotExists(typeof(SystemServerModule).Assembly); + }); + } +} + +public class SystemAppService : ApplicationService, ISystemAppService +{ + private readonly ISystemRepository _systemRepository; + + public SystemAppService(ISystemRepository systemRepository) + { + _systemRepository = systemRepository; + } + + public async Task GetAsync() + { + return "System"; + } + + public async Task CreateAsync(CreateSystemDto input) + { + var system = await _systemRepository.CreateAsync( + new System + { + Name = input.Name, + Description = input.Description + } + ); + return ObjectMapper.Map(system); + } + + public async Task> GetListAsync() + { + var systems = await _systemRepository.GetListAsync(); + return ObjectMapper.Map, List>(systems); + } + + public async Task DeleteAsync(string id) + { + await _systemRepository.DeleteAsync(id); + } +} +``` + +### 3. Client Usage + +```csharp +[DependsOn(typeof(AbpDaprClientModule))] +public class SystemClientModule : AbpModule +{ + private const string RemoteServiceName = "System"; + + public override void ConfigureServices(ServiceConfigurationContext context) + { + // Register proxies + context.Services.AddDaprClientProxies( + typeof(SystemInterfaceModule).Assembly, + RemoteServiceName + ); + + // Configure retry policy + context.Services.AddDaprClientBuilder(builder => + { + builder.ConfigureHttpClient((sp, client) => + { + client.Timeout = TimeSpan.FromSeconds(30); + }); + }); + } +} + +public class SystemService +{ + private readonly ISystemAppService _systemAppService; + + public SystemService(ISystemAppService systemAppService) + { + _systemAppService = systemAppService; + } + + public async Task> GetSystemsAsync() + { + try + { + return await _systemAppService.GetListAsync(); + } + catch (AbpRemoteCallException ex) + { + // Handle remote call exception + _logger.LogError(ex, "Failed to get systems"); + throw; + } + } +} +``` + +## Advanced Usage + +### 1. Custom Request Handling + +```csharp +public class CustomRequestHandler +{ + public void Configure(HttpRequestMessage request) + { + request.Headers.Add("Correlation-Id", Guid.NewGuid().ToString()); + request.Headers.Add("Client-Version", "1.0.0"); + } +} + +// Register in module +Configure(options => +{ + options.ProxyRequestActions.Add((appId, request) => + { + new CustomRequestHandler().Configure(request); + }); +}); +``` + +### 2. Custom Response Handling + +```csharp +public class CustomResponseHandler +{ + public async Task HandleAsync(HttpResponseMessage response) + { + var content = await response.Content.ReadAsStringAsync(); + // Custom response handling logic + return content; + } +} + +// Register in module +Configure(options => +{ + options.OnResponse(async (response, sp) => + { + return await new CustomResponseHandler().HandleAsync(response); + }); +}); +``` + +## Important Notes + +* Remote service interfaces must inherit `IRemoteService` +* Configuration changes require recreating proxy instances +* Configure appropriate timeout and retry policies +* Error handling should consider network exceptions and service unavailability +* Enable service discovery in production environments +* Use health checks to ensure service availability +* Request header configuration should consider security and authentication requirements +* Logging is important for problem diagnosis + +[查看中文](README.md) diff --git a/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr.Client/README.md b/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr.Client/README.md index b82e7471c..345d7f04f 100644 --- a/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr.Client/README.md +++ b/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr.Client/README.md @@ -2,72 +2,118 @@ # LINGYUN.Abp.Dapr.Client -实现了Dapr文档中的服务间调用,项目设计与Volo.Abp.Http.Client一致,通过配置文件即可无缝替代Volo.Abp.Http.Client +实现了Dapr文档中的服务间调用,项目设计与Volo.Abp.Http.Client一致,通过配置文件即可无缝替代Volo.Abp.Http.Client。 -配置参考 [AbpRemoteServiceOptions](https://docs.abp.io/zh-Hans/abp/latest/API/Dynamic-CSharp-API-Clients#abpremoteserviceoptions) +配置参考 [AbpRemoteServiceOptions](https://docs.abp.io/zh-Hans/abp/latest/API/Dynamic-CSharp-API-Clients#abpremoteserviceoptions) + +## 功能特性 + +* 与ABP远程服务系统集成 +* 支持动态代理生成 +* 支持服务发现和负载均衡 +* 支持自定义请求和响应处理 +* 支持错误处理和格式化 +* 支持多服务端点配置 +* 支持请求/响应拦截器 +* 支持自定义DaprClient行为 + +## 配置选项 + +```json +{ + "RemoteServices": { + "Default": { + "AppId": "default-app", // Dapr应用ID + "BaseUrl": "http://localhost:3500", // Dapr HTTP端点 + "HealthCheckUrl": "/health", // 健康检查端点 + "RequestTimeout": 30000, // 请求超时时间(毫秒) + "RetryCount": 3, // 重试次数 + "RetryWaitTime": 1000 // 重试等待时间(毫秒) + }, + "System": { + "AppId": "system-app", + "BaseUrl": "http://localhost:50000", + "Headers": { // 自定义请求头 + "Tenant": "Default", + "Culture": "zh-Hans" + } + } + } +} +``` ## 配置使用 -模块按需引用 +模块按需引用: ```csharp [DependsOn(typeof(AbpDaprClientModule))] public class YouProjectModule : AbpModule { - public override void ConfigureServices(ServiceConfigurationContext context) + public override void ConfigureServices(ServiceConfigurationContext context) { - // 注册代理类似于 Volo.Abp.Http.Client 模块 + // 注册代理,类似于 Volo.Abp.Http.Client 模块 context.Services.AddDaprClientProxies( - typeof(YouProjectInterfaceModule).Assembly, // 搜索 YouProjectInterfaceModule 模块下的远程服务定义 + typeof(YouProjectInterfaceModule).Assembly, // 搜索模块下的远程服务定义 RemoteServiceName ); + + // 配置代理选项 + Configure(options => + { + // 配置请求拦截器 + options.ProxyRequestActions.Add((appId, request) => + { + request.Headers.Add("Custom-Header", "Value"); + }); + + // 配置响应处理 + options.OnResponse(async (response, serviceProvider) => + { + return await response.Content.ReadAsStringAsync(); + }); + + // 配置错误处理 + options.OnError(async (response, serviceProvider) => + { + var error = await response.Content.ReadAsStringAsync(); + return new RemoteServiceErrorInfo + { + Code = response.StatusCode.ToString(), + Message = error + }; + }); + }); } } ``` +## 实现示例 -### 1、接口定义 - -```c# +### 1. 接口定义 +```csharp // IApplicationService 实现了 IRemoteService public interface ISystemAppService : IApplicationService { Task GetAsync(); + Task CreateAsync(CreateSystemDto input); + Task> GetListAsync(); + Task DeleteAsync(string id); } public class SystemInterfaceModule : AbpModule { - -} - -``` - -### 2、服务端 - -引用 Volo.Abp.AspNetCore.Mvc - -* 实现接口 - -```c# - -public class SystemAppService : ApplicationService, ISystemAppService -{ - public Task GetAsync() - { - retuen Task.FromResult("System"); - } } - ``` -* 创建模块 - -```c# +### 2. 服务端实现 +```csharp [DependsOn( - typeof(SystemInterfaceModule), - typeof(AbpAspNetCoreMvcModule))] + typeof(SystemInterfaceModule), + typeof(AbpAspNetCoreMvcModule) +)] public class SystemServerModule : AbpModule { public override void PreConfigureServices(ServiceConfigurationContext context) @@ -79,93 +125,153 @@ public class SystemServerModule : AbpModule } } -``` - -* 发布到Dapr - -```shell -# --app-port .net程序映射端口 -# -H 对外暴露 http 监听端口 -# -G 对外暴露 grpc 监听端口 -dapr run --app-id myapp --app-port 5000 -H 50000 -G 40001 -- dotnet run - -``` +public class SystemAppService : ApplicationService, ISystemAppService +{ + private readonly ISystemRepository _systemRepository; -### 3、客户端 + public SystemAppService(ISystemRepository systemRepository) + { + _systemRepository = systemRepository; + } -引用 LINGYUN.Abp.Dapr.Client + public async Task GetAsync() + { + return "System"; + } -* 配置文件 **appsettings.json** + public async Task CreateAsync(CreateSystemDto input) + { + var system = await _systemRepository.CreateAsync( + new System + { + Name = input.Name, + Description = input.Description + } + ); + return ObjectMapper.Map(system); + } -```json + public async Task> GetListAsync() + { + var systems = await _systemRepository.GetListAsync(); + return ObjectMapper.Map, List>(systems); + } -{ - "RemoteServices": { - "System": { - "AppId": "myapp", - "BaseUrl": "http://127.0.0.1:50000" - } + public async Task DeleteAsync(string id) + { + await _systemRepository.DeleteAsync(id); } } - ``` -* 客户端代码 - -```c# +### 3. 客户端使用 -// 模块依赖 -[DependsOn( - typeof(AbpDaprClientModule))] -public class SystemActorClientModule : AbpModule +```csharp +[DependsOn(typeof(AbpDaprClientModule))] +public class SystemClientModule : AbpModule { private const string RemoteServiceName = "System"; public override void ConfigureServices(ServiceConfigurationContext context) { - // 注册代理类似于 Volo.Abp.Http.Client 模块 + // 注册代理 context.Services.AddDaprClientProxies( - typeof(SystemInterfaceModule).Assembly, // 搜索 SystemInterfaceModule 模块下的IRemoteService定义创建代理 + typeof(SystemInterfaceModule).Assembly, RemoteServiceName ); + + // 配置重试策略 + context.Services.AddDaprClientBuilder(builder => + { + builder.ConfigureHttpClient((sp, client) => + { + client.Timeout = TimeSpan.FromSeconds(30); + }); + }); } } -// 调用方法,直接依赖注入即可 -public class InvokeClass +public class SystemService { private readonly ISystemAppService _systemAppService; - public InvokeClass(ISystemAppService systemAppService) + public SystemService(ISystemAppService systemAppService) { - _systemAppService = systemAppService; + _systemAppService = systemAppService; } - public async Task InvokeAsync() + public async Task> GetSystemsAsync() { - await _systemAppService.GetAsync(); + try + { + return await _systemAppService.GetListAsync(); + } + catch (AbpRemoteCallException ex) + { + // 处理远程调用异常 + _logger.LogError(ex, "Failed to get systems"); + throw; + } } } - ``` +## 高级用法 -## 配置项说明 - -* AbpRemoteServiceOptions.RemoteServices 配置Dapr.AppId +### 1. 自定义请求处理 -```json +```csharp +public class CustomRequestHandler +{ + public void Configure(HttpRequestMessage request) + { + request.Headers.Add("Correlation-Id", Guid.NewGuid().ToString()); + request.Headers.Add("Client-Version", "1.0.0"); + } +} +// 在模块中注册 +Configure(options => { - "RemoteServices": { - "System": { - "AppId": "myapp", - "BaserUrl": "http://127.0.0.1:50000" - } + options.ProxyRequestActions.Add((appId, request) => + { + new CustomRequestHandler().Configure(request); + }); +}); +``` + +### 2. 自定义响应处理 + +```csharp +public class CustomResponseHandler +{ + public async Task HandleAsync(HttpResponseMessage response) + { + var content = await response.Content.ReadAsStringAsync(); + // 自定义响应处理逻辑 + return content; } } +// 在模块中注册 +Configure(options => +{ + options.OnResponse(async (response, sp) => + { + return await new CustomResponseHandler().HandleAsync(response); + }); +}); ``` +## 注意事项 + +* 远程服务接口必须继承`IRemoteService` +* 配置更改需要重新创建代理实例才能生效 +* 建议配置适当的超时和重试策略 +* 错误处理应该考虑网络异常和服务不可用的情况 +* 在生产环境中应该启用服务发现 +* 建议使用健康检查确保服务可用性 +* 请求头配置应考虑安全性和身份验证需求 +* 日志记录对于问题诊断很重要 -## 其他 +[查看英文](README.EN.md) diff --git a/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr/README.EN.md b/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr/README.EN.md new file mode 100644 index 000000000..4de14dc6a --- /dev/null +++ b/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr/README.EN.md @@ -0,0 +1,204 @@ +# LINGYUN.Abp.Dapr + +Dapr integration base module, implementing the named singleton DaprClient as described in the Dapr documentation. + +See: https://docs.dapr.io/developing-applications/sdks/dotnet/dotnet-client/dotnet-daprclient-usage + +## Features + +* Support for creating default and named DaprClient instances +* Support for configuring HTTP and gRPC endpoints +* Support for custom JSON serialization options +* Support for Dapr API Token authentication +* Support for gRPC channel configuration +* Support for DaprClient instance configuration and builder configuration extensions +* Support for multiple Dapr Sidecar connections +* Support for custom DaprClient behaviors + +## Configuration Options + +```json +{ + "Dapr": { + "Client": { + "DaprApiToken": "your-api-token", // Optional, Dapr API Token + "HttpEndpoint": "http://localhost:3500", // Optional, HTTP endpoint + "GrpcEndpoint": "http://localhost:50001", // Optional, gRPC endpoint + "JsonSerializerOptions": { // Optional, JSON serialization options + "PropertyNamingPolicy": "CamelCase", + "PropertyNameCaseInsensitive": true, + "WriteIndented": true, + "DefaultIgnoreCondition": "WhenWritingNull" + }, + "GrpcChannelOptions": { // Optional, gRPC channel options + "Credentials": "Insecure", + "MaxReceiveMessageSize": 1048576, + "MaxSendMessageSize": 1048576 + } + } + } +} +``` + +## Basic Usage + +Module reference as needed: + +```csharp +[DependsOn(typeof(AbpDaprModule))] +public class YouProjectModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + // Create a DaprClient + context.Services.AddDaprClient(); + + // Create a named DaprClient + context.Services.AddDaprClient("__DaprClient"); + + // Configure DaprClient options + Configure(options => + { + options.HttpEndpoint = "http://localhost:3500"; + options.GrpcEndpoint = "http://localhost:50001"; + options.DaprApiToken = "your-api-token"; + + // Add DaprClient configuration actions + options.DaprClientActions.Add(client => + { + // Configure DaprClient instance + }); + + // Add DaprClientBuilder configuration actions + options.DaprClientBuilderActions.Add(builder => + { + // Configure DaprClientBuilder + }); + }); + } +} +``` + +## Advanced Usage + +### 1. Configure DaprClient + +```csharp +public override void ConfigureServices(ServiceConfigurationContext context) +{ + // Configure named DaprClient + context.Services.AddDaprClient("CustomClient", builder => + { + // Configure HTTP endpoint + builder.UseHttpEndpoint("http://localhost:3500"); + + // Configure gRPC endpoint + builder.UseGrpcEndpoint("http://localhost:50001"); + + // Configure API Token + builder.UseDaprApiToken("your-api-token"); + + // Configure JSON serialization options + builder.UseJsonSerializerOptions(new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + PropertyNameCaseInsensitive = true + }); + + // Configure gRPC channel options + builder.UseGrpcChannelOptions(new GrpcChannelOptions + { + MaxReceiveMessageSize = 1024 * 1024, + MaxSendMessageSize = 1024 * 1024 + }); + }); +} +``` + +### 2. Using DaprClient + +```csharp +public class YourService +{ + private readonly IDaprClientFactory _daprClientFactory; + + public YourService(IDaprClientFactory daprClientFactory) + { + _daprClientFactory = daprClientFactory; + } + + public async Task InvokeMethodAsync() + { + // Use default client + var defaultClient = _daprClientFactory.CreateClient(); + + // Use named client + var namedClient = _daprClientFactory.CreateClient("CustomClient"); + + // Invoke service method + var response = await defaultClient.InvokeMethodAsync( + HttpMethod.Get, + "order-service", // Target service ID + "api/orders/1", // Method path + new { id = 1 } // Request parameters + ); + + // Publish event + await defaultClient.PublishEventAsync( + "pubsub", // Pub/sub component name + "order-created", // Topic name + response // Event data + ); + + // Save state + await defaultClient.SaveStateAsync( + "statestore", // State store component name + "order-1", // Key + response // Value + ); + + // Get state + var state = await defaultClient.GetStateAsync( + "statestore", // State store component name + "order-1" // Key + ); + } +} +``` + +### 3. Custom DaprClient Behavior + +```csharp +public class CustomDaprClientBehavior +{ + public void Configure(DaprClient client) + { + // Configure custom behavior + } +} + +// Register in module +public override void ConfigureServices(ServiceConfigurationContext context) +{ + Configure(options => + { + options.DaprClientActions.Add(client => + { + new CustomDaprClientBehavior().Configure(client); + }); + }); +} +``` + +## Important Notes + +* DaprClient instances are thread-safe, singleton pattern is recommended +* Named DaprClients can have different configurations, suitable for scenarios requiring connections to different Dapr Sidecars +* Configuration changes require recreating the DaprClient instance to take effect +* Pay attention to performance and resource consumption when configuring gRPC channels +* JSON serialization options affect all requests using that DaprClient +* API Tokens should be managed through secure configuration management systems +* Recommended to use different named DaprClients for different microservices +* Configure appropriate timeout and retry policies in production environments + +[查看中文](README.md) diff --git a/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr/README.md b/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr/README.md index d7bb22855..05256bf96 100644 --- a/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr/README.md +++ b/aspnet-core/framework/dapr/LINGYUN.Abp.Dapr/README.md @@ -4,23 +4,201 @@ Dapr 集成基础模块, 实现dapr文档中的命名单例DaprClient See: https://docs.dapr.io/developing-applications/sdks/dotnet/dotnet-client/dotnet-daprclient-usage +## 功能特性 + +* 支持创建默认和具名DaprClient实例 +* 支持配置HTTP和gRPC端点 +* 支持自定义JSON序列化选项 +* 支持Dapr API Token认证 +* 支持gRPC通道配置 +* 支持DaprClient实例配置和构建配置的扩展 +* 支持多个Dapr Sidecar连接 +* 支持自定义DaprClient行为 + +## 配置选项 + +```json +{ + "Dapr": { + "Client": { + "DaprApiToken": "your-api-token", // 可选,Dapr API Token + "HttpEndpoint": "http://localhost:3500", // 可选,HTTP端点 + "GrpcEndpoint": "http://localhost:50001", // 可选,gRPC端点 + "JsonSerializerOptions": { // 可选,JSON序列化选项 + "PropertyNamingPolicy": "CamelCase", + "PropertyNameCaseInsensitive": true, + "WriteIndented": true, + "DefaultIgnoreCondition": "WhenWritingNull" + }, + "GrpcChannelOptions": { // 可选,gRPC通道选项 + "Credentials": "Insecure", + "MaxReceiveMessageSize": 1048576, + "MaxSendMessageSize": 1048576 + } + } + } +} +``` + ## 配置使用 -模块按需引用 +模块按需引用: ```csharp [DependsOn(typeof(AbpDaprModule))] public class YouProjectModule : AbpModule { - public override void ConfigureServices(ServiceConfigurationContext context) + public override void ConfigureServices(ServiceConfigurationContext context) { // 创建一个DaprClient context.Services.AddDaprClient(); // 创建一个具名DaprClient context.Services.AddDaprClient("__DaprClient"); + + // 配置DaprClient选项 + Configure(options => + { + options.HttpEndpoint = "http://localhost:3500"; + options.GrpcEndpoint = "http://localhost:50001"; + options.DaprApiToken = "your-api-token"; + + // 添加DaprClient配置动作 + options.DaprClientActions.Add(client => + { + // 配置DaprClient实例 + }); + + // 添加DaprClientBuilder配置动作 + options.DaprClientBuilderActions.Add(builder => + { + // 配置DaprClientBuilder + }); + }); + } +} +``` + +## 高级用法 + +### 1. 配置DaprClient + +```csharp +public override void ConfigureServices(ServiceConfigurationContext context) +{ + // 配置具名DaprClient + context.Services.AddDaprClient("CustomClient", builder => + { + // 配置HTTP端点 + builder.UseHttpEndpoint("http://localhost:3500"); + + // 配置gRPC端点 + builder.UseGrpcEndpoint("http://localhost:50001"); + + // 配置API Token + builder.UseDaprApiToken("your-api-token"); + + // 配置JSON序列化选项 + builder.UseJsonSerializerOptions(new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + PropertyNameCaseInsensitive = true + }); + + // 配置gRPC通道选项 + builder.UseGrpcChannelOptions(new GrpcChannelOptions + { + MaxReceiveMessageSize = 1024 * 1024, + MaxSendMessageSize = 1024 * 1024 + }); + }); +} +``` + +### 2. 使用DaprClient + +```csharp +public class YourService +{ + private readonly IDaprClientFactory _daprClientFactory; + + public YourService(IDaprClientFactory daprClientFactory) + { + _daprClientFactory = daprClientFactory; + } + + public async Task InvokeMethodAsync() + { + // 使用默认客户端 + var defaultClient = _daprClientFactory.CreateClient(); + + // 使用具名客户端 + var namedClient = _daprClientFactory.CreateClient("CustomClient"); + + // 调用服务方法 + var response = await defaultClient.InvokeMethodAsync( + HttpMethod.Get, + "order-service", // 目标服务ID + "api/orders/1", // 方法路径 + new { id = 1 } // 请求参数 + ); + + // 发布事件 + await defaultClient.PublishEventAsync( + "pubsub", // Pub/sub组件名称 + "order-created", // 主题名称 + response // 事件数据 + ); + + // 保存状态 + await defaultClient.SaveStateAsync( + "statestore", // 状态存储组件名称 + "order-1", // 键 + response // 值 + ); + + // 获取状态 + var state = await defaultClient.GetStateAsync( + "statestore", // 状态存储组件名称 + "order-1" // 键 + ); } } ``` -## 其他 +### 3. 自定义DaprClient行为 + +```csharp +public class CustomDaprClientBehavior +{ + public void Configure(DaprClient client) + { + // 配置自定义行为 + } +} + +// 在模块中注册 +public override void ConfigureServices(ServiceConfigurationContext context) +{ + Configure(options => + { + options.DaprClientActions.Add(client => + { + new CustomDaprClientBehavior().Configure(client); + }); + }); +} +``` + +## 注意事项 + +* DaprClient实例是线程安全的,建议使用单例模式 +* 具名DaprClient可以有不同的配置,适用于需要连接不同Dapr Sidecar的场景 +* 配置更改后需要重新创建DaprClient实例才能生效 +* gRPC通道配置需要注意性能和资源消耗 +* JSON序列化选项会影响所有使用该DaprClient的请求 +* API Token应该通过安全的配置管理系统管理 +* 建议为不同的微服务使用不同的具名DaprClient +* 在生产环境中应该适当配置超时和重试策略 + +[查看英文](README.EN.md) diff --git a/aspnet-core/framework/dapr/LINGYUN.Abp.DistributedLocking.Dapr/README.EN.md b/aspnet-core/framework/dapr/LINGYUN.Abp.DistributedLocking.Dapr/README.EN.md new file mode 100644 index 000000000..c16e23a09 --- /dev/null +++ b/aspnet-core/framework/dapr/LINGYUN.Abp.DistributedLocking.Dapr/README.EN.md @@ -0,0 +1,256 @@ +# LINGYUN.Abp.DistributedLocking.Dapr + +An ABP distributed locking implementation based on the Dapr distributed lock API. This module provides seamless integration with Dapr's distributed locking service, supporting cross-service and cross-instance locking capabilities. + +Reference: [Dapr Distributed Lock API](https://docs.dapr.io/developing-applications/building-blocks/distributed-lock/distributed-lock-api-overview/) + +## Features + +* Integration with ABP distributed locking system +* Support for custom lock resource owner identification +* Configurable lock timeout duration +* Automatic lock release support +* Multiple lock storage component support +* Lock acquisition and release event notifications +* Distributed lock health check support + +## Configuration Options + +```json +{ + "DistributedLocking": { + "Dapr": { + "StoreName": "lockstore", // Storage name defined in Dapr component + "DefaultIdentifier": "dapr-lock-owner", // Default lock resource owner identifier + "DefaultTimeout": "00:00:30" // Default lock timeout (30 seconds) + } + } +} +``` + +## Basic Usage + +### 1. Module Configuration + +```csharp +[DependsOn(typeof(AbpDistributedLockingDaprModule))] +public class YourProjectModule : AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + // Basic configuration + Configure(options => + { + options.StoreName = "redis-lock"; // Use Redis as lock storage + options.DefaultIdentifier = "my-service"; // Custom lock owner identifier + options.DefaultTimeout = TimeSpan.FromMinutes(1); // Set default timeout to 1 minute + }); + } +} +``` + +### 2. Basic Usage + +```csharp +public class OrderService +{ + private readonly IDistributedLockProvider _lockProvider; + + public OrderService(IDistributedLockProvider lockProvider) + { + _lockProvider = lockProvider; + } + + public async Task ProcessOrderAsync(string orderId) + { + // Try to acquire lock + using (var handle = await _lockProvider.TryAcquireAsync($"order:{orderId}")) + { + if (handle != null) + { + try + { + // Execute business logic that requires locking + await ProcessOrderInternalAsync(orderId); + } + catch (Exception ex) + { + // Handle exception + _logger.LogError(ex, "Error occurred while processing order"); + throw; + } + } + else + { + throw new ConcurrencyException("Order is being processed by another process"); + } + } + } +} +``` + +### 3. Advanced Usage + +```csharp +public class InventoryService +{ + private readonly IDistributedLockProvider _lockProvider; + private readonly ILogger _logger; + + public InventoryService( + IDistributedLockProvider lockProvider, + ILogger logger) + { + _lockProvider = lockProvider; + _logger = logger; + } + + public async Task UpdateInventoryAsync(string productId, int quantity) + { + // Custom lock configuration + var lockOptions = new DistributedLockOptions + { + Timeout = TimeSpan.FromSeconds(10), // Custom timeout + RetryDelay = TimeSpan.FromMilliseconds(100) // Retry delay + }; + + try + { + using (var handle = await _lockProvider.TryAcquireAsync( + $"inventory:{productId}", + lockOptions)) + { + if (handle == null) + { + _logger.LogWarning("Unable to acquire inventory lock for product ID: {ProductId}", productId); + throw new ConcurrencyException("Unable to acquire inventory lock"); + } + + // Execute inventory update operation + await UpdateInventoryInternalAsync(productId, quantity); + } + } + catch (Exception ex) when (ex is not ConcurrencyException) + { + _logger.LogError(ex, "Error occurred while updating inventory"); + throw; + } + } +} +``` + +## Component Configuration + +### Redis Lock Store Configuration Example + +```yaml +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: redis-lock # Corresponds to StoreName configuration +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 Lock Store Configuration Example + +```yaml +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 +``` + +## Core Interfaces + +### ILockOwnerFinder + +Interface for providing lock resource owner identification. + +```csharp +public interface ILockOwnerFinder +{ + string GetOwner(); +} +``` + +The default implementation `LockOwnerFinder`: +1. Primarily uses the current user ID as the lock owner identifier +2. Falls back to the configured `DefaultIdentifier` if no user is logged in + +### Custom Lock Owner Identifier Implementation + +```csharp +public class CustomLockOwnerFinder : ILockOwnerFinder +{ + private readonly ICurrentTenant _currentTenant; + + public CustomLockOwnerFinder(ICurrentTenant currentTenant) + { + _currentTenant = currentTenant; + } + + public string GetOwner() + { + // Use combination of tenant ID and machine name as lock owner identifier + return $"{_currentTenant.Id ?? "host"}-{Environment.MachineName}"; + } +} + +// Register custom implementation +context.Services.AddTransient(); +``` + +## Best Practices + +1. **Set Appropriate Timeout Duration** + - Set timeout based on expected execution time of business operations + - Avoid setting excessively long timeouts to prevent deadlocks + +2. **Proper Lock Granularity** + - Keep lock scope as small as possible, only lock necessary resources + - Avoid holding locks for extended periods, release promptly + +3. **Exception Handling** + - Always use locks within using blocks + - Handle lock acquisition failures appropriately + - Log critical lock operations + +4. **Performance Optimization** + - Use appropriate storage components + - Configure suitable retry policies + - Monitor lock usage + +## Important Notes + +* Ensure Dapr Sidecar is properly configured and running +* Distributed lock component must be correctly defined in Dapr configuration +* Set appropriate lock timeouts to avoid deadlocks +* Handle lock acquisition failures properly +* Consider performance impact in high-concurrency scenarios +* Configure health checks for lock components +* Add logging for important operations + +[查看中文](README.md) diff --git a/aspnet-core/framework/dapr/LINGYUN.Abp.DistributedLocking.Dapr/README.md b/aspnet-core/framework/dapr/LINGYUN.Abp.DistributedLocking.Dapr/README.md index 587e065ea..a5be5fad3 100644 --- a/aspnet-core/framework/dapr/LINGYUN.Abp.DistributedLocking.Dapr/README.md +++ b/aspnet-core/framework/dapr/LINGYUN.Abp.DistributedLocking.Dapr/README.md @@ -1,38 +1,256 @@ # LINGYUN.Abp.DistributedLocking.Dapr -Abp分布式锁的Dapr实现 +基于Dapr分布式锁API的ABP分布式锁实现。该模块提供了与Dapr分布式锁服务的无缝集成,支持跨服务、跨实例的分布式锁定功能。 -See: https://docs.dapr.io/developing-applications/building-blocks/distributed-lock/distributed-lock-api-overview/ +参考文档: [Dapr Distributed Lock API](https://docs.dapr.io/developing-applications/building-blocks/distributed-lock/distributed-lock-api-overview/) -## 配置使用 +## 功能特性 -模块按需引用 +* 与ABP分布式锁系统集成 +* 支持自定义锁资源拥有者标识 +* 支持可配置的锁定超时时间 +* 支持锁资源自动释放 +* 支持多种锁存储组件 +* 支持锁获取和释放的事件通知 +* 支持分布式锁的健康检查 + +## 配置选项 + +```json +{ + "DistributedLocking": { + "Dapr": { + "StoreName": "lockstore", // Dapr组件中定义的存储名称 + "DefaultIdentifier": "dapr-lock-owner", // 默认锁资源拥有者标识 + "DefaultTimeout": "00:00:30" // 默认锁定超时时间(30秒) + } + } +} +``` + +## 基础使用 + +### 1. 模块配置 ```csharp [DependsOn(typeof(AbpDistributedLockingDaprModule))] -public class YouProjectModule : AbpModule +public class YourProjectModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { + // 基础配置 Configure(options => { - options.StoreName = "store-name"; - options.DefaultIdentifier = "default-owner-id"; - options.DefaultTimeout = TimeSpan.FromSeconds(30); + options.StoreName = "redis-lock"; // 使用Redis作为锁存储 + options.DefaultIdentifier = "my-service"; // 自定义锁拥有者标识 + options.DefaultTimeout = TimeSpan.FromMinutes(1); // 设置默认超时时间为1分钟 }); } } ``` -## 配置说明 +### 2. 基本用法 + +```csharp +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. 高级用法 + +```csharp +public class InventoryService +{ + private readonly IDistributedLockProvider _lockProvider; + private readonly ILogger _logger; + + public InventoryService( + IDistributedLockProvider lockProvider, + ILogger 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锁存储配置示例 + +```yaml +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锁存储配置示例 + +```yaml +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 + +提供锁资源持有者标识的接口。 + +```csharp +public interface ILockOwnerFinder +{ + string GetOwner(); +} +``` + +默认实现 `LockOwnerFinder` 会: +1. 优先使用当前用户ID作为锁拥有者标识 +2. 如果用户未登录,则使用配置的 `DefaultIdentifier` + +### 自定义锁拥有者标识实现 + +```csharp +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(); +``` + +## 最佳实践 + +1. **合理设置超时时间** + - 根据业务操作的预期执行时间设置合适的超时时间 + - 避免设置过长的超时时间,以防止死锁 + +2. **正确的锁粒度** + - 锁的范围应该尽可能小,只锁定必要的资源 + - 避免长时间持有锁,及时释放 + +3. **异常处理** + - 始终在 using 块中使用锁 + - 妥善处理锁获取失败的情况 + - 记录关键的锁操作日志 -* AbpDistributedLockingDaprOptions.StoreName 在dapr component文件中定义的metadata name,默认: lockstore; -* AbpDistributedLockingDaprOptions.DefaultIdentifier 默认锁资源拥有者标识,默认: dapr-lock-owner; -* AbpDistributedLockingDaprOptions.DefaultTimeout 默认锁定超时时间,默认: 30s. +4. **性能优化** + - 使用合适的存储组件 + - 配置适当的重试策略 + - 监控锁的使用情况 -## 接口说明 +## 注意事项 -[ILockOwnerFinder](./LINGYUN/Abp/DistributedLocking/Dapr/ILockOwnerFinder), 提供锁资源持有者标识 -默认实现 [LockOwnerFinder](./LINGYUN/Abp/DistributedLocking/Dapr/LockOwnerFinder), 获取用户标识,如果不存在,返回DefaultIdentifier +* 确保Dapr Sidecar已正确配置并运行 +* 分布式锁组件需要在Dapr配置中正确定义 +* 合理设置锁的超时时间,避免死锁 +* 正确处理锁获取失败的情况 +* 在高并发场景下注意性能影响 +* 建议配置锁组件的健康检查 +* 重要操作建议添加日志记录 -## 其他 +[查看英文](README.EN.md)