.NetCore下Ocelot + Nacos 实现负载均衡

接上一篇:".NetCore 接入 Nacos,实现配置中心和服务注册"

本篇实现 Nacos对接Ocelot实现负载均衡,依旧基于.NetCore3.1实现,如在.Net6以及之上实现,更新组件版本。

文章目录

一、所需类库

nacos-sdk-csharp.AspNetCore 版本1.3.8,.Net6以上,更新到1.3.10
Ocelot 版本16.0.1,.Net6以上可更新到最新版本

二、封装类库

Nacos

此类名称不能修改,因为需要与Ocelot中ServiceDiscoveryProvider的Type匹配

csharp 复制代码
using System.Linq;
using System.Collections.Generic;
using System.Threading.Tasks;
using Ocelot.ServiceDiscovery.Providers;
using Ocelot.Values;
using Nacos.V2;
using Microsoft.Extensions.Options;
using NacosConstants = Nacos.V2.Common.Constants;
using Nacos.AspNetCore.V2;
using Ocelot.Logging;
using Nacos.V2.Naming.Dtos;
using Service = Ocelot.Values.Service;

namespace Ocelot.Provider.Nacos.AspNetCore
{
	
    public class Nacos : IServiceDiscoveryProvider
    {
        private readonly INacosNamingService _client;
        private readonly string _serviceName;
        private readonly string _groupName;
        private readonly IOcelotLogger _logger;
        private readonly List<string> _clusters;

        public Nacos(string serviceName, INacosNamingService client, IOptions<NacosAspNetOptions> options, IOcelotLoggerFactory factory)
        {
            _serviceName = serviceName;
            _client = client;
            _groupName = string.IsNullOrWhiteSpace(options.Value.GroupName) ?
                NacosConstants.DEFAULT_GROUP : options.Value.GroupName;
            _clusters = (string.IsNullOrWhiteSpace(options.Value.ClusterName) ? NacosConstants.DEFAULT_CLUSTER_NAME : options.Value.ClusterName).Split(",").ToList();
            _logger = factory.CreateLogger<Nacos>();
        }


        public async Task<List<Service>> Get()
        {
            var services = new List<Service>();
            _logger.LogInformation($"Trying to get service instances from Nacos for service: {_serviceName}");
            var instances = await _client.SelectInstances(_serviceName, _groupName, _clusters, true);
            if (instances == null || !instances.Any())
            {
                _logger.LogWarning($"No service instances found in Nacos for service: {_serviceName}");
                return services;
            }
            foreach (var serviceEntry in instances)
            {
                if (IsValid(serviceEntry))
                {
                    services.Add(BuildService(serviceEntry));
                }
                else
                {
                    _logger.LogWarning($"Unable to use service Port: {serviceEntry.Port} as it is invalid.");
                }
            }

            _logger.LogInformation($"Found {services.Count} service instances in Nacos for service: {_serviceName}");
            return services;
        }

        private Service BuildService(Instance instance)
        {
            return new Service(
                instance.ServiceName,
                new ServiceHostAndPort(instance.Ip, instance.Port),
                instance.InstanceId,
                GetVersionFromMetadata(instance.Metadata),
               GetTagFromMetadata(instance.Metadata));
        }

        private bool IsValid(Instance instance)
        {
            if (instance.Port <= 0)
            {
                return false;
            }
            return true;
        }
        private IEnumerable<string> GetTagFromMetadata(Dictionary<string, string> metadata)
        {
            if (metadata == null)
            {
                return Enumerable.Empty<string>();
            }
            return metadata.Select(t => { return $"{t.Key}:{t.Value}"; });
        }
        private string GetVersionFromMetadata(Dictionary<string, string> metadata)
        {
            if (metadata.TryGetValue("version", out var version))
            {
                return version;
            }
            return "";
        }
    }
}
NacosServiceDiscoveryProviderFactory
csharp 复制代码
using Ocelot.ServiceDiscovery;
using Microsoft.Extensions.DependencyInjection;
using Nacos.V2;
using Microsoft.Extensions.Options;
using Nacos.AspNetCore.V2;
using Ocelot.Logging;

namespace Ocelot.Provider.Nacos.AspNetCore
{
    public static class NacosServiceDiscoveryProviderFactory
    {
        public static ServiceDiscoveryFinderDelegate Get = (provider, config, route) =>
        {
            var client = provider.GetService<INacosNamingService>();
            if (config.Type?.ToLower() == "nacos" && client != null)
            {
                var option = provider.GetService<IOptions<NacosAspNetOptions>>();
                var logger = provider.GetRequiredService<IOcelotLoggerFactory>();
                return new Nacos(route.ServiceName, client, option, logger);
            }
            return null;
        };

    }
}
OcelotBuilderExtensions
csharp 复制代码
using Microsoft.Extensions.DependencyInjection;
using Nacos.AspNetCore.V2;
using Ocelot.DependencyInjection;

namespace Ocelot.Provider.Nacos.AspNetCore
{
    public static class OcelotBuilderExtensions
    {
        public static IOcelotBuilder AddNacos(this IOcelotBuilder builder, string section = "nacos")
        {
        	//网关服务注册到Nacos
            builder.Services.AddNacosAspNet(builder.Configuration, section);
            builder.Services.AddSingleton(NacosServiceDiscoveryProviderFactory.Get);
            return builder;
        }
    }
}

三、网关对接

引用Ocelot.Provider.Nacos.AspNetCore
appsettings.json配置如下:

