首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >.NET 微服务架构从入门到落地:Docker + Kubernetes + YARP 网关 + 分布式事务实战

.NET 微服务架构从入门到落地:Docker + Kubernetes + YARP 网关 + 分布式事务实战

原创
作者头像
IT大佬 jzit-top
发布2026-07-29 10:58:05
发布2026-07-29 10:58:05
1030
举报

.NET 微服务架构从入门到落地:Docker + Kubernetes + YARP 网关 + 分布式事务实战

本文首发于思否,作者 [你的名字]。微服务早已不是大厂的专属,随着 .NET 生态的成熟和容器化技术的普及,中小团队也能构建高可用、可扩展的微服务体系。本文将带你从零搭建一套基于 .NET 8 的微服务应用,涵盖服务拆分、API 网关(YARP)、服务发现(Consul)、容错(Polly)、分布式追踪(OpenTelemetry)、分布式事务(Saga)以及 Kubernetes 部署,全部代码可运行,适合生产实践参考。


一、为什么要做微服务?—— 不只是“拆”

微服务的核心价值在于 独立部署、独立扩展、独立故障隔离,而非简单的“拆成多个项目”。在 .NET 技术栈中,我们拥有 ASP.NET Core 的高性能、丰富的中间件生态以及强大的云原生支持,可以轻松构建轻量级微服务。

但微服务也引入了一系列挑战:

  • 网络延迟与容错
  • 数据一致性与分布式事务
  • 服务发现与负载均衡
  • 日志聚合与分布式追踪
  • 网关路由与认证鉴权

本文的目标:构建一个“订单服务 + 库存服务 + 支付服务”的典型电商场景,解决上述所有问题。


二、整体架构设计

我们采用以下组件:

组件

选型

说明

服务框架

ASP.NET Core 8 WebApi

跨平台、高性能

API 网关

YARP(Yet Another Reverse Proxy)

微软开源,高性能反向代理

服务注册/发现

Consul

轻量级,支持健康检查

负载均衡

YARP 内置 + 客户端负载均衡

结合 Consul

容错/熔断

Polly

超时、重试、熔断策略

分布式追踪

OpenTelemetry + Jaeger

标准协议,可视化链路

日志聚合

Serilog + Elasticsearch + Kibana(可选)

结构化日志

分布式事务

Saga(MassTransit + RabbitMQ)

异步补偿模式

容器编排

Kubernetes(Minikube 或 K3s)

生产级部署

配置管理

Kubernetes ConfigMap + Secret

或使用 Azure App Configuration

服务拆分:

  • Identity Service:认证授权(JWT),用户管理
  • Product Service:商品信息管理
  • Order Service:订单创建、查询,依赖库存和支付
  • Inventory Service:库存扣减、恢复
  • Payment Service:模拟支付处理
  • Notification Service:发送通知(异步)

三、环境准备

代码语言:javascript
复制
# 安装 .NET 8 SDK
# 安装 Docker Desktop
# 启用 Kubernetes(或使用 Minikube)
# 安装 Consul(开发用 docker 运行)
docker run -d --name consul -p 8500:8500 -e CONSUL_BIND_INTERFACE=eth0 consul:latest

# 安装 RabbitMQ
docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3-management

# 安装 Jaeger(用于追踪)
docker run -d --name jaeger -p 16686:16686 -p 4317:4317 jaegertracing/all-in-one:latest

四、基础共享库与契约

创建 Shared 类库,包含公共 DTO、异常、常量。

代码语言:javascript
复制
// Shared/DTOs/OrderDto.cs
public class OrderDto
{
    public int Id { get; set; }
    public string CustomerId { get; set; } = string.Empty;
    public List<OrderItemDto> Items { get; set; } = new();
    public decimal TotalAmount { get; set; }
    public string Status { get; set; } = "Pending"; // Pending, Paid, Shipped, Cancelled
    public DateTime CreatedAt { get; set; }
}

public class CreateOrderRequest
{
    public string CustomerId { get; set; } = string.Empty;
    public List<OrderItemDto> Items { get; set; } = new();
}

创建事件消息契约(用于 MassTransit 消息传递):

代码语言:javascript
复制
// Shared/Events/OrderSubmitted.cs
public record OrderSubmitted
{
    public Guid CorrelationId { get; init; }
    public int OrderId { get; init; }
    public string CustomerId { get; init; } = default!;
    public List<InventoryItem> Items { get; init; } = new();
}

public record InventoryItem
{
    public int ProductId { get; init; }
    public int Quantity { get; init; }
}

