.Net6注入Consul服务

发布时间 2023-04-07 19:24:43作者: 白码一号

.Net6注入Consul服务

直接上代码了(参考了一下其他博客进行总结一下),我这边项目工程是模拟微服务模式,新建类库把服务打包成了nuget方便调用

现在先大致介绍一下,结尾放demo案例代码

consul心跳 心跳的地址要于下面配置文件的地址一样 这里先定义一会配置文件也要相同

/// <summary>
    /// 写个中间件---扩展
    /// </summary>
    public static class HealthCheckMiddlewareExtension
    {
        /// <summary>
        /// 设置心跳响应
        /// </summary>
        /// <param name="app"></param>
        /// <param name="checkPath">默认是/Health</param>
        /// <returns></returns>
        public static void UseHealthCheckMiddleware(this IApplicationBuilder app, string checkPath = "/healthcheck")
        {
            app.Map(checkPath, applicationBuilder => applicationBuilder.Run(async context =>
            {
                Console.WriteLine($"This is Health Check");
                context.Response.StatusCode = (int)HttpStatusCode.OK;
                await context.Response.WriteAsync("OK");
            }));
        }

    }

配置实体

public class ConsulRegisterOptions
    {
        /// <summary>
        /// Consul 客户端地址
        /// </summary>
        public string Address { get; set; }

        /// <summary>
        /// 健康检查地址
        /// </summary>
        public string HealthCheck { get; set; }

        /// <summary>
        /// 服务名
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 服务绑定Ip
        /// </summary>
        public string Ip { get; set; }

        /// <summary>
        /// 服务绑定端口
        /// </summary>
        public string Port { get; set; }
    }

接口定义

public interface IConsulRegister
    {
        Task ConsulRegistAsync();
    }
