March 4, 2023
By: Kevin

一个环境变量传递引起的bug

  1. 一个神秘的bug
    1. 方案1:指定JVM参数
  2. 方案2: 环境变量
  3. 环境便令的传递
  4. 结论

一个神秘的bug

一个Electron应用中,打包了一个Java服务, 这个服务是在Electron启动的时候spawn出来的, 注意我们设置了 option.detechedfalse, 保证用户关闭UI的时候, 子进程也会一起结束.

childProcess = exec.spawn(
    'java -jar xxx.jar'
    javaStartCmd,
    {
        cwd: javaBuildPath,
        env: {
            detached: false,
        }
    },
    (err, stdout, stderr) => {
        if (err) {
            console.error('启动java失败', err)
        }
        console.log(stdout)
        console.log(stderr)
    }
)

我们发现在部署模式下, (Electron启动的)JVM进程不能正确的处理带有汉语的路径, 通过jps找到这个java进程后, 通过jinfo查看jvm的信息:

jinfo 1217 | grep encoding
file.encoding=UTF-8
sun.jnu.encoding=ANSI_X3.4-1968  # <== 注意

而在调试模式下, 我们的Java进程不是由Electron启动,而是用在shell中启动的, 这种情况下, 文件的处理是正常的.

jinfo 1217 | grep encoding
file.encoding=UTF-8
sun.jnu.encoding=UTF-8

根据以前的经验我们知道 sun.jnu.encodingfile.encoding决定了JVM中对于文件路径和内容的默认编码.

判断是sun.jnu.encoding设置的有问题ANSI_X3.4-1968, 它决定了文件路径的默认编码, 发现国内关于这个参数介绍排名最高的一篇文章介绍说此参数决定了class name中是否可以含有中文, 有点因果倒置.

方案1:指定JVM参数

java -jar -Dsun.jnu.encoding=utf-8 -Dfile.encoding=utf-8

通过System.getProperties();获得的系统属性来看, 只有file.encoding正确的设置了, sun.jnu.encoing依然是ansi.

查了一下资料JVM Options, sun.jnu.encoding并没有暴露出来, 所以方案无效.

方案2: 环境变量

回到最初的现象, 开发模式下(shell启动jvm)时, 执行很正常, sun.jnu.encoding和file.encoding都有正确设置.

所以考虑是不是只要正确的设置环境变量, jvm启动的时候就会正常.

环境便令的传递

父进程派生子进程的时候, 完整的环境变量会传递下去

+------------------+
|   parent process |
+---------+--------+
          |
     env  |  spawn
          v
+------------------+
|  child process   |
+------------------+

shell启动jvm的时候, shell的环境变量传递给jvm, jvm就可以正常的识别utf-8路径. nodejs(Electron)启动jvm的时候, 由于我们手工设置了环境变量, 阻断了这个传递关系, jvm没有拿到正确的环境变量, 所以出问题了.

结论

只要nodejs能够正确的设置语言环境变量, 就可以解决问题.

env: {
  detached: false,
  LANG: 'zh_CN.UTF-8'  // <==  系统语言环境变量
}
Tags: Linux Electron Java