五、服务实现(以 Order Service 为例)

5.1 创建项目结构

代码语言:javascript
复制
dotnet new webapi -n Services.Order
dotnet add Services.Order package Consul
dotnet add Services.Order package Polly
dotnet add Services.Order package MassTransit.RabbitMQ
dotnet add Services.Order package OpenTelemetry.Exporter.Jaeger
dotnet add Services.Order package OpenTelemetry.Extensions.Hosting
dotnet add Services.Order package Serilog.AspNetCore

5.2 依赖注入配置(Program.cs)

代码语言:javascript
复制
using Consul;
using MassTransit;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
using Polly;
using Polly.Extensions.Http;
using Serilog;

var builder = WebApplication.CreateBuilder(args);

// Serilog
builder.Host.UseSerilog((ctx, cfg) =>
    cfg.ReadFrom.Configuration(ctx.Configuration));

// 服务注册 Consul
builder.Services.AddSingleton<IConsulClient, ConsulClient>(p =>
    new ConsulClient(cfg => cfg.Address = new Uri("http://localhost:8500")));
builder.Services.AddHostedService<ConsulRegistrationService>(); // 自定义健康检查注册

// HttpClient 容错(调用 Inventory 和 Payment)
builder.Services.AddHttpClient("InventoryClient", client =>
{
    client.BaseAddress = new Uri("http://localhost:5002/");
})
.AddPolicyHandler(HttpPolicyExtensions
    .HandleTransientHttpError()
    .OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
    .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))));

// MassTransit + RabbitMQ
builder.Services.AddMassTransit(x =>
{
    x.UsingRabbitMq((ctx, cfg) =>
    {
        cfg.Host("localhost", "/", h => { });
        cfg.ConfigureEndpoints(ctx);
    });
});

// OpenTelemetry 追踪
builder.Services.AddOpenTelemetry()
    .WithTracing(tracer => tracer
        .AddAspNetCoreInstrumentation()
        .AddHttpClientInstrumentation()
        .AddJaegerExporter());

// 数据库(使用 EF Core + PostgreSQL)
builder.Services.AddDbContext<OrderDbContext>(options =>
    options.UseNpgsql(builder.Configuration.GetConnectionString("OrderDb")));

// 业务服务
builder.Services.AddScoped<IOrderService, OrderService>();

// 控制器
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// 中间件
app.UseSerilogRequestLogging();
app.UseSwagger();
app.UseSwaggerUI();
app.UseAuthorization();
app.MapControllers();

// 自动迁移数据库
using (var scope = app.Services.CreateScope())
{
    var db = scope.ServiceProvider.GetRequiredService<OrderDbContext>();
    db.Database.Migrate();
}

app.Run();

5.3 Consul 服务注册(后台服务)

代码语言:javascript
复制
public class ConsulRegistrationService : IHostedService
{
    private readonly IConsulClient _consul;
    private readonly IHostApplicationLifetime _lifetime;

    public ConsulRegistrationService(IConsulClient consul, IHostApplicationLifetime lifetime)
    {
        _consul = consul;
        _lifetime = lifetime;
    }

    public async Task StartAsync(CancellationToken cancellationToken)
    {
        var registration = new AgentServiceRegistration()
        {
            ID = $"order-service-{Guid.NewGuid()}",
            Name = "order-service",
            Address = "localhost",
            Port = 5001,
            Check = new AgentHttpHealthCheck()
            {
                Interval = TimeSpan.FromSeconds(10),
                HTTP = "http://localhost:5001/health"
            }
        };
        await _consul.Agent.ServiceRegister(registration, cancellationToken);
    }

    public async Task StopAsync(CancellationToken cancellationToken)
    {
        // 注销逻辑(可选)
    }
}

5.4 Order Service 业务逻辑(Saga 初始步骤)

代码语言:javascript
复制
public class OrderService : IOrderService
{
    private readonly OrderDbContext _db;
    private readonly IHttpClientFactory _httpClientFactory;
    private readonly IPublishEndpoint _publishEndpoint;

    public OrderService(OrderDbContext db, IHttpClientFactory httpClientFactory, IPublishEndpoint publishEndpoint)
    {
        _db = db;
        _httpClientFactory = httpClientFactory;
        _publishEndpoint = publishEndpoint;
    }

