简介

  随着.NET Core的不断发展与成熟,基于.NET Core实现微服务的解决方案也越来越多。这其中必然需要注册中心,Consul成为了.NET Core实现服务注册与发现的首选。类似的解决方案还有很多比如Netflix Eureka,也有关于结合.NET Core的案例比如比较知名的就是SteeltoeOSS.Discovery 这里就不过多的介绍了,有兴趣的小伙伴可以自己在网上查阅资料。接下来我们讲解如何实现HttpClient结合Consul实现服务发现。

入门

  关于Consul如何入门,相信很多小伙伴已经非常熟练了,这里就不再赘述。如果还有不熟悉的小伙伴请查阅Edison Zhou的 https://www.cnblogs.com/edisonchou/p/9124985.html 写的非常详细。

初步实现

如何将发现的地址结合到HttpClient相信很多小伙伴首先想到的就是如下方法

  1. public string LookupService(string serviceName)
  2. {
  3. using (ConsulClient consulClient = new ConsulClient(config=>config.Address=new Uri("http://localhost:8500/")))
  4. {
  5. var services = _consulClient.Catalog.Service(serviceName).Result.Response;
  6. if (services != null && services.Any())
  7. {
  8. //模拟负载均衡算法(随机获取一个地址)
  9. int index = r.Next(services.Count());
  10. var service = services.ElementAt(index);
  11. return $"{service.ServiceAddress}:{service.ServicePort}";
  12. }
  13. return null;
  14. }
  15. }

然后调用的时候采用类似的方法

  1. public async Task<Person> GetPerson(int personId)
  2. {
  3. using (HttpClient client = new HttpClient())
  4. {
  5. var response = await client.GetAsync($"http://{LookupService("PersonService")}/Person/GetPerson?personId={personId}");
  6. var jsonResult = await response.Content.ReadAsStringAsync();
  7. return jsonResult.FromJson<Person>();
  8. }
  9. }

或者封装了一层HttpHelper里面封装了针对Get Post的调用方法。

借助HttpMessageHandler

  上面的方式实现确实是实现了,但是老是感觉差点意思,如果我在HttpHelper里多封装几个方法,那岂不是每个方法里面都得调用一次LookupService方法,总感觉不够优雅。难道没有更好的办法了吗? Of course,有的小伙伴可能发现了HttpClient构造函数有几个重载方法

  1. /// <summary>Initializes a new instance of the <see cref="T:System.Net.Http.HttpClient" /> class using a <see cref="T:System.Net.Http.HttpClientHandler" /> that is disposed when this instance is disposed.</summary>
  2. public HttpClient ()
  3. : base (null);

  4. /// <summary>Initializes a new instance of the <see cref="T:System.Net.Http.HttpClient" /> class with the specified handler. The handler is disposed when this instance is disposed.</summary>
  5. /// <param name="handler">The HTTP handler stack to use for sending requests.</param>
  6. /// <exception cref="T:System.ArgumentNullException">The <paramref name="handler" /> is <see langword="null" />.</exception>
  7. public HttpClient (HttpMessageHandler handler)
  8. : base (null);
  9.  
  10. /// <summary>Initializes a new instance of the <see cref="T:System.Net.Http.HttpClient" /> class with the provided handler, and specifies whether that handler should be disposed when this instance is disposed.</summary>
  11. /// <param name="handler">The <see cref="T:System.Net.Http.HttpMessageHandler" /> responsible for processing the HTTP response messages.</param>
  12. /// <param name="disposeHandler">
  13. /// <see langword="true" /> if the inner handler should be disposed of by HttpClient.Dispose; <see langword="false" /> if you intend to reuse the inner handler.</param>
  14. /// <exception cref="T:System.ArgumentNullException">The <paramref name="handler" /> is <see langword="null" />.</exception>
  15. public HttpClient (HttpMessageHandler handler, bool disposeHandler)
  16. : base (null);

