Nacos3在.NET中的简单应用

一、Nacos简介

1. 什么是 Nacos

Nacos(Dynamic Naming and Configuration Service)即动态命名与配置服务,是阿里巴巴开源的一款集服务发现、配置管理和服务管理于一体的云原生平台核心组件。它致力于帮助开发者更轻松地构建、管理和维护分布式系统中的服务。通过 Nacos,开发者可以实现服务的自动注册与发现,动态配置管理,以及服务的健康检查等功能,大大简化了分布式系统的开发与运维。

2.Nacos 的核心功能

服务发现与服务健康监测:Nacos 允许服务提供者将自己注册到 Nacos 服务器上,并通过 Nacos 服务器提供的服务发现功能,让服务消费者能够轻松找到目标服务。例如,在一个电商系统中,商品服务、订单服务等可以注册到 Nacos,当用户查看商品详情时,商品服务的消费者(如前端应用)能够通过 Nacos 快速发现并调用商品服务。同时,Nacos 内置了实时健康检查机制,能够监控各个服务实例的健康状态,自动剔除异常实例,确保只有健康的服务节点能够处理请求,从而提升整个系统的可靠性和稳定性。

动态配置管理:Nacos 提供了统一的配置管理中心,支持动态配置更新。开发者可以将应用的配置信息集中存储在 Nacos 中,当配置发生变化时,应用能够实时获取到最新的配置而无需重启。这在一些需要频繁调整配置参数(如数据库连接字符串、缓存策略等)或分布式文件更新的场景中非常实用。

3. Nacos 的优势。
易于使用:Nacos 提供了直观的 Web 界面和简洁的 API,为开发和运维人员在云原生环境中带来了便捷的服务注册、发现和配置更新操作,降低了学习成本。
高可用性:Nacos 采用了集群架构,通过多节点部署来保证系统的高可用性,即使部分节点出现故障,整个系统仍能正常运行。
生态丰富:与 Spring Cloud、Dubbo 等主流的微服务框架有良好的集成,方便开发者在不同的技术栈中使用 Nacos。其无缝支持 Kubernetes、Spring Cloud、Spring AI、Dify 在主流公共云上更容易部署和运行 (例如阿里云、华为云、腾讯云、Azure 和 AWS )多租户和多环境支持。为构建云原生和微服务架构提供了一整套简单而全面的解决方案。

二、Nacos下载与安装

在Windows 11中安装Nacos3

1、下载 Nacos及其依赖环境

 Nacos 官方 GitHub 仓库的Releases 页面下载:Windows 下载 .zip 包。Linux / Unix / Mac 下载 .tar.gz 包。

Nacos官网下载界面:

注意:红框中可看到Nacos3要求JDK/JRE版本17及以上,如果未安装,可以从 Oracle 官网下载并安装。

2、解压安装包

将下载的.zip文件解压到指定目录。解压后的目录结构如下:

3、启动Nacos

进入nacos\bin目录,结构如下:

Windows使用startup.cmd,Mac使用 startup.sh启动,双击默认启动集群,如果需要启动单体,可以在命令窗口使用 startup.cmd -m standalone。首次运行出现报错Please set the JAVA_HOME variable in your environment。需设置JAVA_HOME环境变量,将下载JDK17压缩包解压并配置JAVA_HOME环境变量,在命令窗口中执行以下命令:

setx JAVA_HOME "D:\软件\Nacos\microsoft-jdk-17.0.15-windows-x64\jdk17"

新开命令窗口再去启动Nacos,Nacos3在首次启动时会要求输入认证令牌和身份标识,此处一开始没太关注随便输入了字符串,导致下一个启动问题出现

报错:Factory method 'tokenManager' threw exception with message: the length of secret key must great than or equal 32 bytes; And the secret key  must be encoded by base64.Please see https://nacos.io/docs/latest/manual/admin/auth/。出现这个问题是由于nacos3的鉴权引起的,从Nacos3.0.0版本开始控制台访问默认开启鉴权,而我在一开始启动设置鉴权密钥的时候随便输入了秘钥,没有设置Base64编码的字符串,依据提示参阅文档修改application.properties中的配置信息

修改完后再次启动

由上图可以看出Nacos已经启动成功了,可以在浏览器访问http://192.168.1.36:8080/index.html,其中ip是本机电脑的ip,在登录页面输入你设置的用户名和密码就可访问nacos。

三、Nacos在 .NET 中应用案例

本案例在.NET8 中实现,新建一个.NET API项目并安装必要的NuGet包:

