雷达智富

首页 > 内容 > 程序笔记 > 正文

程序笔记

.NET Core Consul服务发现

2024-09-08 15

.NET Core Consul服务注册 一文中讲述了安装Consul,注册服务。

本文讲述在.NET Core项目中的Consul服务发现。和注册一样,为了复用还是新建一个类库。

首先我们还是启动3个AspNetCore服务,注册到Consul,如何注册可以看.NET Core Consul服务注册一文中的介绍。

启动好以后在Consul的监视界面localhost:8500中可以看到3个服务了,如图所示

接下来开始在ConsulServiceDiscovery类库项目中编写代码。

服务提供IServiceProvider接口和ConsulServiceProvider类代码

namespace ConsulServiceDiscovery
{
    public interface IServiceProvider
    {
        Task<IList<string>> GetServicesAsync(string serviceName);
    }
}
namespace ConsulServiceDiscovery
{
    public class ConsulServiceProvider : IServiceProvider
    {
        private readonly ConsulClient _consulClient;
        public ConsulServiceProvider(Uri uri) {
            _consulClient = new ConsulClient(consulConfig =>
            {
                consulConfig.Address = uri;
            });
        }
        public async Task<IList<string>> GetServicesAsync(string serviceName)
        {
            // Health 当前consul里已注册的服务,健康检查的信息也拿过来
            var queryResult = await _consulClient.Health.Service(serviceName, string.Empty, true);
            var result = new List<string>();
            foreach (var item in queryResult.Response) {
                result.Add($"{item.Service.Address}:{item.Service.Port}");
            }
            return result;
        }
    }
}

服务创建IServiceBuilder接口和ServiceBuilder类代码

namespace ConsulServiceDiscovery.Builder
{
    public interface IServiceBuilder
    {
        /// <summary>
        /// 服务提供者
        /// </summary>
        IServiceProvider ServiceProvider { get; set; }
        /// <summary>
        /// 服务名称
        /// </summary>
        string ServiceName { get; set; }
        /// <summary>
        /// Uri方案
        /// </summary>
        string UriScheme { get; set; }
        /// <summary>
        /// 使用哪种策略
        /// </summary>
        ILoadBalancer LoadBalancer { get; set; }
        Task<Uri> BuildAsync(string path);
    }
}
namespace ConsulServiceDiscovery.Builder
{
    public class ServiceBuilder : IServiceBuilder
    {
        public IServiceProvider ServiceProvider { get; set; }
        public string ServiceName { get; set; }
        public string UriScheme { get; set; }
        public ILoadBalancer LoadBalancer { get; set; }
        public ServiceBuilder(IServiceProvider serviceProvider) {
            ServiceProvider = serviceProvider;
        }

        public async Task<Uri> BuildAsync(string path)
        {
            var serviceList = await ServiceProvider.GetServicesAsync(ServiceName);
            var service = LoadBalancer.Resolve(serviceList);
            var baseUri = new Uri($"{UriScheme}://{service}");
            var uri = new Uri(baseUri, path);
            return uri;
        }
    }
}

负载方式,这里列举随机和轮询两种负载方式,ILoadBalancer接口,RandomLoadBalancer实现类,RoundRobinLoadBalancer实现类,TypeLoadBalancer静态类代码

namespace ConsulServiceDiscovery.LoadBalancer
{
    public interface ILoadBalancer
    {
        string Resolve(IList<string> services);
    }
}
namespace ConsulServiceDiscovery.LoadBalancer
{
    public class RandomLoadBalancer : ILoadBalancer
    {
        private readonly Random _random = new Random();
        public string Resolve(IList<string> services)
        {
            var index = _random.Next(services.Count);
            return services[index];
        }
    }
}
namespace ConsulServiceDiscovery.LoadBalancer
{
    public class RoundRobinLoadBalancer : ILoadBalancer
    {
        private readonly object _lock = new object();
        private int _index = 0;
        public string Resolve(IList<string> services)
        {
            lock (_lock) {
                if (_index >= services.Count) {
                    _index = 0;
                }
                return services[_index++];
            }
        }
    }
}
namespace ConsulServiceDiscovery.LoadBalancer
{
    public static class TypeLoadBalancer
    {
        // 单例
        public static ILoadBalancer RandomLoad = new RandomLoadBalancer();
        public static ILoadBalancer RoundRobinLoad = new RoundRobinLoadBalancer();
    }
}

扩展方法创建ServiceBuilder

namespace ConsulServiceDiscovery
{
    public static class ServiceProviderExtension
    {
        public static IServiceBuilder CreateServiceBuilder(this IServiceProvider serviceProvider, Action<IServiceBuilder> config) {
            var builder = new ServiceBuilder(serviceProvider);
            config(builder);
            return builder;
        }
    }
}

这样,服务发现的代码就完成了。

下面是调用测试代码,创建一个控制台项目,在Program.cs里使用轮询的方式代码如下

namespace ConsulTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var serviceProvider = new ConsulServiceProvider(new Uri("http://localhost:8500"));
            var aspNetCoreService = serviceProvider.CreateServiceBuilder(builder => {
                builder.ServiceName = "AspNetCore";
                builder.LoadBalancer = TypeLoadBalancer.RoundRobinLoad;//可选RandomLoad
                builder.UriScheme = Uri.UriSchemeHttp;
            });
            var httpClient = new HttpClient();
            for (int i = 0; i < 100; i++) {
                Console.WriteLine($"----------第{i}次请求-----------");
                try
                {
                    var uri = aspNetCoreService.BuildAsync("/Health").Result;
                    Console.WriteLine($"{DateTime.Now} - 正在调用:{uri}");
                    var content = httpClient.GetStringAsync(uri).Result;
                    Console.WriteLine($"返回结果:{content}");
                }
                catch (Exception ex) {
                    Console.WriteLine(ex.ToString());
                }
                Task.Delay(500).Wait();
            }
            Console.ReadLine();
        }
    }
}

执行效果

可以看到,轮询方式会轮流请求1234,1235,1236端口的三个服务地址。

更新于:8天前
赞一波!

文章评论

全部评论