.NET Core HttpClient源码探究

前言

    在以前的文章咱们介绍过HttpClient相关的服务发现,确实HttpClient是目前.NET Core进行Http网络编程的的主要手段。在以前的介绍中也看到了,咱们使用了一个很重要的抽象HttpMessageHandler,接下来咱们就探究一下HttpClient源码,并找寻它和HttpMessageHandler的关系到底是怎么样的。git

HttpClient源码解析

    首先咱们找到HttpClient源码的位置,微软也提供了专门的网站能够查找.Net Core源码有兴趣的同窗能够自行查阅。接下来咱们查阅一下HttpClient的核心代码。首先,咱们能够看到HttpClient继承自HttpMessageInvoker这个类,待会咱们在探究这个类。github

public class HttpClient : HttpMessageInvoker
{
}

而后咱们看下几个核心的构造函数编程

public HttpClient()
    : this(new HttpClientHandler())
{
}

public HttpClient(HttpMessageHandler handler)
    : this(handler, true)
{
}

public HttpClient(HttpMessageHandler handler, bool disposeHandler)
    : base(handler, disposeHandler)
{
    _timeout = s_defaultTimeout;
    _maxResponseContentBufferSize = HttpContent.MaxBufferSize;
    _pendingRequestsCts = new CancellationTokenSource();
}

    经过这几个构造函数咱们看出,咱们能够传递自定义的HttpMessageHandler。咱们再看无参默认的构造,其实也是实例化了HttpClientHandler传递给了本身的另外一个构造函数,咱们以前讲解过HttpClientHandler是继承自了HttpMessageHandler,经过最后一个构造函数可知最终HttpMessageHandler,传给了父类HttpMessageInvoker。到了这里咱们基本上就能够感觉到HttpMessageHandler在HttpClient中存在的意义。
    接下来,咱们从一个最简单,并且最经常使用的方法为入口开始探索HttpClient的工做原理。这种方式多是咱们最经常使用并且最有效的的探索源码的方式了。我的建议没看过源码,或者刚开始入门看源码的小伙伴们,找源码的入口必定是你最有把握的的一个,而后逐步深刻了解。接下来咱们选用HttpClient的GetAsync开始入手,并且是只传递Url的那一个。网络

public Task<HttpResponseMessage> GetAsync(string? requestUri)
{
    return GetAsync(CreateUri(requestUri));
}

public Task<HttpResponseMessage> GetAsync(Uri? requestUri)
{
    return GetAsync(requestUri, defaultCompletionOption);
}

经过这里咱们能够大体了解到。其实大部分最简单的调用方式,每每都是从最复杂的调用方式,一步步的封装起来的,只是系统帮咱们初始化了一部分参数,让咱们按需使用。顺着方法一直向下找,最后找到了这里。ide

public Task<HttpResponseMessage> GetAsync(Uri? requestUri, HttpCompletionOption completionOption,
            CancellationToken cancellationToken)
{
    return SendAsync(CreateRequestMessage(HttpMethod.Get, requestUri), completionOption, cancellationToken);
}

由此能够看出这里是全部GetAsync方法的执行入口,咱们经过查找SendAsync引用能够发现。不只仅是GetAsync, PostAsync,PutAsync,DeleteAsync最终都是调用了这个方法。也就是说SendAsync是全部发送请求的真正执行者。接下来咱们就查看SendAsync方法,部分边角料代码我粘贴的时候将会作删减。函数