其中我们看到了HttpMessageHandler这是处理HttpClient消息的拦截器类,通过它可以获取和设置HttpClient的请求和返回内容。 具体实现如下

  1. /// <summary>A base type for HTTP message handlers.</summary>
  2. public abstract class HttpMessageHandler : IDisposable
  3. {
  4. /// <summary>Releases the unmanaged resources and disposes of the managed resources used by the <see cref="T:System.Net.Http.HttpMessageHandler" />.</summary>
  5. public void Dispose ();
  6.  
  7. /// <summary>Releases the unmanaged resources used by the <see cref="T:System.Net.Http.HttpMessageHandler" /> and optionally disposes of the managed resources.</summary>
  8. /// <param name="disposing">
  9. /// <see langword="true" /> to release both managed and unmanaged resources; <see langword="false" /> to releases only unmanaged resources.</param>
  10. protected virtual void Dispose (bool disposing);
  11.  
  12. /// <summary>Send an HTTP request as an asynchronous operation.</summary>
  13. /// <param name="request">The HTTP request message to send.</param>
  14. /// <param name="cancellationToken">The cancellation token to cancel operation.</param>
  15. /// <returns>The task object representing the asynchronous operation.</returns>
  16. /// <exception cref="T:System.ArgumentNullException">The <paramref name="request" /> was <see langword="null" />.</exception>
  17. protected internal abstract Task<HttpResponseMessage> SendAsync (HttpRequestMessage request, CancellationToken cancellationToken);
  18. }

  这个一个抽象类,通过继承这个抽象类,实现SendAsync抽象方法可以处理请求消息和返回消息。我们就从这里入手了,我们使用是DelegatingHandler这也是一个抽象类,这个抽象类继承自HttpMessageHandler是系统自带的一个抽象类,其实常用的还有一个叫HttpClientHandler,这个类也是继承自HttpMessageHandler,且提供了一些设置和获取操作输出和输出的具体实现。这里我们选用实现DelegatingHandler抽象类,至于为什么下篇文章会做详解,自定义一个ConsulDiscoveryDelegatingHandler实现类具体代码如下

  1. public class ConsulDiscoveryDelegatingHandler : DelegatingHandler
  2. {
  3. protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
  4. {
  5. var current = request.RequestUri;
  6. try
  7. {
    //调用的服务地址里的域名(主机名)传入发现的服务名称即可
  8. request.RequestUri = new Uri($"{current.Scheme}://{LookupService(current.Host)}/{current.PathAndQuery}");
  9. return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
  10. }
  11. catch (Exception e)
  12. {
  13. throw;
  14. }
  15. finally
  16. {
  17. request.RequestUri = current;
  18. }
  19. }
  20.  
  21. private string LookupService(string serviceName)
  22. {
  23. using (ConsulClient consulClient = new ConsulClient(config=>config.Address=new Uri("http://localhost:8500/")))
  24. {
  25. var services = _consulClient.Catalog.Service(serviceName).Result.Response;
  26. if (services != null && services.Any())
  27. {
  28. //模拟负载均衡算法(随机获取一个地址)
  29. int index = r.Next(services.Count());
  30. var service = services.ElementAt(index);
  31. return $"{service.ServiceAddress}:{service.ServicePort}");
  32. }
  33. return null;
  34. }
  35. }
    }

这样的话就大致实现了一个基于consul 发现的ConsulDiscoveryDelegatingHandler,具体的使用方式如下

  1. public async Task<Person> GetPerson(int personId)
  2. {
  3. using (HttpClient client = new HttpClient(new ConsulDiscoveryDelegatingHandler()))
  4. {
    //调用时的域名(主机名)传入服务发现的名称即可
  5. var response = await client.GetAsync($"http://PersonService/Person/GetPerson?personId={personId}");
  6. var jsonResult = await response.Content.ReadAsStringAsync();
  7. return jsonResult.FromJson<Person>();
  8. }
  9. }

到这里为止,关于HttpClient结合Consul实现服务发现的具体实现大致就差不多了,本身还存在很多不足,比如没有结合自带的IOC,没有做连接异常处理等等。但是能给大家提供一些思路或者帮助本人就已经满足了。

最后

到这里还并没有结束,相信很多小伙伴都知道HttpClient存在不足,就是Dispose的时候,套接字本身会延迟释放,会导致端口号占用的问题,高并发情况下会导致端口号用尽,导致服务器拒绝服务。虽然可以通过单例或者设置系统句柄释放时间解决这个问题,但是还是会存在一定的问题。庆幸的是微软也意识到了这个问题,从.NET Core 2.1版本开始推出了HttpClientFactory 通过池化技术管理HttpClient实例能很好的解决这个问题。在以后的版本里我们去访问网络请求都会使用HttpClientFactory。下一篇文章,我将会通过分析HttpClientFactory源码的方式,一步步探讨如何使用更优雅的方式实现HttpClientFactory+Consul实现服务发现

本系列未完待续。。。

