(精华)2020年9月22日 微服务 Consul工具层封装和使用

封装

Cluster文件夹

/// <summary>
    /// 负载均衡抽象实现
    /// </summary>
    public abstract class AbstractLoadBalance : ILoadBalance
    {
        public ServiceUrl Select(IList<ServiceUrl> serviceUrls)
        {
            if (serviceUrls == null || serviceUrls.Count ==0)
                return null;
            if (serviceUrls.Count == 1)
                return serviceUrls[0];
            return DoSelect(serviceUrls);
        }

        /// <summary>
        /// 子类去实现
        /// </summary>
        /// <param name="serviceUrls"></param>
        /// <returns></returns>
        public abstract ServiceUrl DoSelect(IList<ServiceUrl> serviceUrls);
    }
/// <summary>
    /// 服务负载均衡
    /// </summary>
    public interface ILoadBalance
    {
        /// <summary>
        /// 服务选择
        /// </summary>
        /// <param name="serviceUrls"></param>
        /// <returns></returns>
        ServiceUrl Select(IList<ServiceUrl> serviceUrls);
    }
/// <summary>
    /// 随机负载均衡
    /// 1、还可以实现加权轮询
    /// </summary>
    public class RandomLoadBalance : AbstractLoadBalance
    {
        private readonly Random random = new Random();

        public override ServiceUrl DoSelect(IList<ServiceUrl> serviceUrls)
        {
            // 1、获取随机数
            var index = random.Next(serviceUrls.Count);

            // 2、选择一个服务进行连接
            return serviceUrls[index];
        }
    }

HttpClientConsul文件夹

/// <summary>
    /// consul httpclient扩展
    /// </summary>
   public class ConsulHttpClient
   {
        private readonly IServiceDiscovery serviceDiscovery;
        private readonly ILoadBalance loadBalance;
        private readonly IHttpClientFactory httpClientFactory;
        public ConsulHttpClient(IServiceDiscovery serviceDiscovery,
                                    ILoadBalance loadBalance,
                                    IHttpClientFactory httpClientFactory)
        {
            this.serviceDiscovery = serviceDiscovery;
            this.loadBalance = loadBalance;
            this.httpClientFactory = httpClientFactory;
        }

        /// <summary>
        /// Get方法
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// param name="ServiceSchme">服务名称:(http/https)</param>
        /// <param name="ServiceName">服务名称</param>
        /// <param name="serviceLink">服务路径</param>
        /// <returns></returns>
        public async Task<T> GetAsync<T>(string Serviceshcme, string ServiceName,string serviceLink)
        {
            // 1、获取服务
            IList<ServiceUrl> serviceUrls = await serviceDiscovery.Discovery(ServiceName);

            // 2、负载均衡服务
            ServiceUrl serviceUrl = loadBalance.Select(serviceUrls);

            // 3、建立请求
            HttpClient httpClient = httpClientFactory.CreateClient("mrico");
            HttpResponseMessage response = await httpClient.GetAsync(serviceUrl.Url + serviceLink);

            // 3.1json转换成对象
            if (response.StatusCode == HttpStatusCode.OK)
            {
                string json = await response.Content.ReadAsStringAsync();

                return JsonConvert.DeserializeObject<T>(json);
            } else
            {
                throw new Exception($"{ServiceName}服务调用错误");
            }
        }
    }
/// <summary>
    /// HttpClientFactory conusl下的扩展
    /// </summary>
    public static class ConsulHttpClientServiceCollectionExtensions
    {
        /// <summary>
        /// 添加consul
        /// </summary>
        /// <typeparam name="ConsulHttpClient"></typeparam>
        /// <param name="builder"></param>
        /// <returns></returns>
        public static IServiceCollection AddHttpClientConsul<ConsulHttpClient>(this IServiceCollection services) where ConsulHttpClient : class
        {
            // 1、注册consul
            services.AddConsulDiscovery();

            // 2、注册服务负载均衡
            services.AddSingleton<ILoadBalance, RandomLoadBalance>();

            // 3、注册httpclient
            services.AddSingleton<ConsulHttpClient>();

            return services;
         }
    }

HttpClientPolly文件夹