csharp 复制代码
"Nacos": {
  "Listeners": [ //配置监听列表,包含多个监听项
    {
      "Optional": false, //是否为可选配置。false表示如果配置不存在,应用启动会失败;true表示配置不存在时忽略
      "DataId": "ocelot",
      "Group": "DEFAULT_GROUP" //配置所属的分组,默认为DEFAULT_GROUP
    }
  ],
  "ServerAddresses": [ "http://192.168.5.210:8848" ], //Nacos 服务器地址列表
  "DefaultTimeOut": 15000,
  "Namespace": "8f67799f-0eb9-42b1-94e5-080d9b1c56ea", // 命名空间 ID,用于隔离不同环境的配置和服务,Please set the value of Namespace ID !!!!!!!!
  "ListenInterval": 1000, //监听间隔时间,单位为毫秒
  "ServiceName": "NacosGateway", //注册到注册中心的服务名称
  "Weight": 100, //服务权重,用于服务路由时的负载均衡计算
  "RegisterEnabled": true, //是否启用服务注册
  "InstanceEnabled": true, //实例是否启用
  "Ephemeral": true, //是否为临时实例,true表示是临时实例,服务宕机后会被自动摘除
  "Secure": false, //是否使用安全连接
  "UserName": "nacos",
  "Password": "nacos",
  "ConfigUseRpc": false, //是否使用 RPC 协议获取配置
  "NamingUseRpc": false, //是否使用 RPC 协议进行服务发现
  "NamingLoadCacheAtStart": "", //启动时是否加载服务发现缓存
  "LBStrategy": "WeightRandom", //负载均衡策略,WeightRandom表示加权随机,WeightRoundRobin表示加权轮询
  "Metadata": { //服务实例的元数据信息,为键值对形式
    "version": "1.0",
    "feature": "true"
  }
}

以上Listeners配置是因为ocelot配置内容也存放在Nacos中,如是使用本地ocelot.json文件,则无需此项配置。

Startup中使用AddNacos进行接入

csharp 复制代码
services.AddOcelot().AddNacos();

完整代码

csharp 复制代码
public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers(config =>
        {
        });
        // 添加网关
        services.AddOcelot().AddNacos(); ;

    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHostApplicationLifetime lifetime)
    {
        app.UseOcelot().Wait();
    }
}

Ocelot配置内容如下:

csharp 复制代码
{
  "Routes": [
    {
      "ServiceName": "NacosWebApi",
      "DownstreamScheme": "http",
      "DownstreamPathTemplate": "/{url}",
      "UpstreamPathTemplate": "/api/{url}",
      "UseServiceDiscovery": true,
      "UpstreamHttpMethod": [ "Get" ],
      "LoadBalancerOptions": {
        "Type": "RoundRobin"
      }
    }
  ],
  "GlobalConfiguration": {
    "ServiceDiscoveryProvider": {
      "Type": "Nacos"
    }
  }
}
1、使用项目中本地ocelot.json文件

Program中增加

csharp 复制代码
builder.AddJsonFile("ocelot.json", optional: false, reloadOnChange: true);

完整代码

csharp 复制代码
public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((hostingContext, builder) =>
        {
            builder.AddJsonFile("ocelot.json", optional: false, reloadOnChange: true);
        })
         .ConfigureWebHostDefaults(webBuilder =>
         {
             webBuilder.UseStartup<Startup>();
         });
2、ocelot配置存入Nacos,从Nacos中读取配置

Program中增加

csharp 复制代码
var c = builder.Build();
builder.AddNacosV2Configuration(c.GetSection("Nacos"));

完整代码

csharp 复制代码
public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((hostingContext, builder) =>
        {
            var c = builder.Build();
            builder.AddNacosV2Configuration(c.GetSection("Nacos"));
        })
         .ConfigureWebHostDefaults(webBuilder =>
         {
             webBuilder.UseStartup<Startup>();
         });
相关推荐
流烟默7 小时前
网关 Sentinel 限流实战:API 分组、规则加载与全量匹配踩坑记
nacos·gateway·sentinel·流控规则
上位机妹子8 小时前
EF Core 两种继承映射:每层次一张表(TPH)与每类型一张表(TPT)
数据库·c#·.netcore·ef core
spencer_tseng9 小时前
nacos CVE‑2021‑29441
nacos·alibaba
螺蛳粉 螺蛳粉15 小时前
第五篇:Keepalived + LVS 四层负载均衡高可用实战:DR 模式全流程
运维·负载均衡·lvs·keepalived·高可用
Joy T1 天前
Spring AI 项目中的 pom.xml、application.yml 与 Nacos:从依赖管理到运行配置
spring·nacos·pom.xml·profile·参数配置·application.yml
积流成海2 天前
一致性哈希原理与实现:分布式系统负载均衡
算法·负载均衡·哈希算法·哈希函数·盘子工具站
LoneEon2 天前
CentOS7 部署 Nacos3.x 集群实战:从注册中心到 AI 管理中心
linux·人工智能·nacos
一条大祥脚3 天前
【CS336】lecture4 MoE|负载均衡|MoE并行|训练稳定性|Deepseek回顾
运维·负载均衡
IanSkunk4 天前
从检查设备到训练方案:眼科与视光中心设备联动的数据通路、字段口径与校验规则
服务器·数据库·负载均衡
螺蛳粉 螺蛳粉4 天前
第二篇:keepalived VRRP 原理篇:协议与 VIP 漂移机制深入解析
网络·智能路由器·负载均衡·keepalived·高可用