高级5-6 小时·全栈部署

微服务架构:3 个服务 + API 网关

使用 Python 构建 3 个微服务,Docker Compose 编排,Nginx 作为 API 网关

PythonDockermicroservicesnginxapi-gatewaycompose

微服务架构:3 个服务 + API 网关

使用 Python 构建 3 个微服务,Docker Compose 编排,Nginx 作为 API 网关

你将学到

  • 设计微服务架构
  • 实现服务间通信
  • 使用 Nginx 作为 API 网关
  • 使用 Docker Compose 编排多服务

前置知识

架构设计

                    ┌─────────┐
                    │  Nginx  │
                    │ (网关)  │
                    └────┬────┘
                         │
        ┌────────────────┼────────────────┐
        │                │                │
        ▼                ▼                ▼
   ┌─────────┐     ┌─────────┐     ┌─────────┐
   │  User   │     │  Order  │     │ Product │
   │ Service │     │ Service │     │ Service │
   └────┬────┘     └────┬────┘     └────┬────┘
        │                │                │
        ▼                ▼                ▼
   ┌─────────┐     ┌─────────┐     ┌─────────┐
   │User DB  │     │Order DB │     │ProductDB│
   └─────────┘     └─────────┘     └─────────┘

实现步骤

第一步:服务结构

microservices/
├── services/
│   ├── user/
│   │   ├── Dockerfile
│   │   ├── requirements.txt
│   │   └── app.py
│   ├── order/
│   │   ├── Dockerfile
│   │   ├── requirements.txt
│   │   └── app.py
│   └── product/
│       ├── Dockerfile
│       ├── requirements.txt
│       └── app.py
├── nginx/
│   └── nginx.conf
└── docker-compose.yml

第二步:User 服务

# services/user/app.py
from flask import Flask, jsonify, request
import psycopg2
import os

app = Flask(__name__)

def get_db():
    return psycopg2.connect(
        host="user-db",
        database=os.getenv("DB_NAME", "users"),
        user=os.getenv("DB_USER", "user"),
        password=os.getenv("DB_PASSWORD", "password")
    )

@app.route("/users", methods=["GET"])
def list_users():
    conn = get_db()
    cur = conn.cursor()
    cur.execute("SELECT id, name, email FROM users")
    users = [{"id": r[0], "name": r[1], "email": r[2]} for r in cur.fetchall()]
    cur.close()
    conn.close()
    return jsonify(users)

@app.route("/users", methods=["POST"])
def create_user():
    data = request.json
    conn = get_db()
    cur = conn.cursor()
    cur.execute(
        "INSERT INTO users (name, email) VALUES (%s, %s) RETURNING id",
        (data["name"], data["email"])
    )
    user_id = cur.fetchone()[0]
    conn.commit()
    cur.close()
    conn.close()
    return jsonify({"id": user_id, **data}), 201

@app.route("/health")
def health():
    return jsonify({"status": "healthy"})

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)

第三步:Order 服务

# services/order/app.py
from flask import Flask, jsonify, request
import psycopg2
import requests
import os

app = Flask(__name__)

def get_db():
    return psycopg2.connect(
        host="order-db",
        database=os.getenv("DB_NAME", "orders"),
        user=os.getenv("DB_USER", "user"),
        password=os.getenv("DB_PASSWORD", "password")
    )

@app.route("/orders", methods=["GET"])
def list_orders():
    conn = get_db()
    cur = conn.cursor()
    cur.execute("SELECT id, user_id, product_id, quantity FROM orders")
    orders = [{"id": r[0], "user_id": r[1], "product_id": r[2], "quantity": r[3]} for r in cur.fetchall()]
    cur.close()
    conn.close()
    return jsonify(orders)

@app.route("/orders", methods=["POST"])
def create_order():
    data = request.json

    # 验证用户存在
    user_resp = requests.get(f"http://user-service/users/{data['user_id']}")
    if user_resp.status_code != 200:
        return jsonify({"error": "User not found"}), 400

    # 验证产品存在
    product_resp = requests.get(f"http://product-service/products/{data['product_id']}")
    if product_resp.status_code != 200:
        return jsonify({"error": "Product not found"}), 400

    conn = get_db()
    cur = conn.cursor()
    cur.execute(
        "INSERT INTO orders (user_id, product_id, quantity) VALUES (%s, %s, %s) RETURNING id",
        (data["user_id"], data["product_id"], data["quantity"])
    )
    order_id = cur.fetchone()[0]
    conn.commit()
    cur.close()
    conn.close()
    return jsonify({"id": order_id, **data}), 201

