zoukankan      html  css  js  c++  java
  • WebApiClient的SteeltoeOSS.Discovery扩展

    1 背景

    从园子里看到一些朋友在某些项目开发中,选择的架构是spring cloud搭建底层微服务框架,dotnet core来编写业务逻辑,SteeltoeOSS.Discovery是dotnet和spingcloud的桥梁,为dotnet提供服务注册和服务发现相关功能。在阅读朋友们文章的时候,我发现相关代码里的一些HttpClient相关问题,同时对dotnet寄居于spingcloud下由于没有Feign而产生的那些丑陋的http请求代码进行思考。本文将围绕原生的HttpClient的创建与释放的正确姿势和使用WebApiClient让dotnet也有媲美Feign的服务客户端两个面展开。

    2 正确使用HttpClient

    2.1 HttpClient的创建和释放

    HttpClient有三个构造函数,最终都是调用到public HttpClient(HttpMessageHandler handler, bool disposeHandler)这个函数,HttpClient除了其handler参数之外,本身没有使用到需要Dispose的资源,其实现的IDispose也是为了Dispose掉handler参数而已。

    HttpMessageHandler是一个抽象类,目前主要的HttpMessageHandler具体类型有HttpClientHanlder、SocketsHttpHandler和WebRequestHandler,但HttpClientHanlder在dotnet core2.1下是对和SocketsHttpHandler的包装实现。除了这些主要HttpMessageHandler,还有一个抽象的DelegatingHandler类型,用于实现请求管道,影响请求前后的数据逻辑。HttpClient的默认构造器,使用了HttpClientHanlder类型,同时disposeHandler为true,这时如果对HttpClient实例Dispose了,其内部的HttpClientHanlder自然也被Dispose了,

    正确的创建和释放HttpClient例子

    1. 默认构造器
    var httpClient = new HttpClient();
    ...你的代码...
    httpClient.Dispose();
    
    1. HttpClient控制HttpMessageHandler
    var handler = new HttpClientHandler();
    var httpClient = new HttpClient(handler, true);
    ...你的代码...
    httpClient.Dispose();
    
    1. HttpClient不控制外部HttpMessageHandler
    var handler = 从外部来的HttpMessageHandler;
    var httpClient = new HttpClient(handler, false);
    ...你的代码...
    
    // 这里调用httpClient.Dispose()是无效的
    // handler的生命周期应该由它的创建者来维护
    // 如果这里Dispose掉handler,其它使用了这个handler的HttpClient实例受影响
    

    不正确的创建和释放HttpClient的例子

    1. HttpMessageHandler被创建了,但没有释放
    private readonly DiscoveryHttpClientHandler _handler;
    private const string ProductUrl = "http://product/api/values";
    
    public ValuesController(IDiscoveryClient client, ILoggerFactory logFactory)
    {
        _handler = new DiscoveryHttpClientHandler(client);
    }
    
    [HttpGet("product")]
    public async Task<string> GoProductAsync()
    {
        var client = new HttpClient(_handler, false);
        return await client.GetStringAsync(ProductUrl);
    }
    

    2.2 HttpClient的生命周期

    HttpClient在设计之初,是非常适合使用单例模式的,也就是在应用域中,只维护一份HttpClient的实例就够了,因为它天生支持向不同域名同时多个并发请求。但单例的HttpClient没有遵守DNS 生存时间 (TTL) 设置,如果其HttpClientHandler第一次请求到www.baidu.com指向的ip为123.123.123.123,中途这个域名解析到的ip变化了,但HttpClient并不会自动应用这些变化。

    asp.net core里,微软创建一个HttpClientFactory项目,用于提供HttpClient的创建和生命周期自动管理,完美解决到底选择单例还是每个请求创建和释放HttpClient这个左右难为的问题。所以在asp.net core项目开发中,请别再写手动new HttpClient了,所有HttpClient的实例,都要由HttpClientFactory来创建,所有的外部HttpMessageHandler,也应该配置到HttpClientFactory,让它与HttpClient关联起来。

    HttpClientFactory有三种使用方式:

    1. Using HttpClientFactory Directly
    2. Named Clients
    3. Typed Clients

    具体的使用,可以查看3 ways to use HTTPClientFactory in ASP.NET Core 2.1这篇好文。

    3 寄居下也有Feign

    虽然已经讲解了怎么new一个HttpClient,怎么利用HttpClientFactory,但如果要寄居在spingcloud下,你还是得为请求一个服务接口编写大量的代码,这在java的Feign前面如同马车见到宝马。如果能利用WebApiClient这把利剑,结合HttpClientFactory与DiscoveryHttpClientHandler,你也能变成宝马。将这三者有机结合起来的项目,叫WebApiClient.Extensions.DiscoveryClient ,它是WebApiClient的SteeltoeOSS.Discovery扩展项目,使用非常简单。

    3.1 Nuget引用

    PM> install-package WebApiClient.Extensions.DiscoveryClient

    3.2 声明微服务的WebApiClient调用接口

    [HttpHost("http://NET-API")]
    public interface INetApi : IHttpApi
    {
        [HttpGet("api/values")]
        ITask<string[]> GetValuesAsync();
    
        [HttpGet("api/values/{id}")]
        ITask<string> GetValuesAsync(int id);
    }
    

    3.3 Startup相关配置

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDiscoveryClient(Configuration);
        services.AddDiscoveryTypedClient<INetApi>();
        ...
    }
    
    
    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        ...
        app.UseDiscoveryClient();
    }
    
    3.4 Controller
    public class HomeController : Controller
    {
        public async Task<string> Index([FromServices]INetApi netApi, int id = 0)
        {
            var values = await netApi.GetValuesAsync();
            var value = await netApi.GetValuesAsync(id);
            return "ok";
        }
    }
    

    4. 结束语

    本博主对HttpClient有着比较深入的了解,一直在维护WebApiClient项目,期间走过许许多多的“坑”,如果你有遇到HttpClient的相关问题,欢迎一起讨论解决。

  • 相关阅读:
    ip netns
    PPT动手动脑1
    暑假作业日总结
    暑假作业日总结
    课前测试总结
    暑假作业日总结
    大二上每日总结
    暑假作业日总结
    大二上每日总结
    暑假作业日总结
  • 原文地址:https://www.cnblogs.com/kewei/p/9710778.html
Copyright © 2011-2022 走看看