public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption,
            CancellationToken cancellationToken)
{
    if (request == null)
    {
        throw new ArgumentNullException(nameof(request));
    }
    CheckDisposed();
    CheckRequestMessage(request);

    SetOperationStarted();
    //这里会把发送请求的HttpRequestMessage准备稳当
    PrepareRequestMessage(request);

    CancellationTokenSource cts;
    bool disposeCts;
    bool hasTimeout = _timeout != s_infiniteTimeout;
    long timeoutTime = long.MaxValue;
    if (hasTimeout || cancellationToken.CanBeCanceled)
    {
        disposeCts = true;
        cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _pendingRequestsCts.Token);
        if (hasTimeout)
        {
            timeoutTime = Environment.TickCount64 + (_timeout.Ticks / TimeSpan.TicksPerMillisecond);
            cts.CancelAfter(_timeout);
        }
    }
    else
    {
        disposeCts = false;
        cts = _pendingRequestsCts;
    }
    Task<HttpResponseMessage> sendTask;
    try
    {
        //***这里是核心,最终执行调用的地方!!!
        sendTask = base.SendAsync(request, cts.Token);
    }
    catch (Exception e)
    {
        HandleFinishSendAsyncCleanup(cts, disposeCts);
        if (e is OperationCanceledException operationException && TimeoutFired(cancellationToken, timeoutTime))
        {
            throw CreateTimeoutException(operationException);
        }
        throw;
    }
    //这里处理输出的惟一类型HttpResponseMessage
    return completionOption == HttpCompletionOption.ResponseContentRead && !string.Equals(request.Method.Method, "HEAD", StringComparison.OrdinalIgnoreCase) ?
        FinishSendAsyncBuffered(sendTask, request, cts, disposeCts, cancellationToken, timeoutTime) :
        FinishSendAsyncUnbuffered(sendTask, request, cts, disposeCts, cancellationToken, timeoutTime);
}

经过分析这段代码能够得知,HttpClient类中最终执行的是父类的SendAsync的方法。看来是时候查看父类HttpMessageInvoker的源码了。测试

HttpMessageInvoker源码解析

public class HttpMessageInvoker : IDisposable
{
    private volatile bool _disposed;
    private readonly bool _disposeHandler;
    private readonly HttpMessageHandler _handler;

    public HttpMessageInvoker(HttpMessageHandler handler)
        : this(handler, true)
    {
    }

    public HttpMessageInvoker(HttpMessageHandler handler, bool disposeHandler)
    {
        if (NetEventSource.IsEnabled) NetEventSource.Enter(this, handler);

        if (handler == null)
        {
            throw new ArgumentNullException(nameof(handler));
        }

        if (NetEventSource.IsEnabled) NetEventSource.Associate(this, handler);

        _handler = handler;
        _disposeHandler = disposeHandler;

        if (NetEventSource.IsEnabled) NetEventSource.Exit(this);
    }

    public virtual Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
        CancellationToken cancellationToken)
    {
        if (request == null)
        {
            throw new ArgumentNullException(nameof(request));
        }
        CheckDisposed();

        if (NetEventSource.IsEnabled) NetEventSource.Enter(this, request);

        //***这里是HttpClient调用的本质,其实发送请求的根本是HttpMessageHandler的SendAsync
        Task<HttpResponseMessage> task = _handler.SendAsync(request, cancellationToken);

        if (NetEventSource.IsEnabled) NetEventSource.Exit(this, task);
        return task;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing && !_disposed)
        {
            _disposed = true;

            if (_disposeHandler)
            {
                _handler.Dispose();
            }
        }
    }

    private void CheckDisposed()
    {
        if (_disposed)
        {
            throw new ObjectDisposedException(GetType().ToString());
        }
    }
}

    是的,你并无看错,整个HttpMessageInvoker就这么多代码,并且仍是靠子类初始化过来的基本属性。找到SendAsync方法,这里基本上能够总结一点,负责调用输入输出的类只有两个。一个是提供请求参数的HttpRequestMessage,另外一个是接收输出的HttpResponseMessage。这里也给咱们平常工做编码中提供了一个很好的思路。针对具体某个功能的操做方法,最好只保留一个,其外围调用,都是基于该方法的封装。而后咱们找到了发送请求的地方_handler.SendAsync(request, cancellationToken),而handler正是咱们经过HttpClient传递下来的HttpMessageHandler.由此可知,HttpClient的本质是HttpMessageHandler的包装类。网站

