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

Geth并发创建以太坊账户耗时长、网络冻结原因及解决方案

现象本质与底层原理

并发创建账户卡顿、节点冻结问题,核心来自Geth客户端personal_newAccount接口的实现逻辑和RPC请求处理机制,和区块链共识层没有直接关系:

  • personal_newAccount不是链上交易操作,完全是Geth节点本地的密钥库处理逻辑:生成secp256k1密钥对→用scrypt内存硬哈希算法做密钥派生(单次要占用32MB+内存、100-300ms单核心CPU时间)→加密私钥后写入本地keystore目录。
  • Geth的keystore模块持有全局互斥写锁,同一时间只能处理1个账户写入操作,无论并发发起多少个请求,实际都是串行排队执行,并发数提升不会带来任何速度增益,只会堆积请求。
  • Geth的Websocket/HTTP RPC服务默认会把所有收到的请求放入待处理队列,即使客户端断开连接,已经入队的请求也不会被清除,节点会逐个执行完所有队列任务才会释放资源。
  • 当并发请求数超过15左右时,RPC队列开始堆积:每个排队的请求都会占用连接上下文内存,等待锁的过程会阻塞RPC工作协程,队列长度到千级时,大量scrypt计算任务会占满节点CPU、内存资源,直接阻塞节点主事件循环,导致出块、P2P通信、其他RPC请求全部无法处理,表现为节点冻结;即使终止脚本,队列里的任务仍会继续执行,通常需要数十分钟才能跑完,体感上和永久故障一致,重启节点会直接清空未处理队列,因此可以立刻恢复。
  • 同步串行创建没有异常,是因为同一时间节点只会收到1个待处理请求,不会出现队列堆积。

问题对应的并发实现代码如下:

const Web3 = require('web3');
const web3 = new Web3(new Web3.providers.WebsocketProvider("ws://127.0.0.1:8545"));

const generateAccount = () => {
  const count = 30;
  let promises = []
  for (let i=0; i<count; i++) {
    promises.push(web3.eth.personal.newAccount("Hello@12345"));
  }
  Promise.allSettled(promises).then(
    status => {
      console.log(status)
      process.exit(0)
    }
  ).catch(
    err => {
      console.log(err)
      process.exit(1)
    }
  )
  
}
generateAccount()

该写法会在极短时间内向节点一次性推送count个创建请求,是导致队列堆积的直接原因。

大规模创建账户最佳实践

针对千级以上批量创建账户的需求,按优先级推荐以下方案:

  • 优先采用离线本地生成方案(性能最高、零节点负载)
    以太坊账户本质是独立的secp256k1密钥对,完全不需要连接节点即可生成,生成后只要把加密后的keystore文件放入Geth的keystore目录,节点重启后会自动加载所有账户,整个过程不会对节点运行造成任何影响。
    参考实现:
const Web3 = require('web3');
const fs = require('fs');
const path = require('path');
// 不需要连接节点,初始化空web3实例即可
const web3 = new Web3();
const password = "Hello@12345";
// 替换为你的Geth keystore实际目录
const keystoreDir = "./ethereum/keystore";
const count = 1000;
const accountList = [];

console.log(`开始生成${count}个账户...`);
for (let i = 0; i < count; i++) {
  // 本地生成密钥对,无网络、无RPC开销
  const account = web3.eth.accounts.create();
  // 生成和Geth完全兼容的keystore加密格式
  const keystore = account.encrypt(password);
  // 按照Geth keystore命名规范写入文件
  const fileName = `UTC--${new Date().toISOString().replace(/[:.]/g,'-')}--${account.address.slice(2).toLowerCase()}`;
  fs.writeFileSync(path.join(keystoreDir, fileName), JSON.stringify(keystore));
  accountList.push(account.address);
}
console.log(`生成完成,共${accountList.length}个账户,重启Geth节点即可加载`);

该方案生成1000个账户仅需数秒,完全不会出现节点卡顿问题。如果不想重启节点,可以在生成keystore后逐个调用personal_importRawKey接口导入,配合2-3的低并发控制即可,不会造成队列堆积。

  • 受限于架构必须走RPC创建时,严格控制并发数
    因为keystore全局锁的存在,并发数超过2就不会有任何速度提升,必须通过并发池限制同时在途的请求数不超过2-3,避免请求堆积。
    参考并发控制逻辑:
const Web3 = require('web3');
const web3 = new Web3(new Web3.providers.WebsocketProvider("ws://127.0.0.1:8545"));

const generateAccount = async () => {
  const count = 1000;
  const concurrency = 2; // 不要调大,无性能收益
  const runningTasks = new Set();
  const results = [];

  for (let i = 0; i < count; i++) {
    const task = web3.eth.personal.newAccount("Hello@12345")
      .then(addr => {
        results.push(addr);
        runningTasks.delete(task);
      });
    runningTasks.add(task);
    if (runningTasks.size >= concurrency) {
      await Promise.race(runningTasks);
    }
  }
  await Promise.all(runningTasks);
  console.log(`创建完成,共${results.length}个账户`);
  process.exit(0);
}
generateAccount().catch(err => {
  console.error(err);
  process.exit(1);
});

该方案不会打满节点RPC队列,但创建速度远低于离线生成方案,千级账户预计需要数分钟完成。

  • 不推荐通过调整Geth RPC参数(如队列长度、连接数上限)的方式提升批量创建性能,这类调整无法解决keystore全局锁和scrypt计算资源占用的核心问题,只会提升节点OOM、卡死的概率。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 19:01:17