最近在往 HBase 写中文的时候,发现 hbase 查出来的数据会有部分中文乱码了,而部分中文又是正常的,按理来讲,通常的乱码问题要么全乱,要么不乱。考虑到出现中文的地方都是来源于 hdfs 上的一个配置文件,而这个配置文件能够肯定是 utf-8 编码的,那排除了原始文件致使的乱码,想一想 MR 代码里也没有转码的逻辑,也排除了代码的问题,那就只有一种可能:Hadoop 集群的系统环境是异构的,这里面可能涉及到 linux 、java 的环境变量、配置的问题。java
(1)打印了整个集群的�0�2echo $LANG、echo $LC_ALL 等linux系统变量,发现都是一致的,排除了 os 环境的问题。linux
(2)剩下的重点放在了 java 环境上,在代码里加上以下两句,打印每条记录的 ip 和 jvm 编码,而后看看乱码的记录是那台机器产生的,而且当时 jvm child 的编码状况:apache
java.net.InetAddress test = java.net.InetAddress.getByName("localhost"); put.add(Bytes.toBytes("cf"), Bytes.toBytes("ip"), Bytes.toBytes(test.getLocalHost().getHostAddress())); put.add(Bytes.toBytes("cf"), Bytes.toBytes("ec"), Bytes.toBytes(System.getProperty("file.encoding")));
同时也直接�0�2
System.out.println 出相应的中文字段,看是写进 hbase 以前仍是以后乱掉的。 缓存
跑了一份测试数据后,发现 hbase 里的 ip、jvm 编码是没有规律的,而后查看�0�2syso 打印的 log 发现,在写 hbase 以前已经就已经乱码了,而后想一想 hbase 里的数据乱码之因此没有规律是由于 map 后要 shuffle、reduce 才能到 hbase。PS:sysout自己的编码是根据file.encoding来的。 多线程
而后再次把 ip、jvm编码 统计代码放到 map 阶段输出,果然发现了规律,集群中有两台机器的 jvm 编码不一致,不是 utf-8 的: 并发
到这里咱们能够知道缘由了:因为集群中两台机器的 jvm 参数(file.encoding)不一致致使了部分中文结果的乱码。 jvm
知道缘由了,那就看如何解决了,目的就是要改变 file.encoding 的值�0�2。 函数
因为这个参数是 jvm 的启动参数,运行时不可被更改(你能够理解为这个参数是个全局参数,并且被缓存了,若是一旦运行时更改了, 可能会形成整个 jvm 里面的程序奔溃),你只能修改系统的charset, 或者jvm的启动参数里加上 -Dfile.encoding=”UTF-8″ 来指定,你运行时�0�2setProperty(“file.encoding”,”ISO-8859-1″); 这样是没用的,so,永久的解决办法是:啥时候把这两台机器offline 改编码后再online,而后再手动执行下 data balance。 oop
不想这么大动干戈,想要临时解决方案,也行,那就须要在我们本身的业务代码里绕开 jvm 提供的默认�0�2file.encoding 编码,本身指定编码:
BufferedReader in = new BufferedReader(new FileReader(path.toString())); 换成: BufferedReader in = new BufferedReader((new InputStreamReader(new FileInputStream(path.toString()),"utf-8")));
上面一句是我以前乱码的代码,若是你没有指定读取编码,那么 jvm 会使用本身的 file.encoding,这样就会形成在某些机器上读取文件就乱掉了。下面一句是本身指定编码,这样绕开了 jvm 的默认编码,与 jvm 今后形同陌路~�0�2
PS:FileReader 貌似没有提供指定编码的构造方法,因此换成了下面的类。
为何以前一直都没乱码,而此次读文件却乱码了呢?
那是由于 hbase 的 Bytes、map 的�0�2fileinputformat key/value、mapreduce 的�0�2context.write 默认都是本身硬编码了 utf-8,作到了 和 jvm 编码无关,因此不会遇到上述问题。
上面说了这么多,可能有同窗仍是不大明白:jvm 的这参数有毛用啊?为毛以前都没听过这玩意呢?
恩,没听过正常,以前我也没听过哈~
在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的静态构造方法,�0�2
影响到的方法 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 影响打印的编码
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.
系统变量file.encoding对Java的运行影响有多大?
http://www.blogjava.net/ivanwan/archive/2011/01/31/343810.html
Setting the default Java character encoding?
http://stackoverflow.com/questions/361975/setting-the-default-java-character-encoding