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的默认编码,结果出来的是:ISO-8859-1。

到这里我们可以知道原因了:由于线上那台机子的 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

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

  1. Java file.encoding

    1. file.encoding属性的作用 file.encoding 的值是整个程序使用的编码格式. 可以使用  System.out.println(System.getProperty(&quo ...

  2. file.encoding到底指的是什么呢?

    转载请注明来源:http://blog.csdn.net/loongshawn/article/details/50918506 <Java利用System.getProperty(“file. ...

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

    这个话题来自: Nutz的issue 361 在考虑这个issue时, 我一直倾向于使用系统变量file.encoding来改变JVM的默认编码. 今天,我想到, 这个系统变量,对JVM的影响到底有多 ...

  4. Flume+Kafka+Storm+Hbase+HDSF+Poi整合

    Flume+Kafka+Storm+Hbase+HDSF+Poi整合 需求: 针对一个网站,我们需要根据用户的行为记录日志信息,分析对我们有用的数据. 举例:这个网站www.hongten.com(当 ...

  5. kafka+storm+hbase

    kafka+storm+hbase实现计算WordCount. (1)表名:wc (2)列族:result (3)RowKey:word (4)Field:count 1.解决: (1)第一步:首先准 ...

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

    eclipse的使用-------Text File Encoding没有GBK选项的设置 2013-12-25 09:48:06 标签:java myeclipse使用 有一个项目是使用GBK编码的 ...

  7. readAsDataURL(file) & readAsText(file, encoding)

      readAsDataURL(file)会把文件内容转换为data类型的URL: data:text/plain;base64,b3JkZXItaWQJb3JkZXItaXRlbS1p... 这种d ...

  8. Storm+HBase实时实践

    1.HBase Increment计数器 hbase counter的原理: read+count+write,正好完成,就是讲key的value读出,若存在,则完成累加,再写入,若不存在,则按&qu ...

  9. Android studio 配置file encoding 无效,中文乱码解决办法

    通过配置Android studio 配置file encoding 无效,中文乱码,问题出现在java编译的时候jack采用了默认编码(中文windows默认的GBK编码)而乱码,所以不管更改bui ...

随机推荐

  1. 将Cocos2d-x游戏打包成Android应用程序

    1. 打开Eclipse(已经装好CDT.ADT和NDK),导入cocos2d-x的Android项目. 2. 导入后java的源码会出现编译错误,打开cocos2d-x引擎的根文件夹\cocos2d ...

  2. 如何修改eclipse的默认字符集和修改中文乱码

    转载,以供以后学习.谢谢 有时候 java代码,导入eclipse中会出现 乱码的问题,通过修改字符集就可以解决. 看下面图片演示过程. 发表在 使用教程 | 标签为 eclipse, 乱码 | 留下 ...

  3. Chrome自带恐龙小游戏的源码研究(完)

    在上一篇<Chrome自带恐龙小游戏的源码研究(七)>中研究了恐龙与障碍物的碰撞检测,这一篇主要研究组成游戏的其它要素. 游戏分数记录 如图所示,分数及最高分记录显示在游戏界面的右上角,每 ...

  4. mysql中把空值放在最后,有值的数据放在前面

    order by column is null,column; 如果:order by column,则column中空值的数据放在最前面,有数据的放在后面

  5. 分布式服务框架 Zookeeper(一)介绍

    一.概述 ZooKeeper(动物园管理员),顾名思义,是用来管理Hadoop(大象).Hive(蜜蜂).Pig(小猪)的管理员,同时Apache Hbase.Apache Solr.LinkedIn ...

  6. Linux模块机制浅析_转

    Linux模块机制浅析 转自:http://www.cnblogs.com/fanzhidongyzby/p/3730131.htmlLinux允许用户通过插入模块,实现干预内核的目的.一直以来,对l ...

  7. EasyUI触发方法、触发事件、创建对象的格式??

    创建对象 $("选择器").组件名({ 属性名 : 值, 属性名 : 值 }); 触发方法 $("选择器").组件名("方法名",参数); ...

  8. testVC.modalPresentationStyle = UIModalPresentationFormSheet; 更改 VC大小

    本文转载至 http://www.cocoachina.com/bbs/simple/?t31199.html TestViewController *testVC = [[TestViewContr ...

  9. Devexpress Spreadsheet 中文教程

    http://blog.csdn.net/hotmee/article/details/50554381

  10. 九度OJ 1078:二叉树遍历 (二叉树)

    时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:3748 解决:2263 题目描述: 二叉树的前序.中序.后序遍历的定义: 前序遍历:对任一子树,先访问跟,然后遍历其左子树,最后遍历其右子树 ...