从单体到微服务:一个文科生的实战拆解

GoRoutine散步
2025-12-22 02:30
阅读 481

大家好,我是一个靠自学成功转码的前历史系学生。当初学编程时,最头疼的就是那些“高大上”的架构术语——什么微服务、分布式、服务发现……听起来像天书。但当我真正动手做项目后才发现,这些概念其实并不神秘,关键是要用对工具、理解业务需求,并且一步步来。

今天这篇文章,就是我想给完全零基础的朋友写的。我会用一个真实的 Python 小项目,带你从“单体应用”出发,亲手把它拆成两个微服务。过程中我们会用到实用的工具,也会聊聊运营和资源的问题——毕竟,代码写完只是开始,怎么跑起来、怎么省资源、怎么维护,才是真正的挑战。


为什么我们要从单体走向微服务?

先说清楚一件事:微服务不是银弹。如果你的项目只有几个页面、几十个用户,那老老实实写个单体应用就行,别折腾。

但当你的业务变复杂了——比如用户量暴增、功能模块越来越多、不同团队要并行开发——这时候单体应用就会“卡脖子”:

  • 改一个小功能,得重新部署整个系统
  • 一个模块崩了,整个网站挂掉
  • 数据库越来越臃肿,查询慢得像蜗牛

而微服务的核心思想很简单:把一个大系统拆成多个小服务,每个服务独立开发、部署、扩展

打个比方:
单体应用就像一家“全能餐厅”,厨师、服务员、收银员都是同一个人;
微服务则是“美食广场”,每个档口只做一道菜,互不影响,还能单独招人、单独装修。


环境准备:5分钟搭好开发环境

我们用 Python + Flask(轻量级 Web 框架)来演示,因为它简单、易读,非常适合教学。

安装必备工具

  1. Python 3.8+
    python.org 下载安装,记得勾选 “Add to PATH”。

  2. 虚拟环境(强烈推荐)
    避免包冲突,每个项目用独立环境:

    python -m venv micro-env
    # Windows
    micro-env\Scripts\activate
    # macOS/Linux
    source micro-env/bin/activate
    
  3. 安装依赖

    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 请求)

如何优化资源使用?

  1. 容器化(Docker)
    把每个服务打包成镜像,统一管理资源。比如限制每个服务最多用 200MB 内存。

  2. 服务合并策略
    不是越“微”越好!初期可以把关联强的模块放一起。比如“购物车”和“订单”可以先不拆。

  3. 用轻量级协议
    Flask 默认用 JSON over HTTP,但如果性能要求高,可考虑 gRPC(二进制协议,更快更省带宽)。

  4. 缓存常用数据
    比如用户信息很少变,订单服务可以本地缓存 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 个服务后,容器化几乎是必选项。


学习建议:下一步该学什么?

你现在已经理解了微服务的核心思想。接下来,按这个路径走:

  1. 学 Docker
    把你的两个服务容器化,用 docker-compose.yml 一键启动。

  2. 引入 API 网关
    用 Nginx 做路由:/users/* → 用户服务,/orders/* → 订单服务。

  3. 加配置中心
    试试 python-decoupledynaconf,把端口、超时时间抽成配置文件。

  4. 学监控
    用 Prometheus + Grafana 监控每个服务的 CPU、内存、请求延迟。

  5. 了解领域驱动设计(DDD)
    真正高手拆服务,靠的是业务理解,不是技术。DDD 教你怎么找到“天然的边界”。


最后的话

微服务不是目的,而是解决特定问题的手段。作为开发者,我们的目标不是“用了多少新技术”,而是“用最合适的方案,让系统稳定、可维护、省资源”。

我当初从文科转码,最大的优势不是聪明,而是愿意从真实问题出发。每次遇到新概念,我都会问:“这能解决我手头的什么问题?”

希望这篇教程能帮你迈出第一步。代码不难,难的是思考方式。加油!

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