2020-10-07

Refit集成consul在asp.net core中的实践

前言

github:https://github.com/AlphaYu/RefitConsul

Refit、WebApiClient、Feign等都是支持声名式的Restful服务调用的开源组件。

这个几个组件都综合研究总结了下,Refit fork数多,使用文档易懂,提供的功能基本都满足我的要求。

同时Refit本身集成了HttpClientFactory(Refit.HttpClientFactory)。

综上最后还是选择了Refit。

然而我的项目是使用Consul作为服务注册中心。

Refit、WebApiClient、Feign 这个几个.Net core 社区比较流行的http客户端Restful资源请求组件都没有集成Consul服务发现功能。

Steeltoe扩展了Refit的Euerka的服务发现,配合Refit.HttpClientFactory可以很好的声明服务调用。

在google搜索了下Refit consul关键字,搜索出来的基本都是介绍Refit与Consul的基础使用的文章。

看来只有靠自己造个轮子了。

研究了下Steeltoe组件Refit的Euerka的服务发现。

要集成Consul需要实现一个ConsulHttpMessageHandler,看了下Steeltoel的DiscoveryHttpMessageHandler类代码,关联的文件太多,借鉴它的写法太麻烦了。

原本想放弃了,接着研究了下Refit的相关代码与httpclientfactory相关文章,豁然开朗。

原来很容易实现,只是自己之前没有看懂而已。

只需写一个类继承DelegatingHandler类,覆写SendAsync方法,并把该类注册进去替换缺省的HttpMessageHandler。

核心代码

namespace RefitConsul{ public class ConsulDiscoveryDelegatingHandler : DelegatingHandler {  private readonly ConsulClient _consulClient;  private readonly Func<Task<string>> _token;  public ConsulDiscoveryDelegatingHandler(string consulAddress   , Func<Task<string>> token = null)  {   _consulClient = new ConsulClient(x =>   {    x.Address = new Uri(consulAddress);   });       //获取token的方法,可选参数   _token = token;  }  protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request   , CancellationToken cancellationToken)  {   var current = request.RequestUri;   var cacheKey = $"service_consul_url_{current.Host }";   try   {
          //如果声明接口有验证头,在这里统一处理。 var auth = request.Headers.Authorization; if (auth != null) { if (_token == null) throw new ArgumentNullException(nameof(_token)); var tokenTxt = await _token(); request.Headers.Authorization = new AuthenticationHeaderValue(auth.Scheme, tokenTxt); }
          //服务地址缓存3秒 var serverUrl = CacheManager.GetOrCreate<string>(cacheKey, entry => { entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(3); return LookupService(current.Host); }); request.RequestUri = new Uri($"{current.Scheme}://{serverUrl}{current.PathAndQuery}"); return await base.SendAsync(request, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { CacheManager.Remove(cacheKey); throw; } finally { request.RequestUri = current; } } private string LookupService(string serviceName) {
//根据服务名获取服务地址 var servicesEntry = _consulClient.Health.Service(serviceName, string.Empty, true).Result.Response; if (servicesEntry != null && servicesEntry.Any()) {
          //目前只实现了随机轮询 int index = new Random().Next(servicesEntry.Count()); var entry = servicesEntry.ElementAt(index); return $"{entry.Service.Address}:{entry.Service.Port}"; } return null; } }}

 如何使用

Refit的基本用法就不记录了,重点写Refit集成Consul如何写代码。

1、定义一个服务接口

 public interface IAuthApi {  /// <summary>  /// 不需要验证的接口  /// </summary>  /// <returns></returns>  [Get("/sys/users")]  Task <dynamic> GetUsers();  /// <summary>  /// 接口采用Bearer方式验证,Token在ConsulDiscoveryDelegatingHandler统一获取  /// </summary>  /// <returns></returns>  [Get("/sys/session")]  [Headers("Authorization: Bearer")]  Task<dynamic> GetCurrentUserInfo();  /// <summary>  /// 接口采用Bearer方式验证,Token使用参数方式传递  /// </summary>  /// <returns></returns>  [Get("/sys/session")]  Task<dynamic> GetCurrentUserInfo([Header("Authorization")] string authorization); }

2、在startup文件中注册Refit组件

