利用OSB (Oracle Service Bus)的Content Streaming处理大消息
最近发现有人用OSB (Oracle Service Bus,即原BEA的AquaLogic Service Bus的后续版),传送消息的文本大小平均接近200K,最大有10M。这种消息大小看来不是常规的一些业务字段,而是较大的业务报文。这样大的消息,又是XML的,恐怕对OSB的性能有影响。
这是你在OSB的代理服务里面使用了Message Flow,里面可能用到你的body的一些子元素的值做判断,然后调用什么服务云云,这些事情的背后的一个前提是你的报文XML是经过解析后成一个JavaBean的,否则其他地方何以读取其中的子元素(用XPath取值或者XQuery等等)。要命的是,如果XML报文很大,对内存的占用就也很大,而且你的系统肯定不是单用户单请求的吧。
其实OSB提供了一种解决方法,即是采用一种称为Content Streaming的方式来支持大消息的传输,要深入理解这个功能并不容易,作为第一步,你可以理解成body不XML解析,因此省掉了解析的时间,也省掉了内存上的大消耗。而方法你只需要在Proxy Service上设置Enable Content Streaming即可(其实选项也有几个variation,后面再说),见下图。
除此以外,有几个要点,是应当知道的。
第一点必须知道的重要概念,就是当你为你的Proxy Service打开或者说Enable了Streaming后,你不可以再用修改body的那些Action,如Replace/Delete/Rename。原因不言自明,因为没有做过Parse,也就没有代表XML内容的相应的JavaBean,所以无法修改。
第二点也很重要也应该知道,SOAP的Header还是会照常被Parse的,你可以读取或者修改header对象。因此,如果你想做所谓Content-Based-Routing的话,你更应当把这类Content放到Header中。
第三点就是,如果实在必要,其实你还是可以读取body的一部分内容,但是这里要想不影响性能,就有不少要注意的东西和技巧了,否则,就跟没有用Streaming是一样的了,这里的内容分以下几点补充说明(很重要,这是理解streaming的关键):
- 读取部分内容在OSB的术语上叫做Partial Parsing,即部分解析,只解析XML报文中的部分内容(你想要读的那部分)。
- 可以用Assign这个Action来读取一个子元素,但是千万要注意你的XPath的写法了。假设你的body里面有子元素myElement,其下再有子元素mySub,那么你可能会用$body/myElement/mySub来读取你的子元素的值,但其实这个写法会导致整体XML被解析而不是Partial Parsing。等我给出正确的写法你就明白为什么了,正确的写法是$body/myElement[1]/mySub[1],它表示找到第一个myElement元素,然后读取它底下的第一个mySub子元素。或许你还以为这两者没什么区别,但在这里区别可大了。有了[1](英文术语叫做indexing)除了表示第一个之外,还表示就这一个(后面第二个什么的不要了);但是没有[1]就表示“所有”的这个名称的元素,也就表示了找到第一个还继续往后面找,直到全部的都找出来,换取话说,$body/myElement/mySub逻辑上可能返回多个mySub元素的集合;说到这里,应该很明了了吧。你或许还觉得,不怕,我的XML报文肯定只有一个myElement/mySub。没错,你没错,但是OSB并不知道,只要你没有写明[1],OSB就认为任务还没有完成,还要继续工作,继续找到报文最末段为止。
- 上面这点很是费口舌,同时你或许已猜出,OSB是从body的开头就开始进行Parse,直到找到(找齐)你想要的结果为止才结束Parsing。所以,被Parse的那段报文就是body的开头到你的目标元素的这一段。因此,一般来说,你要读取的内容应当在body里面是靠前的部分,这样才可以尽可能减少不必要的Parsing。如果你要找的元素是出现在最后的话,那么就跟没有streaming没什么差别了。
- 同时,应当减少对body的读取次数,如果可以,你尽量重复利用第一次读取的结果,如:
Assign "$body/myElement[1]/mySub[1]" to "mysub"
Assign "$mysub/ele1" to "value1"
Assign "$mysub/ele2" to "value2"
以上的写法对body的读取只有一次,但如果你读取ele1时写成是Assign "$body/myElement[1]/mySub[1]/ele1" to "value1",那么你就两次使用body了,这是不建议的。
现在说一说设置Streaming时的选项吧,因为Reliability的缘故,OSB还是会把body的内容用一种串行化(Serialized,而不是XML对应的JavaBean,那个叫Materialized)的形式存放起来,选择Memory则是在消耗内存,但没有IO;选择Disk则是消耗IO,不占用内存。而选择Enable Compression则是消耗CPU,较少内存或者Disk的存量。
最后,值得注意的是,消息体越大,使用streaming的价值就越大,反之亦然。所以,不要盲目在小消息体上使用Streaming。