nacos-sdk-csharp.AspNetCore
nacos-sdk-csharp.Extensions.Configuration

在Web Api服务中新增一个测试控制器TestController用来测试:

namespace NETAPI1.Controllers
{
    [ApiController]
    [Route("[controller]/[action]")]
    public class TestController : ControllerBase
    {
        /// <summary>
        /// 配置
        /// </summary>
        private IConfiguration _configuration;

        private readonly INacosNamingService _nacosNamingService;

        public TestController(IConfiguration configuration, INacosNamingService nacosNamingService)
        {
            _configuration = configuration;
            _nacosNamingService = nacosNamingService;
        }
    }
}

1、配置中心应用

在以往的开发中,我们往往需要将数据库连接字符串这类的配置信息放在application.json文件中,这对于单体应用或服务数量不是很多的微服务应用来说没什么大问题,如果配置需要更新了,我们就直接去服务器上修改就可以,但是对于大型微服务应用来说将配置信息放在application.json中就显得很鸡肋了,每次修改配置信息去服务器上一个服务一个服务改的话是很麻烦的,而且还容易出错。这时我们就需要引入配置中心,配置中心是一种集中管理和分发应用配置的系统。它能够将配置从应用程序代码中分离出来,方便管理和修改。配置中心通常支持动态更新配置,不需要重启服务即可生效,并保障配置的一致性和可靠性。Nacos 就具备配置中心的功能。

步骤1:我们在 Nacos 控制台的 “配置管理” -> “配置列表” 中,新增一个Data ID 为 first.setting的配置并发布

步骤2:在.NET API项目中实现 Nacos 配置中心集成

1、在application.json文件中添加Nacos的配置中心信息:

"Nacos": {
	"ServerAddresses": [
		"http://localhost:8848"
	], // Nacos服务器地址
	"Listeners": [
		{
			"Optional": false,
			"DataId": "first.setting", // 配置ID
			"Group": "DEFAULT_GROUP" // 配置组名
		}
	],
	"Namespace":"public",
	"UserName": "nacos", // 用户名
	"Password": "123456" // 密码
}

ServerAddresses是Nacos服务器的地址列表
Namespace表示服务所在的命名空间,默认为public
usernamepassword是访问Nacos服务器的认证信息
Listeners表示配置监听器的数组,包含一个监听器用于监控first.setting配置项的变化
Optional表示监听器是否可选,false表示必需
DataId表示监听的配置项ID,这里是first.setting
Group表示配置项所属的组,这里使用默认的DEFAULT_GROUP

2、在Program 类中新增配置中心集成的代码

//配置中心集成
builder.Configuration.AddNacosV2Configuration(builder.Configuration.GetSection("Nacos"));

3、在控制器TestController中编写获取message配置GetNacosMessage的Action,在这个Action中支持获取Nacos上的message配置,并将这个字符串返回给调用方。

[HttpGet]
public string GetNacosMessage()
{
	//从配置中获取信息
	return _configuration["Message"];
}

步骤3:测试验证

启动应用程序后,在Swagger或直接浏览器中输入http://localhost:5113/Test/GetNacosMessage即可看到我们在Nacos中配置的messgae字符串。这时我们修改Nacos中配置的字符串后,应用会自动获取到最新的配置值,无需重启应用。再次刷新页面显示的字符串改变了。

2、服务注册与发现

服务发现和注册是微服务架构中的关键机制,用于动态管理和协调服务实例之间的通信。服务注册涉及将服务实例的信息(如名称、地址、端口)注册到一个服务注册中心。服务实例在启动时会向注册中心注册,并定期发送心跳信号以保持注册状态。
服务发现则是指客户端或其他服务通过服务注册中心查找和访问服务实例的过程。服务发现有两种主要模式:客户端发现和服务端发现。在客户端发现模式中,客户端直接与注册中心通信获取服务实例信息;在服务端发现模式中,负载均衡器或API网关负责从注册中心获取服务信息并转发请求。
这两者的结合实现了服务的动态扩展和高可用性,简化了微服务之间的通信配置,是构建大规模分布式系统的重要机制。

步骤1:项目中使用服务发现与注册

1.在application.json文件中配置Nacos 服务发现,在Nacos节点下添加配置信息:

        "ServiceName": "FristApi", // 服务名称
        "RegisterEnabled": true, // 启用注册
        "InstanceEnabled": true, // 启用实例
        "Ephemeral": true, // 临时实例
        "GroupName": "DEFAULT_GROUP", // 分组
        "ClusterName": "DEFAULT", // 集群
        "Weight": 100, // 权重
        "Metadata": {
            "version": "1.0.1"
        } //查询特定版本实例

