KOA技术分享

专注 Koa.js 框架的编程知识分享

Koa.js微服务架构设计与服务间通信

微服务架构概述

随着业务复杂度增加,单体应用难以满足快速迭代和弹性扩展的需求。微服务架构将应用拆分为多个独立部署的服务,每个服务负责特定的业务功能,通过服务间通信实现整体业务逻辑。

服务拆分策略

微服务拆分的原则和方法:

拆分维度 说明 示例
业务能力 按业务功能拆分 用户服务、订单服务、支付服务
数据表 按数据库拆分 用户库、订单库、库存库
部署频率 按发布频率拆分 核心服务、非核心服务
团队分工 按开发团队拆分 前端服务、后端服务

Koa.js微服务模板

基于Koa的微服务基础框架:

// 微服务基础架构
const Koa = require('koa');
const Router = require('koa-router');

class Microservice {
  constructor(config) {
    this.app = new Koa();
    this.router = new Router();
    this.config = config;
    this.name = config.serviceName;
    this.port = config.port;
    this.services = config.services || {};
  }

  // 中间件配置
  setupMiddleware() {
    // 错误处理
    this.app.use(async (ctx, next) => {
      try {
        await next();
      } catch (err) {
        ctx.status = err.status || 500;
        ctx.body = {
          error: err.message,
          service: this.name,
          timestamp: new Date().toISOString()
        };
      }
    });

    // 请求日志
    this.app.use(async (ctx, next) => {
      const start = Date.now();
      await next();
      const ms = Date.now() - start;
      console.log(`${ctx.method} ${ctx.url} - ${ms}ms`);
    });

    // 健康检查
    this.router.get('/health', (ctx) => {
      ctx.body = {
        status: 'ok',
        service: this.name,
        uptime: process.uptime()
      };
    });
  }

  // 服务注册
  async registerService() {
    if (this.config.serviceRegistry) {
      await this.config.serviceRegistry.register({
        name: this.name,
        url: `http://localhost:${this.port}`,
        metadata: { version: '1.0.0' }
      });
    }
  }

  // 启动服务
  async start() {
    this.setupMiddleware();
    this.app.use(this.router.routes());
    this.app.use(this.router.allowedMethods());

    await this.registerService();

    this.app.listen(this.port, () => {
      console.log(`${this.name} 启动在端口 ${this.port}`);
    });
  }
}

// 用户服务
const userService = new Microservice({
  serviceName: 'user-service',
  port: 3001,
  serviceRegistry: null
});

userService.router.get('/api/users', async (ctx) => {
  ctx.body = { users: [] };
});

userService.start();

服务间通信

微服务之间可以通过HTTP或消息队列进行通信:

// 服务间HTTP通信
const axios = require('axios');

class ServiceClient {
  constructor(serviceName, baseURL) {
    this.serviceName = serviceName;
    this.client = axios.create({
      baseURL,
      timeout: 5000
    });
  }

  async call(method, path, data) {
    try {
      const response = await this.client.request({
        method,
        url: path,
        data
      });
      return response.data;
    } catch (error) {
      console.error(`调用 ${this.serviceName} 失败:`, error.message);
      throw error;
    }
  }

  get(path) { return this.call('GET', path); }
  post(path, data) { return this.call('POST', path, data); }
  put(path, data) { return this.call('PUT', path, data); }
  delete(path) { return this.call('DELETE', path); }
}

// 服务健康检查
class ServiceDiscovery {
  constructor() {
    this.services = new Map();
  }

  register(serviceName, url) {
    this.services.set(serviceName, { url, timestamp: Date.now() });
  }

  getService(serviceName) {
    const service = this.services.get(serviceName);
    if (!service) {
      throw new Error(`服务 ${serviceName} 未注册`);
    }
    return service.url;
  }

  async healthCheck(serviceName) {
    const url = this.getService(serviceName);
    try {
      const response = await axios.get(`${url}/health`, { timeout: 3000 });
      return response.data.status === 'ok';
    } catch {
      return false;
    }
  }
}

