从单体到微服务:我是如何用C Web API + HttpClientFactory重构老旧系统的

从单体到微服务:我是如何用C Web API + HttpClientFactory重构老旧系统的从单体到微服务 C Web API HttpClientFa 重构实战 去年接手一个遗留的订单管理系统时 我面对的是一个典型的 ASP NET WebForms 单体应用 所有业务逻辑挤在同一个项目里 数据库表关联复杂得像蜘蛛网 每次发布都要全站停机 最要命的是黑五促销期间 订单模块的崩溃直接导致公司损失了 15 的潜在营收 这次惨痛教训让我们下定决心进行服务化改造 而 C

大家好,我是讯享网,很高兴认识大家。这里提供最前沿的Ai技术和互联网信息。

# 从单体到微服务:C# Web API + HttpClientFactory重构实战

去年接手一个遗留的订单管理系统时,我面对的是一个典型的ASP.NET WebForms单体应用——所有业务逻辑挤在同一个项目里,数据库表关联复杂得像蜘蛛网,每次发布都要全站停机。最要命的是黑五促销期间,订单模块的崩溃直接导致公司损失了15%的潜在营收。这次惨痛教训让我们下定决心进行服务化改造,而C# Web API与HttpClientFactory的组合成为了技术栈的核心选择。

1. 重构规划与边界划分

在动手拆解这个运行了7年的庞然大物前,我们花了三周时间进行领域分析。通过事件风暴工作坊,团队在白板上贴出了287个用户故事便签,最终识别出订单处理、库存管理、支付网关和物流跟踪四个核心子域。

关键决策点: 我们决定优先解耦订单处理模块,因为它的业务规则最复杂且性能瓶颈最明显。但保留用户认证等横切关注点作为共享库,避免过早引入分布式会话的复杂性。

服务拆分后的架构对比:

维度 原单体架构 新微服务架构
部署单元 单个IIS应用池 4个独立Docker容器
数据库 共享SQL Server实例 每个服务独占数据库Schema
事务边界 数据库事务 Saga模式+补偿事务
团队协作 代码合并冲突频繁 独立代码仓库+契约测试
// 订单服务的领域模型核心 public class Order public string OrderNumber public List 
  
    
    
      Items public void AddItem(Product product, int quantity) else { Items.Add(new OrderItem(product, quantity)); } } } 
    

> 经验提示:在拆分初期,建议先用命名空间隔离代码而非立即物理拆分,等团队适应了领域驱动设计思维后再进行服务化部署

2. HTTP通信基础设施搭建

选择HttpClientFactory而非裸HttpClient的决定,来自于我们早期原型阶段遇到的端口耗尽问题——在负载测试中,频繁创建HttpClient实例导致系统在3000QPS时就开始抛出SocketException。

2.1 弹性通信管道配置

通过NuGet引入Polly扩展包后,我们为订单服务与库存服务的交互设计了三级防护:

  1. 重试策略:针对网络抖动,设置指数退避重试
  2. 熔断器:当库存服务5分钟内错误率超过30%时熔断30秒
  3. 超时控制:任何跨服务调用必须在800ms内响应
// Startup.cs中的服务注册 services.AddHttpClient("InventoryService") .AddTransientHttpErrorPolicy(policy => policy.WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)))) .AddPolicyHandler(Policy.TimeoutAsync 
  
    
    
      (TimeSpan.FromMilliseconds(800))) .AddCircuitBreakerPolicy(5, TimeSpan.FromSeconds(30)); 
    

实际运行中遇到的坑:

  • 序列化冲突:库存服务使用Newtonsoft.Json而订单服务改用System.Text.Json
  • 头信息丢失:Authorization头在重试时未被自动携带
  • 日志关联:跨服务调用需要注入相同的Request-Id

我们通过统一序列化配置和自定义DelegatingHandler解决了这些问题:

public class CorrelationIdHandler : DelegatingHandler { protected override async Task 
  
    
    
      SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { request.Headers.TryAddWithoutValidation("X-Correlation-ID", Guid.NewGuid().ToString()); var response = await base.SendAsync(request, cancellationToken); _logger.LogInformation($"调用{request.RequestUri}耗时" + $"{response.Headers.GetValues("X-Elapsed-Time").First()}ms"); return response; } } 
    

3. 服务契约与版本控制

当支付团队突然要求在所有金额字段增加货币类型支持时,我们意识到API版本控制不是可选项而是必选项。经过评估,我们采用了URI路径版本内容协商的混合方案:

/api/v1/orders/123 → 返回原始金额格式 /api/v2/orders/123 → 返回包含currency字段的新格式 

对应的控制器实现:

[ApiVersion("1.0")] [Route("api/v{version:apiVersion}/orders")] public class OrdersV1Controller : ControllerBase { [HttpGet("{id}")] public ActionResult 
  
    
    
      Get(int id) { // 返回旧版DTO } } [ApiVersion("2.0")] [Route("api/v{version:apiVersion}/orders")] public class OrdersV2Controller : ControllerBase { [HttpGet("{id}")] public ActionResult 
     
       Get(int id) { // 返回新版DTO } } 
      
    

为了平滑迁移,我们在API网关层实现了版本路由:

// Ocelot配置示例 { "DownstreamPathTemplate": "/api/v{version}/orders/{id}", "UpstreamPathTemplate": "/api/orders/{id}", "UpstreamHttpMethod": [ "Get" ], "DownstreamHttpMethod": "GET", "RouteClaimsRequirement": { "version": "1|2" } } 

4. 监控与故障诊断

当系统变成分布式后,传统的性能计数器已经不够用了。我们搭建的监控体系包含三个层次:

应用指标监控

  • 使用Prometheus收集各服务的HTTP请求指标
  • Grafana仪表盘展示P99延迟、错误率等关键数据
  • 为HttpClientFactory注入指标收集器
services.AddHttpClient("PaymentService") .AddHttpMessageHandler(() => new PrometheusHttpMessageHandler("order_service")); 

分布式追踪

  • 通过OpenTelemetry实现跨服务调用链追踪
  • Jaeger可视化订单创建的全链路调用
  • 在日志中注入SpanId实现日志与追踪关联

业务健康检查

  • 每个服务暴露/health端点
  • 检查数据库连接、下游服务可达性等
  • Kubernetes基于此实现Pod自动恢复
// 健康检查实现示例 public class DatabaseHealthCheck : IHealthCheck { public async Task 
  
    
    
      CheckHealthAsync( HealthCheckContext context, CancellationToken cancellationToken = default) { try { using var conn = new SqlConnection(_config.DbConnection); await conn.OpenAsync(cancellationToken); return HealthCheckResult.Healthy(); } catch (Exception ex) { return HealthCheckResult.Unhealthy(ex.Message); } } } 
    

在实施监控后,我们发现了几个关键问题:

  1. 订单提交时同步调用库存预留是性能瓶颈
  2. 支付服务在MySQL连接数达到50时开始拒绝请求
  3. 物流查询API的响应时间波动达600ms

针对这些问题,我们最终将库存操作改为异步事件驱动,为支付服务增加了连接池,并为物流服务引入了本地缓存。

小讯
上一篇 2026-04-18 23:57
下一篇 2026-04-18 23:55

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/269197.html