SAP NW RFC SDK 和 node-rfc 开发文档

326 阅读5分钟

1. 概述

本项目是为某企业ERP系统对接而开发的集成解决方案。主要目标是通过SAP NW RFC SDK和node-rfc实现与SAP系统的无缝集成。

1.1 组件介绍

  • NW RFC SDK: SAP提供的C语言库,用于实现RFC通信
  • node-rfc: Node.js的RFC客户端库,基于NW RFC SDK开发

1.2 主要功能

  • 建立和管理SAP系统连接
  • 调用SAP函数模块
  • 处理RFC通信错误
  • 支持连接池管理
  • 支持UTF-8编码
  • 支持SSO认证
  • 支持负载均衡

2. 集成配置

2.1 环境要求

  • Windows操作系统
  • Visual C++运行库
  • SAP NW RFC SDK 7.50.15或更高版本
  • Node.js环境
  • 足够的系统内存和磁盘空间

2.2 初始化配置

// 初始化SAP SDK
export async function initializeSapSdk(): Promise<boolean> {
  try {
    // 1. 检查环境变量
    const envVars = {
      SAPNWRFC_HOME: 'SDK安装路径',
      SAPNWRFC_LIB: 'SDK库路径',
      SAPNWRFC_BIN: 'SDK二进制路径',
      SAPNWRFC_INCLUDE: 'SDK头文件路径',
      // UTF-8编码支持
      SAPNWRFC_USE_UTF8: '1',
      SAPNWRFC_USE_UTF8_FOR_HOSTNAME: '1',
      SAPNWRFC_USE_UTF8_FOR_SYSTEMNAME: '1',
      SAPNWRFC_USE_UTF8_FOR_USERNAME: '1',
      SAPNWRFC_USE_UTF8_FOR_PASSWORD: '1',
      SAPNWRFC_USE_UTF8_FOR_COMMAND: '1',
      SAPNWRFC_USE_UTF8_FOR_MESSAGE: '1'
    };

    // 2. 设置环境变量
    Object.assign(process.env, envVars);

    // 3. 更新系统PATH
    updatePathEnvironment([envVars.SAPNWRFC_BIN, envVars.SAPNWRFC_LIB]);

    // 4. 验证环境
    if (!validateSapEnvironment()) {
      throw new Error('SAP SDK环境验证失败');
    }

    // 5. 加载node-rfc模块
    const nodeRfc = require('node-rfc');
    if (!nodeRfc || !nodeRfc.Client || !nodeRfc.Pool) {
      throw new Error('node-rfc模块加载不完整');
    }

    return true;
  } catch (error) {
    logger.error('SAP SDK初始化失败:', error);
    return false;
  }
}

2.3 连接参数配置

// 基本连接参数
const connectionParameters = {
  ashost: 'sap-server',      // SAP服务器地址
  sysnr: '00',              // 系统编号
  client: '100',            // 客户端编号
  user: 'username',         // 用户名
  passwd: 'password',       // 密码
  lang: 'EN',              // 语言
  codepage: '4103',        // 代码页
  // 可选参数
  timeout: 30000,          // 超时时间(毫秒)
  trace: 3,                // 跟踪级别
  stateless: true,         // 无状态模式
  pool: {                  // 连接池配置
    low: 1,               // 最小连接数
    high: 3              // 最大连接数
  }
};

// 负载均衡连接参数
const loadBalancedParameters = {
  mshost: 'message-server',  // 消息服务器
  msserv: '3600',           // 消息服务器端口
  group: 'PUBLIC',          // 服务器组
  r3name: 'SID',           // R3系统名称
  client: '100',
  user: 'username',
  passwd: 'password'
};

// SSO认证参数
const ssoParameters = {
  ashost: 'sap-server',
  sysnr: '00',
  client: '100',
  mysapsso2: 'SSO_TICKET',  // SSO票据
  lang: 'EN'
};

3. 连接管理

3.1 连接池实现