负载均衡实现

在API网关层实现负载均衡:

// 负载均衡策略
class LoadBalancer {
  constructor(strategy = 'round-robin') {
    this.strategy = strategy;
    this.instances = new Map(); // serviceName -> [instances]
    this.currentIndex = new Map(); // serviceName -> index
  }

  // 添加服务实例
  addInstance(serviceName, instance) {
    if (!this.instances.has(serviceName)) {
      this.instances.set(serviceName, []);
      this.currentIndex.set(serviceName, 0);
    }
    this.instances.get(serviceName).push(instance);
  }

  // 获取实例(轮询策略)
  getInstance(serviceName) {
    const instances = this.instances.get(serviceName);
    if (!instances || instances.length === 0) {
      throw new Error(`无可用的 ${serviceName} 实例`);
    }

    let index;
    switch (this.strategy) {
      case 'round-robin':
        index = this.currentIndex.get(serviceName) % instances.length;
        this.currentIndex.set(serviceName, index + 1);
        break;
      case 'random':
        index = Math.floor(Math.random() * instances.length);
        break;
      case 'least-connections':
        index = this.getLeastConnectionsInstance(instances);
        break;
      default:
        index = 0;
    }

    return instances[index];
  }

  // 最少连接数策略
  getLeastConnectionsInstance(instances) {
    return instances.reduce((minIdx, instance, idx, arr) =>
      instance.connections < arr[minIdx].connections ? idx : minIdx, 0);
  }
}

// API网关
const apiGateway = new Koa();
const loadBalancer = new LoadBalancer('round-robin');

// 添加服务实例
loadBalancer.addInstance('user-service', { url: 'http://localhost:3001', connections: 0 });
loadBalancer.addInstance('user-service', { url: 'http://localhost:3002', connections: 0 });
loadBalancer.addInstance('order-service', { url: 'http://localhost:3011', connections: 0 });

apiGateway.use(async (ctx) => {
  const path = ctx.path;
  let targetService;

  if (path.startsWith('/api/users')) {
    targetService = 'user-service';
  } else if (path.startsWith('/api/orders')) {
    targetService = 'order-service';
  }

  if (targetService) {
    const instance = loadBalancer.getInstance(targetService);
    instance.connections++;

    // 代理请求
    const response = await axios({
      method: ctx.method,
      url: `${instance.url}${path}`,
      data: ctx.request.body,
      headers: ctx.headers
    });

    instance.connections--;
    ctx.body = response.data;
  } else {
    ctx.status = 404;
  }
});

服务注册与发现

使用Consul实现服务注册与发现:

// Consul服务注册
const consul = require('consul')({ host: 'localhost', port: 8500 });

class ConsulServiceRegistry {
  constructor(serviceName) {
    this.serviceName = serviceName;
    this.serviceId = `${serviceName}-${process.env.HOSTNAME || 'local'}`;
  }

  async register(port, metadata = {}) {
    await consul.agent.service.register({
      name: this.serviceName,
      id: this.serviceId,
      address: process.env.HOST || 'localhost',
      port: port,
      check: {
        http: `http://localhost:${port}/health`,
        interval: '10s',
        timeout: '5s'
      },
      meta: metadata
    });
    console.log(`服务 ${this.serviceName} 注册成功`);
  }

  async deregister() {
    await consul.agent.service.deregister(this.serviceId);
    console.log(`服务 ${this.serviceName} 注销成功`);
  }

  async discover(serviceName) {
    const services = await consul.catalog.service.nodes(serviceName);
    return services.map(node => ({
      address: node.ServiceAddress,
      port: node.ServicePort
    }));
  }
}

总结

Koa.js微服务架构设计需要考虑服务拆分、通信机制、负载均衡和服务注册等多个方面。通过合理的架构设计,可以实现系统的可扩展性和高可用性。在实际应用中,需要根据业务需求选择合适的技术方案。

← 下一篇:Koa.js微服务架构设计与服务通信 上篇:Koa.js应用容器化与Docker部署 →