//服务注入完后需要在app启动管道中调用方法注入到consul中
public
class ConsulRegister : IConsulRegister { private readonly ConsulRegisterOptions _consulRegisterOptions; public ConsulRegister(IOptionsMonitor<ConsulRegisterOptions> consulRegisterOptions) { _consulRegisterOptions = consulRegisterOptions.CurrentValue; } public async Task ConsulRegistAsync() { var client = new ConsulClient(options => { options.Address = new Uri(_consulRegisterOptions.Address); // Consul客户端地址 }); var registration = new AgentServiceRegistration { ID = Guid.NewGuid().ToString(), // 唯一Id Name = _consulRegisterOptions.Name, // 服务名(分组--多个实例组成的集群) Address = _consulRegisterOptions.Ip, // 服务绑定IP Port = Convert.ToInt32(_consulRegisterOptions.Port), // 服务绑定端口 //Tag 标签 Check = new AgentServiceCheck { DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5), // 服务启动多久后注册 Interval = TimeSpan.FromSeconds(10), // 健康检查时间间隔 HTTP = $"http://{_consulRegisterOptions.Ip}:{_consulRegisterOptions.Port}{_consulRegisterOptions.HealthCheck}", // 健康检查地址 Timeout = TimeSpan.FromSeconds(5) // 超时时间 } }; await client.Agent.ServiceRegister(registration); } }

扩展调度策略 ConsulExtend

 public static class ConsulExtend
    {
        //public static void AddConsulRegister(this IServiceCollection services)
        //{
        //    services.AddTransient<IConsulRegister, ConsulRegister>();
        //}

        /// <summary>
        /// 注册Consul调度策略
        /// </summary>
        /// <param name="services"></param>
        /// <param name="consulDispatcherType"></param>
        public static void AddConsulDispatcher(this IServiceCollection services, ConsulDispatcherType consulDispatcherType)
        {
            switch (consulDispatcherType)
            {
                case ConsulDispatcherType.Average:
                    services.AddTransient<AbstractConsulDispatcher, AverageDispatcher>();
                    break;
                case ConsulDispatcherType.Polling:
                    services.AddTransient<AbstractConsulDispatcher, PollingDispatcher>();
                    break;
                case ConsulDispatcherType.Weight:
                    services.AddTransient<AbstractConsulDispatcher, WeightDispatcher>();
                    break;
                default:
                    break;
            }
        }

        public enum ConsulDispatcherType
        {
            Average = 0,
            Polling = 1,
            Weight = 2
        }
    }

AbstractConsulDispatcher

public abstract class AbstractConsulDispatcher
    {
        protected ConsulRegisterOptions _consulRegisterOptions;
        protected KeyValuePair<string, AgentService>[] _CurrentAgentServiceDictionary;

        public AbstractConsulDispatcher(IOptionsMonitor<ConsulRegisterOptions> consulClientOption)
        {
            this._consulRegisterOptions = consulClientOption.CurrentValue;
        }

        /// <summary>
        /// 负载均衡获取地址
        /// </summary>
        /// <param name="mappingUrl">Consul映射后的地址</param>
        /// <returns></returns>
        public string GetAddress(string mappingUrl)
        {
            Uri uri = new Uri(mappingUrl);
            string serviceName = uri.Host;
            string addressPort = this.ChooseAddress(serviceName);
            return $"{uri.Scheme}://localhost:{addressPort}{uri.PathAndQuery}";
        }

        protected virtual string ChooseAddress(string serviceName)
        {
            ConsulClient client = new ConsulClient(c =>
            {
                c.Address = new Uri($"http://localhost:8500/");
            });
            AgentService agentService = null;

            var response = client.Agent.Services().Result.Response;//获取Consul全部服务清单
            var dictionary = response.Where(s => s.Value.Service.Equals(serviceName, StringComparison.OrdinalIgnoreCase)).ToArray();
            var entrys = client.Health.Service(serviceName).Result.Response;
            List<KeyValuePair<string, AgentService>> serviceList = new List<KeyValuePair<string, AgentService>>();
            for (int i = 0; i < entrys.Length; i++)
            {
                serviceList.Add(new KeyValuePair<string, AgentService>(i.ToString(), entrys[i].Service));
            }
            this._CurrentAgentServiceDictionary = serviceList.ToArray();

            int index = this.GetIndex();
            agentService = this._CurrentAgentServiceDictionary[index].Value;

            return $"{agentService.Port}";
        }

        protected abstract int GetIndex();
    }
AverageDispatcher、PollingDispatcher、WeightDispatcher 剩下的三种实现就不细说了 一会自行下载 打包好后 引入包

或者 把这里调用一下进行注入

 然后是配置文件 配置文件要跟上面心跳地址相同

  "ConsulRegisterOptions": {
    "Address": "http://xxx.xxx.xx.xx:8500", //consul集群客户端的服务地址
    "HealthCheck": "/healthcheck", //consul调用健康检查地址url
    "Name": "VipUser", //当前服务的服务名
    "Ip": "xxx.xx.xx.xxx", //当前api部署在哪台服务器是的ip
    "Port": "5001" //当前api服务端口
  }

以上.net6注入consul进行打包发布完毕

参考的博客:https://blog.csdn.net/sD7O95O/article/details/128630643

demo地址:https://github.com/fengzhonghao8-24/ConsulIntroduction

然后进行打包发布

发布方式有我常用的一种方式是,在本地把代码发布成dll文件,然后通过服务器目录挂载读取DockerFile文件,之前用的方式是把代码直接推到服务器上然后发布后bulid成镜像再run起来,这种方式没docker-compose方便

创建Dockerfile文件进行修改  注意挂载的端口,修改完后要选中dockerfile文件属性设置为始终复制,不能可能出现这里修改完映射端口后但线上容器的端口还是80的情况

FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
ENV TZ=Asia/Shanghai
WORKDIR /app
EXPOSE 5001 5001

RUN sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
RUN sed -i 's|security.debian.org/debian-security|mirrors.ustc.edu.cn/debian-security|g' /etc/apt/sources.list
RUN apt-get clean
RUN apt-get update

# 安装libgdiplus库解决System.Drawing引起的Bug
RUN apt-get install -y --allow-unauthenticated libgdiplus libc6-dev fontconfig
RUN ln -s /usr/lib/libgdiplus.so /usr/lib/gdiplus.dll

COPY ./ ./

ENTRYPOINT ["dotnet", "OneLove.VipUser.Interface.dll"]

然后发布成dll

接下来进行服务器编写consul.yml  (需要注意文件挂载的路径)

version: '3'
services:
  # 项目vipuser
  vipuser_api:
    build: ./vipuser_api/api/publish
    container_name: vipuser_api          
    restart: always
    volumes: 
      - ./vipuser_api/api/publish:/app                              # 方式1:挂载全部的程序文件 + 配置文件
      #- ./configs:/app/configs                      # 方式2:只挂载配置文件
      #- ./logs:/logs                                      # 日志文件
      - /var/run/docker.sock:/var/run/docker.sock  # 容器内部通过 unix socket 使用宿主机 docker engine
    user: root  # 必须确保容器以 root 用户启动!(这样它才有权限读写 docker.socket)
    ports:
      - 5001:5001

然后直接 docker-compose -f consul.yml up -d 

然后开始输入  ip:端口/healthcheck    访问是否正常

正常响应后 查看 ip:8500查看consul集群服务是否正常

 

 这样就 ok了 查看日志也能看到consul心跳记录