2009年6月19日星期五

Java中Tree的序列化

工作中,遇到了在Java里面序列化一颗树、然后反序列化的时候出现Stack Overflow异常的情况。整棵树的层次和在处理每层的当前对象的消耗(在栈上的消耗)累计起来造成了Stack Overflow。
晚上在家里的时候,写了个小程序,实现了Java中对一个Tree的序列化。
这个程序主要想避免层次太深的问题,所以出发点很简单,就是要改变序列化的时候的对象关系,我们要把一个层次的关系变成平的。
这个程序完成了三个版本的序列化方式。先看第一个
第一个版本在写的时候没怎么想:大概的思路是这样的,就是我们对Tree上的每个节点独立序列化,然后我们保存节点间的父子关系,序列化后的结构数据结构是这样的:
NodeCount:4bytes
//每个节点的数据
NodeId:4bytes
Node序列化后的长度:4bytes
Node序列化后的数据:1byte*序列化后的长度
Node的Child个数:4bytes
Child的NodeId:4bytes*Child个数
......表示多个节点数据
其中,NodeId是从0开始自增 

完成这个版本后,大概测试了一下,序列化后的长度比Java直接序列化多了14%左右,不爽。

思考了一下,其实NodeId是不需要的,因此,很快的改出了第二个版本,序列化后格式是这样的:
NodeCount:4bytes
//每个节点的数据
Node序列化后的长度:4bytes
Node序列化后的数据:1byte*序列化后的长度
Child的NodeId:4bytes*Child个数
......表示多个节点数据
测试了一下,长度有所减少,但是非常非常不明显,基本上是比Java序列化多了13%,还是不爽。

最后,想了一下,还是把Tree上的所有节点放到一个Array中,都交给Java序列化吧。这个时候,序列化后的格式就变为了
NodeCount:4bytes
Array序列化后的长度:4bytes
Array序列化后的内容
按照宽度优先的方式的每个TreeNode的ChildCount

这次之后,序列化后的数据和直接Java比,相差无几了,比Java直接序列化差不多少1%不到一些。

2008年5月1日星期四

InnoDB 和 MyISAM中的Blob

   最近在用的一直是Mysql的Innodb,昨天测试了一下包含Blob字段的数据的插入,真的是很惨不忍睹。在表中记录数小于2k的时候,速度还行,超过2k速度就下降,从470/s持续的下降,在表中有1w条记录的时候速度就只有240/s了。到5w的时候,就只有178/s了。到20w的时候,则是130/s。后来换成了Myisam,基本上在200w记录的时候还能够有1000/s的速度。Myisam不支持事务,速度快,这些都是很早就知道的。只是不知道会有这么大的差距。也不清楚是不是innodb的什么设置我没有设置正确。现在从性能上考虑,要采用MyISAM了。而不能保证事务的这个现实,需要在APP上进行处理了。这个差距真的是太惊人了。
     不过如果没有Blob等字段,只有简单类型的话,InnoDB还好了。性能还说的过去。并且关键的是不会因为表中的有一些数据而导致性能急剧下降。

DBCP

DBCP是apache下面的一个开源的数据库连接池,谈谈几个经验
1 Connection出问题后的释放
       Connection建立成功后,如果在某次操作的时候,连接本身出现异常,可能需要废弃掉这个连接,创建新连接。当然对于mysql,支持autoReconnect的则不存在问题(Connection本身不需要废弃),但是如果不支持autoReconnect的driver,就存在这个问题。DBCP中使用的是PoolableConnection,关闭的时候,判断PoolableConnection是否close,因为这个PoolableConnection上还有代理,所以不会重复关闭,那么就判断底层的Connection的isClosed是否为true,如果是true,那么就会丢弃这个连接。JDK中Connection接口的isClosed只在Connection.close被调用后为true,在Driver的实现上,产生和数据库连接的异常后,Driver必须要自己调用close方法,才能保证这个地方让DBCP去丢弃连接。另外就是在close的时候,还调用了ConnectionFactory的passiveObject方法,这个地方出异常也会导致连接被丢弃掉。
2 关于Idle
      默认DBCP的minIdle和maxIdle都是-1,设置以后的话,对于maxIdle,如果maxIdle小于maxActive,那么在调用returnObject的时候,如果当前的idle已经等于maxIdle了,会释放掉这个连接。