ServiceName是注册服务的名称
RegisterEnabled表示是否启用服务注册
InstanceEnabled表示服务实例是否可用
Ephemeral表示服务实例是否为临时实例(非持久)
GroupName是服务所属的分组,默认是DEFAULT_GROUP
ClusterName是服务所属的集群名称,默认是DEFAULT
Weight表示服务实例的权重,用于负载均衡,这里设置为100
Metadata包含服务实例的元数据,版本信息为1.0.0

在配置中我们将服务设置为了临时实例,这里所说的临时实例是指在服务注册和发现系统中,注册中心不会持久化存储的服务实例。这种实例的信息不会被持久化存储在注册中心的数据库中,当服务实例失效或停止时,注册中心会自动删除该实例的信息,不会保留历史记录。临时实例适用于生命周期较短的服务实例,例如短期运行的批处理任务或临时性服务。如果临时实例在规定时间内未发送心跳信号,注册中心会自动将其移除,确保注册中心只保留当前活跃的服务实例,避免无效实例占用资源。临时实例适用于需要快速启动和停止的服务场景,例如容器化应用的实例管理,支持快速扩展和收缩服务实例数量。

在上述Nacos配置中我们设置服务的权重为100,这个权重是用于服务实例负载均衡的重要参数。它决定了请求在多个服务实例之间的分配比例。具体来说,权重值越高的实例,被选择处理请求的概率就越大。通过设置不同的权重值,可以实现对服务实例的流量控制,优化系统性能和资源利用。里如,在微服务架构下,如果某个服务实例的硬件配置较高,处理能力较强,可以将其权重设置得更高,以便更多的请求能够分配到该实例,从而提高整体系统的效率。相反,如果某个实例的资源较为有限,则可以降低其权重,减少其负载压力。

2.在Program 类中新增服务注册的代码

// 将服务注册到nacos
builder.Services.AddNacosAspNet(builder.Configuration);

3.在控制器TestController中获取信息GetMessages和获取并调用服务GetServiceAndInvoke的Action。其中GetMessages返回服务的Ip、端口号以及信息,GetServiceAndInvoke通过选择一个健康实例并调用GetMessages接口返回数据。通过这2个接口展示了Nacos服务发现的能力。

        [HttpGet]
        public string GetMessages()
        {
            // 获取服务ip和端口
            var ip = HttpContext.Connection.LocalIpAddress.MapToIPv4().ToString();
            var port = HttpContext.Connection.LocalPort;
            return "First Service,服务地址:" + ip + ":" + port + ","+ _configuration["Message"];
        }

        [HttpGet]
        public IActionResult GetServiceAndInvoke()
        {
            var instance = _nacosNamingService.SelectOneHealthyInstance("FristApi").Result;
            if (instance != null)
            {
                HttpClient client = new HttpClient();
                var response = client.GetAsync($"http://{instance.Ip}:{instance.Port}/Test/GetMessages").Result;
                return Ok(new
                {
                    Url = $"http://{instance.Ip}:{instance.Port}/Test/GetMessages",
                    instance.Metadata,
                    result = $"ServiceAndInvoke Service,调用FristApi服务成功,调用GetMessages方法返回数据:{response.Content.ReadAsStringAsync().Result}"
                });
            }
            else
            {
                return StatusCode(500, "选择服务实例失败");
            }
        }

步骤2:测试验证

进入测试接口项目的bin目录,开启两个命令窗口分别执行运行命令,指定不同的端口:

NETAPI1.exe --urls=http://localhost:5001/
NETAPI1.exe --urls=http://localhost:5002/

执行上述命令后我们查看Nacos管理界面的服务列表,会看到两个FristApi服务实例。

最后,我们访问在浏览器中访问http://localhost:5001/Test/GetServiceAndInvoke,会看到浏览器显示出了GetServiceAndInvoke中返回的内容,其中包含了选择的实例信息和FristApi中GetMessages接口返回的内容。我们多刷新几次,会发现页面显示的FristApi的地址在改变,这说明Nacos 服务发现起了作用。

案例完整配置与代码文件

appsettings.json

