.NET Core 跨平台之路:从 Windows 到全平台的演进
引言:一个 .NET 开发者的技术旅程
个人技术栈演进回顾
回顾微软的技术开发历程,就像一部浓缩的微软技术发展史:
ASP → VB → VB.NET → ASP.NET WebForms → WinForms → ASP.NET MVC → Silverlight → WPF → .NET Core MVC + Avalonia UI
早期时代:从 ASP 的服务器端脚本起步,经历了 VB.NET 、WebForms、WinForms 的事件驱动与控件化抽象,虽然它屏蔽了底层细节,但开发效率是顶级的。
MVC 时代:ASP.NET MVC 的发布让开发体验有了质的飞跃,分离关注点、可测试性,路由、中间件、过滤器、配置化这些概念至今仍在指导着我的架构设计。
桌面端:WPF 带来的 XAML 和 MVVM 模式,让我对声明式 UI 和数据绑定有了深刻理解。Silverlight 的遗憾退出,让我第一次意识到跨平台的重要性。
跨时代:.NET Core 的出现,加上其跨平台的能力,让我可以在 Linux 上使用熟悉的 C# 和 MVC等技术 开发BS/CS应用(《Avalonia UI:WPF 开发者的跨平台首选》)。
转向 Java:2011年,因项目需求,技术重心逐步转向 Java 开发,并系统性深入研究 Spring、Dubbo 以及 Spring Cloud 生态体系。在这个过程中,我以一个”外来者”的视角,重新审视 .NET 的跨平台发展历程。
文章背景
写下这篇文章的原因,是希望系统性梳理自己在 .NET 平台上的开发经历。从 .NET Core 1.0时期就尝试过其BS的跨平台能力,后探索其CS跨平台特性,再又持续关注社区与生态体系的演进。
之前在 Linux 环境下使用 JetBrains Rider 进行 .NET 开发,整体开发体验与 Windows 基本保持一致,发布的应用跨平台能力也很出色,让我切身体会到:.NET Core 的跨平台能力,已经在工程层面得到了充分实现。相比早期阶段,这一能力在稳定性、开发体验与社区建设上均在成长。
同时,在转向 Java 微服务技术栈后,我发现 .NET 和 Java 两个生态在云原生时代呈现出了有趣的互补关系。希望通过这篇文章,以一个 .NET 开发者的视角,全面回顾 .NET 的跨平台之路,对比 Java 企业级生态,探讨 2021 年 .NET 的现状与未来。
一、.NET 的跨平台之路
历史回顾
graph LR
A[.NET Framework<br/>Windows Only] --> B[Mono<br/>跨平台尝试]
A --> C[Xamarin<br/>移动端]
B --> D[.NET Core<br/>真正的跨平台]
C --> D
D --> E[.NET 5<br/>统一平台]
E --> F[.NET 6<br/>LTS版本]
style A fill:#ff006e,stroke:#d6005c,color:#b3004e
style D fill:#00fff5,stroke:#00b8b0,color:#008b82
style E fill:#faff00,stroke:#e6d900,color:#c9b800
style F fill:#00ff9f,stroke:#00cc7f,color:#009966
.NET Framework 时代:Windows 的特权
早期的 .NET Framework 是微软 Windows 平台的专属框架,虽然在开发体验上非常优秀,但也因此被贴上了”闭源”、”非跨平台”的标签。
优点:
- Visual Studio 强大的开发体验
- 完善的基础类库(BCL)
- Windows 平台的深度集成
局限:
- 仅支持 Windows 系统
- 闭源,社区参与度低
- 版本更新受制于 Windows 发布周期
Mono:跨平台的先行者
Xamarin 公司(后被微软收购)开发的 Mono 项目,是 .NET 跨平台的第一次重要尝试。
Mono 的贡献:
- 首次实现 .NET 在 Linux 和 macOS 上的运行
- 支持 Unity 游戏引擎的脚本开发
- 为后来的 Xamarin 移动开发奠定基础
问题:
- 与 .NET Framework 存在兼容性问题
- 社区维护,功能更新滞后
- 性能和稳定性不及原生 .NET Framework
.NET Core:真正的跨平台
2016年,微软发布了 .NET Core 1.0,这是 .NET 历史上的一个重要里程碑:
.NET Core 是一个开源、跨平台的 .NET 实现,支持 Windows、Linux 和 macOS。
关键特性:
- 完全开源,托管在 GitHub 上
- 模块化设计,可以按需引用
- 高性能,启动速度快
- 真正的跨平台支持
.NET 5:统一的开端
2020年,.NET 5 的发布标志着平台战略的重大转变:
- 命名上跳过了 .NET 4.x,避免与 .NET Framework 混淆
- 统一了 .NET Core、Mono、Xamarin 的 Runtime
- 建立了单一的基础类库(BCL)
- 为 .NET 的未来发展奠定了统一的基础
.NET 6:LTS 版本的成熟
2021年,.NET 6 作为 LTS(长期支持)版本发布:
- 支持 C# 10
- 性能进一步提升
- 云原生特性增强
- MAUI(跨平台移动/桌面框架)正式版
- 热重载功能改进
二、.NET Core 跨平台架构解析
核心组件一览
| 组件 | 说明 | 跨平台支持 |
|---|---|---|
| ASP.NET Core | 高性能 Web 框架 | ✅ 全平台 |
| Entity Framework Core | 轻量级 ORM | ✅ 全平台 |
| Blazor | WebAssembly 前端框架 | ✅ 浏览器 |
| Xamarin | 移动开发框架 | ✅ iOS/Android |
| Avalonia UI | 跨平台桌面框架 | ✅ Win/Linux/macOS |
| .NET MAUI | 统一 UI 框架 | ✅ 2021 发布 |
运行时架构
graph TD
A[应用代码] --> B[语言编译器 Roslyn]
B --> C[IL 中间语言]
C --> D[CLR 运行时]
D --> E[CoreCLR<br/>JIT 编译]
D --> F[CoreRT<br/>AOT 编译]
E --> G[原生机器码]
F --> G
G --> H[操作系统]
D --> I[CoreFX<br/>基础类库]
style D fill:#00fff5,stroke:#00b8b0,color:#008b82
style I fill:#faff00,stroke:#e6d900,color:#c9b800
CoreCLR:跨平台的通用语言运行时,负责 JIT 编译和内存管理。
CoreFX:基础类库,提供集合、IO、线程等基础功能。
Roslyn:编译器平台,支持 C# 和 Visual Basic。
CLI:统一的命令行工具(dotnet),支持所有开发工作流。
开发模型
# 创建项目
dotnet new webapi -n MyApi
# 还原依赖
dotnet restore
# 运行项目
dotnet run
# 发布项目
dotnet publish -c Release -r linux-x64
三、Entity Framework Core:跨平台的 ORM 选择
EF Core 的演进
Entity Framework Core 是 .NET 的官方 ORM 框架,与 .NET Core 同步发展:
| 版本 | 发布时间 | 重要特性 |
|---|---|---|
| EF6 | 2012 | .NET Framework 时代,Windows Only |
| EF Core 1.0 | 2016 | 首个跨平台版本,功能精简 |
| EF Core 2.0 | 2017 | 查询改进、LINQ 增强 |
| EF Core 3.1 | 2019 | 稳定版本,与 .NET Core 3.1 同步 |
| EF Core 6.0 | 2021 | LTS 版本,性能大幅提升 |
EF Core 6.0 新特性(2021)
// 1. 编译模型 - 启动性能提升
public class MyDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlServer("连接字符串")
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
public DbSet<User> Users { get; set; }
public DbSet<Order> Orders { get; set; }
}
// 2. 临时表 - SQL Server 支持
await context.Database.ExecuteSqlRawAsync(
"CREATE TABLE #TempUsers (Id INT, Name NVARCHAR(50))");
// 3. 迁移包 - 部署优化
dotnet ef migrations add InitialCreate
dotnet ef migrations bundle --self-contained
// 4. JSON 列 - 更好的文档数据库支持
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.Property(e => e.Metadata)
.HasJsonConversion();
}
性能优化:
- 查询编译缓存改进
- 批量操作性能提升
- 内存占用优化
数据库提供程序
EF Core 的跨平台能力,很大程度上得益于丰富的数据库提供程序:
// SQL Server - Windows/Linux
options.UseSqlServer("连接字符串");
// PostgreSQL - 全平台
options.UseNpgsql("连接字符串");
// MySQL - 全平台
options.UseMySQL("连接字符串");
// SQLite - 嵌入式,全平台
options.UseSqlite("Data Source=app.db");
// Cosmos DB - 云原生
options.UseCosmos("endpoint", "key", "database");
代码示例:完整的 CRUD 操作
public class UserService
{
private readonly MyDbContext _context;
public UserService(MyDbContext context)
{
_context = context;
}
// 创建
public async Task<User> CreateUserAsync(string name, string email)
{
var user = new User { Name = name, Email = email };
_context.Users.Add(user);
await _context.SaveChangesAsync();
return user;
}
// 查询
public async Task<User> GetUserAsync(int id)
{
return await _context.Users
.Include(u => u.Orders)
.FirstOrDefaultAsync(u => u.Id == id);
}
// 更新
public async Task UpdateUserAsync(User user)
{
_context.Users.Update(user);
await _context.SaveChangesAsync();
}
// 删除
public async Task DeleteUserAsync(int id)
{
var user = await _context.Users.FindAsync(id);
if (user != null)
{
_context.Users.Remove(user);
await _context.SaveChangesAsync();
}
}
// 复杂查询
public async Task<List<User>> GetActiveUsersWithOrdersAsync()
{
return await _context.Users
.Where(u => u.IsActive)
.Include(u => u.Orders)
.Where(u => u.Orders.Any(o => o.Status == "Completed"))
.OrderByDescending(u => u.CreatedAt)
.Take(100)
.ToListAsync();
}
}
四、.NET 微服务生态 vs Java Spring Cloud
服务注册与发现
Java 生态
Eureka(Netflix):
# application.yml
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
instance:
preferIpAddress: true
Consul(HashiCorp):
cloud:
consul:
discovery:
serviceName: user-service
hostname: localhost
Nacos(阿里):
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
.NET 生态
Steeltoe(Spring Cloud 的 .NET 移植):
// Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDiscoveryClient(Configuration);
services.AddMvc();
}
public void Configure(IApplicationBuilder app)
{
app.UseMvc();
app.UseDiscoveryClient();
}
// appsettings.json
{
"spring": {
"application": {
"name": "order-service"
}
},
"eureka": {
"client": {
"serviceUrl": "http://localhost:8761/eureka/",
"shouldRegisterWithEureka": true
},
"instance": {
"port": 8010
}
}
}
Consul 原生支持:
services.AddConsulClient(options =>
{
options.Address = new Uri("http://localhost:8500");
});
services.AddConsulServiceRegistration(options =>
{
options.ID = "order-service-1";
options.Name = "order-service";
options.Address = "localhost";
options.Port = 8010;
});
API 网关对比
| 特性 | Spring Cloud Gateway | .NET YARP | Ocelot |
|---|---|---|---|
| 性能 | 中等 | 高(基于 Kestrel) | 中等 |
| 配置 | Java YAML | .NET JSON/代码 | .NET JSON |
| 扩展性 | Filter 机制 | Middleware 管道 | Middleware |
| 社区 | 成熟 | 微软官方 | 社区维护 |
| 路由 | 声明式 | 编程式 | 声明式 |
YARP(Yet Another Reverse Proxy) 是微软官方开源的 reverse proxy 库:
// Program.cs
builder.Services.AddReverseProxy()
.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(policy =>
{
policy.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
});
});
var app = builder.Build();
app.MapReverseProxy();
app.Run();
// appsettings.json
{
"ReverseProxy": {
"Routes": {
"user-service-route": {
"ClusterId": "user-service",
"Match": {
"Path": "/api/users/{**catch-all}"
},
"Transforms": [
{ "PathPattern": "/api/users/{**catch-all}" }
]
}
},
"Clusters": {
"user-service": {
"Destinations": {
"destination1": {
"Address": "https://localhost:5001"
}
}
}
}
}
}
配置中心
Java
- Spring Cloud Config:Git/ SVN 后端
- Apollo:携程开源
- Nacos:阿里开源
.NET
- Steeltoe Config:与 Spring Cloud Config 兼容
- Azure App Configuration:云原生方案
- Consul KV:简单可靠
// Consul 配置读取
services.AddConsulConfig(options =>
{
options.Options =
{
Address = new Uri("http://localhost:8500"),
Token = "consul-token"
};
});
services.Configure<MyOptions>(
Configuration.GetSection("MyOptions")
);
链路追踪与监控
Java
- Sleuth + Zipkin:Spring Cloud 全家桶
- SkyWalking:国产 APM
- Micrometer:指标门面
.NET
- Application Insights:Azure 原生
- OpenTelemetry:跨平台标准
- Steeltoe Management:与 Java 生态互通
// OpenTelemetry 集成
builder.Services.AddOpenTelemetryTracing(tracing =>
{
tracing.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddSource("MyService")
.SetResourceBuilder(
ResourceBuilder.CreateDefault()
.AddService("my-service"))
.AddZipkinExporter(options =>
{
options.Endpoint = new Uri("http://localhost:9411/api/v2/spans");
});
});
服务间通信
gRPC 在 .NET 中的实现
// protos/greeter.proto
syntax = "proto3";
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
// Server 端
public class GreeterService : Greeter.GreeterBase
{
public override Task<HelloReply> SayHello(
HelloRequest request,
ServerCallContext context)
{
return Task.FromResult(new HelloReply
{
Message = $"Hello {request.Name}"
});
}
}
// Program.cs
app.MapGrpcService<GreeterService>();
// Client 端
using var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new Greeter.GreeterClient(channel);
var reply = await client.SayHelloAsync(
new HelloRequest { Name = "World" });
gRPC vs REST API:
| 特性 | gRPC | REST API |
|---|---|---|
| 协议 | HTTP/2 | HTTP/1.1/2 |
| 序列化 | Protocol Buffers | JSON |
| 性能 | 高 | 中等 |
| 代码生成 | 自动 | 手动 |
| 浏览器支持 | 需要代理 | 原生 |
五、容器化与云原生
Docker 支持
多阶段构建示例
# .NET 5 多阶段构建
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /src
# 复制项目文件
COPY ["MyApp.csproj", "./"]
RUN dotnet restore
# 复制源代码并发布
COPY . .
RUN dotnet publish -c Release -o /app
# 运行时镜像
FROM mcr.microsoft.com/dotnet/aspnet:5.0
WORKDIR /app
COPY --from=build /app .
ENTRYPOINT ["dotnet", "MyApp.dll"]
Docker Compose 编排
version: '3.8'
services:
user-service:
build: ./services/user-service
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ConnectionStrings__DefaultConnection=Server=sql-server;Database=UserDb;...
depends_on:
- sql-server
ports:
- "5001:80"
order-service:
build: ./services/order-service
environment:
- ASPNETCORE_ENVIRONMENT=Development
- UserServiceUrl=http://user-service
depends_on:
- user-service
ports:
- "5002:80"
sql-server:
image: mcr.microsoft.com/mssql/server:2019-latest
environment:
- ACCEPT_EULA=Y
- SA_PASSWORD=YourPassword123
ports:
- "1433:1433"
Kubernetes 支持
Deployment 示例
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service
spec:
replicas: 3
selector:
matchLabels:
app: user-service
template:
metadata:
labels:
app: user-service
spec:
containers:
- name: user-service
image: myregistry/user-service:1.0.0
ports:
- containerPort: 80
env:
- name: ASPNETCORE_ENVIRONMENT
value: "Production"
- name: ConnectionStrings__DefaultConnection
valueFrom:
secretKeyRef:
name: db-secrets
key: connection-string
livenessProbe:
httpGet:
path: /health
port: 80
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /health/ready
port: 80
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: user-service
spec:
selector:
app: user-service
ports:
- port: 80
targetPort: 80
type: LoadBalancer
Health Checks 实现
// Program.cs
builder.Services.AddHealthChecks()
.AddDbContextCheck<MyDbContext>()
.AddSqlServer(connectionString)
.AddUrlGroup(new Uri("https://api.external.com"), "external-api")
.AddCheck("memory", new MemoryHealthCheck(1024));
var app = builder.Build();
app.MapHealthChecks("/health", new HealthCheckOptions
{
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
app.MapHealthChecks("/health/ready", new HealthCheckOptions
{
Predicate = check => check.Tags.Contains("ready")
});
app.MapHealthChecks("/health/live", new HealthCheckOptions
{
Predicate = _ => false
});
Helm Charts
# 官方 Helm Chart
helm repo add bitnami https://charts.bitnami.com/bitnami
helm install my-app bitnami/aspnet-core
# 自定义 Chart
helm install user-service ./charts/user-service \
--set image.tag=1.0.0 \
--set replicaCount=3 \
--set env.ASPNETCORE_ENVIRONMENT=Production
云服务集成对比
| 平台 | Java 支持 | .NET 支持 | 备注 |
|---|---|---|---|
| AWS | Lambda/EKS | Lambda/EKS | .NET Core 支持良好 |
| Azure | App Service | App Service | .NET 原生,最佳支持 |
| GCP | Cloud Run | Cloud Run | 容器化部署 |
| 阿里云 | ECS/ACK | ECS/ACK | 容器化部署 |
| 腾讯云 | CVM/TKE | CVM/TKE | 容器化部署 |
六、开发生态对比
IDE 与工具
.NET 生态
| 工具 | 平台 | 特点 |
|---|---|---|
| Visual Studio 2022 | Windows | 最强大的 .NET IDE |
| JetBrains Rider | Win/Lin/mac | 跨平台,性能优秀 |
| VS Code | 全平台 | 轻量级,插件丰富 |
| Visual Studio for Mac | macOS | 轻量版 VS |
Java 生态
| 工具 | 平台 | 特点 |
|---|---|---|
| IntelliJ IDEA | 全平台 | Java 开发首选 |
| Eclipse | 全平台 | 老牌 IDE |
| VS Code | 全平台 | 轻量级 |
性能基准测试
根据 TechEmpower Web Framework Benchmarks 的数据:
graph TD
A[Web性能基准<br/>TechEmpower Round 19] --> B[.NET 5/6]
A --> C[Java Spring Boot]
A --> D[Node.js Express]
A --> E[Go Gin]
B -->|最高分| F["#1 plaintext<br/>#1 JSON<br/>#2 DB Query"]
C -->|中高分| G[#5 JSON<br/>#7 DB Query]
E -->|高分| H[#2 plaintext<br/>#3 JSON]
D -->|中等| I[#8 JSON<br/>#12 DB Query]
style F fill:#00ff9f,stroke:#00cc7f,color:#009966
style G fill:#ff006e,stroke:#d6005c,color:#b3004e
关键数据:
- .NET 5 在 plaintext 测试中排名第一(~7M req/s)
- .NET 5 在 JSON 序列化测试中排名第一(~1.5M req/s)
- Spring Boot 表现稳定,但性能低于 .NET
- Go 在简单场景下性能接近 .NET
社区与生态规模
Java 优势
| 方面 | 说明 |
|---|---|
| 历史 | 25+ 年历史,生态极其丰富 |
| 开源框架 | Spring 全家桶、Apache 系列 |
| 企业案例 | 阿里、京东等大厂深度实践 |
| 国内社区 | Spring Cloud Alibaba、Dubbo |
.NET 优势
| 方面 | 说明 |
|---|---|
| 官方支持 | Microsoft 全力投入 |
| 性能 | 多项基准测试领先 |
| 开发体验 | C# 语言特性、IDE 工具 |
| 跨平台 | .NET Core 后社区快速增长 |
编程语言对比:C# vs Java
java与c#,从语言层面上c#一直是更先进的代表
| 特性 | C# | Java |
|---|---|---|
| 属性 | ✅ 自动属性 | ❌ 需要 getter/setter |
| LINQ | ✅ 语言集成查询 | ❌ Stream API |
| 异步 | ✅ async/await | ✅ CompletableFuture |
| 泛型 | ✅ 协变/逆变 | ⚠️ 有限支持 |
| 模式匹配 | ✅ 强大 | ⚠️ Java 17 引入 |
| 记录类型 | ✅ record | ✅ Java 17 record |
| 默认方法 | ✅ 扩展方法 | ✅ 默认方法 |
// C# 示例:现代语言特性
public record User(int Id, string Name, string Email);
public async Task<Result<User>> GetUserAsync(int id)
{
return await _context.Users
.Where(u => u.Id == id)
.Select(u => new User(u.Id, u.Name, u.Email))
.FirstOrDefaultAsync()
is User user
? Result.Success(user)
: Result.NotFound<User>();
}
// Java 示例
public record User(int id, String name, String email) {}
public CompletableFuture<Result<User>> getUserAsync(int id) {
return users.stream()
.filter(u -> u.id() == id)
.findFirst()
.map(user -> Result.success(user))
.orElse(Result.notFound());
}
七、实战案例:Spring Cloud + .NET Core 混合架构
架构设计
在实际项目中,我见过很多 Spring Cloud 与 .NET Core 混合部署的架构。参考龙向辉的系列文章,典型的混合架构如下:
┌─────────────────────────────────────────────────────────────┐
│ 客户端层 │
│ Web / Mobile / Desktop │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ API 网关层 │
│ Java Zuul / Spring Cloud Gateway │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 服务注册与发现 │
│ Java Eureka / Consul │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 微服务层 │
├───────────────────────────┬─────────────────────────────────┤
│ .NET Core Services │ Java Spring Services │
│ │ │
│ • Order Service │ • User Service │
│ • Payment Service │ • Product Service │
│ • Inventory Service │ • Notification Service │
│ │ │
│ Steeltoe Integration │ Spring Cloud Integration │
└───────────────────────────┴─────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 认证与授权层 │
│ IdentityServer4 / Keycloak │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 数据层 │
│ SQL Server / PostgreSQL / MongoDB / Redis │
└─────────────────────────────────────────────────────────────┘
.NET Core 服务接入 Eureka
1. 安装 Steeltoe 包
dotnet add package Steeltoe.Discovery.ClientCore
dotnet add package Steeltoe.Extensions.Configuration.ConfigServerCore
2. 配置服务注册
// Startup.cs
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// 添加服务发现客户端
services.AddDiscoveryClient(Configuration);
services.AddControllers();
// 添加 HttpClient 工厂
services.AddHttpClient("UserService")
.AddServiceDiscovery();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
// 启动服务发现客户端
app.UseDiscoveryClient();
}
}
3. 配置文件
// appsettings.json
{
"spring": {
"application": {
"name": "order-service"
}
},
"eureka": {
"client": {
"serviceUrl": "http://localhost:8761/eureka/",
"shouldRegisterWithEureka": true,
"shouldFetchRegistry": true,
"registryFetchIntervalSeconds": 30
},
"instance": {
"port": 8010,
"ipAddress": "localhost",
"preferIpAddress": true,
"healthCheckUrlPath": "/health",
"statusPageUrlPath": "/status"
}
}
}
4. 健康检查端点
[ApiController]
[Route("/")]
public class HealthController : ControllerBase
{
[HttpGet("health")]
public IActionResult Health()
{
return Ok(new { status = "UP" });
}
[HttpGet("status")]
public IActionResult Status()
{
return Ok(new
{
app = "order-service",
status = "UP",
version = "1.0.0"
});
}
}
服务间调用示例
使用 DiscoveryHttpClientHandler
public class OrderController : ControllerBase
{
private readonly DiscoveryHttpClientHandler _handler;
private readonly ILogger<OrderController> _logger;
public OrderController(IDiscoveryClient client, ILogger<OrderController> logger)
{
_handler = new DiscoveryHttpClientHandler(client);
_logger = logger;
}
[HttpGet("{id}/user")]
public async Task<ActionResult<UserDto>> GetUserForOrder(int id)
{
// 创建使用服务发现的 HttpClient
var client = new HttpClient(_handler);
try
{
// 通过服务名调用,自动负载均衡
var response = await client.GetStringAsync("http://user-service/api/users/current");
var user = JsonSerializer.Deserialize<UserDto>(response);
return Ok(user);
}
catch (Exception ex)
{
_logger.LogError(ex, "调用 user-service 失败");
return StatusCode(503, "服务暂时不可用");
}
}
}
使用 HttpClientFactory(推荐)
// Startup.cs
services.AddHttpClient("UserService", client =>
{
client.BaseAddress = new Uri("http://user-service/");
})
.AddServiceDiscovery()
.AddRoundRobinLoadBalancer()
.AddPolicyHandler(GetRetryPolicy());
static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
{
return HttpPolicyExtensions
.HandleTransientHttpError()
.OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.ServiceUnavailable)
.WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
}
// Controller
public class OrderController : ControllerBase
{
private readonly IHttpClientFactory _httpClientFactory;
public OrderController(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
[HttpGet("{id}/user")]
public async Task<ActionResult<UserDto>> GetUserForOrder(int id)
{
var client = _httpClientFactory.CreateClient("UserService");
var response = await client.GetAsync("api/users/current");
response.EnsureSuccessStatusCode();
var user = await response.Content.ReadFromJsonAsync<UserDto>();
return Ok(user);
}
}
配置中心集成
// Program.cs
builder.Configuration.AddConfigServer(options =>
{
options.Environment = "Development";
options.Name = "order-service";
options.Label = "master";
options.Uri = "http://localhost:8888";
});
var app = builder.Build();
混合架构的最佳实践
- 统一服务注册:使用 Consul 或 Eureka 作为统一的服务注册中心
- 统一配置管理:使用 Spring Cloud Config Server 或 Consul KV
- 统一认证授权:使用 IdentityServer4 或 Keycloak
- 统一链路追踪:使用 Zipkin 或 SkyWalking
- 服务边界清晰:.NET 和 Java 服务之间通过明确的 API 通信
- 基础设施即代码:使用 Docker Compose / Kubernetes 编排
八、企业级应用框架
除了基础的 .NET Core 和 Spring Cloud,两个生态都有成熟的企业级应用框架:
| 框架 | 生态 | 特点 |
|---|---|---|
| ABP Framework | .NET | DDD 实践、多租户、模块化 |
| Orchard Core | .NET | CMS 模块化框架 |
| Spring Boot | Java | 约定优于配置、开箱即用 |
| JHipster | Java | 代码生成器、微服务脚手架 |
ABP Framework 是 .NET 生态中值得关注的框架,提供了 DDD 架构的最佳实践,包括模块化、多租户、权限管理、审计日志等企业级特性。
相比之下,Spring Boot 的生态更为成熟,有更多开箱即用的集成方案。
九、2021年的技术选择建议
选择 .NET Core 的场景
| 场景 | 原因 |
|---|---|
| ✅ 需要高性能的 Web API | .NET 在 TechEmpower 基准测试中领先 |
| ✅ Windows 团队转向跨平台 | C# 和 Visual Studio 熟悉度高 |
| ✅ 微服务架构中的高性能服务 | gRPC 原生支持,性能优异 |
| ✅ 需要优秀的开发体验 | C# 语言特性、IDE 工具完善 |
| ✅ Azure 云环境部署 | .NET 在 Azure 上有最佳支持 |
| ✅ 跨平台桌面应用 | Avalonia UI / MAUI |
| ✅ 企业级应用开发 | 有成熟的框架支持(如 ABP) |
选择 Java Spring Cloud 的场景
| 场景 | 原因 |
|---|---|
| ✅ 已有 Java 技术栈 | 避免技术栈分裂 |
| ✅ 需要丰富的开源组件 | Spring 生态极其丰富 |
| ✅ 团队熟悉 Java 生态 | 降低学习成本 |
| ✅ 需要成熟的微服务解决方案 | Spring Cloud 全家桶 |
| ✅ 国内企业级应用 | Spring Cloud Alibaba 完善 |
| ✅ 大数据处理 | Java 大数据生态成熟 |
混合架构的建议
推荐架构模式:
服务注册/发现 → Consul(统一)
API 网关 → 根据团队选择(Zuul / YARP)
认证授权 → IdentityServer4 / Keycloak
配置中心 → Consul KV / Nacos
链路追踪 → Zipkin / SkyWalking
业务服务 → 各语言优势发挥
• 高性能服务 → .NET Core
• 业务复杂服务 → Java Spring
• 数据处理服务 → Java / Go
技术决策框架
技术选型决策
|
┌─────────┴─────────┐
| |
团队技能 项目需求
| |
.NET 经验丰富 → .NET Core
Java 经验丰富 → Java
混合团队 → 混合架构
十、总结与展望
.NET 的成熟度
站在 2021 年的视角,.NET 已经完成了从 Windows Only 到真正跨平台的蜕变:
| 方面 | 2016 (.NET Core 1.0) | 2021 (.NET 6) |
|---|---|---|
| 跨平台 | 实验性 | 生产级 |
| 性能 | 中等 | 行业领先 |
| 生态 | 贫乏 | 快速增长 |
| 工具 | 基础 | 完善 |
| 云原生 | 有限 | 全面支持 |
未来展望
.NET 7/8+
- 持续创新:C# 语言特性增强
- 性能提升:持续优化运行时
- 云原生:更好的 Azure 集成
MAUI
- 统一移动和桌面开发
- 真正的跨平台 UI
Blazor
- WebAssembly 成熟
- 服务端渲染增强
- 与 JavaScript 互操作
AI/ML
- ML.NET 持续发展
- 与 Azure AI 集成
个人感悟
回顾这段技术旅程,我有几点感悟:
-
技术选择无绝对优劣 .NET 和 Java 都在各自领域发光发热。关键是选择适合团队和项目的方案。
-
生态多样性是好事 多种技术并存,促进了技术进步。开发者也应该拥抱多样性,而不是固守一隅。
-
.NET 的进步显著 从闭源的 .NET Framework 到开源跨平台的 .NET Core,微软的转变值得肯定。2021 年的 .NET 已经是成熟的跨平台解决方案。
-
Java 微服务生态仍领先,但差距在缩小 Spring Cloud 的生态深度仍然是 .NET 难以比拟的,但 .NET 社区发展迅速,Steeltoe、YARP 等项目正在缩小差距。
-
混合架构是现实选择 在大型企业中,技术栈往往是多样的。关键是要有统一的治理策略,而不是强制技术栈统一。
参考资料
官方文档
社区资源
相关文章
写于 2021 年 6 月,一个正在探索微服务与云原生的 .NET 开发者