个人编制软件展示

PSI - Purchase Sale Inventory 进销存软件

PSI进销存系统跨平台架构与性能优化

引言

PSI进销存系统基于 Node.js 构建天然具备跨平台能力,但要在不同平台上实现最佳性能和用户体验,需要进行针对性的架构设计和性能优化。本文介绍 PSI 的跨平台架构设计和性能优化实践。

跨平台架构设计

PSI 跨平台架构的核心组件:

组件 功能 跨平台策略
核心框架 业务逻辑处理 Node.js原生兼容
数据库层 数据持久化 抽象适配层
缓存层 热点数据缓存 统一Redis接口
前端UI 用户界面 响应式设计

跨平台兼容层实现

实现统一的跨平台兼容接口:

// 跨平台兼容层
class CrossPlatformAdapter {
  constructor() {
    this.platform = this.detectPlatform();
    this.setPlatformSpecifics();
  }

  // 平台检测
  detectPlatform() {
    const platform = process.platform;
    const arch = process.arch;
    const osrelease = require('os').release();

    // Windows 检测
    if (platform === 'win32') {
      return {
        type: 'windows',
        edition: this.detectWindowsEdition(),
        arch: arch
      };
    }

    // Linux 检测
    if (platform === 'linux') {
      const distro = this.detectLinuxDistro();
      return {
        type: 'linux',
        distro: distro.name,
        version: distro.version,
        arch: arch
      };
    }

    // macOS 检测
    if (platform === 'darwin') {
      return {
        type: 'macos',
        version: osrelease,
        arch: arch
      };
    }

    return { type: 'unknown' };
  }

  // 检测Windows版本
  detectWindowsEdition() {
    if (process.platform !== 'win32') return 'unknown';

    try {
      const { execSync } = require('child_process');
      const result = execSync('systeminfo', { encoding: 'utf8' });
      if (result.includes('Windows 11')) return 'win11';
      if (result.includes('Windows 10')) return 'win10';
      if (result.includes('Server')) return 'server';
    } catch {}

    return 'windows';
  }

  // 检测Linux发行版
  detectLinuxDistro() {
    const releaseFiles = [
      '/etc/os-release',
      '/etc/redhat-release',
      '/etc/SuSE-release',
      '/etc/debian_version'
    ];

    for (const file of releaseFiles) {
      try {
        const content = require('fs').readFileSync(file, 'utf8');

        if (content.includes('Kylin') || content.includes('Neokylin')) {
          return { name: 'kylin', version: this.extractVersion(content) };
        }
        if (content.includes('UOS') || content.includes('UnionTech')) {
          return { name: 'uos', version: this.extractVersion(content) };
        }
        if (content.includes('Ubuntu')) {
          return { name: 'ubuntu', version: this.extractVersion(content) };
        }
        if (content.includes('CentOS')) {
          return { name: 'centos', version: this.extractVersion(content) };
        }
      } catch {}
    }

    return { name: 'linux', version: 'unknown' };
  }

  extractVersion(content) {
    const match = content.match(/(\d+\.\d+(\.\d+)?)/);
    return match ? match[1] : 'unknown';
  }

  // 根据平台设置特定配置
  setPlatformSpecifics() {
    switch (this.platform.type) {
      case 'windows':
        this.pathSeparator = '\\';
        this.lineEnding = '\r\n';
        this.maxPathLength = 260;
        break;
      case 'linux':
      case 'macos':
        this.pathSeparator = '/';
        this.lineEnding = '\n';
        this.maxPathLength = 4096;
        break;
    }
  }

  // 路径处理
  pathJoin(...segments) {
    return segments.join(this.pathSeparator);
  }

  // 临时目录
  getTempDir() {
    if (this.platform.type === 'windows') {
      return process.env.TEMP || process.env.TMP || 'C:\\Temp';
    }
    return process.env.TMPDIR || '/tmp';
  }
}

// 文件操作跨平台适配
class FileAdapter {
  constructor(platformAdapter) {
    this.platform = platformAdapter;
  }

