Tomcat服务器中奇怪的间歇性字符编码行为

时间:2012-12-21 11:44:15

标签: java tomcat encoding character intermittent

在我们的生产环境中,我们不时遇到Tomcat中一个非常奇怪的编码问题。

我还没有确切地指出问题发生在代码中的确切位置,但它涉及将非ascii字符替换为近似的ascii字符。

例如用'a'替换字符'å'。由于该网站是瑞典语,因此字符“å”,“ä”和“ö”非常常见。但由于某种原因,'ö'字符的替换总是有效,所以像“Köepinte grisenisäcken”这样的字符串变成了“Kop inte grisenisäcken”,即'ä'没有被取代,而'' ö'的角色是。

关于这个问题的一些快速事实:

  • 它很少发生(我们已经注意到它3-4次,第一次可能是1 - 2年前)。

  • 重启故障服务器会导致问题消失(直到下一次)。

  • 它从未在同一时间出现在多个前端服务器上。

  • 并不总是发生在同一台前端服务器上。

  • 不涉及前端的用户输入。

  • 所有前端服务器都连接到同一个CMS和DB,相关配置相同。

  • 所有前端服务器都具有相同的相关配置(linux配置,tomcat配置,java环境配置,如“file.encoding”等),并使用相同的脚本启动(全部根据托管/服务提供商) )。

  • 所有前端服务器都使用与网站相同的war文件和相同的jar文件。

  • 发生此字符替换问题时,网站上没有其他编码问题。

  • 我们无法在任何其他环境中重现此问题。

由于CMS要求,我们使用Tomcat 5.5和Java 5.

我只能想到这种行为的两个可能的原因:

  1. 托管服务提供商有时会以不同的方式启动/重新启动前端服务器,可能使用其他用户帐户和其他环境变量或其他文件访问权限,或者使用其他脚本而不是正常的脚本。

  2. 在Tomcat或webapp启动期间运行的某些进程取决于其他一些进程,有时(间歇性但很少)这两个(或更多)进程碰巧以导致此编码缺陷的顺序运行。

  3. 但即使上面有1或2,但它仍然没有完全解释究竟发生了什么。究竟有什么区别可以解释这个?由于所有“file.encoding”,“file.encoding.pkg”“sun.io.unicode.encoding”,“sun.jnu.encoding”和所有其他相关环境变量都在所有前端机器上匹配(使用视觉验证)调试页面,但问题出现了。)

    有人会想到这种奇怪的间歇性行为的一些似是而非的解释吗?简单地升级Tomcat和/或Java版本并不是一个真正的相关答案,因为我们真的不知道这是否能解决问题,而且它仍然无法解释问题所在。我更感兴趣的是要确切地了解问题是由什么造成的。

    此致 /吉米亨德里

    更新:

    我想我找到了执行字符替换的代码。在启动时(由第一次执行替换调用触发),它构建一个HashMap< Character,String>,并像这样填充它:

    lookup.put(new Character('å'), "a");  
    

    然后当它应该替换字符串的字符时,它会循环遍历每个字符,并且每个字符都在哈希映射中以charactar作为键进行查找,如果找到替换字符串则使用它,否则原始字符串使用字符被使用。

    这部分代码已超过3年,由一位早已离开的开发人员编写。如果我今天要重写这段代码,我会做一些完全不同的事情,甚至可能解决问题。但它仍然无法准确解释究竟发生了什么。有人能看到一些可能的解释吗?

1 个答案:

答案 0 :(得分:1)

在进行替换之前,将输入标准化为普通表格C.

例如,ä可以只有1个字符U+00E4,也可以是两个字符,aU+0061)和组合分音{{3} }。

如果您的替代品只是查找撰写的表单,那么分解的表单仍将保留为\u0061\u0308,因为这些表单都不匹配\u00e4

public static void main(String args[]) {
    String decomposed = "\u0061\u0308";
    String composed = "\u00e4";

    System.out.println(decomposed);
    System.out.println(composed);
    System.out.println(composed.equals(decomposed));
    System.out.println(Normalizer
            .normalize(decomposed, Normalizer.Form.NFC).equals(composed));

}

输出

ä
ä
false
true