解决uniapp在内网构建ETIMEDOUT卡住的问题

解决uniapp在内网构建ETIMEDOUT卡住的问题

uni-app 2.0.x。通过 ci/cd内网devops 容器内 运行 npm run build:h5:prod构建时。有以下提示:

复制代码
DONE  Build complete. The dist/build/h5 directory is ready to be deployed.

欢迎将web站点部署到uniCloud前端网页托管平台,高速、免费、安全、省心,详见: https://uniapp.dcloud.io/uniCloud/hosting

Error: read ETIMEDOUT

at TLSWrap.onStreamRead (node:internal/stream_base_commons:217:20) {

errno: -110,

code: 'ETIMEDOUT',

syscall: 'read'

}

Done in 948.11s.。主要是Error: read ETIMEDOUT

一、查信息打印来源

打印来源于:node_modules/@dcloudio/vue-cli-plugin-uni/commands/build.js 217-220 行:

js 复制代码
          if (process.env.UNI_PLATFORM === 'h5' && !isInHBuilderX) {
            console.log()
            console.log('欢迎将web站点部署到uniCloud前端网页托管平台,高速、免费、安全、省心,详见:https://uniapp.dcloud.io/uniCloud/hosting')
          }

问题分析

查看源码是出自这附近 node_modules/@dcloudio/vue-cli-plugin-uni/commands/build.js 217-217 。上述只涉及一个打印信息,还是要全面进行排查,虽然有ETIMEDOUT,但构建最终是成功的,但卡时间占用太多了。

由于内网是不能访问外网的,node_modules 都是使用内网的私有库,所以可以从网络方面排查,加上打印日志。

项目环境和信息涉及的文件有: vue.config.js package.json。

加入信息打印

shell 复制代码
# 在构建前启动(后台),打印时间/源IP/目的IP/SNI 到 stdout。 使用 tshark(能显示 TLS SNI,最精确)
sudo tshark -i any -n -l -Y "tls.handshake.type == 1" \
  -T fields -e frame.time -e ip.src -e ip.dst -e tls.handshake.extensions_server_name 2>/dev/null &

# 打印到 stdout(不要 -w),加 -l 保证行缓冲。 如果没有 tshark,使用 tcpdump(行缓冲输出到 stdout)
sudo tcpdump -n -i any 'tcp port 443 and not src host 127.0.0.1' -l -q &

# 会在 stdout 打出 Node TLS/HTTP 连接相关日志。无抓包工具时,用 Node 内部调试(最轻量)
NODE_DEBUG=http,net,tls npm run build:h5:prod
复制代码
##  GitLab CI job
# 检查并启动抓包(打印到 stdout),失败时降级到 NODE_DEBUG
if command -v tshark >/dev/null 2>&1 && [ "$(id -u)" = "0" ]; then
  echo '[info] starting tshark'
  tshark -i any -n -l -Y "tls.handshake.type == 1" -T fields -e frame.time -e ip.src -e ip.dst -e tls.handshake.extensions_server_name 2>&1 | sed -u 's/^/[tshark] /' & CAP_PID=$!
elif command -v tcpdump >/dev/null 2>&1; then
  echo '[info] starting tcpdump'
  tcpdump -n -i any -s 0 -l 'tcp port 443 and not src host 127.0.0.1' 2>&1 | sed -u 's/^/[tcpdump] /' & CAP_PID=$! || true
else
  echo '[info] no capture tool found; will use NODE_DEBUG fallback'
fi

# 用内部脚本避免外网请求(推荐)并同时打印 Node 网络调试信息
NODE_DEBUG=http,net,tls yarn run build:h5:internal

接着就可以在构建时候查看ci/cd日志,分析是哪些外网请求超时了,还是本地文件访问超时了。

二、尝试的解决方案

(一)禁用统计功能 (无效)

如何解决 timeout 的问题。是由于容器不能请求外网超时,还是不能访问本地文件超时还是其他原因。

可以通过在 package.json 里新增一个构建脚本,禁用统计功能。

json 复制代码
"scripts": {
  "build:h5:prod": "DISABLE_UNI_STATISTICS=true UNI_STATISTICS=false UNI_CLOUD_PROVIDER=false uni-build --mode h5 --dest dist/build/h5"
}

通过多次社区、官方的文档进行了多次配置,发现当前版本的uniapp并无效果。

(二)预加载脚本(推荐)

