应用默认编码不对的问题定位
问题描述
某日线上环境报警,分析后发现是java.lang.String.getBytes()方法在不同环境上表现出来的结果是不一样的。
JDK的String.getBytes()默认采用什么编码
看下JDK的代码不难发现:
先是靠Charset.defaultCharset()
编码,如果不行靠ISO-8859-1
兜底,再不行就直接应用退出了。
Charset.defaultCharset的值取决于什么
看下JDK的代码不难发现先取file.encoding
指定的编码,不行就拿UTF-8
兜底。
做实验验证两个环境
写个很简单的代码在环境上执行一下:
public static void main(String[] args) {
System.out.println(System.getProperty("file.encoding"));
System.out.println(Charset.defaultCharset());
}
结果发现AB两个环境都是:
ANSI_X3.4-1968
US-ASCII
这就有点让人郁闷了,第一:应该是UTF-8才对啊,第二两个环境获取出来值的都一样,那么上面的String.getBytes()在两个环境上的结果也应该是一样的啊,应该将错就错啊。
查看两台机器的locale
A是:
$ locale
LANG=en_US.UTF-8
LC_CTYPE=UTF-8
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=
B是
$ locale
LANG=en_US.UTF-8
LC_CTYPE=UTF-8
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=
按理说都是UTF-8。
继续检查应用进程的环境变量,确认看看有没有-D通过虚拟参数指定,检查后,结果是没有。
检查进程相关的环境变量,检查后,结果是没有。
cat /proc/进程id/environ | tr '\0' '\n'
用locale charmap
命令确认AB两个环境:
得到的结果都是ANSI_X3.4-1968
因为某个奇怪的原因在农神和我一起看这个问题的时候,我用了mac自带的终端工具连了跳板机,跳板机再连到AB量环境。上面我都是用的iterm2连的跳板机,然后再连的环境。
在我用mac自带的终端工具连环境时发现用来验证的那段简单的代码输出结果不是ANSI_X3.4-1968了,而是:
UTF-8
UTF-8
我恍然大悟了,有问题那个环境,确实是去年冬天我用iterm2连接上然后手动重启的,没问的那个应用是ob3系统部署的。
看来问题是出在iterm2这个终端连接工具上了。
为iterm2会导致远端环境编码不对
ssh连接的时候开启-v选项
ssh -v xxx@yy.yyy.yyy.yy
发现iterm2连接时是:
debug1: Sending env LANG = en_US.UTF-8
debug1: Sending env LC_CTYPE = UTF-8
发现mac自带终端连接时是:
debug1: Sending env LANG = zh_CN.UTF-8
debug1: Sending env LC_CTYPE = zh_CN.UTF-8
差异就是在这,LC_CTYPE送的值不一样,导致覆盖操作系统LC_CTYPE的值不一样,导致locale charmap不能识别,导致操作系统采用默认编码ANSI_X3.4-1968。
查到官方issue在这里,iTerm2 LC_CTYPE locale issue on SSH connections,缺陷列表中@tsl0922 提到的版本新与我一致。 MacOS Mojave,iterm2 3.2.1。
为iterm2发送了不一样的LC_CTYPE,因为在有问题的那个版本组合下:
env |grep LC_CTYPE
LC_CTYPE = UTF-8
升级到官方最新版本3.2.7,问题解决。
终端工具还能影响应用的编码,之前完全没往这个方向考虑。