jvm file.encoding 属性引起的storm/hbase乱码

1. 问题

  今天为storm程序添加了一个计算bolt,上线后正常,结果发现之前的另一个bolt在将中文插入到hbase中后查询出来乱码。其中字符串是以UTF-8编码的url加密串,然后我使用的URLDecoder.decode(str, "UTF-8")解码,最后插入到hbase中。

2. 排查

(1)hbase中的数据传输都是使用的UTF-8,因此肯定不会出问题,故排除hbase端的问题;

(2)既然在测试的时候没乱码,线上却乱码,想到肯定是线上机子jvm环境的问题;

(3)确定了是jvm环境的问题,再一想URLDecoder.decode(str, "UTF-8")这句解码肯定用的是UTF-8,如果str编码是UTF-8的解出来当然就不会乱了,于是明确str在jvm中被用非UTF-8编码了;

(4)排查线上那台机子的jvm默认编码

  首先,打印了 echo $LANG、echo $LC_ALL 等linux系统变量,发现都是一致的UTF-8,排除了 os 环境的问题。

  然后,重点放在了 java 环境上,使用System.getProperty("file.encoding");打印jvm的默认编码,结果出来的是:en_US。

到这里我们可以知道原因了:由于线上那台机子的 jvm 参数(file.encoding)不一致导致了中文的乱码。

3. 解决方案

  知道原因了,解决起来就简单了,目标就是改变JVM file.encoding参数的值。

  由于这个参数是jvm的启动参数,运行时不可更改(你可以理解为这个参数是个全局参数,而且被缓存了,如果一旦运行时更改了, 可能会造成整个 jvm 里面的程序奔溃)。

  (1)临时方案

    jvm的启动参数里加上-Dfile.encoding="UTF-8"来指定。

  (2)一劳永逸方案

    修改系统的charset,linux的字符集在/etc/sysconfig/i18n文件中设置,下面是我的机子默认设置: 

LANG="en_US"
SYSFONT="latarcyrheb-sun16"

    有两种修改方式:1.将/etc/sysconfig/i18n中的LANG修改为LANG="en_US.UTF-8",修改后需要重启机子才能生效;2.在/etc/profile中添加export LANG=en_US.UTF-8,然后source /etc/profile即可生效。

4. 疑问

  为何之前这个bolt一直正常?因为storm对bolt的分配是自己控制的(对用户而言相当于随机分配到不同的节点),之前这个bolt分配到的那个机子的jvm编码设置的为en_US.UTF-8,故不会出现问题。

5. 深入理解 jvm 的 -Dfile.encoding 参数

  上面说了这么多,可能有同学还是不大明白:jvm 的这参数有啥用啊?为啥之前都没听过这玩意呢?恩,没听过正常,之前我也没听过哈~

   (1) JVM编码原理

    jvm内部的(字节码)编码方式为unicode,编码和解码过程为:(1)编码:首先将字符串使用jvm默认的编码方式(也可以手动指定)转换为unicode存储到内存中;(2)解码:然后就unicode编码的字符串解码为用户指定的编码字符串。因此,只要保证编码和解码两端的字符集编码方式一致就不会出现乱码。

  (2)查询源码

  在JDK 1.6.0_20的src.zip文件中,查找包含file.encoding字眼的文件,共找到4个:
  (a)先上重头戏 java.nio.Charset类:

public static Charset defaultCharset() {
        if (defaultCharset == null) {
            synchronized (Charset.class) {
                java.security.PrivilegedAction pa = new GetPropertyAction("file.encoding");
                String csn = (String) AccessController.doPrivileged(pa);
                Charset cs = lookup(csn);
                if (cs != null)
                    defaultCharset = cs;
                else
                    defaultCharset = forName("UTF-8");
            }
        }
        return defaultCharset;
    }

  在java中,如果没有指定charset的时候,比如new String(byte[] bytes), 都会调用Charset.defaultCharset()的方法,我们可以清楚的看到defaultCharset是只能被初始化一次,这里还是有点小问题的,在多线程并发调用的时候还是会初始话多次,当然后面都是从cache(lookup的函数)里读出来的,问题也不大。
当我们在改变System.getProperties里的file.encoding 的时候,defaultCharset已经被初始化过了,所以不会在调用初始化的代码。
当jvm 启动的时候,load class, 最后调用main函数之前,defaultCharset已经初始化好,而很多函数里都掉用了这个方法象String.getBytes, 还有 InputStreamReader, InputStreamWriter 都是调用了 Charset.defaultCharset()的方法。

  (b)java.net.URLEncoder的静态方法,  影响到的方法 java.net.URLEncoder.encode(String)

    恩,这里也需要注意,之前已经有同学掉坑里去了,请使用:encode(String s, String enc) 方法

  (c)com.sun.org.apache.xml.internal.serializer.Encoding的getMimeEncoding方法(209行起)

  (d)最后一个javax.print.DocFlavor类的静态构造方法

  可以看到,系统变量file.encoding影响到
  1. Charset.defaultCharset() Java环境中最关键的编码设置
  2. URLEncoder.encode(String) Web环境中最常遇到的编码使用
  3. com.sun.org.apache.xml.internal.serializer.Encoding 影响对无编码设置的xml文件的读取
  4. javax.print.DocFlavor 影响打印的编码

  (3)Java‘s file.encoding property on Windows platform

