我先更新一下依赖缓存,再把镜像源切成国内节点,重新构建的时候,报错依然翻来覆去就那么几行,最扎眼的还是这句timeout of 12000ms exceeded。这问题在部署灯塔(ARL)时太典型了:前端页面能起来,但一调接口就等满 12 秒然后白屏,或者安装阶段拉取资源直接断掉。很多人第一反应是“服务器不行”“网络不行”,但其实根子往往在前端超时阈值、后端服务响应速度,以及 Docker 和 npm 的源配置上。这篇文章我就把 ARL 装不上、跑不起来时遇到timeout of 12000ms exceeded的各种场景、排查方法和根治方案完整拆一遍,给正在踩坑的人一个可以直接抄作业的路径。
1. 先搞懂 ARL 和这个报错到底是怎么回事
1.1 ARL 在安全巡检里到底扮演什么角色
ARL(Asset Reconnaissance Lighthouse,灯塔)是一款开源的资产侦察与监控系统,用来做企业侧资产发现、子域名收集、端口指纹识别、Web 站点监控等。它的典型使用方式是通过 Docker Compose 一键拉起整套服务,前端负责展示任务结果,后端负责调度扫描任务,底层依赖 MongoDB 和 Redis。部署起来以后,日常操作基本就是“添加目标 → 下发任务 → 查看结果”。
很多安全团队的日常巡检、SaaS 资产梳理、攻防演练前的资产摸底都会用到它。它的价值在于把分散的测绘能力整合在一个界面里,既能做被动信息收集,也能联动主动扫描。但它的部署体验并不是零门槛,尤其是网络环境不太好时,装到一半就报错的情况非常常见。而timeout of 12000ms exceeded正是大家最常撞上的那堵墙。
1.2 timeout of 12000ms exceeded 是谁报出来的
这句话本身是 axios 的默认超时提示。ARL 的前端页面使用 Vue 生态开发,HTTP 请求库就是 axios。当前端调用后端接口时,如果 12 秒内没有收到响应,axios 就会主动中断请求,并在浏览器控制台打印timeout of 12000ms exceeded。它不代表后端程序一定崩溃了,只代表“在这段时间内,后端没能把处理结果交回给前端”。
这个 12 秒不是 ARL 开发者随意定的,而是 axios 里timeout参数的一个常见默认值。很多项目图省事直接不配置,或者写死在代码里。ARL 的一些接口在处理大量资产数据、调用第三方测绘平台 API、执行复杂的 MongoDB 聚合查询时,很容易超过这个阈值。于是你看到的现象就是:页面能打开,但“添加任务”“查看详情”“同步数据”这些操作频繁转圈,最后报超时。
1.3 什么场景下最容易触发这个超时
结合我自己和身边朋友的实际部署经历,最容易报这个错的场景有三类。
第一类是安装部署阶段的“假超时”。比如docker pull拉镜像时网络慢,或者npm install安装前端依赖时下载卡住,某些命令会在超时后直接中断,返回类似 timeout 的报错。虽然不完全等同于 axios 的 12000ms 提示,但都指向同一个根源:资源下载与依赖获取环节耗时过长。
第二类是服务刚启动完,MongoDB 和 Redis 还没准备好,后端接口首次调用极慢,前端等不到响应就报超时。这种在低配服务器(1 核 2G)上尤其明显,启动容器后马上打开页面操作,十有八九超时。
第三类是扫描结果数据量变大以后,某些列表页或统计接口要聚合大量数据,MongoDB 查询没走索引,后端响应超过 12 秒,前端又开始报错。
不同场景的解决办法不一样,但它们之间有很强的关联性。要彻底解决,必须从前端配置、后端性能、基础依赖三个层面一起调整,缺一个都容易反复踩坑。
2. 安装部署阶段的原因分析与临时规避手段
2.1 Docker 部署前先把环境底子打牢
ARL 官方推荐用 Docker Compose 部署,所以 Docker 和 Docker Compose 的安装是第一关。很多人在这里就开始出问题,比如 Docker 服务没起来、Compose 版本太老、容器之间网络不通。这里我建议先确认最基础的三件事:Docker 版本不低于 20.10,Docker Compose 版本不低于 1.29,服务器的 CPU 和内存至少 2 核 4G。
这几个要求看着简单,但确实卡住过不少人。版本太老会导致某些镜像的启动参数不被识别,内存太小会导致容器启动后被杀掉,表现就是前端页面加载到一半就没反应。曾有朋友在 1 核 1G 的机器上装 ARL,启动后前端能打开,一点任务列表就卡死,查看 Docker 日志发现 MongoDB 反复被杀进程重启。这就是资源不足引起的连锁反应,从表面看也是“请求超时”,但本质是后端服务根本没稳定运行。
安装完 Docker 后,最好先验证一下 Docker 是否能正常拉取镜像、运行 hello-world 容器。这一步能排除大部分环境问题,避免后面排查时绕远路。
2.2 镜像拉取慢的直接处理方式
ARL 的 Docker 镜像托管在 Docker Hub,国内网络环境拉取时经常出现连接超时、下载中断。你看到的报错可能是net/http: TLS handshake timeout,也可能是dial tcp ... i/o timeout,虽然文案不是 12000ms,但问题本质一样。
解决办法是给 Docker 配置镜像加速器。用 Linux 服务器的话,编辑/etc/docker/daemon.json,填上 registry-mirrors,然后重启 Docker:
{ "registry-mirrors": [ "https://docker.m.daocloud.io", "https://dockerproxy.com" ] }sudo systemctl daemon-reload sudo systemctl restart docker配置完成后先用docker info查看 Registry Mirrors 是否生效,再重新拉取 ARL 相关镜像。注意,网上很多镜像加速地址会不定期调整,要是失效了就换一个可用的,或者找云厂商提供的加速器地址。
实操的小体会是:不要只配一个加速地址,多填两三个,Docker 会按顺序尝试,有备无患。还有,如果服务器能访问外网但速度很慢,也可以考虑在低峰期拉镜像,比如凌晨时段会快很多。拉取完成后把镜像保存成本地 tar 包,以后在局域网环境部署就能直接导入,不用再受网络波动的影响。
2.3 npm install 超时的处理
除了 Docker 镜像,ARL 前端依赖安装也是超时重灾区。前端构建时需要从 npm 官方仓库下载大量包,网络波动时很容易卡住。如果你是从源码构建而不是直接用官方镜像,遇到npm install卡死或超时,要优先切换 npm 源到国内镜像:
npm config set registry https://registry.npmmirror.com然后删除 node_modules 和 package-lock.json 重新安装:
rm -rf node_modules package-lock.json npm install --registry=https://registry.npmmirror.com也可以给 npm 单独加超时时间,避免默认配置太短导致误判:
npm config set fetch-timeout 600000 npm config set fetch-retries 5这里的逻辑是:npm 默认的网络超时和重试策略在某些网络环境下过于保守,稍微慢一点就断掉。调大超时上限、增加重试次数之后,大多数情况都能顺利装完。
2.4 安装阶段的“假超时”别硬等
如果安装脚本卡在某个步骤超过几分钟,不要一味等待,先判断是假死还是真慢。最简单的验证方式是用docker ps查看容器状态,或者用docker logs看当前容器输出。如果容器状态一直在 restarting,说明启动失败,等再久也没用。
另外,Cloning 仓库时如果中途出现stream disconnected before completion之类的提示,也说明数据流被中断了。这时建议手动清理一下未完成的下载缓存,然后重试。比如 Git 仓库克隆到一半断了,可以先删掉目录再重新 clone;Docker 层缓存出错时,可以执行docker system prune清理。
我自己的习惯是部署新服务时先跑一遍官方脚本,但如果发现脚本里有“下载依赖”和“构建镜像”这种容易超时的环节,我会拆出来分步执行。这样哪个环节断了,直接处理哪个环节,比反复跑整套脚本效率高得多。
3. 跑起来以后还超时的根治方案
3.1 定位真正慢的接口
安装阶段的问题处理完毕,ARL 能正常启动,但使用中依然出现timeout of 12000ms exceeded,那就必须进入接口层面排查了。第一步是打开浏览器开发者工具,切到 Network 面板,刷新页面,触发一次报错,观察到底哪个请求超时。
重点关注状态列为 pending 的请求,这些就是超过 12 秒还没返回的接口。通常最容易出问题的集中在/api/asset/、/api/task/、/api/statistics/这些数据聚合类接口。它们背后往往牵扯多张表的关联查询,还可能在实时调用外部测绘平台的数据。
举个例子,你在首页看统计图表时,前端会请求一个聚合接口,后端需要从 MongoDB 里统计任务总数、资产总数、今日新增资产。数据量小的时候响应很快,但资产表过百万行以后,如果 status 字段、date 字段没加索引,MongoDB 就可能做全表扫描,响应时间直线上升。
定位到具体接口后,去后端容器里查日志:
docker logs arl-mongodb --tail 200或者看后端容器的日志,找到对应接口的响应时长。很多响应慢都有规律,比如第一次请求慢、后续请求快,那是缓存没命中;每次请求都慢,那是查询语句或索引问题。
3.2 调大前端 axios 超时阈值
如果确认接口本身业务逻辑比较复杂,短时间内优化不动,可以先临时调大前端 axios 的超时时间,让前端不再 12 秒就中断请求。
ARL 前端源码中,axios 实例一般在src/utils/request.js或类似位置。找到 axios.create 的部分,把 timeout 从 12000 改成 60000 甚至 120000:
import axios from 'axios' const service = axios.create({ baseURL: '/api', timeout: 60000 // 原来是 12000,改大后给后端更充裕的处理时间 })如果你用的是官方 Docker 镜像而不是源码构建,没法直接改前端文件,那就得换一种思路:在 Nginx 反向代理层调整超时时间。ARL 的端口映射最终是 Nginx 对外提供服务,Nginx 本身也有代理超时限制。在 nginx 配置文件里,给 location 加上 proxy_read_timeout:
location / { proxy_read_timeout 60s; proxy_send_timeout 60s; proxy_connect_timeout 30s; }这种方式不需要重新编译前端,只需要改容器内的 Nginx 配置或者挂载自定义配置。
扩容超时时间属于“治标”手段,能让你先用起来,但不要指望改大了就万事大吉。真正要做的还是优化后端响应。
3.3 优化后端与数据库,解决慢的根本
后端慢的常见原因有三个:容器资源受限、MongoDB 查询慢、Redis 没有合理利用。
容器资源受限方面,要检查一下 Compose 文件里有没有给服务配置 mem_limit、cpu_limit。如果资源限制太小,后端处理并发请求时就会频繁 GC,导致接口超时。建议把资源限制调大,或者在测试环境直接去掉限制。
MongoDB 查询慢的话,最直接的优化是建立索引。比如查询资产列表时会按domain、create_date排序,就针对这些字段建索引:
db.asset.createIndex({ domain: 1 }) db.asset.createIndex({ create_date: -1 }) db.asset.createIndex({ source: 1, create_date: -1 })用 explain 分析慢查询:
db.asset.find({ source: "fofa" }).sort({ create_date: -1 }).explain("executionStats")看executionTimeMillis和totalDocsExamined,如果扫描的行数远大于返回行数,说明索引没有命中。
Redis 方面,ARL 用 Redis 做缓存和任务队列。检查 Redis 是否有持久化策略、是否设置了内存淘汰策略。如果 Redis 内存满了,写入可能会阻塞,间接拖慢接口。
3.4 外部数据源调用慢怎么处理
ARL 会集成 FOFA、Quake、Hunter 等网络空间测绘平台的数据,这些外部 API 调用也是超时的常见来源。具体表现是:点“同步接口数据”或“导入测绘数据”时长时间无响应,最后超时。
这类接口慢不一定是 ARL 本身问题,而是第三方平台 API 响应慢,或者你的网络到第三方平台不够通畅。处理办法是在 ARL 的管理后台里检查第三方平台的 API 配置是否正确,如果不需要这些数据源,可以先禁用掉,避免每次扫描都卡在外部调用上。
如果你确实需要外部数据源,可以考虑用定时任务预取数据,再导入到本地,而不是在用户操作时实时同步。这样即使第三方平台响应慢,也不影响前端交互体验。
3.5 验证调整是否生效
改完配置后,不要直接关掉控制台就算完。需要完整验证一遍:重启相关容器,刷新页面,再次触发之前超时的接口,看 Network 面板里的 Time 是否明显下降,页面是否不再报错。
如果改了源码并重新构建了前端镜像,记得把新镜像重新打 tag,并让 Docker Compose 使用新镜像启动:
docker-compose down docker-compose up -d如果只改了 Nginx 配置,执行docker exec进入容器重载 Nginx:
docker exec -it arl-nginx nginx -s reload验证超时配置是否生效的最快方法,是故意触发一个慢接口,看请求是否还能在 12 秒内被中断。如果在 Network 面板里看到请求等待时间超过 12 秒仍未中断,说明新的超时阈值已生效。
4. 常见问题排查与避坑技巧实录
4.1 重启后又变回原样,配置没保住
这个问题很常见。很多人改完容器里的配置后,直接 docker-compose restart,发现重启完配置又变回去了。原因是容器本身是临时的,修改写在容器可写层里,Compose 重建容器时会拋弃这些改动。
解决办法是把自定义配置通过 volume 挂载进去。比如 Nginx 配置,在 docker-compose.yml 里把宿主机上的配置目录映射到容器内:
services: nginx: image: arl_nginx:latest volumes: - ./nginx/conf.d:/etc/nginx/conf.d同理,MongoDB 的数据也要挂载到宿主机,防止容器重建后数据丢失。这些配置在第一次部署时就应该规划好,否则后面每次调整都会重复踩坑。
4.2 stream disconnected before completion 与超时是什么关系
这是个在安装和运行阶段都可能出现的报错。它的含义是:HTTP 流式响应在完成之前连接被断开。对比 axios 的 timeout,它更像是服务端主动关闭连接,或者中间的代理设备断开了连接。
如果是在下载镜像或克隆代码时出现,通常是网络层的连接被重置。解决办法和前面类似,换源、提速、重试。如果是在网页访问时出现,通常是 Nginx 的 proxy_buffering 或 proxy_read_timeout 设置不合理。可以关闭 Nginx 的代理缓冲,或者调大超时:
location / { proxy_buffering off; proxy_read_timeout 300s; }stream disconnected这类问题有时候会和idle timeout waiting for sse一起出现,后者常见于需要 Server-Sent Events 的长连接场景。ARL 中如果有实时推送任务进度的功能,Nginx 默认的空闲超时可能导致 SSE 连接中断。解决办法是在 Nginx 代理配置中加上:
proxy_set_header Connection ''; proxy_http_version 1.1; proxy_buffering off; proxy_read_timeout 3600s;4.3 日志里看不到明显异常,页面却一直超时
这种情况往往不是后端程序报错,而是数据库锁等待、连接数耗尽或 DNS 解析慢。你先检查 MongoDB 的连接数:
docker exec -it arl-mongodb mongosh --eval "db.serverStatus().connections"如果连接数接近上限,要把连接池调大,或者排查是否有慢查询占住了连接。Redis 也可能出现连接数打满的情况,看info clients的输出。
还有一种隐蔽情况:DNS 解析慢。当后端需要查询外部域名时,如果服务器的 DNS 解析不稳定,每个请求都可能卡在解析阶段。这时可以在容器里手动指定 DNS 服务器,或者在 docker-compose.yml 中配置 dns:
services: worker: dns: - 223.5.5.5 - 114.114.114.114这里的思路是把每一步都量化。不要只看“页面超时”,要一层层拆:是 DNS 慢、TCP 连接慢、还是响应体生成慢。前端 Network 面板能告诉你是 Waiting 时间过长,还是 Content Download 时间过长,这两个阶段对应的排查方向完全不同。
4.4 完整解决路径速查表
我把整个排查和解决路径整理成一份速查表,方便你对照操作:
| 阶段 | 症状 | 可能原因 | 解决动作 |
|---|---|---|---|
| 安装部署 | docker pull 超时/中断 | Docker Hub 网络不通畅 | 配置镜像加速器,多地址备用 |
| 安装部署 | npm install 卡死 | npm 官方源访问慢 | 切换 npmmirror 源,调大 fetch-timeout |
| 安装部署 | git clone 中断 | 网络连接被重置 | 清缓存重试,或下载压缩包解压 |
| 启动运行 | 页面可开,接口超时 | 后端容器资源不足 | 调大内存/CPU限制,确保 2核4G 以上 |
| 启动运行 | 首次访问超时,后面正常 | 数据库未初始化完成 | 启动后等 1-2 分钟再访问 |
| 使用过程 | 列表页接口超时 | MongoDB 缺少索引 | 针对查询字段建索引 |
| 使用过程 | 接口全超时 | 连接数被打满 | 检查 Mongo/Redis 连接数,调连接池 |
| 使用过程 | 同步外部数据超时 | 第三方 API 慢 | 禁用或定时预取数据 |
| 使用过程 | 前端 12 秒中断 | axios timeout 过短 | 改源码或 Nginx 超时阈值 |
这张表是我在多次部署 ARL 的过程中总结出来的,覆盖了绝大多数超时场景。遇到问题先按阶段归类,再对症处理,不要一上来就重装系统或换服务器。
5. 部署与调优后的一些个人体会
那台一开始频繁报超时的服务器,在加了镜像加速、调大前端超时阈值、给 MongoDB 建好索引之后,用起来已经非常丝滑了。整个过程里我最大的感受是:timeout of 12000ms exceeded这个报错本身并不难解决,难的是很多人不知道这个 12 秒是 axios 的默认值,于是到处怀疑代码有问题,甚至反复重建容器,结果越搞越乱。
如果你是第一次装 ARL,提前把这些超时相关的配置都调整好再去跑,可以少走很多弯路。在网上搜索 ARL 部署问题时,也经常能看到有人推荐改核心代码、替换组件甚至换操作系统,其实多数时候没那个必要。先确认基础环境没问题,再考虑调参,最后再动代码,这个顺序不要颠倒了。
还有一点想强调:调大超时时间只是缓解手段,最好还是要弄清楚后端为什么慢。如果是因为机器配置太差,那就升配置或减少并发任务;如果是因为数据库没建索引,那就老老实实建索引;如果是因为外部数据源慢,那就把同步逻辑异步化。否则你今天把超时调到 60 秒,明天数据量涨上来,接口响应变成 80 秒,还是会继续报错。
ARL 本身是个很实用的系统,部署和调优并没有想象中那么复杂。把这些超时问题处理干净之后,后续用起来会顺手很多。如果你后续在资产发现、任务调度或者数据展示上遇到其他问题,也可以沿着“先定位、再调参、再优化”的思路继续排查,大部分坑都能自己踩平。