日志显示即便加了 DISABLE_UNI_STATISTICS=1 / --no-socket,uni-build 仍试图向 uniapp.dcloud.net.cn 发 POST(最后 read ETIMEDOUT),因此需要在构建时彻底拦截/模拟这些出站请求。构建本身能完成,但会卡在等待远端响应并产生日志噪音/超时。

增加一个预加载脚本,在 Node 启动时 monkey patch http/https,把访问 uniapp.dcloud.net.cn 的请求直接拦截并快速返回(不真实网络调用)。把脚本通过 node -r 或 NODE_OPTIONS=--require 预加载到 build 命令中,保证任何模块发起请求都会被拦截。

  1. 新建 disable-network-plugin.js:
js 复制代码
// ...existing code...
if (process.env.DISABLE_UNI_STATISTICS === '1') {
  const http = require('http');
  const https = require('https');
  const { PassThrough } = require('stream');

  const makeStub = () => {
    return function stubRequest(options, callback) {
      try {
        const host = (typeof options === 'string') ? options : (options && (options.hostname || options.host || options.href));
        if (host && String(host).includes('uniapp.dcloud.net.cn')) {
          const req = new PassThrough();
          // simulate immediate response next tick
          process.nextTick(() => {
            const res = new PassThrough();
            res.statusCode = 200;
            res.headers = {};
            if (callback) callback(res);
            req.emit('response', res);
            res.end('{}');
          });
          // mimic minimal interface
          req.end = (data) => { if (data) PassThrough.prototype.write.call(req, data); PassThrough.prototype.end.call(req); };
          return req;
        }
      } catch (e) { /* ignore */ }
      // fallback to original
      return original.apply(this, arguments);
    };
  };

  const original = http.request;
  http.request = makeStub();
  http.get = function() {
    const r = http.request.apply(this, arguments);
    r.end();
    return r;
  };

  const originalHttps = https.request;
  https.request = makeStub();
  https.get = function() {
    const r = https.request.apply(this, arguments);
    r.end();
    return r;
  };
}
  1. 修改 package.json 构建脚本:

(1)主要改动是这块:

复制代码
DISABLE_UNI_STATISTICS=1 node -r ./disable-network-plugin.js ./node_modules/@vue/cli-service/bin/vue-cli-service.js
json 复制代码
    "build:h5:internal": "cross-env NODE_ENV=production UNI_PLATFORM=h5 UNI_STATISTICS=false UNI_CLOUD_PROVIDER=false DISABLE_UNI_STATISTICS=1 node -r ./disable-network-plugin.js ./node_modules/@vue/cli-service/bin/vue-cli-service.js uni-build --base=/bcrm/h5/ --mode prod --no-socket"

(2)可选,CI 层(建议把 env 也加为 pipeline 变量,保证更早生效)

yml 复制代码
variables:
  DISABLE_UNI_STATISTICS: "1"
  UNI_STATISTICS: "false"
  UNI_CLOUD_PROVIDER: "false"

build_job:
  script:
    - export NODE_DEBUG=http,net,tls    # 调试时可保留
    - yarn run build:h5:internal

(3)构建就生效了。如图

效果显著:

相关推荐
游戏开发爱好者818 小时前
抓包工具有哪些?代理抓包、数据流抓包、拦截转发工具
android·ios·小程序·https·uni-app·iphone·webview
郑州光合科技余经理1 天前
技术解析:如何打造适应多国市场的海外跑腿平台
java·开发语言·javascript·mysql·spring cloud·uni-app·php
前端 贾公子1 天前
uniapp之实现拖拽排序
uni-app
一室易安1 天前
uniapp vue3 小程序中 手写模仿京东淘宝加入购物车红点曲线飞入样式效果 简化版代码
小程序·uni-app
00后程序员张1 天前
混合 App 怎么加密?分析混合架构下常见的安全风险
android·安全·小程序·https·uni-app·iphone·webview
2501_915921431 天前
Flutter App 到底该怎么测试?如何在 iOS 上进行测试
android·flutter·ios·小程序·uni-app·cocoa·iphone
2501_915909061 天前
如何在 Windows 上上架 iOS App,分析上架流程哪些是不用mac的
android·macos·ios·小程序·uni-app·iphone·webview
2501_915921431 天前
分析 iOS 描述文件创建与管理中常见的问题
android·ios·小程序·https·uni-app·iphone·webview
Aftery的博客1 天前
Uniapp-vue实现语言功能切换(多语言)
javascript·vue.js·uni-app