项目过程中的找bug总结

来源:互联网 发布:sage数据库 编辑:程序博客网 时间:2024/04/27 21:43

经历了近3个月的奋战,终于和一帮兄弟姐妹们完成了退款一期项目的改造工程。项目过程还是蛮辛苦的,免不了加班加点的了。在这里感谢下项目组成员的付出。不过项目发布后的反响还是不错的。这个也是项目组成员值得欣慰的事。闲话少说,进入正题。在这里纪录下项目过程中几个比较突出问题的排查。

问题一:

先说说系统上的一个技术点。因为退款系统里面的各种规则众多,比如各种情况下的不同超时时间设置,各种情况下的不同导航提示,各种情况下的退款原因提示等等。 为了统一的维护这些规则,所以我们采用了一个类似规则引擎的处理方式。我们采用excel表格的形式,定义了各种组合状态下的规则,好处是对于规则的整体维护和理解非常的方便,缺点在svn 的管理上不是很好对比,但是我们补充完善了这块的单元测试防止人工编辑的失误。对于这个规则的存储和检索,我们采用了hsqldb的内存数据库,在系统启动的时候加载。

问题就出在和内存数据库的关系上了。在进行压力测试的时候,抛出来异常如下:

java.sql.SQLException: Column not found: TMS_LINK
at org.hsqldb.jdbc.Util.sqlException(Unknown Source)
at org.hsqldb.jdbc.Util.sqlException(Unknown Source)
at org.hsqldb.jdbc.jdbcResultSet.findColumn(Unknown Source)
at org.hsqldb.jdbc.jdbcResultSet.getString(Unknown Source)
at com.taobao.refund.web.HyperSQLDBEngine.queryTMS(HyperSQLDBEngine.java:85)

期间有怀疑过是否HyperSQL 的连接方式使用上有问题,等等。翻阅了下 HyperSQL相关的文档未果。走了一圈弯路之后开始检查是否代码的编写上存在多线程的安全问题。问题就出在这里。

问题原因的是系统中定义了一个单例的对象(HyperSQLDBEngine)这个对象里面定义了一个类属性Statement,当多实例的对象注入这个单实例对象的时候,就会出现类属性在多线程中共用。而读取 Statement 对象生成的 ResultSet 对象时候就出现了读取交叉现象,即出现读取某个 ResultSet的时候,这个对象是由另外一个不同的 Statement 对象生成的。因为如果存在某个语句的打开的当前 ResultSet 对象,则 Statement 接口中的所有执行方法都会隐式关闭它。

对于Statement ,在默认情况下,同一时间每个 Statement 对象在只能打开一个 ResultSet 对象。因此,如果读取一个 ResultSet 对象与读取另一个交叉,则这两个对象必须是由不同的 Statement 对象生成的。这里也正是因为出现了交叉读取 Statement 对象生成的 ResultSet 对象。那么问题也就随之出现了。问题找到后解决的还是蛮快的了。

解决方案一 :通过对访问statement执行的返回结果集加锁,如:
synchronized (this) {
    rs = statement.executeQuery(sql);
    if (rs.next()) {
        String validOperations = rs.getString("OPERATION");
    }
}

解决方案二 :针对每次的数据库操作生成独立的statement对象实例。
Statement statement = connection.createStatement();

不过幸好这个是在压力测试阶段出现了,也被及时解决了。
但是另外一个问题就没有这么幸运了。

问题二:

系统上线后,在灰度期间的一天下午,也就是一个高峰点出现了一些奇怪的现象。有些会员提示无法申请退款之类的。收集到返回的错误信息后核实了下发现提示是订单不是他本人的无法申请。通过排查后发现原来开发同学通过在webx 的screen层定义了属性,然后通过继承的方式来传递这个属性的值。screen是单例的。那个这个定义的属性就是问题的根源了。当然action层也是存在同样的问题的。这个问题在代码review 的时候是有发现的,提及修改。一是因为自己没有坚持修改,二是认为系统的量还到不了那个并发的级别,又因为时间关系,所以决定发布后修复。但是往往存在可能发生的事情,上线后往往都会发生。这也应验了墨菲定律。墨菲定律主要内容是:事情如果有变坏的可能,不管这种可能性有多小,它总会发生。

所以这也导致了杯具的发生。

这也是吸取教训吧。

问题三:

这个也是发生在测试阶段的事情。

问题的表象是这样的。每次重启应用的时候就会有比较大的概率出现类型转换异常。
java.lang.Integer cannot be cast to java.lang.Long

出现代码异常的地方在commons-cs 包里面的Codec_V1类:
caseFieldType.LONG:
    if(null == value){
           value = Long.valueOf(0L);
    }
    buf.putLong(((Long)value).longValue());
    break;
                   
这个是Notify消息体在根据属性类型做转换的时候报出来的。
Debug到这里的时候发现是因为一个bizType属性的类型转换引起。
翻阅了整个工程代码没有发现有定义 bizType属性定义为Long型的。

迷惑不解,猜测是否和commons-cs 包解析类型属性有问题,后咨询了下 华黎,,
经过黎叔 指点。埋点了一下这个Converter的属性定义,
发现系统正常的时候获取出来的类型就是是Integer类型的
在系统异常的时候获取出来的类型就是是Long类型的

为什么在系统重启后有时候是好的,有时候是异常的呢,后经细致排查发现
TcRefundDOPacket 里面定义的 types
public staticfinal ArrayList<FieldDef> types = new ArrayList<FieldDef>(20);

这个types在初始化的时候加载了基类的types,
types.addAll(AbstractBasePacket.types);

而这个基类的types被switch-client  这个包里面的 OrderSub类做了重置了ArrayListtypes中的bizType的属性值
这里就是导致了notify消息做字节码转换的时候抛异常的根源。
所以消息的正常与否就依赖于这个 OrderSub类和你的启动的工程里面用到Biztype属性的package对象的 的加载顺序
如果OrderSub类后加载那问题就出现了。

问题的解决方案:
   一:就是排除掉对switch-client  这个包的依赖。
   二:修改switch-client  这个包
不过建议在做设计的时候,如果对象属性里面的值如果不能够被修改的时候,这个属性最好是不要被暴露出去,这样就避免了被修改的可能性。



原创粉丝点击