首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >仅限HttpClient 502坏网关异步

仅限HttpClient 502坏网关异步
EN

Stack Overflow用户
提问于 2022-03-04 21:58:08
回答 2查看 1.1K关注 0票数 0

我在异步调用web服务时遇到了问题。我在循环中同步调用了它数百次,但是当我异步调用它时,我会得到一个502 Bad Gateway错误,通常是在大约250个异步调用之后。正如您所看到的,我尝试过用Thread.Sleep()等待50 As,每次重试5次。当我无法验证记录时,预期会出现404 Not Found错误。我的框架是netstandard2.0。

代码语言:javascript
运行
复制
class Program
    {
        static void Main(string[] args)
        {
            List<OriginalRecord> originalRecords = new List<OriginalRecord>();
            originalRecords.Add(new OriginalRecord()
            {
                Id = "IdOne",
                Foo = "fooOne"
            });
            originalRecords.Add(new OriginalRecord()
            {
                Id = "IdTwo",
                Foo = "fooTwo"
            });

            List<RecordValidation> unvalidatedRecords = new List<RecordValidation>();

            try
            {
                RecordValidator validator = new RecordValidator("myAccessToken", "myBarId");
                var task = validator.ValidateRecordsAsync(originalRecords.Select(r => r.Id).ToList());
                task.Wait();
                unvalidatedRecords = task.Result;
            }
            catch
            {
                throw;
            }
        }
    }
    public class RecordValidator
    {
        public RecordValidator(string AccessToken, string BarId)
        {
            httpClient = new HttpClient();
            httpClient.DefaultRequestHeaders.UserAgent.TryParseAdd("MessageService/3.1");

            httpClient.DefaultRequestHeaders.Authorization =
                new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", AccessToken);

            httpClient.DefaultRequestHeaders.Add("Accept", "application/hal+json");
            httpClient.DefaultRequestHeaders.ExpectContinue = false;

            barId = BarId;
        }

        private static HttpClient httpClient { get; set; }
        
        public string barId { get; set; }

        public async Task<List<RecordValidation>> ValidateRecordsAsync(List<string> Records)
        {
            List<Task<RecordValidation>> tasks = new List<Task<RecordValidation>>();

            foreach (var record in Records)
            {
                tasks.Add(Task.Run(() => ValidateRecordAsync(record)));
            }

            var results = await Task.WhenAll(tasks);
            return results.Where(r => !r.Validated).ToList();
        }

        private async Task<RecordValidation> ValidateRecordAsync(string Record)
        {
            string url = $"https://data.somewebapp.com/api/bar/{barId}/record/{Uri.EscapeDataString(Record)}";
            int retries = 0;
            bool success = false;
            HttpResponseMessage response = new HttpResponseMessage();
            try
            {

                do
                {
                    response = await httpClient.GetAsync(url);
                    if (response.IsSuccessStatusCode)
                    {
                        success = true;
                    }
                    else
                    {
                        Thread.Sleep(50);
                        retries++;
                    }
                } while (retries < 5 && !success);


                response.EnsureSuccessStatusCode();

                string responseString = await response.Content.ReadAsStringAsync();

                var output = JsonConvert.DeserializeObject<RecordValidation>(responseString);

                if (!string.IsNullOrEmpty(output.RecordId))
                    output.Validated = true;
                return output;
            }

            catch (HttpRequestException ex) when (ex.Message == "Response status code does not indicate success: 404 (Not Found).")
            {
                return new RecordValidation()
                {
                    RecordId = Record,
                    Validated = false
                };
            }

            catch
            {
                throw;
            }
        }
    }
    public class RecordValidation
    {
        public RecordValidation()
        {
            RecordId = string.Empty;
            Validated = false;
        }

        [JsonProperty("id")]
        public string RecordId { get; set; }

        public bool Validated { get; set; }
    }

    public class OriginalRecord
    {
        public string Id { get; set; }
        public string Foo { get; set; }
    }
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2022-03-05 00:29:14

502响应是实际响应。HTTP请求成功,服务器返回502响应。这很可能是因为你击中服务器的速度太快,太难了,而且它把你切断了。

当您同步执行时,它将发送请求并等待响应,然后再转到下一个响应。

但在这个循环中:

代码语言:javascript
运行
复制
foreach (var record in Records)
{
    tasks.Add(Task.Run(() => ValidateRecordAsync(record)));
}

您正在为每个请求创建一个新线程,其效果是几乎同时发送每个请求。显然,服务器不喜欢这样做。

您不需要为每个请求创建一个新线程。您可以在没有Task.Run()的情况下异步启动它们,例如:

代码语言:javascript
运行
复制
foreach (var record in Records)
{
    tasks.Add(ValidateRecordAsync(record));
}

这只会稍微减缓它的速度,但不会太大。这将在同一个线程上运行ValidateRecordAsync(),直到在进入下一个循环迭代并启动下一个循环之前发送HTTP请求。但是对于服务器来说,这可能还是太快了,因为在前面的请求完成之前,您仍然在发送所有的请求。

你很可能不得不抑制你的请求。如果您希望它尽可能快,您可以知道web服务将允许您发送多少请求,您可以根据web服务定制您的代码。否则,你就只能猜测了。

您可以发送,例如每次10,并在中间添加一个暂停。

代码语言:javascript
运行
复制
foreach (var record in Records)
{
    tasks.Add(ValidateRecordAsync(record));
    if (tasks.Count % 10 == 0) await Task.Delay(100);
}

或者等到这10个人完成后再继续前进:

代码语言:javascript
运行
复制
foreach (var record in Records)
{
    tasks.Add(ValidateRecordAsync(record));
    if (tasks.Count % 10 == 0) await Task.WhenAll(tasks);
}

您可以在同一任务列表上多次调用Task.WhenAll

票数 2
EN

Stack Overflow用户

发布于 2022-03-07 16:44:07

250个并行电话太多了。应该是10-30。对于C# Http客户端的默认最大并发请求数为10,因此如果您没有覆盖该值,那么尝试同时打开10个以上的套接字连接是没有意义的。另外,请记住,502状态很可能是由反向代理返回的,这意味着您没有真正地访问web,代理告诉您“慢下来,我不能再处理了”。因此,在您的情况下,这并不是真正的HttpClient问题,否则,您将得到一些异常,比如套接字耗尽导致的SocketException,由于HttpClient将并发性限制为10,您可能不会得到这些异常。此外,您有大量的线程阻塞代码,如Thread.Sleep()task.Wait(),将您的主要方法签名更改为static async Task Main()并在任何地方使用await,否则,您将遇到其他类型的问题,如线程耗尽。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71357621

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档