润乾报表性能管理之用户间共享缓存原理

更确切点说,不仅仅是用户间,同一个用户本人再次访问同一个报表,也共享先前的缓存。

功能背景

用户间共享缓存的目的,是为了避免不同用户访问同一张报表时重复运算的问题。因此可以把A用户访问报表时计算出的结果报表保存下来,当B用户也访问这个报表时,直接把保存下来的结果报表返回给B用户,不再重新计算。

对于带有参数和宏的报表,当用户采用相同的参数和宏进行重复访问时,也可以利用缓存,减少重复的计算。但是如果参数和宏不一样,报表就只能重新计算,因为不同参数运算出来的结果报表不一样,无法利用缓存。因此,对于有参数和宏的报表,缓存时还必须识别参数和宏的值。

实现原理

当第一个用户用参数B访问报表A时,引擎会产生一个entry1,这个entry1把报表A的模板保存下来;报表计算完毕后,又会产生一个entry11,这个entry11保存了报表A的计算结果以及参数B的值,因为不同参数算出来的结果报表是不同的,因此结果报表和参数必须绑定。接着对结果报表进行分页,又会产生一个entry111,这个entry111保存了分页后的报表以及分页参数,例如纸张大小之类的,因为分页结果取决于分页参数,所以分页结果和分页参数也是绑定的。
以上entry对象产生后,系统立马会启动一个任务往硬盘写缓存。这个任务是独立的,和其它任务并发运行,且互相没有影响。因此,写缓存的过程对用户来说是透明的,用户往往感觉不到,因为不影响报表的计算。
注意,往硬盘写是需要花时间的,速度取决于硬盘的转速以及entry里对象的大小,有时候这个时间可能比计算报表的时间还长。
当写完硬盘后,内存中的这三个entry才变成了真正意义上的软引用,此时才能由JVM决定是否进行垃圾回收。

以上过程结束后,我们假设第二个用户用参数C访问报表A,此时引擎会把报表A的名字和entry1的报表名进行对比,发现完全一样后,直接返回entry1;接着引擎再对比参数B和参数C,发现不一样,于是引擎不再重新装载报表模板,而是直接利用entry1和参数C进行计算,计算结果存为entry12,接着再进行分页计算,…..步骤同上。

可以想象,这样的计算过程不断重复,于是缓存成了一棵树,如下图所示:

相关配置包括:

<config> <!– 是否每次重新装载报表模板 –>
<name>alwaysReloadDefine</name>
<value>no</value>
</config>
<config> <!– 配置缓存报表目录 –>
<name>cachedReportDir</name>
<value>c:\runqian\cached</value>
</config>
<config> <!– 配置报表最大未访问时长,以分钟为单位 –>
<name>cachedReportTimeout</name>
<value>120</value>
</config>
<config> <!– 配置参数最大未访问时长,以分钟为单位 –>
<name>cachedParamsTimeout</name>
<value>120</value>
</config>

需要注意的是:
1、 多用户间共享缓存和单用户缓存是同一套缓存程序,缓存本身是不可以关闭的,目前授权里只能控制是否允许多用户间共享缓存。
2、 判断报表模板是否相同的依据是报表模板名,即tag标签里的reportFileName 或者beanName,如果相同的报表取不同的名字,视为不同的模板。
3、 alwaysReloadDefine这个参数配置,仅仅用于控制是否每次重新装载报表模板,我们从上图的缓存结构树里可以看出,一旦这个参数被设为yes,意味着每次计算都是直接重新装载模板,因此不会进行entry比对,不会利用已经有的缓存。但是重新计算出的报表本身还是照样会缓存,为了翻页和打印导出。因此,一旦这个参数被关闭了,意味着缓存中的entry数量会无限庞大,直到缓存时间过期才会被清除。所以,如果不是特殊需要,一般建议不要关闭alwaysReloadDefine。
4、 从上图可以看出,用户间共享缓存包括三个层面,模板共享、结果报表共享、分页后报表共享,而我们的授权控制的是后两个,即结果报表共享和分页后报表共享,不能控制模板共享。如果希望模板不要共享,就要通过alwaysReloadDefine这个参数来控制。
5、 在模板共享且授权支持结果报表共享的前提下,可以通过tag标签控制当前是否使用缓存报表,使用方法如下:
<report:html name=”report1″
reportFileName=”api/wangge.raq”

useCache=”yes” //是否使用缓存
timeout=”30″ //从缓存系统中取多少分钟内产生的报表,如果没有此时间内的,则产生一个新报表,该属性的单位为分钟
/>

6、 如果用户的授权里没有缓存共享的功能,那么前述的entry1下面会挂上n多的entry1x,由此也会导致entry树比较庞大。
7、 entry的内容被写进硬盘后,其本身成了软引用,由JVM决定是否进行回收。从实际项目看,似乎存在JVM不能及时回收的现象,具体的原因,有待商酌

热门文章