3 evict
     如果设置了evict的time,那么会启动一个evictor的线程,这个是对idle的object进行检查的。基本上的逻辑是验证idle的object,删除idle超时的object,然后要保证idle的数量到达minIdle的值。

LoadRunner lrs_send发送数据

LoadRunner的lrs_一族的函数是socket操作的函数,可以方便的创建、释放socket,并通过创建的socket收发数据。socket发送的数据可以是固定的数据,也可以是在buffer中使用param来使得发送的内容具有动态 性。那么如果这个param是从用户自定义函数中返回的,那么存在一个没有办法释放的问题。那么有没有什么更好的办法呢。一个办法是通过加载dll,然后传入一个char[]来获取生成的信息,然后把这个信息写给一个buffer,然后发送这个buffer,还有一个方式就是直接发送char[]中的内容,第一种方法要使用lrs_save_param或者lrs_save_param_ex来保存数据到param中,但是我自己没有实验成功过。第二种可以通过lrs_set_send_buffer调用使得发送的buffer就是我们的char[].那么在这个函数调用后,lrs_send中的第二个参数,也就是buffer的名称就没有意义了。

LoadRunnder 使用外部的动态链接库

在LoadRunner中使用外部的动态链接库有两种方式,一种是通过Param,另外一个就是直接加载并且使用。开始看到网上有人说是用dll中的函数的话,返回不能是字符串,另外就是参数是字符串的也是只读的,不能更改。这样的话,调用dll就真的是限制太大了。不过后来测试发现,不存在上面两个限制的。 

通过Param来使用 外部的dll,要设置Param的类型是User Defined Function,设置dll的路径和方法名称,这个方法貌似是不能有参数的。这样就可以使用了。不过对于这样的方式,Param的值是从函数返回的,返回字符串就比较麻烦了。因为返回字符串,除非是常量字符串,否则都要new(malloc)那么没有地方去释放。 

另外一种方式就是类似于C的写法了,直接使用lr_load_dll加载动态链接库,然后直接使用动态链接库中的函数,不过这里面要注意一点,如果函数返回值不是int,要事先声明一下,就是要在代码头部写 extern char * yourFunc();就可以使用了。开始在写C的时候,忘了C必须在代码前定义变量,而不能在代码中定义,然后编译出错,看看是写在Action中,以为有什么限制,很是不解,后来是在另外一个同事那里又试的时候,想到的。唉。基础都忘了。另外就是如果需要dll中产生以下数据,传递接收的buffer给函数,在dll外部分配好空间。这很重要,因为dll内部分配的内存在外部是没有办法直接释放的,因为EXE和DLL,也包括多个dll都是由自己独立的堆的!!!

上帝会保佑我们的--来个技术无关的。

一个人溺水,等上帝来救他。来了一艘船要救他,他说不,上帝回来救我的,船走了。后来又有两艘船路过要救他,被他用同样的理由拒绝了,结果这个人被淹死了。当他碰见上帝时,他说,上帝啊,我是这么的信仰你,你为什么没有来救我呢?上帝说,我已经派了三艘船去救你,你都拒绝了,上帝也没有办法救你了! 

Eclipse下XmlBuddy和jpdl-gpd的冲突

近日想学习一下jBPM,看到有一个插件可以图形化的进行流程的编辑,就下载了一个。就是jbpm中的jpdl-gpd。上次在兄弟公司看到他们的技术人员在介绍jbpm在他们那边的应用。上周就下载了jbpm,似乎现在的版本比较新了一点儿。以前还有一个starter-kit,现在貌似没有了。自己下载好了以后,这个编辑流程的差价也装好了,可是就是工作不正常。也不知道具体是什么原因。奇怪。 今天偶然发现是xmlBuddy和这个gpd有冲突,想来真是奇怪。不过也不知道Eclipse的工作的原理。只能是先禁用XmlBuddy了。或者说是可以通过怎样的设置使得两者共存?有时间自己去研究一下吧。在网上搜了下,只是看到大家说gpd不正常,我看到他们写的自己的eclipse的配置,都是有xmlbuddy的。自己只是能告诉他们原因是冲突,但是,暂时还是没有solution。谁知道的话也告诉我一下。