【问题标题】:How to avoid my HttpClient opening multiple connections for single instance of the client?如何避免我的 HttpClient 为单个客户端实例打开多个连接?
【发布时间】:2019-12-06 00:32:56
【问题描述】:

基于this article,我决定在我的项目中为我的HttpClient 创建单个实例,从它的consumer 向我的WebAPI 发送两个请求。另外为了终止连接,我决定使用this article 的解决方案。在使用单例发送两个请求后,我收到了这个异常:

System.InvalidOperationException:此实例已启动 一个或多个请求。

所以我决定使用this SO answer的界面和配置(类似)。一切似乎都运行良好,但是在运行 netstat.exe 之后,我注意到,对于我的 API 使用者,打开了两 (2) 个不同端口号的不同连接:

注意[::1]:49153 是我的WebAPI 端口,所以我假设[::1]:57612[::1]:57614 是为我的消费者 开放的。为什么?如果他们应该使用同一个客户端?

不应该像first mentioned article那样吗?

我的HttpClientFactory

public interface IHttpClientFactory
    {
        HttpClient CreateClient();
    }

    public class HttpClientFactory : IHttpClientFactory
    {
        static AppConfig config = new AppConfig();

        static string baseAddress = config.ConnectionAPI;

        public HttpClient CreateClient()
        {
            var client = new HttpClient();
            SetupClientDefaults(client);
            return client;
        }

        protected virtual void SetupClientDefaults(HttpClient client)
        {
            //client.Timeout = TimeSpan.FromSeconds(30);
            client.BaseAddress = new Uri(baseAddress);
            client.DefaultRequestHeaders.ConnectionClose = true;
        }
    }

还有我的两种请求发送方式:

public async Task<bool> SendPostRequestAsync(string serializedData)
        {
            CallerName = _messageService.GetCallerName();
            HttpResponseMessage response = new HttpResponseMessage();

            Console.Write(MessagesInfo[CallerName]);

            try
            {
                HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, Config.ApiPostUri);
                request.Content = new StringContent(serializedData, Encoding.UTF8, "application/json");

                response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);

                if (response.IsSuccessStatusCode)
                {
                    Console.WriteLine(MessagesResult[CallerName]); //success status

                    return true;
                }
                else
                {
                    throw new Exception("Status code: " + response.StatusCode.ToString());
                }
            }
            catch (Exception e)
            {
                _logger.Error(e.Message.ToString() + ", " + MessagesError[CallerName]);

                CloseConnection();

                return false;
            }

        }

public async Task<bool> SendGetRequestAsync()
        {
            CallerName = _messageService.GetCallerName();
            HttpResponseMessage response = new HttpResponseMessage();

            Console.Write(MessagesInfo[CallerName]);

            try
            {
                response = await _httpClient.GetAsync(Config.ApiGetUri, HttpCompletionOption.ResponseHeadersRead);

                if (response.IsSuccessStatusCode)
                {
                    Console.WriteLine(MessagesResult[CallerName]); //success status

                    return true;
                }
                else
                {
                    throw new Exception("Status code: " + response.StatusCode.ToString());
                }
            }
            catch (Exception e)
            {
                _logger.Error(e.Message.ToString() + ", " + MessagesError[CallerName]);

                CloseConnection();

                return false;
            }
        }

连接关闭:

public void CloseConnection()
        {
            CallerName = _messageService.GetCallerName();

            var sp = ServicePointManager.FindServicePoint(new Uri(Config.ConnectionAPI));
            sp.ConnectionLeaseTimeout = 1 * 1000;

            Console.WriteLine();
            Console.WriteLine(MessagesResult[CallerName]);
        }

【问题讨论】:

  • 您可能希望考虑将HttpClientFactory 更改为存储 HttpClient(在static Lazy 中),以便它提供相同 @ 987654338@ 而不是旋转新的。

标签: c# singleton httprequest httpclient


【解决方案1】:

根据this discussion,您需要Disposeresponse您可能希望使用using

另外(来自该链接):

预计连接会保持打开状态。你不希望他们 保持开放?默认情况下,连接保持活动状态 连接池几分钟,以避免花费 为每个请求创建和拆除它们。

换句话说,连接池是为了未来的性能(在很短的时间窗口内)。

此外,您的:

var sp = ServicePointManager.FindServicePoint(new Uri(Config.ConnectionAPI));
sp.ConnectionLeaseTimeout = 1 * 1000;

应该在启动时运行一次,而不是重复。完成此操作后,您可以删除 CloseConnection 方法。

【讨论】:

  • 关于 var sp ... 它仅在 catch 块中调用,并且在程序中正确执行两个请求之后调用。它是在处置客户吗? ...我会检查文档...
  • 不,它不是在处理。阅读 ConnectionLeaseTimeout 的文档,了解它的作用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-08-31
  • 2010-09-25
  • 2019-05-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多