  // 安全的路径处理
  safePath(filePath) {
    // 规范化路径
    let normalized = filePath.replace(/\\/g, '/');

    // 防止路径遍历攻击
    if (normalized.includes('..')) {
      throw new Error('Invalid path: path traversal detected');
    }

    // Windows 保留字符检查
    if (this.platform.platform.type === 'windows') {
      const invalidChars = /[<>"|?*]/;
      if (invalidChars.test(normalized)) {
        throw new Error('Invalid path: contains invalid characters');
      }
    }

    return normalized;
  }

  // 文件锁实现
  async withLock(filePath, callback) {
    const lockFile = filePath + '.lock';

    // 尝试创建锁文件
    const fs = require('fs').promises;
    const maxRetries = 10;
    let retries = 0;

    while (retries < maxRetries) {
      try {
        await fs.writeFile(lockFile, String(process.pid));
        break;
      } catch (error) {
        if (error.code === 'EEXIST') {
          await new Promise(r => setTimeout(r, 100));
          retries++;
        } else {
          throw error;
        }
      }
    }

    try {
      return await callback();
    } finally {
      // 释放锁
      try {
        await fs.unlink(lockFile);
      } catch {}
    }
  }
}

性能优化策略

全面的性能优化方案:

// 性能优化服务
class PerformanceOptimizer {
  constructor(app) {
    this.app = app;
    this.metrics = {
      requestTime: [],
      memoryUsage: [],
      cpuUsage: []
    };
  }

  // 初始化优化中间件
  initOptimizationMiddleware() {
    const compression = require('compression');
    const helmet = require('helmet');

    return [
      // 响应压缩
      compression({
        filter: (req, res) => {
          if (req.headers['x-no-compression']) return false;
          return compression.filter(req, res);
        },
        level: 6
      }),

      // 安全头
      helmet({
        contentSecurityPolicy: {
          directives: {
            defaultSrc: ["'self'"],
            scriptSrc: ["'self'"],
            styleSrc: ["'self'", "'unsafe-inline'"],
            imgSrc: ["'self'", "data:", "https:"]
          }
        }
      })
    ];
  }

  // 数据库查询优化
  optimizeDatabaseQueries() {
    return async (ctx, next) => {
      const queryStart = Date.now();
      const originalQuery = ctx.db.query;

      // 包装查询方法,添加慢查询日志
      ctx.db.query = async (sql, params) => {
        const start = Date.now();
        const result = await originalQuery.call(ctx.db, sql, params);
        const duration = Date.now() - start;

        // 记录慢查询
        if (duration > 100) {
          console.warn(`Slow query (${duration}ms): ${sql}`);
        }

        return result;
      };

      await next();

      // 记录请求响应时间
      this.metrics.requestTime.push({
        path: ctx.path,
        method: ctx.method,
        duration: Date.now() - queryStart
      });
    };
  }

  // 缓存层实现
  createCacheLayer(cacheConfig) {
    const Redis = require('ioredis');
    const redis = new Redis(cacheConfig);

    const cache = {
      async get(key) {
        const data = await redis.get(key);
        return data ? JSON.parse(data) : null;
      },

      async set(key, value, ttl = 3600) {
        await redis.set(key, JSON.stringify(value), 'EX', ttl);
      },

      async invalidate(pattern) {
        const keys = await redis.keys(pattern);
        if (keys.length > 0) {
          await redis.del(...keys);
        }
      }
    };

    // 缓存中间件
    return async (ctx, next) => {
      const cacheKey = `cache:${ctx.method}:${ctx.path}:${JSON.stringify(ctx.query)}`;

      // GET 请求尝试从缓存读取
      if (ctx.method === 'GET') {
        const cached = await cache.get(cacheKey);
        if (cached) {
          ctx.set('X-Cache', 'HIT');
          ctx.body = cached;
          return;
        }
        ctx.set('X-Cache', 'MISS');
      }

      await next();

      // 缓存响应
      if (ctx.method === 'GET' && ctx.status === 200 && ctx.body) {
        // 排除分页请求的缓存
        if (!ctx.query.page || ctx.query.page === '1') {
          await cache.set(cacheKey, ctx.body, 300); // 缓存5分钟
        }
      }
    };
  }