/// <summary>
    /// HttpClient熔断降级策略选项
    /// </summary>
   public class PollyHttpClientOptions
   {
        /// <summary>
        /// 超时时间设置,单位为秒
        /// </summary>
        public int TimeoutTime { set; get; }

        /// <summary>
        /// 失败重试次数
        /// </summary>
        public int RetryCount { set; get; }

        /// <summary>
        /// 执行多少次异常,开启短路器(例:失败2次,开启断路器)
        /// </summary>
        public int CircuitBreakerOpenFallCount { set; get; }

        /// <summary>
        /// 断路器开启的时间(例如:设置为2秒,短路器两秒后自动由开启到关闭)
        /// </summary>
        public int CircuitBreakerDownTime { set; get; }

        /// <summary>
        /// 降级处理(将异常消息封装成为正常消息返回,然后进行响应处理,例如:系统正在繁忙,请稍后处理.....)
        /// </summary>
        public HttpResponseMessage httpResponseMessage { set; get; }
    }
/// <summary>
   /// 微服务中HttpClient熔断,降级策略扩展
   /// </summary>
   public static class PollyHttpClientServiceCollectionExtensions
   {
        /// <summary>
        /// Httpclient扩展方法
        /// </summary>
        /// <param name="services">ioc容器</param>
        /// <param name="name">HttpClient 名称(针对不同的服务进行熔断,降级)</param>
        /// <param name="action">熔断降级配置</param>
        /// <param name="TResult">降级处理错误的结果</param>
        /// <returns></returns>
        public static IServiceCollection AddPollyHttpClient(this IServiceCollection services, string name,Action<PollyHttpClientOptions> action)
        {
            // 1、创建选项配置类
            PollyHttpClientOptions options = new PollyHttpClientOptions();
            action(options);

            // 2、配置httpClient,熔断降级策略
            services.AddHttpClient(name)
           //1.1 降级策略
           .AddPolicyHandler(Policy<HttpResponseMessage>.HandleInner<Exception>().FallbackAsync(options.httpResponseMessage, async b =>
           {
               // 1、降级打印异常
               Console.WriteLine($"服务{name}开始降级,异常消息:{b.Exception.Message}");
               // 2、降级后的数据
               Console.WriteLine($"服务{name}降级内容响应:{options.httpResponseMessage.Content.ToString()}");
               await Task.CompletedTask;
           }))
            // 1.2 断路器策略
            .AddPolicyHandler(Policy<HttpResponseMessage>.Handle<Exception>().CircuitBreakerAsync(options.CircuitBreakerOpenFallCount, TimeSpan.FromSeconds(options.CircuitBreakerDownTime), (ex, ts) => {
                Console.WriteLine($"服务{name}断路器开启,异常消息:{ex.Exception.Message}");
                Console.WriteLine($"服务{name}断路器开启时间:{ts.TotalSeconds}s");
            }, () => {
                Console.WriteLine($"服务{name}断路器关闭");
            }, () => {
                Console.WriteLine($"服务{name}断路器半开启(时间控制,自动开关)");
            }))
            // 1.3 重试策略
            .AddPolicyHandler(Policy<HttpResponseMessage>
              .Handle<Exception>()
              .RetryAsync(options.RetryCount)
            )
            // 1.4 超时策略
            .AddPolicyHandler(Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(options.TimeoutTime)));
            
            return services;
        }


    }

Registry文件夹

/// <summary>
    /// consul服务发现实现
    /// </summary>
    public class ConsulServiceDiscovery : IServiceDiscovery
    {
        private readonly IConfiguration configuration;
        public ConsulServiceDiscovery(IConfiguration configuration)
        {
            this.configuration = configuration;
        }

        public async Task<IList<ServiceUrl>> Discovery(string serviceName)
        {
            ServiceDiscoveryConfig serviceDiscoveryConfig = configuration.GetSection("ConsulDiscovery").Get<ServiceDiscoveryConfig>();

            // 1、创建consul客户端连接
            var consulClient = new ConsulClient(configuration =>
            {
                //1.1 建立客户端和服务端连接
                configuration.Address = new Uri(serviceDiscoveryConfig.RegistryAddress);
            });

            // 2、consul查询服务,根据具体的服务名称查询
            var queryResult = await consulClient.Catalog.Service(serviceName);

            // 3、将服务进行拼接
            var list = new List<ServiceUrl>();
            foreach (var service in queryResult.Response)
            {
                list.Add(new ServiceUrl { Url = service.ServiceAddress + ":" + service.ServicePort });
            }
            return list;
        }
    }