    public async Task<Order> CreateOrderAsync(CreateOrderRequest request)
    {
        // 1. 计算总价(调用 Product Service 获取价格)
        var total = await CalculateTotalAsync(request.Items);

        // 2. 创建订单(状态为 Submitted)
        var order = new Order
        {
            CustomerId = request.CustomerId,
            TotalAmount = total,
            Status = "Submitted",
            CreatedAt = DateTime.UtcNow,
            Items = request.Items.Select(i => new OrderItem
            {
                ProductId = i.ProductId,
                Quantity = i.Quantity,
                UnitPrice = i.UnitPrice // 从 Product Service 获取
            }).ToList()
        };
        _db.Orders.Add(order);
        await _db.SaveChangesAsync();

        // 3. 发布 OrderSubmitted 事件(触发 Saga)
        await _publishEndpoint.Publish(new OrderSubmitted
        {
            CorrelationId = Guid.NewGuid(),
            OrderId = order.Id,
            CustomerId = order.CustomerId,
            Items = order.Items.Select(i => new InventoryItem
            {
                ProductId = i.ProductId,
                Quantity = i.Quantity
            }).ToList()
        });

        return order;
    }

    private async Task<decimal> CalculateTotalAsync(List<OrderItemDto> items)
    {
        // 调用 Product Service 获取价格(带 Polly 重试)
        var client = _httpClientFactory.CreateClient("ProductClient");
        decimal total = 0;
        foreach (var item in items)
        {
            var response = await client.GetAsync($"/api/products/{item.ProductId}");
            if (response.IsSuccessStatusCode)
            {
                var product = await response.Content.ReadFromJsonAsync<Product>();
                total += product.Price * item.Quantity;
            }
        }
        return total;
    }
}

5.5 其他服务(Inventory/Payment)类似,但各司其职


六、分布式事务 Saga 实现(使用 MassTransh + RabbitMQ)

我们采用 异步 Saga 模式,通过消息驱动协调多个服务。

6.1 Saga 状态机定义

创建 OrderSagaStateOrderSaga 类(可使用 MassTransit 的 MassTransitStateMachine):

代码语言:javascript
复制
public class OrderSaga : MassTransitStateMachine<OrderSagaState>
{
    public State Submitted { get; private set; }
    public State InventoryReserved { get; private set; }
    public State PaymentProcessed { get; private set; }
    public State Completed { get; private set; }
    public State Faulted { get; private set; }

    public Event<OrderSubmitted> OrderSubmitted { get; private set; }
    public Event<InventoryReserved> InventoryReservedEvent { get; private set; }
    public Event<PaymentCompleted> PaymentCompletedEvent { get; private set; }
    public Event<Fault<InventoryReserveFailed>> InventoryFailed { get; private set; }

    public OrderSaga()
    {
        InstanceState(x => x.CurrentState);

        // 定义事件关联
        Event(() => OrderSubmitted, x => x.CorrelateById(m => m.Message.CorrelationId));
        Event(() => InventoryReservedEvent, x => x.CorrelateById(m => m.Message.CorrelationId));
        Event(() => PaymentCompletedEvent, x => x.CorrelateById(m => m.Message.CorrelationId));
        Event(() => InventoryFailed, x => x.CorrelateById(m => m.Message.CorrelationId));

        Initially(
            When(OrderSubmitted)
                .Then(context =>
                {
                    context.Instance.OrderId = context.Message.OrderId;
                    context.Instance.CustomerId = context.Message.CustomerId;
                })
                .PublishAsync(context => context.Init<ReserveInventory>(new
                {
                    CorrelationId = context.Instance.CorrelationId,
                    OrderId = context.Instance.OrderId,
                    Items = context.Message.Items
                }))
                .TransitionTo(Submitted)
        );

        During(Submitted,
            When(InventoryReservedEvent)
                .Then(context => context.Instance.ReservationId = context.Message.ReservationId)
                .PublishAsync(context => context.Init<ProcessPayment>(new
                {
                    CorrelationId = context.Instance.CorrelationId,
                    OrderId = context.Instance.OrderId,
                    Amount = context.Message.Amount
                }))
                .TransitionTo(InventoryReserved),

            When(InventoryFailed)
                .PublishAsync(context => context.Init<OrderFailed>(new
                {
                    CorrelationId = context.Instance.CorrelationId,
                    OrderId = context.Instance.OrderId,
                    Reason = "库存不足"
                }))
                .TransitionTo(Faulted)
        );

        During(InventoryReserved,
            When(PaymentCompletedEvent)
                .Then(context => context.Instance.PaymentId = context.Message.PaymentId)
                .PublishAsync(context => context.Init<OrderCompleted>(new
                {
                    CorrelationId = context.Instance.CorrelationId,
                    OrderId = context.Instance.OrderId
                }))
                .Finalize()
        );

        SetCompletedWhenFinalized();
    }
}