  // 连接池优化
  createDatabasePoolOptimizer(config) {
    return {
      // MySQL 连接池优化
      mysql: {
        pool: {
          connections: config.maxConnections || 20,
          queueLimit: config.queueLimit || 0,
          waitForConnections: true,
          enableKeepAlive: true,
          keepAliveInitialDelay: 0
        }
      },

      // Redis 连接池优化
      redis: {
        maxRetriesPerRequest: 3,
        enableReadyCheck: true,
        enableOfflineQueue: true,
        family: 4, // IPv4
        keepAlive: 30000
      }
    };
  }

  // 内存和CPU监控
  startMonitoring() {
    setInterval(() => {
      const memUsage = process.memoryUsage();
      const cpuUsage = process.cpuUsage();

      this.metrics.memoryUsage.push({
        rss: memUsage.rss,
        heapUsed: memUsage.heapUsed,
        heapTotal: memUsage.heapTotal,
        timestamp: Date.now()
      });

      // 如果内存使用超过阈值,触发GC建议
      if (memUsage.heapUsed > memUsage.heapTotal * 0.85) {
        console.warn(`Memory usage high: ${(memUsage.heapUsed / memUsage.heapTotal * 100).toFixed(2)}%`);
      }
    }, 10000);
  }
}

负载均衡配置

多实例部署的负载均衡方案:

// 会话保持配置
class SessionAffinity {
  constructor(redisClient) {
    this.redis = redisClient;
  }

  // 生成会话ID
  generateSessionId() {
    return `psi:${Date.now()}:${Math.random().toString(36).substr(2, 16)}`;
  }

  // 会话中间件
  middleware() {
    return async (ctx, next) => {
      let sessionId = ctx.cookies.get('PSI_SESSION');

      if (!sessionId) {
        sessionId = this.generateSessionId();
        ctx.cookies.set('PSI_SESSION', sessionId, {
          httpOnly: true,
          maxAge: 86400000, // 24小时
          sameSite: 'lax'
        });
      }

      // 将会话绑定到特定服务器实例
      const instanceId = process.env.INSTANCE_ID || 'instance-1';
      await this.redis.hset(`session:${sessionId}`, 'instance', instanceId);

      ctx.sessionId = sessionId;
      await next();
    };
  }
}

// 健康检查端点
function createHealthCheck() {
  return async (ctx) => {
    const health = {
      status: 'ok',
      timestamp: new Date().toISOString(),
      uptime: process.uptime(),
      checks: {}
    };

    // 数据库检查
    try {
      await db.query('SELECT 1');
      health.checks.database = 'ok';
    } catch (error) {
      health.checks.database = 'error';
      health.status = 'degraded';
    }

    // Redis检查
    try {
      await redis.ping();
      health.checks.redis = 'ok';
    } catch (error) {
      health.checks.redis = 'error';
      health.status = 'degraded';
    }

    // 内存检查
    const memUsage = process.memoryUsage();
    if (memUsage.heapUsed / memUsage.heapTotal > 0.9) {
      health.checks.memory = 'warning';
      health.status = 'degraded';
    } else {
      health.checks.memory = 'ok';
    }

    ctx.status = health.status === 'ok' ? 200 : 503;
    ctx.body = health;
  };
}

// 优雅关闭
function setupGracefulShutdown(server) {
  const shutdown = async (signal) => {
    console.log(`Received ${signal}, starting graceful shutdown...`);

    // 停止接受新连接
    server.close(async () => {
      console.log('HTTP server closed');

      // 关闭数据库连接
      await db.close();
      console.log('Database connections closed');

      // 关闭 Redis
      await redis.quit();
      console.log('Redis connection closed');

      process.exit(0);
    });

    // 强制关闭超时
    setTimeout(() => {
      console.error('Forced shutdown after timeout');
      process.exit(1);
    }, 10000);
  };

  process.on('SIGTERM', () => shutdown('SIGTERM'));
  process.on('SIGINT', () => shutdown('SIGINT'));
}

性能指标监控

指标 目标值 优化方法
响应时间 P99 < 500ms 缓存、数据库索引
吞吐量 > 1000 QPS 负载均衡、连接池
CPU使用率 < 70% 异步处理、压缩
内存使用 < 80% 对象池、流处理

总结

PSI 进销存系统跨平台与性能优化的核心价值:

通过持续的架构优化和性能调优,PSI系统可以在各种环境下提供稳定、高效的服务体验。

← 下一篇:PSI进销存系统信创适配与国产化替代方案