长安的Docker教程长安的Docker教程
首页
快速开始
编程指南
首页
快速开始
编程指南
  • 🎯 Docker入门篇

    • 快速开始
    • Docker是什么?
    • 为什么要用Docker?
    • 安装Docker
    • 第一个容器
  • 📦 Docker基础篇

    • Docker镜像详解
    • 容器操作详解
    • 编写Dockerfile
    • 数据卷(Volumes)
    • Docker网络
  • ⚡ Docker进阶篇

    • Docker Compose
    • 多阶段构建
    • Docker性能优化
    • Docker最佳实践
  • 🚀 实战项目

    • 实战:部署Node.js应用
    • 实战:搭建数据库环境
    • 实战:微服务架构
  • 💡 常见问题

    • 常见问题排查
    • 实用技巧

实战:微服务架构

嘿!长安来教你用Docker构建微服务架构!这是真正的企业级应用!🚀

🎯 项目概述

我们要构建一个电商系统的微服务架构,包括:

  • API Gateway:统一入口
  • User Service:用户服务
  • Product Service:商品服务
  • Order Service:订单服务
  • MySQL:关系型数据库
  • MongoDB:文档数据库
  • Redis:缓存
  • RabbitMQ:消息队列

这就是真实的微服务架构! 🎉

📁 项目结构

microservices-ecommerce/
├── services/
│   ├── api-gateway/
│   │   ├── Dockerfile
│   │   ├── package.json
│   │   └── src/
│   ├── user-service/
│   │   ├── Dockerfile
│   │   ├── package.json
│   │   └── src/
│   ├── product-service/
│   │   ├── Dockerfile
│   │   ├── package.json
│   │   └── src/
│   └── order-service/
│       ├── Dockerfile
│       ├── package.json
│       └── src/
├── docker-compose.yml
├── .env.example
└── README.md

🔧 服务实现

1. API Gateway(API网关)

services/api-gateway/Dockerfile:

FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
USER node
EXPOSE 3000
CMD ["node", "src/index.js"]

services/api-gateway/src/index.js:

const express = require('express');
const axios = require('axios');
const app = express();

app.use(express.json());

const PORT = process.env.PORT || 3000;

// 服务地址
const SERVICES = {
  user: process.env.USER_SERVICE_URL || 'http://user-service:3001',
  product: process.env.PRODUCT_SERVICE_URL || 'http://product-service:3002',
  order: process.env.ORDER_SERVICE_URL || 'http://order-service:3003'
};

// 健康检查
app.get('/health', (req, res) => {
  res.json({ status: 'healthy', service: 'api-gateway' });
});

// 路由转发
app.all('/users/*', async (req, res) => {
  try {
    const url = req.url.replace('/users', '');
    const response = await axios({
      method: req.method,
      url: `${SERVICES.user}${url}`,
      data: req.body
    });
    res.json(response.data);
  } catch (error) {
    res.status(error.response?.status || 500).json({
      error: error.message
    });
  }
});

app.all('/products/*', async (req, res) => {
  try {
    const url = req.url.replace('/products', '');
    const response = await axios({
      method: req.method,
      url: `${SERVICES.product}${url}`,
      data: req.body
    });
    res.json(response.data);
  } catch (error) {
    res.status(error.response?.status || 500).json({
      error: error.message
    });
  }
});

app.all('/orders/*', async (req, res) => {
  try {
    const url = req.url.replace('/orders', '');
    const response = await axios({
      method: req.method,
      url: `${SERVICES.order}${url}`,
      data: req.body
    });
    res.json(response.data);
  } catch (error) {
    res.status(error.response?.status || 500).json({
      error: error.message
    });
  }
});

app.listen(PORT, () => {
  console.log(`🚪 API Gateway 运行在 ${PORT}`);
});

2. User Service(用户服务)

services/user-service/src/index.js:

const express = require('express');
const mysql = require('mysql2/promise');
const redis = require('redis');

const app = express();
app.use(express.json());

const PORT = process.env.PORT || 3001;

// MySQL连接池
const pool = mysql.createPool({
  host: process.env.DB_HOST || 'mysql',
  user: process.env.DB_USER || 'user',
  password: process.env.DB_PASSWORD || 'password',
  database: process.env.DB_NAME || 'users_db',
  waitForConnections: true,
  connectionLimit: 10
});

// Redis客户端
const redisClient = redis.createClient({
  url: process.env.REDIS_URL || 'redis://redis:6379'
});
redisClient.connect();

// 健康检查
app.get('/health', (req, res) => {
  res.json({ status: 'healthy', service: 'user-service' });
});

