乐趣区

关于javascript:一次纯线上接口异常的排查过程

背景

线上接口产生异样,在测试环境及本地环境均失常返回无奈复现异样问题。

技术栈

前端 umi + antd,后端 egg + egg-sequelize,次要排查方向在后端。

开始排查

开始排查异样,异样接口返回无具体错误信息。返回错误信息只有一个简略的谬误提醒 其余异样,该提醒是接口异样默认的提醒。

EXCEPTION_MSG: '其余异样'

然而接口异样失常会传入具体的异样信息,到前端却成了默认值,可见此处传入了一个undefined

try {...code} catch (e) {return ctx.EXCEPTION(e && e.toString())
}

通过排查是其中一个逻辑解决中的 try catch 异样时没有在 reject 中返回对应的谬误内容,导致最初返回给前端时变成了默认值。

return new Promise(async(resolve, reject) => {
  try {...code} catch (error) {-   reject()
+   reject(error)
  }
})

通过解决后谬误异样信息呈现了,提醒正则表达式有效,依据谬误提醒的源头找到对应的业务代码函数,但在此函数中却没有应用正则相干的代码。

SyntaxError: Invalid regular expression: /^:(?<name>[a-z_][0-9a-z_]*)(?:\)|,|$|\s|::|;|])/: Invalid group
  at injectReplacements (/opt/web/node/xxx/node_modules/sequelize/lib/utils/sql.js:120:37)
  at Sequelize.query (/opt/web/node/xxx/node_modules/sequelize/lib/sequelize.js:282:13)
  at Promise (/opt/web/node/xxx/app/service/sentry/xxx.js:628:45)
  at new Promise (<anonymous>)
  ...

因为该函数中存在调用其余的业务性能,通过日志打印的形式排查出产生异样的代码如下,这里就是一个 sql 查问,因须要查问字段的程序与返回的列表统一,用到了 replacements,因其余环境也是失常的,排除此语法问题。

await this.model.query(sql, {replacements: { name: sortList},
  type: QueryTypes.SELECT
})

回到下面抛出异样的调用关系,调用业务代码之后顺次调用了 Sequelize.queryinjectReplacements,产生了异样,则问题出在 injectReplacements。但查阅本地 Sequelize.query 源码并没有呈现 injectReplacements 调用,源码针对 replacements 配置只会有以下解决,这就有些奇怪了。

if (options.replacements) {if (Array.isArray(options.replacements)) {sql = Utils.format(.concat(options.replacements), this.options.dialect);
  } else {sql = Utils.formatNamedParameters(sql, options.replacements, this.options.dialect);
  }
}

既然本地代码找不到,那么就去服务器上的源码查找看看,果然不出所料,服务器上的源码竟然不统一。

if (options.replacements) {sql = injectReplacements(sql, this.dialect, options.replacements);
}

injectReplacements 函数中最终调用到了以下的正则,就是本文最开始的异样提醒内容,提醒正则有效。

const match = remainingString.match(/^:(?<name>[a-z_][0-9a-z_]*)(?:\)|,|$|\s|::|;|])/i);
const replacementName = (_d = match == null ? void 0 : match.groups) == null ? void 0 : _d.name;
if (!replacementName) {continue;}

而后别离查看了两个环境依赖包 sequelize 的版本号,本地环境是 sequelize@6.16.1,而线上环境的理论装置版本是 sequelize@6.21.3

"_from": "sequelize@^6.0.0",
"_id": "sequelize@6.21.3",

既然是版本的问题,那么对立版本号看看本地是否能够复现,因为这个依赖不是间接依赖包,无奈间接锁定版本,所以先删除本地 node_modulespackage-lock.json 后重新安装,最终装置实现的版本号和服务器统一了,都是sequelize@6.21.3,然而此时本地运行失常。🤷‍♀️

当初依赖版本都统一,运行后果却不同。那么查问两端运行版本区别,通过查问,本地 node 版本号 v12.22.1,服务器v8.17.0。后果显而易见了,因为运行零碎的版本不统一导致正则辨认谬误,在网上也查到其他人也有同样的问题,node 在某些版本下对正则辨认的问题。通过在远端服务器和本地服务器别离执行同样的代码,服务器 node 版本为 v8.17.0 执行产生异样。

然而为什么会呈现这样的问题呢,之前不是始终好好的吗?起因是被依赖的 sequelize 没有锁死版本号,从我的项目开始到当初 sequelize 始终在一直降级。通过查问官网 github,因为有sql 注入的问题,从 6.19.2 版本开始对此问题进行了修复,导致正则问题在较老的 node 版本中无奈辨认,产生异样。github issue 地址:https://github.com/sequelize/…

解决

能够用两种形式解决此问题,因为产生异样的依赖包并不是间接的依赖包,无奈间接写死对应的版本号,那么能够批改 package-lock 文件中依赖包的版本号,但此形式并不稳固,在前期装置还是会被笼罩。第二种形式就是降级 node 版本,因为公司外部服务器的我的项目较多,须要有肯定的测试回归笼罩才行,有肯定的风险性和老本。

最初

本文到此结束,通过本次排查线上问题得出两个论断。接口异样没有返回具体的错误信息,代码问题后续需注意,进步异样产生时的解决效率;排查问题时在保障一份代码的同时也要保障不同环境的零碎版本以及依赖版本的一致性,对于依赖包的版本尽可能锁定版本号,防止主动降级带来未知的危险。

专一前端开发,分享前端相干技术干货,公众号:南城大前端(ID: nanchengfe)

退出移动版