.NET Core 接入 Nacos + gRPC 实战:从零搭建跨语言微服务
技术栈:.NET 8 + nacos-sdk-csharp + Grpc.AspNetCore + NacosNetCore.Extensions
本文从一个真实项目出发,手把手带你完成 .NET Core 接入 Nacos 的服务注册/发现,并基于 gRPC 实现 .NET 与 Java(Dubbo)之间的跨语言互调。所有代码均来自实际项目,可直接复用。
一、背景与痛点
在微服务架构中,Java 生态有 Nacos + Dubbo 的成熟方案,但 .NET 侧接入 Nacos 的资料相对较少。当你的团队需要:
-
.NET 服务注册到 Nacos,让 Java 侧能发现并调用
-
.NET 作为消费者,从 Nacos 发现并调用 Java Dubbo-gRPC 服务
-
REST 和 gRPC 双协议共存,对外提供 HTTP API 的同时支持 gRPC 高性能调用
网上能搜到的文章多是"跑通 Hello World",缺少真实项目的分层设计、自动注册、Proto 生成等工程化实践。本文补上这块空白。
二、最终效果
项目跑起来后,你将获得:
| 能力 | 说明 |
|---|---|
| 服务自动注册到 Nacos | 启动即注册,无需手动操作 |
| 提供 gRPC 服务给 Java 调用 | Java 通过 Dubbo-gRPC 协议直接调用 |
| 提供 REST API 给前端调用 | 标准 HTTP 接口,Swagger 文档自动生成 |
| 调用远程 Java gRPC 服务 | 从 Nacos 发现实例,发起 gRPC 调用 |
| 业务层与协议层分离 | 加新接口只需写业务逻辑,协议层自动适配 |
| Proto 文件自动生成 | C# 代码定义接口,一键生成 .proto 交给 Java 团队 |
三、环境准备
| 依赖 | 版本 | 说明 |
|---|---|---|
| .NET SDK | 8.0+ | dotnet --version 验证 |
| Nacos Server | 2.2.0+ | Docker 部署或独立部署均可 |
| IDE | VS 2022 / Rider | 支持 .NET 8 项目 |
四、项目搭建
4.1 创建项目
dotnet new webapi -n NacosDemo-order --no-https
cd NacosDemo-order
4.2 安装 NuGet 包
这是本文用到的核心包,版本亲测可用:
# Nacos SDK
dotnet add package nacos-sdk-csharp --version 1.3.10
dotnet add package nacos-sdk-csharp.AspNetCore --version 1.3.10
dotnet add package nacos-sdk-csharp.Extensions.Configuration --version 1.3.10
dotnet add package nacos-sdk-csharp.IniParser --version 1.3.10
dotnet add package nacos-sdk-csharp.YamlParser --version 1.3.10
# Nacos 扩展(简化注册)
dotnet add package NacosNetCore.Extensions --version 1.0.4.4
# gRPC
dotnet add package Grpc.AspNetCore --version 2.80.0
dotnet add package Grpc.Tools --version 2.80.0
dotnet add package Google.Protobuf --version 3.34.1
dotnet add package Google.Api.CommonProtos --version 2.17.0
# 其他
dotnet add package Swashbuckle.AspNetCore --version 10.1.7
dotnet add package Newtonsoft.Json --version 13.0.4
踩坑提示:
nacos-sdk-csharp的 ASP.NET Core 集成需要配合NacosNetCore.Extensions使用,后者封装了AddNacosAspNet方法,一行代码搞定注册。如果只用nacos-sdk-csharp.AspNetCore,需要手动配置较多内容。
五、核心配置
5.1 appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.AspNetCore.Hosting.Diagnostics": "Error",
"Grpc": "Information"
}
},
"AllowedHosts": "*",
"nacos": {
"ServerAddresses": [ "http://xx.xxx.xxx.xxx:8848" ],
"Namespace": "dubbo",
"ServiceName": "order-service-new",
"GroupName": "dubbo_core_group",
"UserName": "nacos",
"Password": "nacos",
"PreferredNetworks": "xx.xxx.",
"Port": 5073
},
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "http://0.0.0.0:5073",
"Protocols": "Http1AndHttp2"
},
"Grpc": {
"Url": "http://0.0.0.0:8084",
"Protocols": "Http2"
}
}
}
}
几个关键配置说明:
| 配置项 | 说明 |
|---|---|
nacos.Namespace |
Nacos 命名空间 ID,不是名称!在 Nacos 控制台创建命名空间后获取 |
nacos.PreferredNetworks |
多网卡环境下指定注册 IP 的前缀,避免注册了 127.0.0.1 或内网不可达的 IP |
nacos.Port |
注册到 Nacos 的端口号,Java 侧通过这个端口发现你的 HTTP 服务 |
Kestrel.Endpoints |
双端口配置:5073 承载 REST + Swagger,8084 专供 gRPC(HTTP/2) |
踩坑提示:gRPC 要求 HTTP/2,而浏览器和部分客户端走 HTTP/1.1。Kestrel 的双端口方案让两种协议各走各的,互不干扰。5073 端口设置
Http1AndHttp2是为了支持 Swagger UI 和 gRPC-Web。
5.2 csproj 配置 Proto 文件
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<RootNamespace>NacosDemo_order</RootNamespace>
</PropertyGroup>
<ItemGroup>
<!-- 消费端 proto:生成 gRPC Client -->
<Protobuf Include="Protos\Consumer\dictservice.proto" GrpcServices="Client" />
<!-- 生产端 proto:生成 gRPC Server -->
<Protobuf Include="Protos\Producer\classification_service_generated.proto" GrpcServices="Server" />
</ItemGroup>
<!-- ... NuGet 引用省略 ... -->
</Project>
GrpcServices="Client" 和 "Server" 决定了生成的是客户端桩代码还是服务端基类,别搞反了。
六、Nacos 服务注册
6.1 一行代码注册 Nacos
在 Program.cs 中:
var builder = WebApplication.CreateBuilder(args);
// 注册 Nacos —— 核心就这一行
builder.Services.AddNacosAspNet(builder.Configuration, "nacos");
var app = builder.Build();
app.Run();
AddNacosAspNet 是 NacosNetCore.Extensions 提供的扩展方法,它会:
-
读取
appsettings.json中"nacos"节点的配置 -
向 Nacos Server 注册当前服务实例
-
启动心跳保活(Nacos 2.x 使用 gRPC 长连接,不再依赖 HTTP 心跳)
-
应用关闭时自动注销
6.2 验证注册成功
启动项目后,打开 Nacos 控制台 → 服务列表,应该能看到:
-
服务名:
order-service-new -
分组:
dubbo_core_group -
命名空间:
dubbo -
实例 IP 和端口正确
也可以通过 HTTP 接口验证:
curl http://localhost:8848/nacos/v1/ns/instance/list?serviceName=order-service-new&groupName=dubbo_core_group&namespaceId=dubbo
七、gRPC 服务提供(Provider 端)
这部分是 .NET 作为 gRPC 服务提供者,让 Java 侧能通过 Dubbo-gRPC 调用我们。
7.1 定义 Proto 文件
syntax = "proto3";
package com.cn.order.api.dubbo;
option csharp_namespace = "NacosDemo_order.Protos";
message ClassificationRequest {
string key = 1;
}
message ClassificationListResponse {
repeated ClassificationInfo classifications = 1;
}
message ClassificationInfo {
string id = 1;
string name = 2;
int32 sort = 3;
}
message HealthCheckRequest {
}
message HealthCheckResponse {
string status = 1;
string message = 2;
string version = 3;
}
service ClassificationDubboService {
rpc GetClassificationList(ClassificationRequest) returns (ClassificationListResponse);
rpc HealthCheck(HealthCheckRequest) returns (HealthCheckResponse);
}
注意:
package名称必须和 Java Dubbo 侧的包名完全一致(如com.cn.order.api.dubbo),否则 Java 侧通过 Nacos 找到服务后无法正确路由到 gRPC 方法。
7.2 分层架构实现 gRPC 服务
这里我采用了 Business 层 + Grpc 层 的分层设计:
Services/
├── Business/ # 业务逻辑层(核心)
│ └── ClassificationService # 纯 C# 业务逻辑,无协议依赖
└── Grpc/ # gRPC 协议层(适配器)
└── ClassificationGrpcService # 协议转换,调用 Business 层
为什么要分层? 因为业务逻辑不应被 gRPC 协议绑架。将来如果要加 REST、GraphQL 或消息队列,只需新增对应的协议适配层,Business 层不动。
接口定义
// Interface/IClassificationService.cs
[ProtoService(
Package = "com.cn.order.api.dubbo",
ServiceName = "ClassificationDubboService",
CSharpNamespace = "NacosDemo_order.Protos"
)]
public interface IClassificationService
{
Task<ClassificationListResponse> GetClassificationList(ClassificationRequest request);
Task<HealthCheckResponse> HealthCheck(HealthCheckRequest request);
}
Business 层(纯业务逻辑)
// Services/Business/ClassificationService.cs
public class ClassificationService : IClassificationService
{
public async Task<ClassificationListResponse> GetClassificationList(ClassificationRequest request)
{
// 模拟数据源
string json = @"
[
{""id"":""1"",""name"":""AA"",""sort"":1},
{""id"":""2"",""name"":""BB"",""sort"":2},
{""id"":""3"",""name"":""CC"",""sort"":3},
{""id"":""4"",""name"":""DD"",""sort"":4}
]";
var classificationList = JsonSerializer.Deserialize<List<ClassificationInfo>>(json);
if (!string.IsNullOrEmpty(request.key))
{
classificationList = classificationList?
.Where(x => x.name.Contains(request.key)).ToList();
}
return new ClassificationListResponse
{
classifications = classificationList ?? new List<ClassificationInfo>()
};
}
public async Task<HealthCheckResponse> HealthCheck(HealthCheckRequest request)
{
return new HealthCheckResponse
{
status = "UP",
message = "服务运行正常",
version = "1.0.0"
};
}
}
Grpc 层(协议适配)
// Services/Grpc/ClassificationGrpcService.cs
public class ClassificationGrpcService : Protos.ClassificationDubboService.ClassificationDubboServiceBase
{
private readonly IClassificationService _classificationService;
private readonly ILogger<ClassificationGrpcService> _logger;
public ClassificationGrpcService(
IClassificationService classificationService,
ILogger<ClassificationGrpcService> logger)
{
_classificationService = classificationService;
_logger = logger;
}
public override async Task<Protos.ClassificationListResponse> GetClassificationList(
Protos.ClassificationRequest request,
ServerCallContext context)
{
try
{
_logger.LogInformation($"收到 gRPC 调用 GetClassificationList,key={request.Key}");
// DTO 转换
var dtoRequest = new DTO.ClassificationRequest { key = request.Key };
var dtoResponse = await _classificationService.GetClassificationList(dtoRequest);
// Proto 转换
var response = new Protos.ClassificationListResponse();
foreach (var item in dtoResponse.classifications)
{
response.Classifications.Add(new Protos.ClassificationInfo
{
Id = item.id,
Name = item.name,
Sort = item.sort
});
}
return response;
}
catch (Exception ex)
{
_logger.LogError(ex, "gRPC GetClassificationList 调用失败");
throw;
}
}
}
Grpc 层只做三件事:接收请求 → 转换 DTO → 调用 Business 层 → 转换响应。不写任何业务逻辑。
八、gRPC 服务消费(Consumer 端)
.NET 同时也是消费者,需要调用 Java 侧的 Dubbo-gRPC 服务(如字典服务)。
8.1 获取 Java 侧的 Proto 文件
从 Java 团队拿到 .proto 文件后放到 Protos/Consumer/ 目录:
// Protos/Consumer/dictservice.proto
syntax = "proto3";
package com.cn.common.api.dubbo;
option csharp_namespace = "GrpcServiceDemo";
message DictRequest {
int32 tenant_id = 1;
string key = 2;
}
message DictListResponse {
repeated DictResponse dicts = 1;
}
// ... 其他消息定义省略 ...
service DictDubboService {
rpc getDictsByKey(DictRequest) returns (DictListResponse);
// ... 其他方法 ...
}
8.2 从 Nacos 发现服务并发起 gRPC 调用
// Controllers/ValuesController.cs
[ApiController]
[Route("api/[controller]")]
public class ValuesController : ControllerBase
{
private readonly Nacos.V2.INacosNamingService _svc;
private readonly ILogger<ValuesController> _logger;
public ValuesController(
Nacos.V2.INacosNamingService svc,
ILogger<ValuesController> logger)
{
_svc = svc;
_logger = logger;
}
[HttpGet("TestCallBygRPC")]
public async Task<string> TestCallBygRPC()
{
try
{
// 1. 从 Nacos 获取健康实例
var instance = await _svc.SelectOneHealthyInstance(
"providers:com.cn.common.api.dubbo.DictDubboService::",
"dubbo_core_group"
);
if (instance == null) return "服务实例不可用";
// 2. 拼接 gRPC 地址(注意:用 gRPC 端口,不是 Dubbo 注册的端口)
var address = $"http://{instance.Ip}:8084";
var channel = GrpcChannel.ForAddress(address);
// 3. 构造请求
var request = new DictRequest
{
TenantId = 0,
Key = "busReqTypeDict"
};
// 4. 调用远程服务
var client = new DictDubboService.DictDubboServiceClient(channel);
var response = await client.getDictsByKeyAsync(request);
return response.ToString();
}
catch (RpcException ex)
{
_logger.LogError(ex, $"gRPC 调用失败:{ex.Status}");
return $"错误:{ex.Status.Detail}";
}
}
}
几个关键点:
-
服务名格式:Dubbo 注册到 Nacos 的服务名是
providers:{接口全限定名}::这种格式,不是简单的服务名 -
端口问题:Nacos 返回的
instance.Port是 Dubbo 协议端口,gRPC 通常在不同端口(如 8084),需要硬编码或通过元数据获取 -
SelectOneHealthyInstance:Nacos SDK 自带负载均衡,自动选择一个健康实例
九、自动注册机制(告别手动配置)
每新增一个 Service 或 gRPC 服务都要手动注册?太累了。通过反射扫描实现自动注册。
9.1 自动注册 Business 层
// Common/ServiceCollectionExtensions.cs
public static IServiceCollection AddServicesByAssembly(this IServiceCollection services)
{
var assembly = Assembly.GetExecutingAssembly();
var serviceTypes = assembly.GetTypes()
.Where(t =>
t.IsClass &&
!t.IsAbstract &&
t.Name.EndsWith("Service") &&
t.GetInterfaces().Any()
).ToList();
foreach (var type in serviceTypes)
{
var interfaceType = type.GetInterfaces()
.FirstOrDefault(i => i.Name == $"I{type.Name}");
if (interfaceType != null)
{
services.AddScoped(interfaceType, type);
}
}
return services;
}
约定规则:
-
类名以
Service结尾 -
实现了对应的
I{Name}Service接口 -
例如
ClassificationService→IClassificationService
9.2 自动映射 gRPC 服务
public static IEndpointRouteBuilder MapGrpcServicesByAssembly(this IEndpointRouteBuilder endpoints)
{
var assembly = Assembly.GetExecutingAssembly();
var grpcServiceTypes = assembly.GetTypes()
.Where(t =>
t.IsClass &&
!t.IsAbstract &&
t.Name.EndsWith("GrpcService") &&
t.BaseType != null &&
t.BaseType.Name.EndsWith("ServiceBase")
).ToList();
var mapGrpcServiceMethod = typeof(GrpcEndpointRouteBuilderExtensions)
.GetMethods(BindingFlags.Public | BindingFlags.Static)
.FirstOrDefault(m =>
m.Name == "MapGrpcService" &&
m.IsGenericMethod &&
m.GetParameters().Length == 1
);
foreach (var serviceType in grpcServiceTypes)
{
if (mapGrpcServiceMethod != null)
{
var genericMethod = mapGrpcServiceMethod.MakeGenericMethod(serviceType);
genericMethod.Invoke(null, new object[] { endpoints });
}
}
return endpoints;
}
约定规则:
-
类名以
GrpcService结尾 -
继承自 proto 生成的
ServiceBase
9.3 Program.cs 一行搞定
// 自动注册所有 Service
builder.Services.AddServicesByAssembly();
// ... 其他配置 ...
// 自动映射所有 gRPC 服务
app.MapGrpcServicesByAssembly();
之后新增业务,只需要:
-
写
I{Name}Service接口 -
写
{Name}Service实现类 -
写
{Name}GrpcService适配类
零配置,自动生效。
十、Proto 自动生成工具(亮点功能)
这是本项目最值得分享的功能:从 C# 代码反向生成 .proto 文件。
传统流程是先写 .proto → 生成 C# 代码 → 写业务逻辑。但在 .NET 先行的项目中,我们往往先设计 C# 接口,再把契约给 Java 团队。手动写 .proto 容易出错且重复劳动。
10.1 设计思路
C# 接口定义 + DTO → [ProtoService] / [ProtoMessage] 标记 → 运行生成工具 → 输出 .proto 文件
10.2 自定义属性
// Common/ProtoGen/ProtoAttributes.cs
/// <summary>
/// 标记需要生成 .proto 的服务接口
/// </summary>
[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class)]
public class ProtoServiceAttribute : Attribute
{
public string Package { get; set; } // proto 包名
public string ServiceName { get; set; } // 服务名
public string CSharpNamespace { get; set; } // C# 命名空间
}
/// <summary>
/// 标记需要生成 proto 消息的类
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Enum)]
public class ProtoMessageAttribute : Attribute { }
/// <summary>
/// 标记字段编号
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class ProtoFieldAttribute : Attribute
{
public int Number { get; set; }
public ProtoFieldAttribute(int number) => Number = number;
}
10.3 标记 DTO
// DTO/ProtoMessageClasses.cs
[ProtoMessage]
public class ClassificationRequest
{
[ProtoField(1)]
public string key { get; set; }
}
[ProtoMessage]
public class ClassificationInfo
{
[ProtoField(1)]
public string id { get; set; }
[ProtoField(2)]
public string name { get; set; }
[ProtoField(3)]
public int sort { get; set; }
}
[ProtoMessage]
public class ClassificationListResponse
{
[ProtoField(1)]
public List<ClassificationInfo> classifications { get; set; }
}
10.4 核心生成器
生成器通过反射扫描程序集,自动完成:
-
找到所有
[ProtoService]标记的接口 -
收集接口方法中用到的请求/响应类型
-
额外收集
[ProtoMessage]标记的类型 -
生成
message和service定义 -
C# 类型自动映射为 proto 类型(
string→string、int→int32、List<T>→repeated T) -
属性名自动转 snake_case(
ClassificationList→classification_list)
// 核心类型映射
private string GetProtoType(Type type)
{
if (type == typeof(string)) return "string";
if (type == typeof(int) || type == typeof(int?)) return "int32";
if (type == typeof(long) || type == typeof(long?)) return "int64";
if (type == typeof(bool) || type == typeof(bool?)) return "bool";
if (type == typeof(double) || type == typeof(double?)) return "double";
if (type == typeof(float) || type == typeof(float?)) return "float";
if (type == typeof(byte[])) return "bytes";
if (type.IsEnum) return type.Name;
return type.Name; // 自定义消息类型
}
10.5 一键生成
dotnet run -- --generate-proto
输出到 Protos/Producer/classification_service_generated.proto,直接交给 Java 团队即可。
10.6 生成结果
syntax = "proto3";
package com.cn.order.api.dubbo;
option csharp_namespace = "NacosDemo_order.Protos";
message ClassificationRequest {
string key = 1;
}
message ClassificationListResponse {
repeated ClassificationInfo classifications = 1;
}
message HealthCheckRequest {
}
message HealthCheckResponse {
string status = 1;
string message = 2;
string version = 3;
}
message ClassificationInfo {
string id = 1;
string name = 2;
int32 sort = 3;
}
service ClassificationDubboService {
rpc GetClassificationList(ClassificationRequest) returns (ClassificationListResponse);
rpc HealthCheck(HealthCheckRequest) returns (HealthCheckResponse);
}
十一、Program.cs 完整配置
using Nacos.AspNetCore.V2;
using NacosDemo_order.Common;
using NacosDemo_order.Tools;
// Proto 生成工具入口(独立运行)
if (args.Length > 0 && args[0] == "--generate-proto")
{
ProtoGenerationTool.Run(args);
return;
}
var builder = WebApplication.CreateBuilder(args);
// 1. 自动注册 Business 层服务
builder.Services.AddServicesByAssembly();
// 2. 注册控制器和 gRPC
builder.Services.AddControllers();
builder.Services.AddGrpc();
builder.Services.AddHealthChecks();
// 3. 注册 Nacos(一行搞定)
builder.Services.AddNacosAspNet(builder.Configuration, "nacos");
// 4. 注册 HttpClientFactory
builder.Services.AddHttpClient("nacosService");
// 5. Swagger
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo
{
Title = "Order Service API",
Version = "v1",
Description = "基于 .NET Core + Nacos + gRPC 的订单服务接口文档"
});
});
var app = builder.Build();
// 中间件
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint("/swagger/v1/swagger.json", "Order Service v1");
options.RoutePrefix = string.Empty;
});
}
app.UseRouting();
app.UseAuthorization();
// 映射控制器
app.MapControllers();
// 自动映射所有 gRPC 服务(无需手动添加)
app.MapGrpcServicesByAssembly();
// 健康检查端点
app.MapHealthChecks("/health");
app.Run();
十二、踩坑记录
坑 1:Nacos 注册 IP 不对
现象:Nacos 控制台显示的 IP 是 127.0.0.1 或某个内网不可达的 IP,Java 侧调不通。
解决:配置 PreferredNetworks,指定注册 IP 的前缀匹配:
"PreferredNetworks": "XX.XXX."
SDK 会优先选择 IP 前缀匹配的网卡地址注册。
坑 2:gRPC 端口与 Dubbo 端口混淆
现象:从 Nacos 拿到实例后直接用 instance.Port 连 gRPC,报连接失败。
解决:Dubbo 注册到 Nacos 的端口是 Dubbo 协议端口(如 20880),gRPC 通常在另一个端口(如 8084)。需要单独约定或通过 Nacos 元数据传递:
// 方式 1:硬编码 gRPC 端口(简单场景)
var address = $"http://{instance.Ip}:8084";
// 方式 2:通过元数据获取(推荐)
var grpcPort = instance.Metadata.TryGetValue("grpcPort", out var port) ? port : "8084";
var address = $"http://{instance.Ip}:{grpcPort}";
坑 3:Proto 包名不匹配
现象:Java 侧通过 Nacos 找到服务,但 gRPC 调用报 UNIMPLEMENTED。
解决:.proto 文件中的 package 必须与 Java Dubbo 接口的包名完全一致。Java 侧是 com.cn.order.api.dubbo,proto 也必须是同样的包名,否则 gRPC 的 service 路径对不上。
坑 4:HTTP/2 和浏览器不兼容
现象:Swagger 页面正常,但 gRPC 调用失败。
解决:Kestrel 双端口方案,HTTP 端口(5073)走 Http1AndHttp2,gRPC 专用端口(8084)走纯 Http2。这样 Swagger 和 gRPC 各走各的。
十三、Java 侧调用指南
把生成的 .proto 文件给 Java 团队后,他们需要:
-
将 proto 文件放入项目中,使用
protoc生成 Java Stub -
通过 Nacos 发现 .NET 服务实例
-
使用 Dubbo-gRPC 协议调用
服务发现信息:
| 项 | 值 |
|---|---|
| Nacos 服务名 | providers:com.cn.order.api.dubbo.ClassificationDubboService:: |
| 分组 | dubbo_core_group |
| 命名空间 | dubbo |
| gRPC 端口 | 8084 |
十四、扩展指南
新增业务服务
只需 3 步:
# 1. 写接口
Interface/IOrderService.cs
# 2. 写业务实现
Services/Business/OrderService.cs
# 3. 写 gRPC 适配
Services/Grpc/OrderGrpcService.cs
编译启动,自动注册,零配置。
新增 Proto 消费
-
把 Java 团队的
.proto文件放到Protos/Consumer/ -
在
.csproj中添加<Protobuf Include="Protos/Consumer/xxx.proto" GrpcServices="Client" /> -
注入
INacosNamingService发现服务,用生成的 Client 类发起调用
十五、总结
本文从实战角度,完整覆盖了 .NET Core 接入 Nacos + gRPC 的关键路径:
| 模块 | 核心要点 |
|---|---|
| Nacos 注册 | AddNacosAspNet 一行搞定,注意 PreferredNetworks |
| gRPC Provider | 分层架构(Business + Grpc),协议与逻辑解耦 |
| gRPC Consumer | Nacos 发现 → 拼地址 → 创建 Client → 调用 |
| 自动注册 | 反射扫描,约定优于配置,新增服务零配置 |
| Proto 生成 | C# 代码 → .proto 文件,跨团队协作利器 |
| 双端口方案 | Kestrel 多 Endpoint,HTTP/1.1 和 HTTP/2 各得其所 |

浙公网安备 33010602011771号