  public void ConfigureServices(IServiceCollection services)  {   services.AddControllers();   //重试策略   var retryPolicy = Policy.Handle<HttpRequestException>()         .OrResult<HttpResponseMessage>(response => response.StatusCode== System.Net.HttpStatusCode.BadGateway)         .WaitAndRetryAsync(new[]         {          TimeSpan.FromSeconds(1),          TimeSpan.FromSeconds(5),          TimeSpan.FromSeconds(10)         });   //超时策略   var timeoutPolicy = Policy.TimeoutAsync<HttpResponseMessage>(5);   //隔离策略   var bulkheadPolicy = Policy.BulkheadAsync<HttpResponseMessage>(10, 100);   //回退策略   //断路策略   var circuitBreakerPolicy = Policy.Handle<Exception>()       .CircuitBreakerAsync(2, TimeSpan.FromMinutes(1));   //注册RefitClient   //用SystemTextJsonContentSerializer替换默认的NewtonsoftJsonContentSerializer序列化组件   //如果调用接口是使用NewtonsoftJson序列化则不需要替换   services.AddRefitClient<IAuthApi>(new RefitSettings(new SystemTextJsonContentSerializer()))     //设置服务名称,andc-api-sys是系统在Consul注册的服务名     .ConfigureHttpClient(c => c.BaseAddress = new Uri(""))     //注册ConsulDiscoveryDelegatingHandler,     .AddHttpMessageHandler(() =>     {      //:8550是consul服务器的地址      //() => Helper.GetToken() 获取token的方法,是可选参数,如果不需要token验证不需要传递。      return new ConsulDiscoveryDelegatingHandler("", () => Helper.GetToken());     })     //设置httpclient生命周期时间,默认也是2分钟。     .SetHandlerLifetime(TimeSpan.FromMinutes(2))     //添加polly相关策略     .AddPolicyHandler(retryPolicy)     .AddPolicyHandler(timeoutPolicy)     .AddPolicyHandler(bulkheadPolicy);  }

 

3、如何在controller中调用

 public class HomeController : ControllerBase {  private readonly IAuthApi _authApi;  private readonly string _token = Helper.GetToken().Result;  /// <summary>  /// RefitConsul测试  /// </summary>  /// <param name="authApi">IAuthApi服务</param>  public HomeController(IAuthApi authApi)  {   _authApi = authApi;  }  [HttpGet]  public async Task<dynamic> GetAsync()  {   //不需要验证的服务   var result1 = await _authApi.GetUsers();   //需要验证,token采用参数传递   var result2 = await _authApi.GetCurrentUserInfo($"Bearer {_token}");   //需要验证,token在ConsulDiscoveryDelegatingHandler获取。   var result3 = await _authApi.GetCurrentUserInfo();   return result3;  } }

 

原文转载:http://www.shaoqun.com/a/479869.html

菜鸟网:https://www.ikjzd.com/w/1547

stylenanda官网:https://www.ikjzd.com/w/1675.html

淘粉吧:https://www.ikjzd.com/w/1725


前言github:https://github.com/AlphaYu/RefitConsulRefit、WebApiClient、Feign等都是支持声名式的Restful服务调用的开源组件。这个几个组件都综合研究总结了下,Refitfork数多,使用文档易懂,提供的功能基本都满足我的要求。同时Refit本身集成了HttpClientFactory(Refit.HttpClientFactory
outfit:https://www.ikjzd.com/w/938
55海淘网:https://www.ikjzd.com/w/1723
干货:Lazada入驻条件与流程详解!:https://www.ikjzd.com/home/106566
外贸骗局预警!骗子利用斋月赖账?:https://www.ikjzd.com/home/22325
Wish2019年6大政策解读:流量和促销是重点:https://www.ikjzd.com/home/13367

No comments:

Post a Comment