文档章节

一例 jvm file.encoding 属性引起的 MapReduce/HBase 乱码问题

大数据之路
 大数据之路
发布于 2013/08/28 03:38
字数 1680
阅读 2979
收藏 19

1、题:

最近在往 HBase 写中文的时候,发现 hbase 查出来的数据会有部分中文乱码了,而部分中文又是正常的,按理来说,一般的乱码问题要么全乱,要么不乱。考虑到出现中文的地方都是来源于 hdfs 上的一个配置文件,而这个配置文件可以确定是 utf-8 编码的,那排除了原始文件导致的乱码,想想 MR 代码里也没有转码的逻辑,也排除了代码的问题,那就只有一种可能:Hadoop 集群的系统环境是异构的,这里面可能涉及到 linux 、java 的环境变量、配置的问题。

2、排查:

(1)打印了整个集群的 echo $LANG、echo $LC_ALL 等linux系统变量,发现都是一致的,排除了 os 环境的问题。

(2)剩下的重点放在了 java 环境上,在代码里加上如下两句,打印每条记录的 ip 和 jvm 编码,然后看看乱码的记录是那台机器产生的,并且当时 jvm child 的编码情况:

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")));

同时也直接  System.out.println 出相应的中文字段,看是写进 hbase 之前还是之后乱掉的。

跑了一份测试数据后,发现 hbase 里的 ip、jvm 编码是没有规律的,然后查看 syso 打印的 log 发现,在写 hbase 之前已经就已经乱码了,然后想想 hbase 里的数据乱码之所以没有规律是因为 map 后要 shuffle、reduce 才能到 hbase。PS:sysout本身无编码概念,类似 linux 下的 cat、head、more 等。

然后再次把 ip、jvm编码 统计代码放到 map 阶段输出,果真发现了规律,集群中有两台机器的 jvm 编码不一致,不是 utf-8 的:

到这里我们可以知道原因了:由于集群中两台机器的 jvm 参数(file.encoding)不一致导致了部分中文结果的乱码。

3、解决方案:

知道原因了,那就看如何解决了,目的就是要改变 file.encoding 的值 。

(1)永久方案:

由于这个参数是 jvm 的启动参数,运行时不可被更改(你可以理解为这个参数是个全局参数,而且被缓存了,如果一旦运行时更改了, 可能会造成整个 jvm 里面的程序奔溃),你只能修改系统的charset, 或者jvm的启动参数里加上 -Dfile.encoding="UTF-8" 来指定,你运行时 setProperty("file.encoding","ISO-8859-1"); 这样是没用的,so,永久的解决办法是:啥时候把这两台机器offline 改编码后再online,然后再手动执行下 data balance。

或者可以在提交作业的时候设置作业参数: –Dmapred.child.env="LANG=en_US.UTF-8,LC_ALL=en_US.UTF-8"

(2)临时工方案:

不想这么大动干戈,想要临时解决方案,也行,那就需要在咱们自己的业务代码里绕开 jvm 提供的默认 file.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 从此形同陌路~ 

PS:FileReader 貌似没有提供指定编码的构造方法,所以换成了下面的类。

(3)疑问:

为什么之前一直都没乱码,而这次读文件却乱码了呢?

那是因为 hbase 的 Bytes、map 的 fileinputformat key/value、mapreduce 的 context.write 默认都是自己硬编码了 utf-8,做到了 和 jvm 编码无关,所以不会遇到上述问题。

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

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

恩,没听过正常,之前我也没听过哈~

(1)从源码开始追踪

在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 影响打印的编码

(2)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、Refer:

[1] 系统变量file.encoding对Java的运行影响有多大?

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

[2] Setting the default Java character encoding?

http://stackoverflow.com/questions/361975/setting-the-default-java-character-encoding

[3] Hadoop的map/reduce作业输入非UTF-8编码数据的处理原理

http://blogread.cn/it/article/3736?f=wb

[4] Hadoop集群字符集编码不一致导致Reduce重复记录问题排查

http://bit.ly/2w9JMeW

© 著作权归作者所有