This property is used for the default encoding in Java, all readers and writers would default to use this property. “file.encoding” is set to the default locale of Windows operationg system since Java 1.4.2. System.getProperty(“file.encoding”) can be used to access this property. Code such as System.setProperty(“file.encoding”, “UTF-8”) can be used to change this property. However, the default encoding can not be changed dynamically even this property can be changed. So the conclusion is that the default encoding can’t be changed after JVM starts. “java -Dfile.encoding=UTF-8” can be used to set the default encoding when starting a JVM. I have searched for this option Java official documentation. But I can’t find it.

5. 参考文章

(1)系统变量file.encoding对Java的运行影响有多大

  http://www.blogjava.net/ivanwan/archive/2011/01/31/343810.html

(2)linux查看系统和修改系统编码

  http://www.poluoluo.com/server/201401/258604.html

(3)en_US.UTF-8和zh_CN.UTF-8的区别

  http://www.iteye.com/problems/90396

时间: 2024-08-06 13:30:41

jvm file.encoding 属性引起的storm/hbase乱码的相关文章

系统属性file.encoding在JVM启动后,再次设置无法对系统的默认编码造成影响

原因和解决方法: https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4163515 https://stackoverflow.com/questions/361975/setting-the-default-java-character-encoding/362006#362006 file.encoding由谁设置? 首先file.encoding是一个系统属性.可以在启动JVM 前设置 如: java -Dfile.encoding=

kafka+storm+hbase

kafka+storm+hbase实现计算WordCount. (1)表名:wc (2)列族:result (3)RowKey:word (4)Field:count 1.解决: (1)第一步:首先准备kafka.storm和hbase相关jar包.依赖如下: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance&qu

系统变量file.encoding对Java的运行影响有多大?(转)good

这个话题来自: Nutz的issue 361 在考虑这个issue时, 我一直倾向于使用系统变量file.encoding来改变JVM的默认编码. 今天,我想到, 这个系统变量,对JVM的影响到底有多大呢? 我使用最简单的方法看看这个变量的影响--在JDK 1.6.0_20的src.zip文件中,查找包含file.encoding字眼的文件. 共找到4个, 分别是: 先上重头戏 java.nio.Charset类: public static Charset defaultCharset() {

maven File encoding has not been set

原pom.xml配置文件: <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org

eclipse的使用-------Text File Encoding没有GBK选项的设置

2013-12-25 09:48:06 标签:java myeclipse使用 有一个项目是使用GBK编码的,在导入到Myeclipse10之后,由于我整个eclipse环境是UTF-8编码的,从而导致中文乱码. 解决办法: 右键项目->Properties->Text File Encoding中没有GBK这个选项.此时你可以手动输入编码方式,如输入GBK,然后点确定,编码格式就成为GBK了,解决了中文乱码问题.

how to solve &quot;[WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent!&quot;

[WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent! Saw this warning message when using failsafe maven plugin, found the fix after a little search. add following to pom.xml <properties> <pro

Jenkins maven 构建乱码,修改file.encoding系统变量编码为UTF-8

一切都是windows的控制台默认编码GBK问题 情景: 使用jenkins构建,console 输出的中文乱码.代码编码格式是utf-8,因为Jenkins会默认读取当前系统的编码格式,导致构建日志乱码和selenium自动化测试输入的中文乱码. 控制台输出乱码 摸索 不能忍,果断百度一下,按照设置全局配置那里设置LANG :zn_CH.utf-8 无效. 在jenkins下的jenkins.xml设置什么启动为utf-8也是无效. 但是查找资料期间发现,jenkins系统管理的系统信息  想

解决maven打包编译出现File encoding has not been set问题

maven打包编译时后台一直输出警告信息 [WARNING] File encoding has not been set, using platform encoding GBK, i.e. build is platform dependent! 找了半天,原来只要在pom.xml文件中增加一个配置项即可 <properties>         <project.build.sourceEncoding>UTF-8</project.build.sourceEncodi

【转】Struts1.x系列教程(3):属性(资源)文件乱码问题的解决之道

转载地址:http://www.blogjava.net/nokiaguy/archive/2009/01/archive/2009/01/archive/2009/01/14/251244.html 资源文件不仅仅需要保存英文信息,在很多时候,也需要保存其他的语言信息,如中文信息.但Java的属性文件只支持英文,要想在属性文件中保存中文,需要使用"uxxxx格式,但这非常不方便,因此,本文将分别介绍如何使用native2ascii.exe和Eclipse插件的方式来解决这个问题. 一.使用na