zoukankan      html  css  js  c++  java
  • 彻底解决Solr日期类型的时区问题

    彻底解决Solr日期类型的时区问题

    声明

    1. 文档是基于Solr6.6写的
    2. Solr是部署在Tomcat上的
      3.Tomcat是部署在CentOS上的,不过Linux、Windows差不多
    3. 文章的问题的最终解决是用第四种方式,前三种想看看看,不想看可以直接看第四种方式
    4. 本文使用的最终解决方案适用于Solr4到Solr6.6,Solr7没有试过

    问题描述

    Solr的日期类型是基于UTC时间的,也就是英国的格林尼治天文台的时间,而我们中国在东八区,用的是GMT时间,比UTC时间多了8个小时。真不知道开发Solr的程序员是怎么想的,全世界的人用Solr,不管你是在哪儿的,都得用UTC时间,太TMD坑了。
    这个问题前前后后搞了一周了,总结起来有有四种解决思路,本文最终采用的是第四种方案,这里前三种方案也说一下。

    解决方案

    方案1:修改配置文件的时区改为东8区

    先说下这种方法不可行
    这种方式网上搜到的最多,大致意思是说在Tomcat的setenv.sh文件里添加配置时区为为东八区:

    -Duser.timezone=Asia/Shanghai
    

    我这边配置过以后重启Tomcat发现日志的时间是过来了,但是往Solr里写数据和从Solr里读数据不是一样少了8个小时,看下Solr的源码你就知道怎么回事儿了:
    org.apache.solr.response.TextResponseWriter

    public void writeDate(String name, Date val) throws IOException {
      writeDate(name, val.toInstant().toString());
    }
    

    这个是Solr写入日期类型的数据的方法,看源码我们就发现问题了:java.util.Date类的toInstant()方法返回的就是一个UTC时间,你在Tomcat的配置文件里改,当然不起作用啦

    方案2:不用Solr的日期类型,直接用String代替

    这种方式确实可以很多人用,我们之前的时候就是用这种方式,不过这不是我们讨论的范围,直接Pass掉
    下面两种方式都是修改Solr的源码

    方案3:修改数据类型源码,不使用UTC时间

    修改TrieDateField类(DatePointField,DateRangeField)的源码
    参考文章:
    Solr Date类型的哪些你不得不了解的细节
    文章的作者对Solr的日期类型讲解得非常详细,
    按作者写的进行操作的时候,发现不太好操作,于是就联系到文章的作者,交流了一下,发现该博客的作者用的Solr是6.5的版本,源码有些不一样(我用的是Solr6.1),幸运的是作者人非常非常好,非常非常耐心(没错4个非常)地回复了我所有的疑问,直到把问题解决。这种方式这里就不再多说了,需要的话可以直接点开作者的博客
    不过问题虽然解决了,但是要修改好几处地方,而且如果Solr再版本升级的话说不定需要修改的地方又变了,Solr6.1和Solr6.5两个版本需要修改的地方就不一样。
    有没有更好的办法呢

    方案4:修改工具类源码,使UTC时间与本地时间显示得一致(最终方案)

    此方案只需要修改一个类的两个地方(读、写的方法),所有的日期类型全部搞定
    大致思路是:
    UTC时间不是比东八区少了8个小时吗?在往Solr里写数据的时候加上8个小时,从Solr里查数据的时候减去8个小时,这样UTC时间显示得就和咱大中国的一致了。
    操作是:
    修改JavaBinCodec工具类
    具体路径是solr-core-6.6.jar包里的org.apache.solr.common.util.JavaBinCodec
    把这个类的源码复制出来,包名类名不要变,修改readObject()和writePrimitive()方法具体如下:
    readObject()方法:

    switch (tagByte) {
        case NULL:
            return null;
        case DATE:
            //存储的时候solr的时间格式是utc的会少8个小时, 所以在writeVal方法里加上了8小时
            // 这里再读取的时候就需要再减去8个小时
            // 28800000l为8小时的毫秒数
            return new Date(dis.readLong() - 28800000l);
        case INT:
            return dis.readInt();
    ...
    

    writePrimitive()

    else if (val instanceof Date) {
                daos.writeByte(DATE);
                //UTF时间比东8区少了8个小时,这里加8小时
                daos.writeLong((((Date) val).getTime() / 1000) * 1000 + 28800000l);
                return true;
            } else if (val instanceof Boolean) {
    ...
    

    然后打包,扔到Tomcat里,并重启Tomcat
    Schema.xml的相关配置:

    <!-- 当前时间与另外的三种类型做对比 -->
    <field name="cur_date" type="string" indexed="false" stored="true"/>
    <!-- DateField日期类型 -->
    <field name="import_time" type="date" indexed="true" stored="false" docValues="true"/>
    <!-- DatePointField日期类型 -->
    <field name="capture_time" type="pdate" indexed="true" stored="false" docValues="true"/>
    <!-- DateRangeField日期类型 -->
    <field name="work_time" type="rdate" indexed="true" stored="true"/>
    ...
    <fieldType name="date" class="solr.TrieDateField" docValues="true" precisionStep="0" positionIncrementGap="0"/>
    <fieldType name="pdate" class="solr.DatePointField" docValues="true"/>
    <fieldType name="rdate" class="solr.DateRangeField" docValues="false" />
    
    解决Solr的时区问题-Scham配置文件1.png
    解决Solr的时区问题-Schema配置文件2.png

    下面是往Solr写测试数据的部分代码:
    往Solr里写数据的代码:

    SolrInputDocument doc = new SolrInputDocument();
    doc.addField("cur_date", new SimpleDateFormat("yyyy-MM-dd HH:MM:SS").format(new Date()));
    doc.addField("import_time", new Date());
    doc.addField("capture_time", new Date());
    doc.addField("work_time", new Date());
    ...
    

    从Solr里读取数据的代码:

    System.out.println("import_time 	" +  doc.get("import_time"));
    System.out.println("capture_time 	" + doc.get("capture_time"));
    System.out.println("work_time 	" + doc.get("work_time"));
    

    说明一下:
    import_time的类型是TrieDateField
    capture_time的类型是DatePointField
    work_time的类型是DateRangeField

    下面是从Solr里查出出来的结果

    解决Solr的时区问题-Solr管理界面查询结果.png

    通过Java API查询结果:

    解决Solr的时区问题-Java API查询结果.png


    对比发现work_time、import_time、capture_time与cur_date日期是一致的
    问题解决

    参考文章是:Hadoop技巧(04):简易处理solr date 时区问题

    最后

    看了下这篇博客的作者用的Solr版本是Solr4的,我用的Solr版本是6.6的,用同样的方式解决都没有问题,所以如果你用的是在这之间的Solr版本都是没有问题的

  • 相关阅读:
    VOA 2009/11/02 DEVELOPMENT REPORT In Kenya, a Better Life Through Mobile Money
    2009.11.26教育报道在美留学生数量创历史新高
    Java中如何实现Tree的数据结构算法
    The Python Tutorial
    VOA HEALTH REPORT Debate Over New Guidelines for Breast Cancer Screening
    VOA ECONOMICS REPORT Nearly Half of US Jobs Now Held by Women
    VOA ECONOMICS REPORT Junior Achievement Marks 90 Years of Business Education
    VOA 2009/11/07 IN THE NEWS A Second Term for Karzai; US Jobless Rate at 10.2%
    Ant入门
    Python 与系统管理
  • 原文地址:https://www.cnblogs.com/cuihongyu3503319/p/13833328.html
Copyright © 2011-2022 走看看