侧边栏壁纸
博主头像
komi

Bona Fides

  • 累计撰写 9 篇文章
  • 累计创建 17 个标签
  • 累计收到 2 条评论

目 录CONTENT

文章目录

为项目的HttpClient添加超时重试中间件

komi
2024-09-16 / 0 评论 / 0 点赞 / 37 阅读 / 627 字

起因

因为公司业务中有对第三方接口进行定时查询的需求(通过调用对方的API来同步订单车辆GPS安装状态) 前两天有运营说门店那边的GPS已经安装好了 但是系统里查到的还是安装中的状态 奇了个大怪 不得不排查下到底是代码中出了鬼 还是第三方服务有问题

问题排查

几经折腾 发现自己的代码逻辑是自洽的 之后开始怀疑是不是第三方接口稳定性不太过关 后来用ApiFox进行接口测试 不到20次得有快4次请求超时甚至有getaddrinfo ENOTFOUND这样的DNS问题(有时候属于弱网) 这下基本笃定是网络问题 框架是 .Net Framework 4.5 使用 HttpClient 经过查阅资料 可以在HttpClient构建的时候传入一个处理器给自己的请求增加中间件达到重试效果(这里没有直接使用Polly 毕竟不想让框架依赖太臃肿)

Code

public class TimeoutHandler : DelegatingHandler
{
    private int timeOut;
    private int maxRetry;

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="max_count">最多重试次数</param>
    /// <param name="timeOutMilliseconds">超时时间(毫秒)</param>
    public TimeoutHandler(int max_count = 3, int timeOutMilliseconds = 5000)
    {
        InnerHandler = new HttpClientHandler();
        timeOut = timeOutMilliseconds;
        maxRetry = max_count;
    }

    protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        HttpResponseMessage response = default;

        for (int i = 1; i <= maxRetry + 1; i++)
        {
            var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);

            cts.CancelAfter(timeOut);

            try
            {
                response = await base.SendAsync(request, cts.Token);

                if (response.IsSuccessStatusCode)
                {
                    return response;
                }
            }
            catch (Exception ex)
            {
                //请求超时
                if (ex is TaskCanceledException)
                {
                    Log.Write($"接口请求超时 {request.RequestUri} : " + ex.ToString(), "Http接口异常");
                    if (i > maxRetry)
                    {
                        return new HttpResponseMessage(HttpStatusCode.RequestTimeout)
                        {
                            Content = new StringContent("{\"code\":-1,\"data\":\"\",\"msg\":\"接口请求超时\"}", Encoding.UTF8, "text/json")
                        };
                    }
                    Log.Write($"接口第{i}次重新请求 {request.RequestUri}", "Http接口异常");
                }
                else
                {
                    Log.Write($"接口请求异常 {request.RequestUri} :" + ex.ToString(), "Http接口异常");
                    return new HttpResponseMessage(HttpStatusCode.InternalServerError)
                    {
                        Content = new StringContent("{\"code\":-1,\"data\":\"\",\"msg\":\"接口请求异常\"}", Encoding.UTF8, "text/json")
                    };
                }
            }
        }
        return response;
    }

}

代码量不大 主要的工作内容还是在构造函数上以及重写SendAsync方法(实际上还是用到InnerHandler里的SendAsync) 整个代码中的重试功能内核是靠CancellationTokenSource生成的Token实现的
Log这块用的是CYQ.Data 这里可以替换成SeriLog或者别的之类的

0

评论区