.NET Core HttpClient+Consul实现服务发现的更多相关文章

  1. .NET Core HttpClientFactory+Consul实现服务发现

    前言 上篇文章.NET Core HttpClient+Consul实现服务发现提到过,HttpClient存在套接字延迟释放的问题,高并发情况导致端口号被耗尽引起服务器拒绝服务的问题.好在微软意识到 ...

  2. .net core grpc consul 实现服务注册 服务发现 负载均衡(二)

    在上一篇 .net core grpc 实现通信(一) 中,我们实现的grpc通信在.net core中的可行性,但要在微服务中真正使用,还缺少 服务注册,服务发现及负载均衡等,本篇我们将在 .net ...

  3. .Net Core Grpc Consul 实现服务注册 服务发现 负载均衡

    本文是基于..net core grpc consul 实现服务注册 服务发现 负载均衡(二)的,很多内容是直接复制过来的,..net core grpc consul 实现服务注册 服务发现 负载均 ...

  4. Api网关Kong集成Consul做服务发现及在Asp.Net Core中的使用

    写在前面   Api网关我们之前是用 .netcore写的 Ocelot的,使用后并没有完全达到我们的预期,花了些时间了解后觉得kong可能是个更合适的选择. 简单说下kong对比ocelot打动我的 ...

  5. 使用Consul做服务发现的若干姿势

    从2016年起就开始接触Consul,使用的主要目的就是做服务发现,后来逐步应用于生产环境,并总结了少许使用经验.最开始使用Consul的人不多,为了方便交流创建了一个QQ群,这两年微服务越来越火,使 ...

  6. Consul做服务发现

    使用Consul做服务发现的若干姿势 https://www.cnblogs.com/bossma/p/9756809.html 从2016年起就开始接触Consul,使用的主要目的就是做服务发现,后 ...

  7. Go | Go 使用 consul 做服务发现

    Go 使用 consul 做服务发现 目录 Go 使用 consul 做服务发现 前言 一.目标 二.使用步骤 1. 安装 consul 2. 服务注册 定义接口 具体实现 测试用例 3. 服务发现 ...

  8. go-micro使用Consul做服务发现的方法和原理

    go-micro v4默认使用mdns做服务发现.不过也支持采用其它的服务发现中间件,因为多年来一直使用Consul做服务发现,为了方便和其它服务集成,所以还是选择了Consul.这篇文章将介绍go- ...

  9. ASP.NET CORE 使用Consul实现服务治理与健康检查(2)——源码篇

    题外话 笔者有个习惯,就是在接触新的东西时,一定要先搞清楚新事物的基本概念和背景,对之有个相对全面的了解之后再开始进入实际的编码,这样做最主要的原因是尽量避免由于对新事物的认知误区导致更大的缺陷,Bu ...

随机推荐

  1. MySQL根据业务场景归纳常用SQL语句

    素材表数据:user[{"id":1,"name":"x"},{"id":2,"name":&quo ...

  2. 基于国内某云的 Domain Fronting 技术实践

    发布时间:2019-12-16 11:30:53 一.简介 Domain Fronting,中文译名 “域前置” 或 “域名前置”,是一种用于隐藏真实C2服务器IP且同时能伪装为与高信誉域名通信的技术 ...

  3. CentOS 6.5下通过yum安装MongoDB记录

    安装MongoDB 1.创建repo vi /etc/yum.repos.d/mongodb-org-3.6.repo   [mongodb-org-3.6]   name=MongoDB Repos ...

  4. IEEE 754标准--维基百科

    IEEE二进制浮点数算术标准(IEEE 754) 是20世纪80年代以来最广泛使用的浮点数运算标准,为许多CPU与浮点运算器所采用.这个标准定义了表示浮点数的格式(包括负零-0)与反常值(denorm ...

  5. [CodeForces-259C] Little Elephant and Bits

    C. Little Elephant and Bits time limit per test 2 seconds memory limit per test 256 megabytes input ...

  6. Ant 环境安装

    1.下载安装 Ant,配置环境变量 进入 http://ant.apache.org/bindownload.cgi 下载 Ant 配置环境变量 新建 ANT_HOME 配置 Path 环境变量 配置 ...

  7. 虚拟化云计算平台Proxmox VE

    1.虚拟化技术介绍 1.1.OpenVZ 简介 OpenVZ 是开源软件, 是基于Linux平台的操作系统级服务器虚拟化解决方案,它是基于Linux内核和作业系统的操作系统级虚拟化技术. OpenVZ ...

  8. TCP 可靠传输

    TCP报文段首部 序号: TCP是面向字节流的.在一个TCP连接中传送的字节流中的每一个字节都按顺序编号.整个要传送的字节流的起始序号必须在连接建立时设置.首部中的序号字段值则指的是本报文段所发送的数 ...

  9. libevent(九)evhttp

    用libevent构建一个http server非常方便,可参考libevent(六)http server. 主要涉及的一个结构体是evhttp: struct evhttp { /* Next v ...

  10. Java——HTTP超详细总结

    HTTP协议概述 HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的 ...