/// <summary>
    /// consul服务注册实现
    /// </summary>
    public class ConsulServiceRegistry : IServiceRegistry
    {
        /// <summary>
        /// 注册服务
        /// </summary>
        /// <param name="serviceNode"></param>
        public void Register(ServiceRegistryConfig serviceNode)
        {
            // 1、创建consul客户端连接
            var consulClient = new ConsulClient(configuration =>
            {
                //1.1 建立客户端和服务端连接
                configuration.Address = new Uri(serviceNode.RegistryAddress);
            });

            // 2、获取服务内部地址

            // 3、创建consul服务注册对象
            var registration = new AgentServiceRegistration()
            {
                ID = serviceNode.Id,
                Name = serviceNode.Name,
                Address = serviceNode.Address,
                Port = serviceNode.Port,
                Tags = serviceNode.Tags,
                Check = new AgentServiceCheck
                {
                    // 3.1、consul健康检查超时间
                    Timeout = TimeSpan.FromSeconds(10),
                    // 3.2、服务停止5秒后注销服务
                    DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5),
                    // 3.3、consul健康检查地址
                    HTTP = serviceNode.HealthCheckAddress,
                    // 3.4 consul健康检查间隔时间
                    Interval = TimeSpan.FromSeconds(10),
                }
            };

            // 4、注册服务
            consulClient.Agent.ServiceRegister(registration).Wait();

            // 5、关闭连接
            consulClient.Dispose();
        }

        /// <summary>
        /// 注销服务
        /// </summary>
        /// <param name="serviceNode"></param>
        public void Deregister(ServiceRegistryConfig serviceNode)
        {
            // 1、创建consul客户端连接
            var consulClient = new ConsulClient(configuration =>
            {
                //1.1 建立客户端和服务端连接
                configuration.Address = new Uri(serviceNode.RegistryAddress);
            });

            // 2、注销服务
            consulClient.Agent.ServiceDeregister(serviceNode.Id);

            // 3、关闭连接
            consulClient.Dispose();
        }

    }
/// <summary>
   /// 服务发现
   /// </summary>
   public interface IServiceDiscovery
   {
        /// <summary>
        /// 服务发现
        /// </summary>
        /// <param name="serviceName">服务名称</param>
        /// <returns></returns>
        Task<IList<ServiceUrl>> Discovery(string serviceName);
    }
/// <summary>
    /// 服务注册
    /// </summary>
   public interface IServiceRegistry
   {
        /// <summary>
        /// 注册服务
        /// </summary>
        void Register(ServiceRegistryConfig serviceNode);

        /// <summary>
        /// 撤销服务
        /// </summary>
        void Deregister(ServiceRegistryConfig serviceNode);
    }
/// <summary>
    /// 服务发现配置
    /// </summary>
    public class ServiceDiscoveryConfig
    {
        /// <summary>
        /// 服务注册地址
        /// </summary>
        public string RegistryAddress { set; get; }
    }
/// <summary>
   /// 服务注册节点
   /// </summary>
   public class ServiceRegistryConfig
   {
        // 服务ID
        public string Id { get; set; }

        // 服务名称
        public string Name { get; set; }

        // 服务标签(版本)
        public string[] Tags { set; get; }

        // 服务地址(可以选填 === 默认加载启动路径)
        public string Address { set; get; }

        // 服务端口号(可以选填 === 默认加载启动路径端口)
        public int Port { set;get; }

        // 服务注册地址
        public string RegistryAddress { get; set; }

        // 服务健康检查地址
        public string HealthCheckAddress { get; set; }
    }
/// <summary>
    /// 服务url
    /// </summary>
    public class ServiceUrl
    {
        public string Url { set; get; }
    }

使用的扩展方法

