从单体到微服务:基于 ABP vNext 模块化设计的演进之路
🚀 从单体到微服务:基于 ABP vNext 模块化设计的演进之路
🧩 引言
在需求多变且性能压力日益增大的背景下,传统单体应用在部署、维护和扩展方面存在显著挑战。
ABP vNext 作为基于 ASP.NET Core 的框架,自带模块化设计支持,方便开发者实现从单体到微服务的平滑转换。
本文通过一个简单项目,展示如何使用 ABP vNext 实现服务化设计,包括模块划分、远程调用、API 网关配置、CAP 分布式事务等。
🏗️ 架构演进概览
单体架构
在单体架构中,所有功能模块集成在同一个项目中,模块之间通过类方法调用,部署和维护相对简单。
微服务架构
系统被拆分成多个独立模块服务,通过 HTTP API 或消息队列通信,展示更好的扩展性和维护性能。
🧱 模块划分与项目结构
在 ABP vNext 中,推荐根据 DDD 思想划分模块,每个模块包含:
- Domain.Shared:定义公共定义
- Domain:实体、仓库、基础进程
- Application.Contracts:应用服务接口
- Application:应用服务实现
- HttpApi:Controller层
- HttpApi.Client:远程调用代理
- EntityFrameworkCore:数据库配置
示例项目目录结构:
├── modules
│ ├── Product
│ │ ├── Product.Domain.Shared
│ │ ├── Product.Domain
│ │ ├── Product.Application.Contracts
│ │ ├── Product.Application
│ │ ├── Product.HttpApi
│ │ ├── Product.HttpApi.Client
│ │ └── Product.EntityFrameworkCore
│ ├── Order
│ │ ├── Order.Domain.Shared
│ │ ├── Order.Domain
│ │ ├── Order.Application.Contracts
│ │ ├── Order.Application
│ │ ├── Order.HttpApi
│ │ ├── Order.HttpApi.Client
│ │ └── Order.EntityFrameworkCore
│ └── Customer
│ ├── Customer.Domain.Shared
│ ├── Customer.Domain
│ ├── Customer.Application.Contracts
│ ├── Customer.Application
│ ├── Customer.HttpApi
│ ├── Customer.HttpApi.Client
│ └── Customer.EntityFrameworkCore
├── gateways
│ └── ApiGateway
├── services
│ ├── IdentityService
│ └── AdministrationService
└── shared└── Shared.Hosting
💡 远程调用方案
以下示例展示两种远程调用方案,任选其一即可,不必同时使用。
特性 | 手写防腐层(ProductServiceClient ) | ABP HTTP 客户端代理(AddHttpClientProxies ) |
---|---|---|
实现成本 | 需手动编写接口、实现类和异常包装;工作量大 | 一行注册代码,自动生成所有 RPC 接口的代理,零手写 |
依赖注入 | 需显式注册:AddTransient<IProductServiceClient, ProductServiceClient>() | 直接注入业务接口:IProductAppService |
维护与升级 | 接口或地址变更时需手动修改,多点维护 | 修改 RemoteServices 配置即可全局生效 |
序列化与配置 | 需手动配置 HttpClient 、序列化、超时等 | 开箱即用,内置序列化、重试和标头继承 |
异常与重试策略 | 需自行捕获异常并实现重试/熔断 | 可集中配置 Polly 策略,代码更简洁 |
性能与一致性 | 可针对单接口优化,但全局策略不统一 | 统一客户端池复用,保证跨接口一致性 |
典型场景 | 接口少、调用简单、需自定义逻辑 | 接口多、需统一治理、微服务大规模场景 |
建议
- 若仅少量跨模块调用或需自定义特殊逻辑,可使用手写防腐层;
- 对于微服务化项目、接口众多或需统一鉴权/重试策略,推荐使用
AddHttpClientProxies
自动代理方案。
🔗 服务通信与防腐层设计
以 Order 服务调用 Product 服务为例:
IProductServiceClient.cs
public interface IProductServiceClient : ITransientDependency
{Task<ProductDto> GetProductByIdAsync(Guid productId);
}
ProductServiceClient.cs
public class ProductServiceClient : IProductServiceClient
{private readonly IProductAppService _productAppService;public ProductServiceClient(IProductAppService productAppService){_productAppService = productAppService;}public async Task<ProductDto> GetProductByIdAsync(Guid productId){return await _productAppService.GetAsync(productId);}
}
OrderAppService.cs
public class OrderAppService : ApplicationService, IOrderAppService
{private readonly IProductServiceClient _productServiceClient;private readonly IRepository<Order, Guid> _orderRepository;public OrderAppService(IProductServiceClient productServiceClient,IRepository<Order, Guid> orderRepository){_productServiceClient = productServiceClient;_orderRepository = orderRepository;}public async Task<OrderDto> CreateOrderAsync(CreateOrderDto input){// 远程调用获取产品信息var product = await _productServiceClient.GetProductByIdAsync(input.ProductId);// 简单的库存校验if (product.Stock < input.Quantity){throw new UserFriendlyException("库存不足,无法下单。", "InsufficientStock");}// 构造订单实体var order = new Order(GuidGenerator.Create(), CurrentTenant.Id){ProductId = input.ProductId,Quantity = input.Quantity,UnitPrice = product.Price,TotalPrice = product.Price * input.Quantity,Status = OrderStatus.Pending};// 插入并保存order = await _orderRepository.InsertAsync(order, autoSave: true);// 返回映射后的 DTOreturn ObjectMapper.Map<Order, OrderDto>(order);}
}
提示
如果保留手写 ProductServiceClient,记得在模块里 ConfigureServices 注册:
context.Services.AddTransient<IProductServiceClient, ProductServiceClient>();
🚀 HTTP API 客户端代理配置
🌐 1. 在宿主项目的 appsettings.json
中配置远程服务
{"RemoteServices": {"ProductService": {"BaseUrl": "http://product-service" // 💡 使用容器名}}
}
🔧 2. 在宿主的 Program.cs
中注册 HTTP 客户端代理
using Volo.Abp.Http.Client.Configuration;
using Product.HttpApi.Client;var builder = WebApplication.CreateBuilder(args);// 可选:覆盖或添加 RemoteService 配置 ✨
builder.Services.Configure<RemoteServiceOptions>(options =>
{// ...
});// 注册 HttpClient 代理,自动生成 IProductAppService 的实现 🛠️
builder.Services.AddHttpClientProxies(typeof(ProductHttpApiClientModule).Assembly,remoteServiceName: "ProductService");var app = builder.Build();
// 其他中间件...
app.Run();
📦 3. 在 OrderAppService
中注入并调用
public class OrderAppService : ApplicationService, IOrderAppService
{private readonly IProductAppService _productAppService;private readonly IRepository<Order, Guid> _orderRepository;public OrderAppService(IProductAppService productAppService,IRepository<Order, Guid> orderRepository){_productAppService = productAppService;_orderRepository = orderRepository;}public async Task<OrderDto> CreateOrderAsync(CreateOrderDto input){// 远程调用获取产品信息var product = await _productAppService.GetAsync(input.ProductId);// 简单的库存校验if (product.Stock < input.Quantity){throw new UserFriendlyException("库存不足,无法下单。","InsufficientStock");}// 构造订单实体var order = new Order(GuidGenerator.Create(), CurrentTenant.Id){ProductId = input.ProductId,Quantity = input.Quantity,UnitPrice = product.Price,TotalPrice = product.Price * input.Quantity,Status = OrderStatus.Pending};// 插入并保存order = await _orderRepository.InsertAsync(order, autoSave: true);// 返回映射后的 DTOreturn ObjectMapper.Map<Order, OrderDto>(order);}
}
🌐 API 网关配置
ocelot.json
{"Routes": [{"DownstreamPathTemplate": "/api/product/{everything}","DownstreamScheme": "http","DownstreamHostAndPorts": [{"Host": "product-service","Port": 80}],"UpstreamPathTemplate": "/api/product/{everything}","UpstreamHttpMethod": [ "GET", "POST", "PUT", "DELETE" ]},{"DownstreamPathTemplate": "/api/order/{everything}","DownstreamScheme": "http","DownstreamHostAndPorts": [{"Host": "order-service","Port": 80}],"UpstreamPathTemplate": "/api/order/{everything}","UpstreamHttpMethod": [ "GET", "POST", "PUT", "DELETE" ]}],"GlobalConfiguration": {"BaseUrl": "http://localhost:5000"}
}
Program.cs
var builder = WebApplication.CreateBuilder(args)builder.Services.AddOcelot(builder.Configuration);var app = builder.Build();// 添加路由中间件
app.UseRouting();app.UseAuthentication();
app.UseAuthorization();app.UseEndpoints(endpoints =>
{// 留空即可,由 Ocelot 接管路由
});// 配置 Ocelot
await app.UseOcelot();await app.RunAsync();
🔄 分布式事务处理(CAP 集成)
在微服务架构中,分布式事务是一项挑战。CAP 是一个基于 .NET 的分布式事务框架,支持最终一致性和事件驱动。
★ 实现过程可参考我之前的博客:
ABP vNext 集成 CAP + RabbitMQ 实现可靠事件总线
🔢 日志监控
集成 Serilog 作为日志记录框架,配合 Prometheus + Grafana 实现持续性监控和分析:
- 通过
appsettings.json
配置 Serilog 输出到文件或 Elasticsearch - Prometheus + .NET Exporter 接入应用跟踪时间性数据
- Grafana 中查看调用指标、服务状态、运行时间
🚠️ Docker 部署
使用 docker-compose.yml
同时启动多个服务,包括:
- Product / Order 服务
- CAP Dashboard
- RabbitMQ
- API Gateway
示例 docker-compose.yml 配置:
version: '3.4'networks:app-net:driver: bridgeservices:rabbitmq:image: rabbitmq:3.13-managementcontainer_name: rabbitmqrestart: alwaysnetworks:- app-netports:- "5672:5672" # AMQP 协议端口- "15672:15672" # 管理界面端口product-service:build: ./src/ProductServicecontainer_name: product-servicerestart: alwaysnetworks:- app-netports:- "5001:80" # 将容器内部 80 端口映射到宿主机 5001depends_on:- rabbitmqorder-service:build: ./src/OrderServicecontainer_name: order-servicerestart: alwaysnetworks:- app-netports:- "5002:80" # 将容器内部 80 端口映射到宿主机 5002depends_on:- rabbitmq- product-serviceapi-gateway:build: ./src/ApiGatewaycontainer_name: api-gatewayrestart: alwaysnetworks:- app-netports:- "5000:80" # 将容器内部 80 端口映射到宿主机 5000depends_on:- product-service- order-servicecap-dashboard:image: dnccommunity/cap-dashboard:v6.2.1container_name: cap-dashboardrestart: alwaysnetworks:- app-netports:- "8080:80" # CAP Dashboard Web UIdepends_on:- rabbitmq# 如果需要持久化 RabbitMQ 数据,可在此处添加 volumes 映射
# volumes:
# rabbitmq-data:
# driver: local
📊 总结
本文采用 ABP vNext 框架,实现了从单体到微服务的优雅转换,合理分布模块,支持分布式事务、日志和监控。
相关文章:
从单体到微服务:基于 ABP vNext 模块化设计的演进之路
🚀 从单体到微服务:基于 ABP vNext 模块化设计的演进之路 🧩 引言 在需求多变且性能压力日益增大的背景下,传统单体应用在部署、维护和扩展方面存在显著挑战。 ABP vNext 作为基于 ASP.NET Core 的框架,自带模块化设…...

USB集线器芯片革新之战:CH334U如何以工业级性能重新定义HUB控制器
一、当工业智能化遭遇接口瓶颈 在智能制造与边缘计算蓬勃发展的今天,工程师们正面临一个看似微小却至关重要的挑战——如何让USB集线器在极端工况下保持稳定?传统HUB控制器在-20℃以下频繁出现信号失真,产线突然断电导致的静电击穿更是让设备…...
C#学习7_面向对象:类、方法、修饰符
一、类 1class 1)定义类 访问修饰符class 类名{ 字段 构造函数:特殊的方法(用于初始化对象) 属性 方法... } eg: public class Person { // 字段 private string name; private int a…...

基于 Spring Boot 瑞吉外卖系统开发(十)
基于 Spring Boot 瑞吉外卖系统开发(十) 修改菜品 修改菜品是在原有的菜品信息的上对菜品信息进行更新,对此修改菜品信息之前需要将原有的菜品信息在修改界面进行展示,然后再对菜品信息进行修改。 修改菜品分为回显菜品信息和更…...

C++ 与 Lua 联合编程
在软件开发的广阔天地里,不同编程语言各有所长。C 以其卓越的性能、强大的功能和对硬件的直接操控能力,在系统开发、游戏引擎、服务器等底层领域占据重要地位,但c编写的程序需要编译,这往往是一个耗时操作,特别对于大型…...
中介者模式(Mediator Pattern)详解
文章目录 1. 中介者模式概述1.1 定义1.2 基本思想2. 中介者模式的结构3. 中介者模式的UML类图4. 中介者模式的工作原理5. Java实现示例5.1 基本实现示例5.2 飞机空中交通控制示例5.3 GUI应用中的中介者模式6. 中介者模式的优缺点6.1 优点6.2 缺点7. 中介者模式的适用场景8. 中介…...

Linux系统(OpenEuler22.03-LTS)部署FastGPT
在 openEuler 22.03 LTS 系统上通过 Docker Compose 安装 FastGPT 的步骤如下: 官方参考文档:https://doc.fastgpt.cn/docs/development/docker/ 1. 安装 Docker 和 Docker Compose 可以参考我之前离线安装Docker的文章:openEuler 22.03 LT…...

Kubernetes控制平面组件:Controller Manager 之 内置Controller详解
云原生学习路线导航页(持续更新中) kubernetes学习系列快捷链接 Kubernetes架构原则和对象设计(一)Kubernetes架构原则和对象设计(二)Kubernetes架构原则和对象设计(三)Kubernetes控…...

结合Splash与Scrapy:高效爬取动态JavaScript网站
在当今的Web开发中,JavaScript的广泛应用使得许多网站的内容无法通过传统的请求-响应模式直接获取。为了解决这个问题,Scrapy开发者经常需要集成像Splash这样的JavaScript渲染引擎。本文将详细介绍Splash JS引擎的工作原理,并探讨如何将其与S…...

用于构建安全AI代理的开源防护系统
大家读完觉得有帮助记得及时关注!!! 大型语言模型(LLMs)已经从简单的聊天机器人演变为能够执行复杂任务的自主代理,例如编辑生产代码、编排工作流程以及基于不受信任的输入(如网页和电子邮件&am…...
算法与数据结构 - 常用图算法总结
在图论中,图算法非常重要,广泛应用于计算机科学、网络分析、社交网络、地理信息系统等领域。下面是一些常用的图算法,按不同功能和应用场景分类: 1. 图的遍历 图遍历算法用于遍历图中的节点和边。主要有两种常见的图遍历方法&am…...

克里金模型+多目标优化+多属性决策!Kriging+NSGAII+熵权TOPSIS!
目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 克里金模型多目标优化多属性决策!KrigingNSGAII熵权TOPSIS!!matlab2023b语言运行! 1.克里金模型(Kriging Model)是一种基于空间统计学的插值方法…...

LLM 论文精读(三)Demystifying Long Chain-of-Thought Reasoning in LLMs
这是一篇2025年发表在arxiv中的LLM领域论文,主要描述了长思维链 Long Chain-of-Thought 对LLM的影响,以及其可能的生成机制。通过大量的消融实验证明了以下几点: 与shot CoT 相比,long CoT 的 SFT 可以扩展到更高的性能上限&…...

【Prompt工程—文生图】案例大全
目录 一、人物绘图 二、卡通头像 三、风景图 四、logo设计图 五、动物形象图 六、室内设计图 七、动漫风格 八、二次元图 九、日常场景图 十、古风神化图 十一、游戏场景图 十二、电影大片质感 本文主要介绍了12种不同类型的文生图技巧,通过加入不同的图像…...
本地可执行命令的智能体部署方案
本地可执行命令的智能体部署方案,目标是让大语言模型(LLM)在本地接收自然语言指令,并自动调用系统命令、脚本或应用程序,完成任务自动化。这类系统通常被称为 LLM Agent with Tool Use 或 本地 Agent 实体系统。 &…...

rust程序静态编译的两种方法总结
1. 概述 经过我的探索,总结了两种rust程序静态编译的方法,理论上两种方法都适用于windows、mac os和linux(mac os未验证),实测方法一性能比方法二好,现总结如下,希望能够帮到你. 2.方法一 2.1 添加配置文件 在项目的同级文件夹下新…...
验证码(笔记)
为什么要有验证码: 为什么验证码这么让人厌烦,每个网站还要使用它呢?换句话说,这些网站为什么要“故意为难”用户呢? 其实验证码主要是为了区分用户是计算机还是人。假设一个黑客知道了你的账号,根据账号可…...

【Linux系列】目录大小查看
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...

2048游戏(含Python源码)
前言 相关参考游戏: 像素飞机大战(含Python源码)-CSDN博客https://blog.csdn.net/weixin_64066303/article/details/147693018?spm1001.2014.3001.5501使用DeepSeek定制Python小游戏——以“俄罗斯方块”为例-CSDN博客https://blog.csdn.n…...
SwiftData 数据持久化解决方案
什么是 SwiftData? SwiftData 是苹果在 WWDC23 上推出的全新数据持久化框架,它构建在 Core Data 之上,但提供了更加 Swift 友好的 API。SwiftData 旨在简化数据模型的创建和管理,让开发者能够以更少的代码实现强大的数据持久化功…...

中间件-RocketMQ
RocketMQ 基本架构消息模型消费者消费消息模式顺序消息机制延迟消息批量消息事务消息消息重试最佳实践 基本架构 nameServer: 维护broker列表信息,客户端连接时只需要连接nameServer。可配置成集群。 broker:broker分为master和slave,master负…...
PostgreSQL 的 pg_current_logfile 函数
PostgreSQL 的 pg_current_logfile 函数 pg_current_logfile() 是 PostgreSQL 9.6 版本引入的一个系统管理函数,用于获取当前正在使用的日志文件路径。 一 基本用法 1 函数定义 pg_current_logfile([text]) → text2 简单查询 -- 获取当前日志文件路径 SELECT …...

Python就业方向有哪些?
Python 作为一门通用、易学且功能强大的编程语言,在多个领域都有广泛的应用,因此就业方向也非常多样化。以下是 Python 主要的就业方向及相关技能要求。 1. Web 开发 岗位:Python Web 开发工程师、后端工程师、全栈工程师技术栈:…...

iptables 访问控制列表使用记录
iptables 是linux操作系统上自带的防火墙程序,功能强大,能够依据策略过滤掉一些恶意访问流量,本次记录一下iptables的常见使用方法,未尽之处,欢迎补充。 一、iptables 下载 我这里使用的是华为openEuler 22.03版本&am…...

16. Qt系统相关:事件、定时器
1. Qt事件 1.1 简介 事件是应用程序内部或者外部产生的事情或者动作的统称。在Qt中使用一个对象来表示一个事件。所有的Qt事件均继承于抽象类QEvent。事件是由系统或者Qt平台本身在不同的时刻发出的。当用户按下鼠标、敲下键盘,或者是窗口需要重新绘制的时候&#…...

云平台搭建
物联网云平台的基本概述 基本概念 随着物联网技术的快速发展,越来越多的设备需要接入网络以实现智能化功能,物联网平台应运而生。 物联网云平台(IoT Cloud Platform)是物联网生态系统中的核心组件,它通过提供一系列…...

数学实验(Matlab语言环境和线性代数实验)
一、Matlab语言环境和线性代数实验 1.Matlab语言环境 Matlab简介 Matlab:Matrix Laboratry 矩阵实验室 Matlab 提供了强大的科学计算、灵活的程序设计流程、高质量的图形可视化与界面设计等功能,被广泛应用于科学计算、控制系统、信息处理等领域的分…...
sherpa:介绍
更多内容:XiaoJ的知识星球 目录 1. sherpa 介绍 1. sherpa 介绍 sherpa是 Next-gen Kaldi 项目的部署框架。 sherpa 支持在各种平台上部署与语音相关的预训练模型,并提供多种语言绑定。 目前,sherpa 拥有以下子项目: k2-fsa/sh…...
图片上传的util和使用
图片上传的util package com.ruoyi.web.controller.common.utils;import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.spri…...
2025年4月个人工作生活总结
本文为 2025年4月工作生活总结。 研发编码 一个项目的临时记录 自2月份领导让我牵头负责一个项目起,在本月算是有较多时间投入——但也是与之前的相比。 月初,清明节前一晚上,因某事务被叫上参加临时紧急远程会议,几方领导都在…...