{
    "Logging": {
        "LogLevel": {
            "Default": "Information",
            "Microsoft.AspNetCore": "Warning"
        }
    },
    "AllowedHosts": "*",
    "Nacos": {
        "ServerAddresses": [
            "http://localhost:8848"
        ], // Nacos服务器地址
        "Listeners": [
            {
                "Optional": false,
                "DataId": "first.setting", // 配置ID
                "Group": "DEFAULT_GROUP" // 配置组名
            }
        ],
        "UserName": "nacos", // 用户名
        "Password": "123456", // 密码
        "ServiceName": "FristApi", // 服务名称
        "RegisterEnabled": true, // 启用注册
        "InstanceEnabled": true, // 启用实例
        "Ephemeral": true, // 临时实例
        "GroupName": "DEFAULT_GROUP", // 分组
        "ClusterName": "DEFAULT", // 集群
        "Weight": 100, // 权重
        "Metadata": {
            "version": "1.0.1"
        } //查询特定版本实例
    }
}

Program.cs

using Nacos.AspNetCore.V2;

var builder = WebApplication.CreateBuilder(args);

//配置中心集成
builder.Configuration.AddNacosV2Configuration(builder.Configuration.GetSection("Nacos"));
// 将服务注册到nacos
builder.Services.AddNacosAspNet(builder.Configuration);
// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

TestController.cs

using Microsoft.AspNetCore.Mvc;
using Nacos.V2;

namespace NETAPI1.Controllers
{
    [ApiController]
    [Route("[controller]/[action]")]
    public class TestController : ControllerBase
    {
        /// <summary>
        /// 配置
        /// </summary>
        private IConfiguration _configuration;

        private readonly INacosNamingService _nacosNamingService;

        public TestController(IConfiguration configuration, INacosNamingService nacosNamingService)
        {
            _configuration = configuration;
            _nacosNamingService = nacosNamingService;
        }

        [HttpGet]
        public string GetNacosMessage()
        {
            //从配置中获取信息
            return _configuration["Message"];
        }

        [HttpGet]
        public string GetMessages()
        {
            // 获取服务ip和端口
            var ip = HttpContext.Connection.LocalIpAddress.MapToIPv4().ToString();
            var port = HttpContext.Connection.LocalPort;
            return "First Service,服务地址:" + ip + ":" + port + ","+ _configuration["Message"];
        }

        [HttpGet]
        public IActionResult GetServiceAndInvoke()
        {
            var instance = _nacosNamingService.SelectOneHealthyInstance("FristApi").Result;
            if (instance != null)
            {
                HttpClient client = new HttpClient();
                var response = client.GetAsync($"http://{instance.Ip}:{instance.Port}/Test/GetMessages").Result;
                return Ok(new
                {
                    Url = $"http://{instance.Ip}:{instance.Port}/Test/GetMessages",
                    instance.Metadata,
                    result = $"ServiceAndInvoke Service,调用FristApi服务成功,调用GetMessages方法返回数据:{response.Content.ReadAsStringAsync().Result}"
                });
            }
            else
            {
                return StatusCode(500, "选择服务实例失败");
            }
        }
    }
}

四、问题总结

1、服务器地址配置错误导致登录失败

起初在项目集成配置中心时,在配置文件appsettings.json中Nacos服务器地址想当然的设置为了http://localhost:8080,导致启动一直失败报错:login failed: caused: No static resource nacos/v1/auth/users/login.

后查找是因为Nacos3.0对端口进行了拆分,控制台使用独立端口 8080,主服务仍为 8848。在Nacos3.0.0更新说明中有:

访问方式:

Nacos 3.0 将根据不同 API 类型默认采用不同的安全认证策略:对于 InnerAPI 和 AdminAPI,将默认启用 ServerIdentity 进行身份验证;对于 ConsoleAPI,将默认启用用户名和密码的身份及权限校验;而对于客户端和应用程序使用的 OpenAPI,则默认与 Nacos 2.0 保持一致,即默认不开启安全认证,需要用户自行查找并启用。
控制台:http://localhost:8080/nacos(需输入鉴权密钥或关闭鉴权)
Open API:http://localhost:8848(用于客户端交互)

通过将配置文件中的Nacos服务器地址改成http://localhost:8848后解决

2、Nacos开启鉴权后未配置UserName和Password导致连接失败

报错:code={"content":null,"contentType":null,"md5":null,"isBeta":false,"tag":null,"lastModified":0,"encryptedDataKey":null,"resultCode":500,"errorCode":403,"message":"Code: 401, Message: User not found! Please check user exist or password is right!.","requestId":null}

这个问题是由于在Nacos的配置文件application.properties启用了认证,因此必须配置UserName和Password。也可以在通过关闭鉴权解决:nacos.core.auth.enabled=false。Nacos鉴权功能(nacos.core.auth.enabled)默认是不开启的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值