菜的像徐坤
排名
6
文章
6
粉丝
16
评论
8
{{item.articleTitle}}
{{item.blogName}} : {{item.content}}
ICP备案 :渝ICP备18016597号-1
网站信息:2018-2024TNBLOG.NET
技术交流:群号656732739
联系我们:contact@tnblog.net
公网安备:50010702506256
欢迎加群交流技术

net 6 使用 consul 做服务注册与服务发现(下)

2110人阅读 2023/8/27 21:21 总访问:865449 评论:0 收藏:0 手机
分类: .net core

前言

 在consul 服务已经准备好的情况下,如何在代码中获取指定服务

服务发现

 首先在代码中同样需要引入 consul 的nuget 包


我提供一个获取服务的接口IServiceHelper

  public interface IServiceHelper
    {
    
        /// <summary>
        /// 获取订单数据
        /// </summary>
        /// <returns></returns>
        Task<string> GetOrder();
    }

代码实现为,这里我指定了获取 服务下 orders 控制器下的get 方法。可以根据自己代码替换成自己的api 路径

 
    public class ServiceHelper: IServiceHelper
    {
        private readonly IConfiguration _configuration;

        public ServiceHelper(IConfiguration configuration)
        {
            _configuration = configuration;
        }
        
         public async Task<string> GetOrder()
        {

            var consulClient = new ConsulClient(c =>
            {
                //consul地址
                c.Address = new Uri(_configuration["ConsulSetting:ConsulAddress"]); 
            });
            //获取健康的服务
            var services = consulClient.Health.Service("OrderService", null, true, null).Result.Response;
            //地址列表组装
            string[] serviceUrls = services.Select(p => $"http://{p.Service.Address + ":" + p.Service.Port}").ToArray();

            if (!serviceUrls.Any())
            {
                return await Task.FromResult("【订单服务】服务列表为空");
            }

            //每次随机访问一个服务实例
            var Client = new RestClient(serviceUrls[new Random().Next(0, serviceUrls.Length)]);

            var request = new RestRequest("/orders", Method.Get);

            var response = await Client.ExecuteAsync(request);
            return response.Content;
        } 
        
     }

appsettings.json如下(地址替换成自己的)

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "ConsulSetting": {
    "ConsulAddress": "http://192.168.1.3:8500"
  }
}


接下来我使用该接接口,获取consul 中的服务 ,并获取到指定api 返回的值

  private readonly ILogger<HomeController> _logger;
        private readonly IServiceHelper _serviceHelper;

        public HomeController(ILogger<HomeController> logger, IServiceHelper serviceHelper)
        {
            _logger = logger;
            _serviceHelper = serviceHelper;
        }
        
         public async Task<IActionResult> Index()
        { 
            ViewBag.OrderData = await _serviceHelper.GetOrder(); 
            return View();
        }

 
前端是这样

<div class="text-center">
    <h1 class="display-4">Welcome</h1>
    <p>
        @ViewBag.OrderData
    </p> 
</div>

现在来运行看一下,下边端口在不停变换,那么证明,随机访问端口成功,也正确获取到了consul 中的服务


 


这样虽然成功了,但是新的问题又出现了,在我请求一个接口时,我都会先去consul 获取请求地址,然后再发送请求,这样请求就变得啰嗦了,而且增加了请求耗时
可以使用Consul提供的解决方案:——Blocking Queries (阻塞的请求)

在接口中新增一个服务列表的方法

 public interface IServiceHelper
    { 

        /// <summary>
        /// 获取订单数据
        /// </summary>
        /// <returns></returns>
        Task<string> GetOrder();

        /// <summary>
        /// 获取服务列表
        /// </summary>
        void GetServices();
    }

实现如下,GetService 方法主要就是实现,将服务地址放进一个数组中,当consul 的版本号发生变化,那么才会重新到consul 去取一次健康的地址,这样大大的避免了每个请求都会先去consul  找地址

    public class ServiceHelper: IServiceHelper
    {
        private readonly IConfiguration _configuration;
        private readonly ConsulClient _consulClient;
        private ConcurrentBag<string> _orderServiceUrls; 

        public ServiceHelper(IConfiguration configuration)
        {
            _configuration = configuration;
            _consulClient = new ConsulClient(c =>
            {
                //consul地址
                c.Address = new Uri(_configuration["ConsulSetting:ConsulAddress"]);
            });
        }

        public async Task<string> GetOrder()
        {
                        if (_productServiceUrls == null)
                        return await Task.FromResult("【订单服务】正在初始化服务列表...");
                
                        //每次随机访问一个服务实例(从_orderServiceUrls 中获取)
                         var Client = new RestClient(_orderServiceUrls.ElementAt(new Random().Next(0, _orderServiceUrls.Count())));
            var request = new RestRequest("/orders", Method.Get);

            var response = await Client.ExecuteAsync(request);
            return response.Content;
        }

        public void GetServices()
        {
            var serviceNames = new string[] { "OrderService" };
            Array.ForEach(serviceNames, p =>
            {
                Task.Run(() =>
                {
                    //WaitTime默认为5分钟
                    var queryOptions = new QueryOptions { WaitTime = TimeSpan.FromMinutes(10) };
                    while (true)
                    {
                        GetServices(queryOptions, p);
                    }
                });
            });
        }
        private void GetServices(QueryOptions queryOptions, string serviceName)
        {
            var res = _consulClient.Health.Service(serviceName, null, true, queryOptions).Result;

            //控制台打印一下获取服务列表的响应时间等信息
            Console.WriteLine($"{DateTime.Now}获取{serviceName}:queryOptions.WaitIndex:{queryOptions.WaitIndex}  LastIndex:{res.LastIndex}");

            //版本号不一致 说明服务列表发生了变化
            if (queryOptions.WaitIndex != res.LastIndex)
            {
                queryOptions.WaitIndex = res.LastIndex;

                //服务地址列表
                var serviceUrls = res.Response.Select(p => $"http://{p.Service.Address + ":" + p.Service.Port}").ToArray();

                if (serviceName == "OrderService")
                    _orderServiceUrls = new ConcurrentBag<string>(serviceUrls); 
            }
        }
   }


然后再在管道中初始化一下,第一次的请求列表


接下来运行再试试,发现快了很多



现在我来停止一个服务试试,发现cunsul 与控制台几乎同时更新



这就是consul 的服务发现,

评价