每个服务需要实现对应的消费者:

  • Inventory Service 监听 ReserveInventory,尝试扣减库存,回复 InventoryReservedInventoryReserveFailed
  • Payment Service 监听 ProcessPayment,尝试支付,回复 PaymentCompletedPaymentFailed

6.2 库存服务的消费者(示例)

代码语言:javascript
复制
public class ReserveInventoryConsumer : IConsumer<ReserveInventory>
{
    private readonly InventoryDbContext _db;
    private readonly IPublishEndpoint _publish;

    public ReserveInventoryConsumer(InventoryDbContext db, IPublishEndpoint publish)
    {
        _db = db;
        _publish = publish;
    }

    public async Task Consume(ConsumeContext<ReserveInventory> context)
    {
        var message = context.Message;
        try
        {
            // 检查并扣减库存(带并发控制)
            foreach (var item in message.Items)
            {
                var stock = await _db.Stocks.FindAsync(item.ProductId);
                if (stock == null || stock.Quantity < item.Quantity)
                    throw new InsufficientStockException($"产品 {item.ProductId} 库存不足");
                stock.Quantity -= item.Quantity;
            }
            await _db.SaveChangesAsync();

            // 成功回复
            await _publish.Publish(new InventoryReserved
            {
                CorrelationId = message.CorrelationId,
                OrderId = message.OrderId,
                ReservationId = Guid.NewGuid().ToString(),
                Amount = message.TotalAmount // 需传递总金额
            });
        }
        catch (Exception ex)
        {
            // 失败回复
            await _publish.Publish(new InventoryReserveFailed
            {
                CorrelationId = message.CorrelationId,
                OrderId = message.OrderId,
                Reason = ex.Message
            });
        }
    }
}

七、API 网关 YARP 配置

YARP 是微软开源的代理库,可以基于路由配置将请求转发到不同服务。

7.1 创建 YARP 网关项目

代码语言:javascript
复制
dotnet new webapi -n ApiGateway
dotnet add ApiGateway package Yarp.ReverseProxy

7.2 配置 appsettings.json

代码语言:javascript
复制
{
  "ReverseProxy": {
    "Routes": {
      "orders": {
        "ClusterId": "order-cluster",
        "Match": {
          "Path": "/api/orders/{**catch-all}"
        },
        "Transforms": [
          { "PathPattern": "/api/orders/{**catch-all}" }
        ]
      },
      "inventory": {
        "ClusterId": "inventory-cluster",
        "Match": {
          "Path": "/api/inventory/{**catch-all}"
        }
      },
      "products": {
        "ClusterId": "product-cluster",
        "Match": {
          "Path": "/api/products/{**catch-all}"
        }
      }
    },
    "Clusters": {
      "order-cluster": {
        "Destinations": {
          "order1": {
            "Address": "http://order-service:5001/"
          }
        },
        "LoadBalancingPolicy": "RoundRobin"
      },
      // 其他集群类似
    }
  }
}

Program.cs 中启用 YARP:

代码语言:javascript
复制
builder.Services.AddReverseProxy()
    .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));

app.MapReverseProxy();

7.3 结合 Consul 动态服务发现

YARP 原生支持 Consul,但需安装 Yarp.ReverseProxy.ServiceDiscovery.Consul 并配置:

代码语言:javascript
复制
builder.Services.AddReverseProxy()
    .LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"))
    .AddConsulServiceDiscoveryProvider();

并在配置中更改 Cluster 的 Destination 为动态源:

代码语言:javascript
复制
"Clusters": {
  "order-cluster": {
    "DiscoveryMode": "Consul",
    "Consul": {
      "ServiceName": "order-service"
    }
  }
}

八、Kubernetes 部署

8.1 编写 Dockerfile(以 Order Service 为例)

代码语言:javascript
复制
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 80

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY . .
RUN dotnet publish Services.Order/Services.Order.csproj -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "Services.Order.dll"]

8.2 构建镜像并推送至仓库

代码语言:javascript
复制
docker build -t myapp/order-service:latest -f Services.Order/Dockerfile .
docker tag myapp/order-service:latest myregistry/order-service:1.0
docker push myregistry/order-service:1.0

8.3 编写 Kubernetes 资源清单

order-service-deployment.yaml

代码语言:javascript
复制
apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
  namespace: prod