/// <summary>
    /// 微服务注册发现使用扩展
    /// </summary>
   public static class MicroServiceConsulApplicationBuilderExtensions
   {
        public static IApplicationBuilder UseConsulRegistry(this IApplicationBuilder app)
        {
            // 1、从IOC容器中获取Consul服务注册配置
            var serviceNode = app.ApplicationServices.GetRequiredService<IOptions<ServiceRegistryConfig>>().Value;

            // 2、获取应用程序生命周期
            var lifetime = app.ApplicationServices.GetRequiredService<IHostApplicationLifetime>();

            // 2.1 获取服务注册实例
            var serviceRegistry = app.ApplicationServices.GetRequiredService<IServiceRegistry>();

            // 3、获取服务地址
            var features = app.Properties["server.Features"] as FeatureCollection;
            var address = features.Get<IServerAddressesFeature>().Addresses.First();
            var uri = new Uri(address);

            // 4、注册服务
            serviceNode.Id = Guid.NewGuid().ToString();
            serviceNode.Address = $"{uri.Scheme}://{uri.Host}";
            serviceNode.Port = uri.Port;
            serviceNode.HealthCheckAddress = $"{uri.Scheme}://{uri.Host}:{uri.Port}{serviceNode.HealthCheckAddress}";
            serviceRegistry.Register(serviceNode);

            // 5、服务器关闭时注销服务
            lifetime.ApplicationStopping.Register(() =>
            {
                serviceRegistry.Deregister(serviceNode);
            });

            return app;
        }
   }
/// <summary>
    /// Console 注册中心扩展(加载配置)
    /// </summary>
    public static class MicroServiceConsulServiceCollectionExtensions
    {
        // consul服务注册
        public static IServiceCollection AddConsulRegistry(this IServiceCollection services, IConfiguration configuration)
        {
            // 1、加载Consul服务注册配置
            services.Configure<ServiceRegistryConfig>(configuration.GetSection("ConsulRegistry"));

            // 2、注册consul注册
            services.AddSingleton<IServiceRegistry, ConsulServiceRegistry>();
            return services;
        }

        // consul服务发现
        public static IServiceCollection AddConsulDiscovery(this IServiceCollection services)
        {
            // 1、加载Consul服务发现配置
           // services.Configure<ServiceDiscoveryConfig>(configuration.GetSection("ConsulDiscovery"));

            // 2、注册consul服务发现
            services.AddSingleton<IServiceDiscovery, ConsulServiceDiscovery>();
            return services;
        }

    }

服务注册

首先在配置文件中添加如下配置

"ConsulRegistry": {
    "Name": "TeamService",
    "RegistryAddress": "http://127.0.0.1:8500",
    "HealthCheckAddress": "/HealthCheck"
  }
public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // 1、注册上下文到IOC容器
            services.AddDbContext<TeamContext>(options => {
                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
            });
            // 2、注册团队service
            services.AddScoped<ITeamService, TeamServiceImpl>();

            // 3、注册团队仓储
            services.AddScoped<ITeamRepository, TeamRepository>();

            // 4、添加映射
            //services.AddAutoMapper();

            // 5、添加服务注册条件
            services.AddConsulRegistry(Configuration);

            services.AddControllers();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();

            // 1、consul服务注册
            app.UseConsulRegistry();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }

聚合层发现服务

"ConsulDiscovery": {
    "RegistryAddress": "http://127.0.0.1:8500"
  }