自定义HttpClient

    探究到这里咱们也差很少大概了解到HttpClient类的本质是什么了。其实到这里咱们能够借助HttpMessageHandler的相关子类,封装一个简单的Http请求类.接下来我将动手实现一个简单的Http请求类,咱们定义一个类叫MyHttpClient,实现代码以下this

public class MyHttpClient : IDisposable
{
    private readonly MyHttpClientHandler _httpClientHandler;
    private readonly bool _disposeHandler;
    private volatile bool _disposed;

    public MyHttpClient()
        :this(true)
    {
    }

    public MyHttpClient(bool disposeHandler)
    {
        _httpClientHandler = new MyHttpClientHandler();
        _disposeHandler = disposeHandler;
    }

    public Task<HttpResponseMessage> GetAsync(string url)
    {
        return GetAsync(new Uri(url));
    }

    public Task<HttpResponseMessage> GetAsync(Uri uri)
    {
        HttpRequestMessage httpRequest = new HttpRequestMessage
        {
            Method = HttpMethod.Get,
            RequestUri = uri
        };
        return SendAsync(httpRequest,CancellationToken.None);
    }

    public Task<HttpResponseMessage> PostAsync(string url, HttpContent content)
    {
        return PostAsync(new Uri(url),content,null);
    }

    public Task<HttpResponseMessage> PostAsync(Uri uri, HttpContent content,Dictionary<string,string> headers)
    {
        HttpRequestMessage httpRequest = new HttpRequestMessage
        {
            Method = HttpMethod.Post,
            RequestUri = uri,
            Content = content
        };
        if (headers != null && headers.Any())
        {
            foreach (var head in headers)
            {
                httpRequest.Headers.Add(head.Key,head.Value);
            }
        }
        return SendAsync(httpRequest, CancellationToken.None);
    }

    private Task<HttpResponseMessage> SendAsync(HttpRequestMessage httpRequest, CancellationToken cancellationToken)
    {
        if (httpRequest.RequestUri == null || string.IsNullOrWhiteSpace(httpRequest.RequestUri.OriginalString))
        {
            throw new ArgumentNullException("RequestUri");
        }
        return _httpClientHandler.SendRequestAsync(httpRequest, cancellationToken);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing && !_disposed)
        {
            _disposed = true;

            if (_disposeHandler)
            {
                _httpClientHandler.Dispose();
            }
        }
    }
}

因为HttpMessageHandler的SendAsync是protected非子类没法直接调用,因此我封装了一个MyHttpClientHandler继承自HttpClientHandler在MyHttpClient中调用,具体实现以下编码

public class MyHttpClientHandler : HttpClientHandler
{
    public Task<HttpResponseMessage> SendRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        return this.SendAsync(request, cancellationToken);
    }

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        return base.SendAsync(request, cancellationToken);
    }
}

最后写了一段测试代码

using (MyHttpClient httpClient = new MyHttpClient())
{
    Task<HttpResponseMessage> httpResponse = httpClient.GetAsync("http://localhost:5000/Person/GetPerson?userId=1");
    HttpResponseMessage responseMessage = httpResponse.Result;
    if (responseMessage.StatusCode == HttpStatusCode.OK)
    {
        string content = responseMessage.Content.ReadAsStringAsync().Result;
        if (!string.IsNullOrWhiteSpace(content))
        {
            System.Console.WriteLine(content);
        }
    }
}

到这里本身实现MyHttpClient差很少到此结束了,由于只是讲解大体思路,因此方法封装的相对简单,只是封装了Get和Post相关的方法。

总结

    经过本文分析HttpClient的源码,咱们大概知道了HttpClient本质仍是HttpMessageHandler的包装类。最终的发送仍是调用的HttpMessageHandler的SendAsync方法。最后,我根据HttpClientHandler实现了一个MyHttpClient。以上只是本人理解,若是处在理解不正确或者不恰当的地方,望多多包涵,同时也指望能指出理解不周的地方。我写文章的主要一部分是想把个人理解传递给你们,欢迎你们多多交流。

👇欢迎扫码关注👇
相关文章
相关标签/搜索