class SapConnectionPool {
  private pool: Pool;
  private config: any;
  
  constructor(config: any) {
    this.config = config;
  }
  
  async init() {
    // 创建连接池
    this.pool = new Pool({
      connectionParameters: this.config,
      clientOptions: { 
        stateless: true,
        timeout: 30000,
        logLevel: 3
      },
      poolOptions: { 
        low: 1, 
        high: 3,
        timeout: 30000
      }
    });
  }
  
  async acquire() {
    return await this.pool.acquire();
  }
  
  async release(client: any) {
    await this.pool.release(client);
  }
  
  async close() {
    await this.pool.close();
  }
}

3.2 连接状态管理

// 检查连接有效性
async function checkConnection(client: any): Promise<boolean> {
  try {
    await client.ping();
    return true;
  } catch (error) {
    logger.error('连接检查失败:', error);
    return false;
  }
}

// 重连机制
async function reconnect(client: any, maxRetries = 3): Promise<boolean> {
  for (let i = 0; i < maxRetries; i++) {
    try {
      await client.reconnect();
      return true;
    } catch (error) {
      logger.warn(`重连尝试 ${i + 1}/${maxRetries} 失败:`, error);
      await new Promise(resolve => setTimeout(resolve, 2000));
    }
  }
  return false;
}

4. 错误处理

4.1 错误类型

enum RFC_RC {
  RFC_OK,                     // 成功
  RFC_COMMUNICATION_FAILURE,  // 网络通信错误
  RFC_LOGON_FAILURE,         // 登录失败
  RFC_ABAP_RUNTIME_FAILURE,  // ABAP运行时错误
  RFC_ABAP_MESSAGE,          // ABAP消息
  RFC_ABAP_EXCEPTION,        // ABAP异常
  RFC_CLOSED,                // 连接关闭
  RFC_TIMEOUT,               // 超时
  RFC_MEMORY_INSUFFICIENT,   // 内存不足
  RFC_INVALID_PARAMETER,     // 参数无效
  RFC_INVALID_HANDLE,        // 句柄无效
  RFC_NOT_FOUND,             // 未找到
  RFC_NOT_SUPPORTED,         // 不支持
  RFC_ILLEGAL_STATE,         // 非法状态
  RFC_INVALID_CHARSET,       // 字符集无效
  RFC_CANCELLED,             // 已取消
  RFC_EXTERNAL_FAILURE       // 外部错误
}

4.2 错误处理最佳实践

async function handleSapError(error: any): Promise<void> {
  // 1. 记录错误日志
  logger.error('SAP错误:', {
    code: error.code,
    message: error.message,
    key: error.key,
    abapMsgClass: error.abapMsgClass,
    abapMsgType: error.abapMsgType,
    abapMsgNumber: error.abapMsgNumber
  });

  // 2. 根据错误类型采取相应措施
  switch (error.code) {
    case RFC_RC.RFC_COMMUNICATION_FAILURE:
      // 网络错误,尝试重连
      await reconnect(client);
      break;
    case RFC_RC.RFC_LOGON_FAILURE:
      // 登录失败,检查凭证
      checkCredentials();
      break;
    case RFC_RC.RFC_TIMEOUT:
      // 超时,增加超时时间重试
      increaseTimeout();
      break;
    default:
      // 其他错误,显示错误信息
      showErrorDialog(error);
  }

  // 3. 必要时清理资源
  if (error.code === RFC_RC.RFC_CLOSED) {
    await cleanupResources();
  }
}

5. 性能优化

5.1 连接池配置优化

const optimizedPoolConfig = {
  connectionParameters: {
    // ... 连接参数
  },
  clientOptions: {
    stateless: true,      // 使用无状态模式
    timeout: 30000,       // 连接超时
    logLevel: 2,          // 适当的日志级别
    keepAlive: true,      // 保持连接活跃
    keepAliveInterval: 30000  // 保活间隔
  },
  poolOptions: {
    low: 2,              // 最小连接数
    high: 5,             // 最大连接数
    timeout: 30000,      // 获取连接超时
    maxWaitingClients: 10  // 最大等待客户端数
  }
};

