04 - 服务之间怎么”说话”
拆成微服务后,最直接的问题来了:原来在一个进程里,订单模块要拿用户信息,直接调个函数 get_user(id) 就行。现在用户服务和订单服务是两个独立的应用,中间隔着网络——它们该怎么互相说话?
答案分两大类:同步调用 和 异步消息。
方式一:同步调用 —— 像”打电话”
订单服务需要用户信息,就直接通过网络发一个请求给用户服务,然后等它回复。
订单服务 用户服务
│ "喂,用户 42 的信息给我一下" │
│ ───────────────────────────────▶ │
│ │ 查库…
│ "他叫张三,是 VIP" │
│ ◀─────────────────────────────── │
│ (拿到结果,继续往下走) │
这就像打电话:我问,你答,在你回答之前,我得一直拿着话筒等着。
你已经很熟悉这种形态了——它本质就是一个 HTTP 请求,跟你用 FastAPI 写的接口、前端用 Axios 调接口是一回事,只不过这次是一个服务去调用另一个服务的接口。
常见的两种同步方式:
- REST / HTTP:最常见,就是你熟悉的
GET /users/42,通用、好调试 - gRPC:性能更高、更省流量,服务间内部调用常用,了解即可
特点:能立刻拿到结果,但必须等对方——对方慢,我就跟着慢;对方挂了,我这次调用就失败。
方式二:异步消息 —— 像”发微信”
有些事,我根本不需要等结果。比如”用户下单成功后要发一封通知邮件”——订单服务只要把”下单成功”这件事喊一声,就可以继续干自己的事了,发邮件让别人慢慢去做。
这时候就用消息队列(你已经学过了):
订单服务 消息队列 通知服务
│ 丢一条"订单已创建"消息 │ │
│ ──────────────────────▶ │ (消息在这排队) │
│ 丢完就走,不等 │ ────────────────▶ │ 慢慢取出来
│ │ │ 发邮件/发短信
这就像发微信:消息发出去我就走了,你什么时候看、什么时候回,不耽误我做别的事。
特点:发出去就不管了(解耦),对方挂了消息也不会丢(在队列里等着),高峰期还能削峰。代价是:拿不到即时结果。
这正是你在消息队列入门里学的”异步、解耦、削峰”,在微服务里派上了大用场。
到底该用哪种?一句话决策
我现在马上就要这个结果,
拿不到就没法往下走?
│
┌───┴───┐
是 否
│ │
▼ ▼
同步调用 异步消息
(打电话) (发微信)
例:下单前要先查用户是不是VIP → 必须立刻知道 → 同步
例:下单后要发通知邮件 → 可以慢慢来 → 异步
- 要实时结果、拿不到就走不下去 → 用同步调用
- 通知类、耗时任务、不用等结果 → 用异步消息
真实系统里,两种是混着用的(第 08 章的下单例子你会看到)。
一个补充概念:服务的”接口约定”
服务之间能对话的前提,是事先约定好接口长什么样:用户服务对外提供 GET /users/{id},返回哪些字段——这就是它对外的契约(API)。
调用方(订单服务)只依赖这个约定,不关心用户服务内部是怎么实现的、用什么语言写的。只要约定不变,双方就能各自独立升级——这也是微服务能”独立开发”的基础。
小结
- 服务间通信分两大类:同步调用(打电话,要等) 和 异步消息(发微信,不等)。
- 同步:REST/HTTP、gRPC,能拿即时结果,但会被对方拖累。
- 异步:走消息队列,解耦、削峰、不怕对方挂,但拿不到即时结果。
- 决策:要不要立刻拿到结果——要就同步,不要就异步。真实系统两者混用。
- 服务之间靠**接口约定(API 契约)**协作,这是”独立开发”的基础。
上一章 ← 03 - 微服务解决了什么,又带来了什么 | 下一章 → 05 - 数据怎么办:每个服务一个数据库 | 回到 README 目录