从单体到微服务:一个文科生的实战拆解
大家好,我是一个靠自学成功转码的前历史系学生。当初学编程时,最头疼的就是那些“高大上”的架构术语——什么微服务、分布式、服务发现……听起来像天书。但当我真正动手做项目后才发现,这些概念其实并不神秘,关键是要用对工具、理解业务需求,并且一步步来。
今天这篇文章,就是我想给完全零基础的朋友写的。我会用一个真实的 Python 小项目,带你从“单体应用”出发,亲手把它拆成两个微服务。过程中我们会用到实用的工具,也会聊聊运营和资源的问题——毕竟,代码写完只是开始,怎么跑起来、怎么省资源、怎么维护,才是真正的挑战。
为什么我们要从单体走向微服务?
先说清楚一件事:微服务不是银弹。如果你的项目只有几个页面、几十个用户,那老老实实写个单体应用就行,别折腾。
但当你的业务变复杂了——比如用户量暴增、功能模块越来越多、不同团队要并行开发——这时候单体应用就会“卡脖子”:
- 改一个小功能,得重新部署整个系统
- 一个模块崩了,整个网站挂掉
- 数据库越来越臃肿,查询慢得像蜗牛
而微服务的核心思想很简单:把一个大系统拆成多个小服务,每个服务独立开发、部署、扩展。
打个比方:
单体应用就像一家“全能餐厅”,厨师、服务员、收银员都是同一个人;
微服务则是“美食广场”,每个档口只做一道菜,互不影响,还能单独招人、单独装修。
环境准备:5分钟搭好开发环境
我们用 Python + Flask(轻量级 Web 框架)来演示,因为它简单、易读,非常适合教学。
安装必备工具
Python 3.8+
去 python.org 下载安装,记得勾选 “Add to PATH”。虚拟环境(强烈推荐)
避免包冲突,每个项目用独立环境:python -m venv micro-env # Windows micro-env\Scripts\activate # macOS/Linux source micro-env/bin/activate安装依赖
pip install flask requests
我当初学的时候,经常忘记激活虚拟环境,结果装了一堆包在全局,后来重装系统才清净……血泪教训!
核心概念:微服务到底“微”在哪?
别被术语吓住,记住这三个关键词就够了:
| 概念 | 通俗解释 | 关键点 |
|---|---|---|
| 服务拆分 | 把大功能切成小模块 | 按业务边界拆,比如“用户管理”和“订单处理” |
| 独立部署 | Each service 自己跑 | 修改用户服务,不用动订单服务 |
| 远程调用 | 服务之间通过 HTTP/API 通信 | 用 requests 或 gRPC 调对方接口 |
另外,微服务离不开几个配套工具:
- API 网关:所有请求的“门卫”,统一入口(比如 Nginx)
- 服务注册与发现:自动知道“谁在哪”(比如 Consul、Eureka)
- 配置中心:集中管理配置,不用改代码
- 监控告警:知道哪个服务挂了(比如 Prometheus + Grafana)
但我们今天先不搞这么复杂!先用最原始的方式——两个 Flask 服务互相调用,感受核心逻辑。
实战项目:把一个单体商城拆成两个微服务
第一步:先写个单体版(baseline)
假设我们有个超简单的电商系统,包含两个功能:
- 查看商品列表
- 创建订单(需要验证用户是否存在)
单体代码(monolith.py):
from flask import Flask, jsonify, request
app = Flask(__name__)
# 模拟数据库
users = {1: "Alice", 2: "Bob"}
products = {101: "笔记本", 102: "手机"}
orders = []
@app.route('/products', methods=['GET'])
def get_products():
return jsonify(products)
@app.route('/order', methods=['POST'])
def create_order():
user_id = request.json.get('user_id')
product_id = request.json.get('product_id')
if user_id not in users:
return jsonify({"error": "用户不存在"}), 404
if product_id not in products:
return jsonify({"error": "商品不存在"}), 404
order = {"user": users[user_id], "product": products[product_id]}
orders.append(order)
return jsonify({"msg": "下单成功", "order": order})
if __name__ == '__main__':
app.run(port=5000)
运行它:
python monolith.py
测试:
# 获取商品
curl http://localhost:5000/products
# 下单(成功)
curl -X POST http://localhost:5000/order -H "Content-Type: application/json" -d '{"user_id":1,"product_id":101}'
# 下单(用户不存在)
curl -X POST http://localhost:5000/order -d '{"user_id":999,"product_id":101}'
这个单体应用虽然能跑,但问题很明显:用户逻辑和订单逻辑耦合在一起。如果以后要加“积分系统”或“物流跟踪”,代码会越来越乱。
第二步:拆!拆成两个微服务
我们按业务边界拆成:
- 用户服务(User Service):管用户信息
- 订单服务(Order Service):管下单,但需要调用户服务验证身份
1. 用户服务(user_service.py)
from flask import Flask, jsonify
app = Flask(__name__)
users = {1: "Alice", 2: "Bob"}
@app.route('/users/<int:user_id>', methods=['GET'])
def get_user(user_id):
name = users.get(user_id)
if name:
return jsonify({"id": user_id, "name": name})
else:
return jsonify({"error": "用户不存在"}), 404
if __name__ == '__main__':
app.run(port=5001) # 注意端口不同!
2. 订单服务(order_service.py)
from flask import Flask, jsonify, request
import requests
app = Flask(__name__)
products = {101: "笔记本", 102: "手机"}
orders = []
@app.route('/order', methods=['POST'])
def create_order():
user_id = request.json.get('user_id')
product_id = request.json.get('product_id')
# 调用用户服务验证用户
try:
user_resp = requests.get(f"http://localhost:5001/users/{user_id}")
if user_resp.status_code != 200:
return jsonify({"error": "用户不存在"}), 404
user_data = user_resp.json()
except requests.exceptions.ConnectionError:
return jsonify({"error": "用户服务暂时不可用"}), 503
if product_id not in products:
return jsonify({"error": "商品不存在"}), 404
order = {
"user": user_data["name"],
"product": products[product_id]
}
orders.append(order)
return jsonify({"msg": "下单成功", "order": order})
if __name__ == '__main__':
app.run(port=5002)
3. 启动两个服务
开两个终端窗口:
# 终端1
python user_service.py
# 终端2
python order_service.py
4. 测试微服务版
# 下单(成功)
curl -X POST http://localhost:5002/order -H "Content-Type: application/json" -d '{"user_id":1,"product_id":101}'
# 模拟用户服务宕机 → 订单服务会返回“服务不可用”
# (先 Ctrl+C 停掉 user_service,再试一次下单)
恭喜!你已经完成了第一次微服务拆分!
运营与资源:别只顾写代码
很多新手(包括我当初)以为“能跑就行”,但上线后才发现:运维成本才是大头。
微服务带来的新问题
| 问题 | 单体应用 | 微服务 |
|---|---|---|
| 部署复杂度 | 1次部署 | N次部署(每个服务都要部署) |
| 资源占用 | 1个进程 | N个进程(内存/CPU翻倍) |
| 日志追踪 | 一个文件搞定 | 多个服务日志分散 |
| 网络延迟 | 无(函数调用) | 有(HTTP 请求) |
如何优化资源使用?
容器化(Docker)
把每个服务打包成镜像,统一管理资源。比如限制每个服务最多用 200MB 内存。服务合并策略
不是越“微”越好!初期可以把关联强的模块放一起。比如“购物车”和“订单”可以先不拆。用轻量级协议
Flask 默认用 JSON over HTTP,但如果性能要求高,可考虑 gRPC(二进制协议,更快更省带宽)。缓存常用数据
比如用户信息很少变,订单服务可以本地缓存 1 分钟,减少调用户服务的次数。
我见过一个团队,把一个本来 100 行代码的功能拆成 5 个微服务,结果服务器费用涨了 10 倍……老板差点哭出来。
新手常见问题解答
Q1:两个服务怎么知道对方的地址?写死 localhost:5001 不好吗?
不好! 生产环境 IP 和端口会变。正确做法是:
- 开发阶段:用
.env文件配置 - 生产阶段:用 服务发现工具(如 Consul)或 Kubernetes Service
Q2:如果用户服务响应慢,订单服务会不会卡住?
会!这就是“雪崩效应”。解决方案:
- 加 超时设置:
requests.get(url, timeout=2) - 用 熔断器模式(比如
pybreaker库):失败太多次就直接拒绝,不再尝试
Q3:数据库要不要也拆?
要! 每个微服务应该有自己的数据库(或至少自己的 schema)。避免跨服务直接查表——那等于没拆!
Q4:微服务一定要用 Docker/K8s 吗?
不一定。小项目可以用 systemd 或 supervisor 管理进程。但超过 3 个服务后,容器化几乎是必选项。
学习建议:下一步该学什么?
你现在已经理解了微服务的核心思想。接下来,按这个路径走:
学 Docker
把你的两个服务容器化,用docker-compose.yml一键启动。引入 API 网关
用 Nginx 做路由:/users/*→ 用户服务,/orders/*→ 订单服务。加配置中心
试试python-decouple或dynaconf,把端口、超时时间抽成配置文件。学监控
用 Prometheus + Grafana 监控每个服务的 CPU、内存、请求延迟。了解领域驱动设计(DDD)
真正高手拆服务,靠的是业务理解,不是技术。DDD 教你怎么找到“天然的边界”。
最后的话
微服务不是目的,而是解决特定问题的手段。作为开发者,我们的目标不是“用了多少新技术”,而是“用最合适的方案,让系统稳定、可维护、省资源”。
我当初从文科转码,最大的优势不是聪明,而是愿意从真实问题出发。每次遇到新概念,我都会问:“这能解决我手头的什么问题?”
希望这篇教程能帮你迈出第一步。代码不难,难的是思考方式。加油!

评论 0