You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AWS Lambda容器部署LibreOffice进程线程耗尽问题求助

AWS Lambda中LibreOffice调用后进程泄漏导致资源耗尽的解决方法

问题描述

基于public.ecr.aws/lambda/nodejs:16-x86_64镜像的AWS Lambda函数,负责将DOCX文件转换为PDF并上传至S3存储桶。初始运行正常,但经过约200次热调用后,进程和线程数持续增长(每次调用新增约6个),最终达到Lambda的1024进程/线程限制,函数执行失败并报以下错误:

Error: Command failed: libreoffice7.2 -env:UserInstallation=file:///tmp/lo-profile-... --headless --convert-to pdf --outdir /tmp/ /tmp/test1.docx
/usr/bin/libreoffice7.2: fork: retry: Resource temporarily unavailable
/usr/bin/libreoffice7.2: fork: retry: No child processes
javaldx: Could not find a Java Runtime Environment!
Warning: failed to read path from javaldx
fork: Resource temporarily unavailable
terminate called after throwing an instance of 'std::runtime_error'
what(): osl::Thread::create failed

当前执行LibreOffice的代码如下:

import { exec } from 'child_process';
import { promisify } from 'util';

const execAsync = promisify(exec);

const fileId = fileName + FileExtension.DOCX;
const binaryPath = 'libreoffice7.2';

const argumentsString =
  '--headless --invisible --nodefault --view --nolockcheck --nologo --norestore --convert-to pdf --outdir /tmp /tmp/';

const command = `${binaryPath} ${argumentsString}${fileId}`;

await execAsync(command);

解决方法

1. 彻底终止LibreOffice关联进程组

exec无法直接追踪LibreOffice启动的所有子进程,改用spawn并终止整个进程组,确保所有关联进程被清理:

import { spawn } from 'child_process';
import { promisify } from 'util';
const waitForEvent = promisify((emitter, event) => new Promise(resolve => emitter.once(event, resolve)));

async function convertDocxToPdf(filePath) {
  const args = [
    '--headless', '--invisible', '--nodefault', '--nolockcheck', '--nologo', '--norestore',
    '--convert-to', 'pdf', '--outdir', '/tmp',
    filePath
  ];
  // 启用detached模式,方便终止整个进程组
  const child = spawn('libreoffice7.2', args, { detached: true });

  try {
    await Promise.all([
      waitForEvent(child.stdout, 'end'),
      waitForEvent(child.stderr, 'end'),
    ]);
    if (child.exitCode !== 0) {
      throw new Error(`LibreOffice转换失败,退出码:${child.exitCode}`);
    }
  } finally {
    // 终止整个进程组,清理所有关联子进程
    try {
      process.kill(-child.pid, 'SIGTERM');
    } catch (err) {
      // 忽略进程已终止的错误
      if (err.code !== 'ESRCH') throw err;
    }
  }
}

2. 使用独立的临时配置目录

每次调用生成唯一的用户配置目录,避免进程间共享状态导致泄漏,转换完成后删除该目录:

import { spawn } from 'child_process';
import { promisify } from 'util';
import { randomUUID } from 'crypto';
import fs from 'fs/promises';

const waitForEvent = promisify((emitter, event) => new Promise(resolve => emitter.once(event, resolve)));

async function convertDocxToPdf(filePath) {
  // 生成唯一配置目录
  const profileDir = `/tmp/lo-profile-${randomUUID()}`;
  const args = [
    `-env:UserInstallation=file://${profileDir}`,
    '--headless', '--invisible', '--nodefault', '--nolockcheck', '--nologo', '--norestore',
    '--convert-to', 'pdf', '--outdir', '/tmp',
    filePath
  ];

  const child = spawn('libreoffice7.2', args, { detached: true });

  try {
    await Promise.all([
      waitForEvent(child.stdout, 'end'),
      waitForEvent(child.stderr, 'end'),
    ]);
    if (child.exitCode !== 0) {
      throw new Error(`LibreOffice转换失败,退出码:${child.exitCode}`);
    }
  } finally {
    // 终止进程组
    try {
      process.kill(-child.pid, 'SIGTERM');
    } catch (err) {
      if (err.code !== 'ESRCH') throw err;
    }
    // 删除临时配置目录
    await fs.rm(profileDir, { recursive: true, force: true });
  }
}

3. 禁用Java依赖

错误中提示找不到JRE,DOCX转PDF无需Java支持,可通过以下方式禁用相关逻辑:

  • 在启动参数中添加:--nocrashreport --nofirststartwizard
  • 设置环境变量:process.env.JAVA_HOME = '';

4. 辅助优化

  • 降低Lambda函数的并发数,减少热容器的复用压力
  • 在Docker镜像构建时预安装轻量版本的LibreOffice,避免冗余组件

内容的提问来源于stack exchange,提问作者emblaszkiewicz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.02 05:13:09