HttpClient如何判断是同一终结点

前言

官方文档对 HttpClientHandler.MaxConnectionsPerServer 属性有如下说明:

获取或设置使用 HttpClient 对象发出请求时允许的最大并发连接数(每个服务器终结点)。请注意,该限制针对每个服务器终结点,例如,值为 256 表示允许 256 个到 http://www.adatum.com/ 的并发连接,以及另外 256 个到 http://www.adventure-works.com/ 的并发连接。

有网友在交流群中询问,终结点到底指的啥,怎么判断是同一终结点?.

HttpClient如何判断是同一终结点

让我们通过源码来看看吧!

源码分析

HttpClientHandler.MaxConnectionsPerServer

HttpClientHandler.MaxConnectionsPerServer 的实现代码如下:

public int MaxConnectionsPerServer
{
    get => _underlyingHandler.MaxConnectionsPerServer;
    set => _underlyingHandler.MaxConnectionsPerServer = value;
}

实际使用的是_underlyingHandler.MaxConnectionsPerServer。而_underlyingHandlerSocketsHttpHandler类的实例:

using HttpHandlerType = System.Net.Http.SocketsHttpHandler;

private readonly HttpHandlerType _underlyingHandler;

SocketsHttpHandler.MaxConnectionsPerServer

SocketsHttpHandler.MaxConnectionsPerServer 的实现代码如下:

public int MaxConnectionsPerServer
{
    get => _settings._maxConnectionsPerServer;
    set
    {
        ...

        _settings._maxConnectionsPerServer = value;
    }
}

实际使用的是_settings._maxConnectionsPerServer。那么,谁在使用这个设置值呢?

HttpConnectionPool

我们定位到了HttpConnectionPool的构造函数:

public HttpConnectionPool(HttpConnectionPoolManager poolManager, HttpConnectionKind kind, string? host, int port, string? sslHostName, Uri? proxyUri)
{
    ...

    _maxHttp11Connections = Settings._maxConnectionsPerServer;

    ...
}

HttpConnectionPool的作用就是,提供到同一终结点的连接池。看来我们离真相越来越近了。

/// <summary>Provides a pool of connections to the same endpoint.</summary>
internal sealed class HttpConnectionPool : IDisposable

HttpConnectionPoolManager

HttpConnectionPool的构造函数只在HttpConnectionPoolManager类中被调用:

public ValueTask<HttpResponseMessage> SendAsyncCore(HttpRequestMessage request, Uri? proxyUri, bool async, bool doRequestAuth, bool isProxyConnect, CancellationToken cancellationToken)
{
    HttpConnectionKey key = GetConnectionKey(request, proxyUri, isProxyConnect);

    HttpConnectionPool? pool;
    while (!_pools.TryGetValue(key, out pool))
    {
        pool = new HttpConnectionPool(this, key.Kind, key.Host, key.Port, key.SslHostName, key.ProxyUri);

可以看到,HttpConnectionPool是从ConcurrentDictionary _pools 中获取的,而key的值是HttpConnectionKey类型。

HttpConnectionKey

而 HttpConnectionKey 是这样判断是否相等的:

public readonly HttpConnectionKind Kind;
public readonly string? Host;
public readonly int Port;
public readonly string? SslHostName;     // null if not SSL
public readonly Uri? ProxyUri;
public readonly string Identity;

public bool Equals(HttpConnectionKey other) =>
                Kind == other.Kind &&
                Host == other.Host &&
                Port == other.Port &&
                ProxyUri == other.ProxyUri &&
                SslHostName == other.SslHostName &&
                Identity == other.Identity;

结论

由上面的代码,我们可以得出如下结论:

如果连接类型、服务器地址、服务器端口、代理(如果有)全部相同,则认为是同一终结点。