微服务架构入门与实践指南

微服务核心概念

微服务 vs 单体架构

// 单体架构示例
// 一个大型应用包含所有功能模块
app/
├── controllers/
├── models/
├── views/
├── services/
└── utils/

// 微服务架构示例
// 多个独立的小服务
services/
├── user-service/        # 用户服务
├── product-service/     # 产品服务
├── order-service/       # 订单服务
├── payment-service/     # 支付服务
└── notification-service/# 通知服务

服务拆分原则

  • 单一职责:每个服务只负责一个业务领域
  • 独立部署:服务可以独立部署和扩展
  • 技术异构:不同服务可以使用不同技术栈
  • 数据自治:每个服务有自己的数据库
  • 轻量级通信:通过API进行通信

微服务通信模式

同步通信(REST/gRPC)

// REST API调用
const axios = require('axios');

class OrderService {
    async createOrder(userId, products) {
        // 调用用户服务
        const user = await axios.get(`http://user-service/users/${userId}`);
        
        // 调用产品服务验证库存
        const inventoryCheck = await axios.post(
            'http://product-service/check-inventory',
            { products }
        );
        
        // 创建订单
        const order = await axios.post('http://order-service/orders', {
            userId,
            products,
            total: inventoryCheck.data.total
        });
        
        return order.data;
    }
}

// gRPC示例
// 定义proto文件
syntax = "proto3";
service UserService {
    rpc GetUser (UserRequest) returns (UserResponse);
}

message UserRequest {
    string id = 1;
}

message UserResponse {
    string id = 1;
    string name = 2;
    string email = 3;
}

异步通信(消息队列)

// 使用RabbitMQ进行异步通信
const amqp = require('amqplib');

class NotificationService {
    async start() {
        // 连接消息队列
        const connection = await amqp.connect('amqp://localhost');
        const channel = await connection.createChannel();
        
        // 声明队列
        await channel.assertQueue('order.created', { durable: true });
        await channel.assertQueue('payment.processed', { durable: true });
        
        // 消费消息
        channel.consume('order.created', async (msg) => {
            const order = JSON.parse(msg.content.toString());
            await this.sendOrderConfirmation(order);
            channel.ack(msg);
        });
        
        channel.consume('payment.processed', async (msg) => {
            const payment = JSON.parse(msg.content.toString());
            await this.sendPaymentReceipt(payment);
            channel.ack(msg);
        });
    }
    
    async sendOrderConfirmation(order) {
        // 发送订单确认邮件
        console.log(`发送订单确认邮件给用户 ${order.userId}`);
    }
    
    async sendPaymentReceipt(payment) {
        // 发送支付收据
        console.log(`发送支付收据给用户 ${payment.userId}`);
    }
}

// 生产者发送消息
class OrderService {
    async createOrder(orderData) {
        // 创建订单逻辑...
        const order = await this.saveOrder(orderData);
        
        // 发布订单创建事件
        const channel = await this.getChannel();
        channel.sendToQueue('order.created', 
            Buffer.from(JSON.stringify(order)),
            { persistent: true }
        );
        
        return order;
    }
}

服务发现与配置

使用Consul进行服务发现

// 服务注册
const consul = require('consul')();

class UserService {
    constructor() {
        this.serviceId = `user-service-${process.pid}`;
    }
    
    async registerService() {
        await consul.agent.service.register({
            id: this.serviceId,
            name: 'user-service',
            address: 'localhost',
            port: 3001,
            check: {
                http: 'http://localhost:3001/health',
                interval: '10s',
                timeout: '5s'
            }
        });
    }
    
    async deregisterService() {
        await consul.agent.service.deregister(this.serviceId);
    }
}

// 服务发现
class ServiceDiscovery {
    async discoverService(serviceName) {
        const services = await consul.agent.service.list();
        const service = Object.values(services)
            .find(s => s.Service === serviceName);
        
        if (!service) {
            throw new Error(`服务 ${serviceName} 未找到`);
        }
        
        return `http://${service.Address}:${service.Port}`;
    }
}

配置管理

// 使用环境变量和配置中心
// .env文件
DATABASE_URL=postgresql://user:pass@localhost:5432/db
REDIS_URL=redis://localhost:6379
JWT_SECRET=your-secret-key

// 配置类
class Config {
    constructor() {
        this.databaseUrl = process.env.DATABASE_URL;
        this.redisUrl = process.env.REDIS_URL;
        this.jwtSecret = process.env.JWT_SECRET;
        this.servicePort = parseInt(process.env.PORT) || 3000;
    }
    
    // 从配置中心获取配置
    async loadFromConfigCenter() {
        const response = await fetch('http://config-center/config/user-service');
        const config = await response.json();
        Object.assign(this, config);
    }
}

// Docker Compose配置
version: '3.8'
services:
  config-center:
    image: hashicorp/consul:latest
    ports:
      - "8500:8500"
    
  user-service:
    build: ./user-service
    environment:
      - CONSUL_HTTP_ADDR=config-center:8500
    depends_on:
      - config-center

部署和监控

Docker化部署

# Dockerfile
FROM node:18-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

COPY . .

EXPOSE 3000

CMD ["node", "server.js"]

# docker-compose.yml
version: '3.8'
services:
  user-service:
    build: ./user-service
    ports:
      - "3001:3000"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgres://user:pass@db:5432/users
    depends_on:
      - db
      - redis
  
  product-service:
    build: ./product-service
    ports:
      - "3002:3000"
    
  api-gateway:
    build: ./api-gateway
    ports:
      - "80:3000"
    depends_on:
      - user-service
      - product-service
  
  db:
    image: postgres:15
    environment:
      POSTGRES_PASSWORD: pass
    
  redis:
    image: redis:alpine

监控和日志

// 集成Prometheus监控
const prometheus = require('prom-client');

// 创建指标
const httpRequestDurationMicroseconds = new prometheus.Histogram({
    name: 'http_request_duration_ms',
    help: 'HTTP请求持续时间(毫秒)',
    labelNames: ['method', 'route', 'code'],
    buckets: [50, 100, 200, 300, 400, 500, 1000]
});

// 中间件记录指标
app.use((req, res, next) => {
    const end = httpRequestDurationMicroseconds.startTimer();
    res.on('finish', () => {
        end({ 
            method: req.method, 
            route: req.route?.path || req.path,
            code: res.statusCode 
        });
    });
    next();
});

// 暴露metrics端点
app.get('/metrics', async (req, res) => {
    res.set('Content-Type', prometheus.register.contentType);
    res.end(await prometheus.register.metrics());
});

// 集中式日志
const winston = require('winston');
const { ElasticsearchTransport } = require('winston-elasticsearch');

const logger = winston.createLogger({
    level: 'info',
    format: winston.format.json(),
    transports: [
        new winston.transports.File({ filename: 'error.log', level: 'error' }),
        new winston.transports.File({ filename: 'combined.log' }),
        new ElasticsearchTransport({
            level: 'info',
            clientOpts: { node: 'http://elasticsearch:9200' }
        })
    ]
});

// 使用日志
logger.info('用户服务启动', { port: 3001 });
logger.error('数据库连接失败', { error: err.message });