Ticket #66 (closed Bug: fixed)

Opened 14 years ago

Last modified 14 years ago

mongo硬缓存GZIP数据存取异常

Reported by: huangxianduan Owned by: huangxianduan
Priority: major Milestone:
Component: 报价库 Version: 报价库4.0
Keywords: GZIP Cc:
Due Date: 15/11/2011

Description (last modified by huangxianduan) (diff)

异常信息

[08-30 10:38:42.841] 2011-08-30 10:38:42,841 [http-app-a-8081-5887$1521484495] ERROR cn.com.pconline.core.r.MongoRClientHelper - [M
ongoRClientHelper] get  cache:http://pdlib.pconline.com.cn/product/service2011/product_forum.jsp?pid=431809&style=1&time=7200 fail,me
ssage:java.io.IOException: Not in GZIP format
定位是R存/取mongo硬缓存时对较长的数据做GZIP压缩/解压有问题

排查及解决

  1. 测试R写硬缓存数据GZIP压缩是否有问题(报价前台是要求内容超过1024个字),测试GZIP压缩是没有问题的。
  2. 测试R读取硬缓存GZIP数据解压是否有问题(mongo中保存了内容有保存了是否是压缩状态),测试如果数据标识了是GZIP的,读取解压也正常。
  3. 怀疑是不是GZIP的API有bug呢,连续的用1做GZIP压缩写到mongo中,再用2解压读取出来,也是没有问题的。
  4. 对单条数据进行多次的压缩和解压测试,每次内容不一样,有时长有时短,这个偶尔会重现异常,有些眉目了,怀疑是特殊字符的问题,但用报错的内容重新压缩再读取又没问题了,还是悲剧,不能定位到问题在哪。
  5. 实在没办法,还是返回头仔细检查代码的写入和读取的判断是否一致,最后在代码上发现了写的判断有问题,代码如下:
    public void setContent(byte[] content) {
            if (content != null){
                    if(content.length > ZIP_SIZE) {//内容长度大于1024,请注意并没有else
                            ByteArrayOutputStream bos = null;
                            GZIPOutputStream gzip = null;
                            try {
                                    bos = new ByteArrayOutputStream();
                                    gzip = new GZIPOutputStream(bos);
                                    gzip.write(content);
                                    gzip.flush();
                                    gzip.finish();
                                    gzip.close();
                                    bos.flush();
                                    content = bos.toByteArray();
                                    this.zip = true;//压缩,保存到数据库做标记
                            } catch (IOException e) {
                                    e.printStackTrace();
                                    this.zip = false;
                            } finally {
                                    try {
                                            if (bos != null) {
                                                    bos.close();
                                            }
                                    } catch (IOException e) {
                                    }
                            }
                    }//为什么没有else呢???
                    this.size = content.length;
            }
            this.content = content;
    }
    再来看看读取的方法
    public byte[] getContent() {
            if (!zip) {
                    return content;
            } else {//只要数据库标识是GZIP的就用GZIP解压读取
                    byte[] result = null;
                    ByteArrayInputStream bis = null;
                    GZIPInputStream gzip = null;
                    ByteArrayOutputStream baos = null;
                    try {
                            bis = new ByteArrayInputStream(content);
                            gzip = new GZIPInputStream(bis);
                            byte[] buf = new byte[1024];
                            int num = -1;
                            baos = new ByteArrayOutputStream();
                            while ((num = gzip.read(buf, 0, buf.length)) != -1) {
                                    baos.write(buf, 0, num);
                            }
                            baos.flush();
                            result = baos.toByteArray();
                    } catch (Exception ex) {
                            throw new RuntimeException(ex);
                    } finally {
                            try {
                                    if (gzip != null) {
                                            gzip.close();
                                    }
                                    if (bis != null) {
                                            bis.close();
                                    }
                                    if (baos != null) {
                                            baos.close();
                                    }
                            } catch (Exception e) {
                            }
                    }
                    return result;
            }
    }
    
  • 问题很明显了,当第一次写入的数据长度大于1024的以GZIP格式写入,但第二次再更新写入是数据长度小于1024是却没有改变GZIP的状态,这样导致读取是还是当做GZIP格式来解压读取,肯定是会报格式不对的异常的。找到了问题所在,解决问题却是这么简单,就是加上个else {this.zip = false;}语句。

总结

  1. 在处理这个问题时,排查问题可能走了些弯路,要是一开始就认真的从代码的存取逻辑一致行来入手可能就很容易找到问题
  2. 在写代码时有分支语句的时候要多考虑下分支是否可以省略

Change History

comment:1 Changed 14 years ago by huangxianduan

  • Description modified (diff)

comment:2 Changed 14 years ago by huangxianduan

  • Status changed from new to closed
  • Resolution set to fixed
  • Description modified (diff)
Note: See TracTickets for help on using tickets.