5.2 批量处理优化

async function processBatchData(data: any[], batchSize = 100) {
  const results = [];
  const batches = Math.ceil(data.length / batchSize);

  for (let i = 0; i < batches; i++) {
    const batch = data.slice(i * batchSize, (i + 1) * batchSize);
    const batchResults = await Promise.all(
      batch.map(item => processItem(item))
    );
    results.push(...batchResults);
  }

  return results;
}

5.3 缓存优化

class SapCache {
  private cache: Map<string, any>;
  private ttl: number;

  constructor(ttl = 3600000) { // 默认1小时
    this.cache = new Map();
    this.ttl = ttl;
  }

  async get(key: string, fetchFn: () => Promise<any>) {
    const cached = this.cache.get(key);
    if (cached && Date.now() - cached.timestamp < this.ttl) {
      return cached.data;
    }

    const data = await fetchFn();
    this.cache.set(key, {
      data,
      timestamp: Date.now()
    });
    return data;
  }
}

6. 安全最佳实践

6.1 凭证管理

class CredentialManager {
  private secureStorage: any;

  async storeCredentials(credentials: any) {
    // 加密存储凭证
    const encrypted = await this.encrypt(credentials);
    await this.secureStorage.set('sap_credentials', encrypted);
  }

  async getCredentials() {
    // 解密获取凭证
    const encrypted = await this.secureStorage.get('sap_credentials');
    return await this.decrypt(encrypted);
  }
}

6.2 连接安全

const secureConnectionConfig = {
  // ... 基本连接参数
  snc_mode: 1,              // 启用SNC
  snc_qop: 9,               // 安全级别
  snc_myname: 'p:CN=client', // 客户端标识
  snc_partnername: 'p:CN=server' // 服务器标识
};

7. 监控和维护

7.1 健康检查

class SapHealthMonitor {
  private checkInterval: number;
  private timer: NodeJS.Timeout;

  constructor(interval = 300000) { // 默认5分钟
    this.checkInterval = interval;
  }

  start() {
    this.timer = setInterval(async () => {
      try {
        const status = await this.checkHealth();
        this.logStatus(status);
      } catch (error) {
        this.handleError(error);
      }
    }, this.checkInterval);
  }

  async checkHealth() {
    return {
      connectionStatus: await this.checkConnection(),
      poolStatus: await this.checkPool(),
      performanceMetrics: await this.getMetrics()
    };
  }
}

7.2 日志记录

class SapLogger {
  log(level: string, message: string, context: any) {
    const logEntry = {
      timestamp: new Date().toISOString(),
      level,
      message,
      context,
      environment: process.env.NODE_ENV
    };

    // 写入日志文件
    this.writeToFile(logEntry);
    
    // 发送到监控系统
    this.sendToMonitoring(logEntry);
  }
}

8. 常见问题及解决方案

8.1 初始化问题

  • 问题:VC++运行库缺失
  • 解决方案:
    1. 安装Visual C++ Redistributable
    2. 检查SAP SDK安装路径
    3. 验证环境变量配置
    4. 检查系统权限

8.2 连接问题

  • 问题:连接超时或失败
  • 解决方案:
    1. 检查网络连接
    2. 验证SAP系统状态
    3. 确认连接参数正确性
    4. 调整超时设置
    5. 检查防火墙配置

8.3 编码问题

  • 问题:字符编码错误
  • 解决方案:
    1. 确保使用UTF-8编码
    2. 配置正确的语言参数
    3. 检查SAP系统编码设置
    4. 验证数据传输编码

8.4 性能问题

  • 问题:响应缓慢
  • 解决方案:
    1. 优化连接池配置
    2. 实现请求缓存
    3. 使用批量处理
    4. 监控资源使用

9. 参考资料