public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // 1、自定义异常处理(用缓存处理)
            var fallbackResponse = new HttpResponseMessage
            {
                Content = new StringContent("系统正繁忙,请稍后重试"),// 内容,自定义内容
                StatusCode = HttpStatusCode.GatewayTimeout // 504
            };
          
            //services.AddHttpClient().AddHttpClientConsul<ConsulHttpClient>();
            #region polly配置
            {
                /*services.AddHttpClient("mrico")
                // 1.1 降级(捕获异常,进行自定义处理)
                .AddPolicyHandler(Policy<HttpResponseMessage>.Handle<Exception>().FallbackAsync(fallbackResponse, async b =>
                {
                    // 1、降级打印异常
                    Console.WriteLine($"开始降级,异常消息:{b.Exception.Message}");
                    // 2、降级后的数据
                    //Console.WriteLine($"降级内容响应:{}");
                    await Task.CompletedTask;
                }))
                // 1.2 熔断机制
                .AddPolicyHandler(Policy<HttpResponseMessage>.Handle<Exception>().CircuitBreakerAsync(3, TimeSpan.FromSeconds(10), (ex, ts) =>
                {
                    Console.WriteLine($"断路器开启,异常消息:{ex.Exception.Message}");
                    Console.WriteLine($"断路器开启时间:{ts.TotalSeconds}s");
                }, () =>
                {
                    Console.WriteLine($"断路器重置");
                }, () =>
                {
                    Console.WriteLine($"断路器半开启(一会开,一会关)");
                }))
                // 1.3 失败重试
                .AddPolicyHandler(Policy<HttpResponseMessage>
                  .Handle<Exception>()
                  .RetryAsync(3)
                )
                //1.4、超时
                .AddPolicyHandler(Policy.TimeoutAsync<HttpResponseMessage>(TimeSpan.FromSeconds(2)));*/
            }
            #endregion


            // 1.2 封装之后的调用PollyHttpClient
            services.AddPollyHttpClient("mrico", options => {
                options.TimeoutTime = 1; // 1、超时时间
                options.RetryCount = 3;// 2、重试次数
                options.CircuitBreakerOpenFallCount = 2;// 3、熔断器开启(多少次失败开启)
                options.CircuitBreakerDownTime = 100;// 4、熔断器开启时间
                options.httpResponseMessage = fallbackResponse;// 5、降级处理
            })
                // 2、consul封装
                .AddHttpClientConsul<ConsulHttpClient>();

            /*// 2、注册consul服务发现
            services.AddConsulDiscovery();

            // 3、注册负载均衡
            services.AddSingleton<ILoadBalance, RandomLoadBalance>();*/

            // 4、注册team服务
            services.AddSingleton<ITeamServiceClient, HttpTeamServiceClient>();

            services.AddControllers();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
/// <summary>
    /// 服务调用实现
    /// </summary>
    public class HttpTeamServiceClient : ITeamServiceClient
    {
        /*public readonly IServiceDiscovery serviceDiscovery;
        public readonly ILoadBalance loadBalance;*/
        private readonly IHttpClientFactory httpClientFactory;
        private readonly string ServiceSchme = "https";
        private readonly string ServiceName = "teamservice"; //服务名称
        private readonly string ServiceLink = "/Teams"; //服务名称
        //private readonly ConsulHttpClient consulHttpClient;
        public HttpTeamServiceClient(/*IServiceDiscovery serviceDiscovery, 
                                    ILoadBalance loadBalance,*/
                                    IHttpClientFactory httpClientFactory/*,
                                    ConsulHttpClient consulHttpClient*/)
        {
            /*this.serviceDiscovery = serviceDiscovery;
            this.loadBalance = loadBalance;*/
            this.httpClientFactory = httpClientFactory;
            //this.consulHttpClient = consulHttpClient;
        }

        public async Task<IList<Team>> GetTeams()
        {
            // 1、获取服务
            /* IList<ServiceUrl> serviceUrls = await serviceDiscovery.Discovery(ServiceName);

             // 2、负载均衡服务
             ServiceUrl serviceUrl = loadBalance.Select(serviceUrls);

             // string name = "https://localhost:5001";*/

            // 3、建立请求
            for (int i =0;i < 100;i++)
            {
                try
                {
                    Thread.Sleep(1000);
                    HttpClient httpClient = httpClientFactory.CreateClient("mrico");
                    HttpResponseMessage response = await httpClient.GetAsync("https://localhost:5001" + ServiceLink);

                    // 3.1json转换成对象
                    IList<Team> teams = null;
                    if (response.StatusCode == HttpStatusCode.OK)
                    {
                        string json = await response.Content.ReadAsStringAsync();

                        teams = JsonConvert.DeserializeObject<List<Team>>(json);
                    } else
                    {
                        Console.WriteLine($"降级处理:{await response.Content.ReadAsStringAsync()}");
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine($"异常捕获:{e.Message}");
                }
                
            }
            
           //  List<Team> teams = await consulHttpClient.GetAsync<List<Team>>(ServiceSchme, ServiceName, ServiceLink);

            return null;
        }

       

    }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

愚公搬代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值