惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Tor Project blog
博客园 - 聂微东
Microsoft Azure Blog
Microsoft Azure Blog
博客园 - 【当耐特】
G
Google Developers Blog
J
Java Code Geeks
The Cloudflare Blog
Attack and Defense Labs
Attack and Defense Labs
宝玉的分享
宝玉的分享
Last Week in AI
Last Week in AI
Cisco Talos Blog
Cisco Talos Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
I
Intezer
Jina AI
Jina AI
T
Tenable Blog
P
Palo Alto Networks Blog
Project Zero
Project Zero
D
DataBreaches.Net
Hugging Face - Blog
Hugging Face - Blog
The Hacker News
The Hacker News
F
Full Disclosure
Cloudbric
Cloudbric
量子位
H
Heimdal Security Blog
K
Kaspersky official blog
有赞技术团队
有赞技术团队
罗磊的独立博客
V
Vulnerabilities – Threatpost
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
阮一峰的网络日志
阮一峰的网络日志
Vercel News
Vercel News
Recent Announcements
Recent Announcements
WordPress大学
WordPress大学
GbyAI
GbyAI
S
SegmentFault 最新的问题
M
MIT News - Artificial intelligence
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
I
InfoQ
Recorded Future
Recorded Future
Security Archives - TechRepublic
Security Archives - TechRepublic
AI
AI
Webroot Blog
Webroot Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
爱范儿
爱范儿
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
T
The Exploit Database - CXSecurity.com
Apple Machine Learning Research
Apple Machine Learning Research
C
Cybersecurity and Infrastructure Security Agency CISA
H
Hacker News: Front Page
Latest news
Latest news

博客园 - hu晓峰

记winform程序异常排查 记一次wpf 背景图的坑点 【Unity踩坑】Unity项目管理员权限问题(Unity is running as administrator ) 依赖注入 libmodbus编译为64位动态库 一文读懂Modbus协议:工业设备的“普通话“通信指南 Mysql union与union all有什么区别? 理解Systemd服务重启策略:on-failure vs always Redis分布式锁正确的实现方法 C# 解决串口通讯中,返回数据不完整 字典Dictionary.Add不是把新的元素插入到字典最后面 c# Avalonia 架构开发跨平台应用 ‌索引基数 MySQL InnoDB损坏修复:使用innodb_force_recovery 整数取低字节 C#汉字-区位码相互转化类 avalonia在linux下运行出现Default font family name can't be null or empty问题的解决 ICMP timestamp请求响应漏洞CVE-1999-0524解决方法 详解mysql的for update 使用Redis的SETNX命令实现分布式锁 ASP.NET Core中如何对不同类型的用户进行区别限流
微服务聚合查询
hu晓峰 · 2026-03-06 · via 博客园 - hu晓峰

核心比喻:订阅报纸

想象一下,你要了解整个城市(系统)每天发生了什么。你有两种方式:

  1. 传统方式(API组合):每天早上,你自己打电话给公安局气象局证券交易所体育局...挨个询问最新消息,然后自己整理成简报。(费时费力,依赖对方有空)

  2. 事件驱动方式:你订阅了所有这些机构的“每日新闻快报”(事件)。每天早上,各机构会自动把快报发到你的邮箱。你已经雇了一个私人秘书(报表服务),他的工作就是阅读所有快报,并更新你办公室墙上那张巨大的、汇总所有信息的“城市综合看板”。你想知道什么,看一眼看板就行。(高效、解耦、信息集中)

在微服务中,这个“看板”就是为查询优化的读模型


事件驱动同步的核心流程(四步走)

我们以一个经典的电商场景为例:“在用户个人中心,显示用户信息和其最近订单”

image

第一步:事件产生(出版)

  • 用户服务​ 在处理“用户注册”后,发布一个 UserCreatedEvent事件,事件体包含 {userId: 123, name: "小明"}

  • 订单服务​ 在处理“下单”后,发布一个 OrderCreatedEvent事件,事件体包含 {orderId: 001, userId: 123, product: "手机", amount: 5000}

  • 关键:这些服务只负责发布,不关心谁去听。它们之间完全解耦。

第二步:事件传递(订阅/广播)

  • 这些事件被发送到一个事件总线/消息中间件,如 Kafka、RabbitMQ。它就像城市的广播系统或邮局,确保事件可靠地送达所有订阅者。

第三步:事件消费与聚合(建立“看板”)

  • 有一个专门的 报表服务​ 或 查询服务​ 订阅了它关心的所有事件(UserCreatedEvent, OrderCreatedEvent...)。

  • 它的核心工作是:

    1. 监听UserCreatedEvent-> 在自己的数据库里创建或更新一条用户记录。

    2. 监听OrderCreatedEvent-> 去自己的数据库查找对应的用户记录,然后把这条订单信息追加到该用户的订单列表中(可能存成一个JSON字段,或关联到另一张订单表)。

  • 最终,这个服务在自己的数据库里,维护了一张聚合视图表,比如叫 user_order_summary,表结构是专门为“查询用户及其订单”优化的宽表,可能长这样:

    userId

    userName

    orderList (JSON)

    123

    小明

    [{"id":001, "product":"手机"...}, ...]

第四步:高效查询(查看“看板”)

  • 当客户端(如前端)需要查询“用户123的个人中心”时,它不再调用用户服务和订单服务,而是直接调用这个报表服务

  • 报表服务直接查询自己本地的 user_order_summary,一次简单的查询(甚至根据userId是主键),毫秒级返回所有聚合好的数据。


为什么这是最佳实践?

  1. 性能极致:查询变成了单服务、单数据库、本地查询,速度极快,没有任何网络开销和串联延迟。

  2. 彻底解耦

    • 写端(用户、订单服务)完全不知道谁在用它们的数据,只负责发事件。

    • 读端(报表服务)完全掌控自己的数据模型,可以为了查询效率任意优化,不受制于上游服务的数据库设计。

  3. 高可用:即使“用户服务”临时挂机,只要报表服务的数据是新的,查询功能依然完全可用,只是数据暂时不更新了(最终一致性)。

  4. 职责清晰:符合 CQRS(命令查询职责分离)​ 模式,写模型和读模型各司其职,独立演化。