// 获取所有用户
app.get('/', async (req, res) => {
  try {
    // 尝试从缓存获取
    const cached = await redisClient.get('users:all');
    if (cached) {
      return res.json({
        source: 'cache',
        users: JSON.parse(cached)
      });
    }

    // 从数据库查询
    const [rows] = await pool.query('SELECT * FROM users');
    
    // 存入缓存
    await redisClient.setEx('users:all', 60, JSON.stringify(rows));
    
    res.json({ source: 'database', users: rows });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// 创建用户
app.post('/', async (req, res) => {
  try {
    const { name, email } = req.body;
    const [result] = await pool.query(
      'INSERT INTO users (name, email) VALUES (?, ?)',
      [name, email]
    );
    
    // 清除缓存
    await redisClient.del('users:all');
    
    res.status(201).json({
      id: result.insertId,
      name,
      email
    });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.listen(PORT, () => {
  console.log(`👥 User Service 运行在 ${PORT}`);
});

3. Product Service(商品服务)

services/product-service/src/index.js:

const express = require('express');
const mongoose = require('mongoose');

const app = express();
app.use(express.json());

const PORT = process.env.PORT || 3002;

// MongoDB连接
mongoose.connect(process.env.MONGO_URI || 'mongodb://mongo:27017/products_db');

// 商品模型
const Product = mongoose.model('Product', {
  name: String,
  description: String,
  price: Number,
  stock: Number,
  createdAt: { type: Date, default: Date.now }
});

// 健康检查
app.get('/health', (req, res) => {
  res.json({ status: 'healthy', service: 'product-service' });
});

// 获取所有商品
app.get('/', async (req, res) => {
  try {
    const products = await Product.find();
    res.json({ products });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// 创建商品
app.post('/', async (req, res) => {
  try {
    const product = new Product(req.body);
    await product.save();
    res.status(201).json({ product });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// 获取单个商品
app.get('/:id', async (req, res) => {
  try {
    const product = await Product.findById(req.params.id);
    if (!product) {
      return res.status(404).json({ error: 'Product not found' });
    }
    res.json({ product });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.listen(PORT, () => {
  console.log(`📦 Product Service 运行在 ${PORT}`);
});

4. Order Service(订单服务)

services/order-service/src/index.js:

const express = require('express');
const mongoose = require('mongoose');
const amqp = require('amqplib');

const app = express();
app.use(express.json());

const PORT = process.env.PORT || 3003;

// MongoDB连接
mongoose.connect(process.env.MONGO_URI || 'mongodb://mongo:27017/orders_db');

// 订单模型
const Order = mongoose.model('Order', {
  userId: Number,
  productId: String,
  quantity: Number,
  totalPrice: Number,
  status: { type: String, default: 'pending' },
  createdAt: { type: Date, default: Date.now }
});

// RabbitMQ连接
let channel;
async function connectRabbitMQ() {
  try {
    const connection = await amqp.connect(
      process.env.RABBITMQ_URL || 'amqp://rabbitmq:5672'
    );
    channel = await connection.createChannel();
    await channel.assertQueue('orders');
    console.log('✅ RabbitMQ连接成功');
  } catch (error) {
    console.error('❌ RabbitMQ连接失败:', error);
    setTimeout(connectRabbitMQ, 5000);
  }
}
connectRabbitMQ();

// 健康检查
app.get('/health', (req, res) => {
  res.json({ status: 'healthy', service: 'order-service' });
});

// 创建订单
app.post('/', async (req, res) => {
  try {
    const order = new Order(req.body);
    await order.save();
    
    // 发送消息到队列
    if (channel) {
      channel.sendToQueue('orders', Buffer.from(JSON.stringify(order)));
    }
    
    res.status(201).json({ order });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// 获取所有订单
app.get('/', async (req, res) => {
  try {
    const orders = await Order.find();
    res.json({ orders });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

app.listen(PORT, () => {
  console.log(`🛒 Order Service 运行在 ${PORT}`);
});

🎨 Docker Compose配置

docker-compose.yml:

version: '3.8'

services:
  # API Gateway
  api-gateway:
    build: ./services/api-gateway
    container_name: ecommerce-gateway
    ports:
      - "3000:3000"
    environment:
      USER_SERVICE_URL: http://user-service:3001
      PRODUCT_SERVICE_URL: http://product-service:3002
      ORDER_SERVICE_URL: http://order-service:3003
    depends_on:
      - user-service
      - product-service
      - order-service
    networks:
      - frontend
    restart: unless-stopped

  # User Service
  user-service:
    build: ./services/user-service
    container_name: ecommerce-user
    environment:
      DB_HOST: mysql
      DB_USER: user
      DB_PASSWORD: password
      DB_NAME: users_db
      REDIS_URL: redis://redis:6379
    depends_on:
      mysql:
        condition: service_healthy
      redis:
        condition: service_started
    networks:
      - frontend
      - backend
    restart: unless-stopped

  # Product Service
  product-service:
    build: ./services/product-service
    container_name: ecommerce-product
    environment:
      MONGO_URI: mongodb://mongo:27017/products_db
    depends_on:
      mongo:
        condition: service_healthy
    networks:
      - frontend
      - backend
    restart: unless-stopped

  # Order Service
  order-service:
    build: ./services/order-service
    container_name: ecommerce-order
    environment:
      MONGO_URI: mongodb://mongo:27017/orders_db
      RABBITMQ_URL: amqp://rabbitmq:5672
    depends_on:
      mongo:
        condition: service_healthy
      rabbitmq:
        condition: service_healthy
    networks:
      - frontend
      - backend
    restart: unless-stopped

  # MySQL(用户服务数据库)
  mysql:
    image: mysql:8.0
    container_name: ecommerce-mysql
    environment:
      MYSQL_ROOT_PASSWORD: root123
      MYSQL_DATABASE: users_db
      MYSQL_USER: user
      MYSQL_PASSWORD: password
    volumes:
      - mysql-data:/var/lib/mysql
      - ./init/mysql:/docker-entrypoint-initdb.d
    networks:
      - backend
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped

  # MongoDB(商品和订单服务)
  mongo:
    image: mongo:6
    container_name: ecommerce-mongo
    volumes:
      - mongo-data:/data/db
    networks:
      - backend
    healthcheck:
      test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped

  # Redis(缓存)
  redis:
    image: redis:7-alpine
    container_name: ecommerce-redis
    volumes:
      - redis-data:/data
    networks:
      - backend
    restart: unless-stopped

  # RabbitMQ(消息队列)
  rabbitmq:
    image: rabbitmq:3-management-alpine
    container_name: ecommerce-rabbitmq
    ports:
      - "15672:15672"  # 管理界面
    volumes:
      - rabbitmq-data:/var/lib/rabbitmq
    networks:
      - backend
    healthcheck:
      test: ["CMD", "rabbitmq-diagnostics", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge

volumes:
  mysql-data:
  mongo-data:
  redis-data:
  rabbitmq-data:

🚀 启动和测试

启动所有服务

# 构建并启动
docker-compose up --build -d

# 查看状态
docker-compose ps

# 查看日志
docker-compose logs -f

测试API

# 1. 健康检查
curl http://localhost:3000/health

# 2. 创建用户
curl -X POST http://localhost:3000/users \
  -H "Content-Type: application/json" \
  -d '{
    "name": "长安",
    "email": "changan@example.com"
  }'

# 3. 获取用户列表
curl http://localhost:3000/users

# 4. 创建商品
curl -X POST http://localhost:3000/products \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Docker教程",
    "description": "长安的Docker教程",
    "price": 99,
    "stock": 100
  }'

# 5. 获取商品列表
curl http://localhost:3000/products

# 6. 创建订单
curl -X POST http://localhost:3000/orders \
  -H "Content-Type: application/json" \
  -d '{
    "userId": 1,
    "productId": "商品ID",
    "quantity": 2,
    "totalPrice": 198
  }'

# 7. 获取订单列表
curl http://localhost:3000/orders

访问管理界面

  • RabbitMQ管理界面:http://localhost:15672
    用户名:guest
    密码:guest

📊 监控和调试

查看服务日志

# 所有服务
docker-compose logs -f

# 特定服务
docker-compose logs -f api-gateway
docker-compose logs -f user-service

查看资源使用

docker stats

进入容器调试

# 进入API Gateway
docker exec -it ecommerce-gateway sh

# 进入MySQL
docker exec -it ecommerce-mysql mysql -uuser -ppassword

# 进入MongoDB
docker exec -it ecommerce-mongo mongosh

# 进入Redis
docker exec -it ecommerce-redis redis-cli

💡 微服务架构优势

1. 独立部署

# 只更新用户服务
docker-compose up -d --no-deps --build user-service

2. 技术栈自由

  • User Service:MySQL + Redis
  • Product Service:MongoDB
  • Order Service:MongoDB + RabbitMQ

3. 横向扩展

# 扩容商品服务到3个实例
docker-compose up -d --scale product-service=3

4. 故障隔离

一个服务挂了,不影响其他服务。

🎯 生产环境优化

1. 添加负载均衡(Nginx)

services:
  nginx:
    image: nginx:alpine
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
    ports:
      - "80:80"
    depends_on:
      - api-gateway

2. 添加服务发现(Consul)

services:
  consul:
    image: consul:latest
    ports:
      - "8500:8500"

3. 添加监控(Prometheus + Grafana)

services:
  prometheus:
    image: prom/prometheus
    ports:
      - "9090:9090"
      
  grafana:
    image: grafana/grafana
    ports:
      - "3001:3000"

💡 小结

今天长安带你构建了一个完整的微服务架构:

完成的功能

✅ API Gateway(统一入口)
✅ 3个微服务(用户、商品、订单)
✅ 多数据库(MySQL、MongoDB、Redis)
✅ 消息队列(RabbitMQ)
✅ 服务隔离和通信

学到的技能

  1. 微服务架构设计
  2. 服务间通信
  3. 数据库选型
  4. 消息队列使用
  5. Docker多容器编排

🚀 下一步

实战项目都完成了!遇到问题怎么办?看 常见问题!


💬 长安的微服务心得:

微服务不是银弹,别为了微服务而微服务!

什么时候用微服务?

  • 团队大(10人+)
  • 项目复杂
  • 需要独立部署
  • 需要技术栈多样性

小项目别用微服务!

  • 团队小(5人以下)
  • 项目简单
  • 单体架构就够了

记住:简单是美德,复杂是负担!

但学习微服务还是很有必要的,这是未来趋势!💪

在 GitHub 上编辑此页
Prev
实战:搭建数据库环境