【踩坑】action重复执行和gRPC的重试策略
问题1:测试反馈,有个邮件发送接口在点击后,会发两封响应邮件(.NET6 MVC async action)。
/// <summary>
/// 控制器耗时统计
/// </summary>
public class LogActionFilter : ActionFilterAttribute
{
private readonly ILogger<LogActionFilter> _logger;
private readonly Common _common;
public LogActionFilter(ILogger<LogActionFilter> logger, Common common)
{
_logger = logger;
_common = common;
}
public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var id = Activity.Current?.Id ?? context.HttpContext.TraceIdentifier;
var dt = DateTime.Now;
if (id == null)
{
_logger.LogInformation("id 为空,不继续后面逻辑!");
}
else
{
_logger.LogInformation($"id:{id}!");
}
await next();
if (id == null)
{
_logger.LogInformation("id 为空,不继续后面逻辑!");
}
else
{
if (context.HttpContext.Response.StatusCode == HttpStatusCode.Redirect.GetHashCode())
{
_logger.LogInformation("302,不继续后面逻辑!");
_ = base.OnActionExecutionAsync(context, next);
return;
}
_logger.LogInformation($"StatusCode={context.HttpContext.Response.StatusCode}");
var ts = DateTime.Now - dt;
_common.WriteLog(OperationType.View, context.HttpContext, ts);
}
await base.OnActionExecutionAsync(context, next);
}
}
至于为什么用OnActionExecutionAsync方法,因为里面日志写入涉及了异步的代码,开始和结束方法不支持异步。之前、之后就是自定义业务逻辑。
查了下日志,都是不可用状态。有点好奇出现的原因,因为gRPC是一个成熟组件不应该出现这种明显的错误。我在这位开发兄弟的博文上得到了了解(https://beckjin.com/2019/09/25/grpc-docker-swarm/)。我们线上也是docker swarm,也会存在这个问题,奇怪的是测试环境没出现这个问题。因为gRPC服务的服务器我们改不了,我只好采用重试策略来进行恢复。但在这块又遇到了另一个问题。就是异步调用gRPC方法时无法使用Polly进行重试(拦截器方法,如果是同步方法可以正常使用)。只能写一个公共的方法来处理gRPC的异步调用。
public async Task<TResponse> GrpcPollyAsync<TResponse>(Func<AsyncUnaryCall<TResponse>> func)
{
return await Policy.Handle<RpcException>(t => t.Status.StatusCode == StatusCode.Unavailable)
.RetryAsync(3, (exception, retryCount) =>
{
_logger.LogError(exception, $"输出错误。进行重试。Retry {retryCount}。");
})
.ExecuteAsync(async () => await func());
}
// 调用
var response = await _polly.GrpcPollyAsync(() => _searchClient.QueryAsync(request));