spec:
  replicas: 3
  selector:
    matchLabels:
      app: order-service
  template:
    metadata:
      labels:
        app: order-service
    spec:
      containers:
      - name: order-service
        image: myregistry/order-service:1.0
        ports:
        - containerPort: 80
        env:
        - name: ConnectionStrings__OrderDb
          valueFrom:
            secretKeyRef:
              name: db-secret
              key: connection-string
        - name: Consul__Address
          value: "http://consul-service:8500"
        readinessProbe:
          httpGet:
            path: /health
            port: 80
          initialDelaySeconds: 15
---
apiVersion: v1
kind: Service
metadata:
  name: order-service
  namespace: prod
spec:
  selector:
    app: order-service
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
  type: ClusterIP

同理为其他服务和网关创建资源。

8.4 部署到 K8s

代码语言:javascript
复制
kubectl apply -f deploy/
kubectl expose deployment api-gateway --type=NodePort --port=80 --target-port=80 -n prod

九、监控与追踪

9.1 集成 OpenTelemetry

所有服务添加上述追踪配置后,会自动上报 Trace 到 Jaeger。访问 Jaeger UI http://localhost:16686 可查看请求链路。

9.2 日志聚合(Serilog + Seq / Elastic)

建议在每个服务中配置 Serilog 输出到 Elasticsearch,使用 Kibana 进行集中日志查询。

代码语言:javascript
复制
Log.Logger = new LoggerConfiguration()
    .WriteTo.Elasticsearch(new ElasticsearchSinkOptions(new Uri("http://elasticsearch:9200"))
    {
        IndexFormat = "logs-{0:yyyy.MM.dd}"
    })
    .CreateLogger();

十、生产环境注意事项

  • 配置管理:使用 K8s ConfigMap 管理环境变量,避免硬编码。
  • Secret 管理:数据库密码、JWT Key 等使用 K8s Secret 或 Vault。
  • 健康检查:实现 /health 端点,用于 K8s 探针和 Consul 检查。
  • 日志分级:区分 Debug/Info/Error,生产环境限制日志量。
  • 熔断降级:在 Polly 策略中添加 CircuitBreaker,防止级联故障。
  • 消息幂等性:确保事件消费者可重复处理(使用 CorrelationId 去重)。
  • 分布式数据一致性:Saga 模式不保证强一致,但最终一致,需设计补偿操作。
  • 安全:使用 OAuth2 + JWT,网关验证 Token 并转发用户身份到下游服务。

十一、总结与扩展

本文完整展示了基于 .NET 8 的微服务解决方案,涵盖了通信、注册发现、网关、容错、追踪、事务和容器化。这套架构可以支撑每天百万级请求的中型系统。

进阶方向

  • 引入 Dapr 简化服务间调用和状态管理。
  • 使用 Azure Service Bus 替代 RabbitMQ。
  • 加入 KEDA 实现基于事件的弹性伸缩。
  • 部署 Istio 服务网格,实现更细粒度的流量管理和安全策略。

微服务不是银弹,但它为我们提供了应对复杂业务场景的武器。合理拆分、稳步推进,才能充分发挥其优势。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • .NET 微服务架构从入门到落地:Docker + Kubernetes + YARP 网关 + 分布式事务实战
    • 一、为什么要做微服务?—— 不只是“拆”
    • 二、整体架构设计
    • 三、环境准备
    • 四、基础共享库与契约
    • 五、服务实现(以 Order Service 为例)
      • 5.1 创建项目结构
      • 5.2 依赖注入配置(Program.cs)
      • 5.3 Consul 服务注册(后台服务)
      • 5.4 Order Service 业务逻辑(Saga 初始步骤)
      • 5.5 其他服务(Inventory/Payment)类似,但各司其职
    • 六、分布式事务 Saga 实现(使用 MassTransh + RabbitMQ)
      • 6.1 Saga 状态机定义
      • 6.2 库存服务的消费者(示例)
    • 七、API 网关 YARP 配置
      • 7.1 创建 YARP 网关项目
      • 7.2 配置 appsettings.json
      • 7.3 结合 Consul 动态服务发现
    • 八、Kubernetes 部署
      • 8.1 编写 Dockerfile(以 Order Service 为例)
      • 8.2 构建镜像并推送至仓库
      • 8.3 编写 Kubernetes 资源清单
      • 8.4 部署到 K8s
    • 九、监控与追踪
      • 9.1 集成 OpenTelemetry
      • 9.2 日志聚合(Serilog + Seq / Elastic)
    • 十、生产环境注意事项
    • 十一、总结与扩展
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档