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

发布于:2025-09-03 ⋅ 阅读:(19) ⋅ 点赞:(0)

接上一篇:“.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匹配

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
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
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配置如下:

"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进行接入

services.AddOcelot().AddNacos();

完整代码

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配置内容如下:

{
  "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中增加

builder.AddJsonFile("ocelot.json", optional: false, reloadOnChange: true);

完整代码

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中增加

var c = builder.Build();
builder.AddNacosV2Configuration(c.GetSection("Nacos"));

完整代码

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>();
         });

网站公告

今日签到

点亮在社区的每一天
去签到