美文网首页
彻底解决Solr日期类型的时区问题

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

作者: 荒野雄兵 | 来源:发表于2018-01-25 11:58 被阅读0次

    声明

    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 \t" +  doc.get("import_time"));
    System.out.println("capture_time \t" + doc.get("capture_time"));
    System.out.println("work_time \t" + 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版本都是没有问题的
    有用就点个赞吧

    相关文章

      网友评论

          本文标题:彻底解决Solr日期类型的时区问题

          本文链接:https://www.haomeiwen.com/subject/trhpaxtx.html