大数据之路
粉丝 1588
博文 516
码字总数 340535
作品 0
武汉
架构师
私信 提问
加载中

评论(4)

王二铁
王二铁
最近也被这个坑了,指定了InputStreamReader(in,"UTF-8") 解决,楼主分析的很细致
10000011
10000011
这个坑太深了,之前都是在代码中指定UTF-8.
大数据之路
大数据之路 博主

引用来自“震秦”的评论

这个确实是个坑。
如果是纯Linux或者Windows还没这些问题。Windows和Linux搭配问题太多了。

我是纯linux,只是系统环境配置 op 没统一。生产环境不会用 windows 的,否则属于自找麻烦。
震秦
震秦
这个确实是个坑。
如果是纯Linux或者Windows还没这些问题。Windows和Linux搭配问题太多了。
HBase实践 | HBase ThriftServer Kerberos认证

1.前置 用户可以通过ThriftServer来访问HBase服务,它的特点如下: ThriftServer代理用户访问HBase服务返回操作结果,用户客户端不需要直接跟HBase进行通信 用户可以使用java/python/php/c++...

封神
2018/11/27
0
0
hadoop命令执行hbase应用jar包时的环境变量加载问题

问题描述 使用hadoop命令执行hbase应用jar包时,报如下错误: 问题分析 查看hadoop的classpath发现并无hbase的相关依赖jar包 尝试在在环境变量CLASSPATH中添加$HBASE_HOME/lib/*无效。 查看H...

Yulong_
2016/11/13
0
0
Hadoop2.x下安装HBase

环境:CentOS6.5 Hadoop2.5.2 HBase1.0.0 1.安装好 hadoop 集群,并启动 [grid@hadoop4 ~]$ sh hadoop-2.5.2/sbin/start-dfs.sh [grid@hadoop4 ~]$ sh hadoop-2.5.2/sbin/start-yarn.sh 查看......

张超
2015/03/19
0
3
hadoop--Hadoop生态上几个技术的关系与区别:hive、pig、hbase 关系与区别

Pig 一种操作hadoop的轻量级脚本语言,最初又雅虎公司推出,不过现在正在走下坡路了。当初雅虎自己慢慢退出pig的维护之后将它开源贡献到开源社区由所有爱好者来维护。不过现在还是有些公司在...

寒月谷
2018/08/02
0
0
关于学习Hadoop中未总结的资料

1)Cygwin相关资料   (1)Cygwin上安装、启动ssh服务失败、ssh localhost失败的解决方案   地址:http://blog.163.com/pwcrab/blog/static/16990382220107267443810/   (2)windows...

Carl_
2015/06/25
0
0

没有更多内容

加载失败,请刷新页面

加载更多

PostgreSQL build

https://buildfarm.postgresql.org/cgi-bin/show_log.pl?nm=lumpsucker&dt=2019-06-23%2020%3A57%3A27...

MtrS
今天
3
0
八、Docker Swarm

Docker Swarm有两件事:一个企业级的Docker主机安全集群,另一个是用于协调微服务应用程序的引擎。 在集群方面,它将一个或多个Docker节点组合在一起,并允许你将他们作为一个集群来管理。开...

倪伟伟
昨天
7
0
Fragment懒加载其实很简单

前言 记得去年面试的时候, 面了一家小公司, 那个面试官问我, fragment的懒加载做过吗?我说没做过(确实没做过).后来面试快结束了, 又问我, 懒加载没做过是吗?后来可想而知也没收到offer, (ಥ_...

天王盖地虎626
昨天
5
0
聊聊dubbo的TimeoutFilter

序 本文主要研究一下dubbo的TimeoutFilter ListenableFilter dubbo-2.7.2/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/ListenableFilter.java public abstract class Liste......

go4it
昨天
9
0
方法与数组

方法 方法就是完成特定功能的代码块;在很多语言里面都有函数的定义,函数在Java中被称为方法 格式: 修饰符 返回值类型 方法名(参数类型 参数名1,参数类型 参数名2…) throw 异常{ 函数体;...

凹凸凸
昨天
10
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部