@app.route("/health")
def health():
    return jsonify({"status": "healthy"})

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)

第四步:Product 服务

# services/product/app.py
from flask import Flask, jsonify, request
import psycopg2
import os

app = Flask(__name__)

def get_db():
    return psycopg2.connect(
        host="product-db",
        database=os.getenv("DB_NAME", "products"),
        user=os.getenv("DB_USER", "user"),
        password=os.getenv("DB_PASSWORD", "password")
    )

@app.route("/products", methods=["GET"])
def list_products():
    conn = get_db()
    cur = conn.cursor()
    cur.execute("SELECT id, name, price FROM products")
    products = [{"id": r[0], "name": r[1], "price": float(r[2])} for r in cur.fetchall()]
    cur.close()
    conn.close()
    return jsonify(products)

@app.route("/products/<int:product_id>", methods=["GET"])
def get_product(product_id):
    conn = get_db()
    cur = conn.cursor()
    cur.execute("SELECT id, name, price FROM products WHERE id = %s", (product_id,))
    row = cur.fetchone()
    cur.close()
    conn.close()
    if row:
        return jsonify({"id": row[0], "name": row[1], "price": float(row[2])})
    return jsonify({"error": "Not found"}), 404

@app.route("/health")
def health():
    return jsonify({"status": "healthy"})

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)

第五步:Docker Compose

# docker-compose.yml
version: "3.8"

services:
  # API Gateway
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - user-service
      - order-service
      - product-service

  # User Service
  user-service:
    build: ./services/user
    environment:
      - DB_NAME=users
      - DB_USER=user
      - DB_PASSWORD=password
    depends_on:
      - user-db

  user-db:
    image: postgres:15-alpine
    environment:
      - POSTGRES_DB=users
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=password
    volumes:
      - user-data:/var/lib/postgresql/data

  # Order Service
  order-service:
    build: ./services/order
    environment:
      - DB_NAME=orders
      - DB_USER=user
      - DB_PASSWORD=password
    depends_on:
      - order-db

  order-db:
    image: postgres:15-alpine
    environment:
      - POSTGRES_DB=orders
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=password
    volumes:
      - order-data:/var/lib/postgresql/data

  # Product Service
  product-service:
    build: ./services/product
    environment:
      - DB_NAME=products
      - DB_USER=user
      - DB_PASSWORD=password
    depends_on:
      - product-db

  product-db:
    image: postgres:15-alpine
    environment:
      - POSTGRES_DB=products
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=password
    volumes:
      - product-data:/var/lib/postgresql/data

volumes:
  user-data:
  order-data:
  product-data:

第六步:Nginx 配置

# nginx/nginx.conf
server {
    listen 80;

    location /users/ {
        proxy_pass http://user-service:5000/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    location /orders/ {
        proxy_pass http://order-service:5000/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    location /products/ {
        proxy_pass http://product-service:5000/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

完整项目结构

microservices/
├── services/
│   ├── user/
│   │   ├── Dockerfile
│   │   ├── requirements.txt
│   │   └── app.py
│   ├── order/
│   │   ├── Dockerfile
│   │   ├── requirements.txt
│   │   └── app.py
│   └── product/
│       ├── Dockerfile
│       ├── requirements.txt
│       └── app.py
├── nginx/
│   └── nginx.conf
└── docker-compose.yml

最佳实践

  1. 服务独立数据库:每个服务有自己的数据库
  2. API 网关:统一入口,路由分发
  3. 服务发现:使用 Docker 网络的服务名
  4. 健康检查:每个服务提供健康检查端点
  5. 错误处理:服务间调用要有错误处理

常见问题

Q: 如何处理服务间通信失败? A: 使用重试机制和断路器模式。

Q: 如何查看服务日志? A: docker-compose logs -f [service-name]

Q: 如何扩展单个服务? A: docker-compose up -d --scale user-service=3

扩展挑战

  1. 添加服务注册与发现
  2. 实现分布式追踪
  3. 添加消息队列

相关课程

课程相关章节
Python使用 Flask 构建 REST API
DockerDocker Compose 基础
DockerBridge 网络
DockerDocker Compose