Java Web 服务性能优化案例.docx
- 文档编号:9927341
- 上传时间:2023-02-07
- 格式:DOCX
- 页数:15
- 大小:206.61KB
Java Web 服务性能优化案例.docx
《Java Web 服务性能优化案例.docx》由会员分享,可在线阅读,更多相关《Java Web 服务性能优化案例.docx(15页珍藏版)》请在冰豆网上搜索。
JavaWeb服务性能优化案例
Java Web 服务性能优化案例
简介:
本文介绍如何提升JavaWeb服务性能,主要介绍了三种方法:
一是采用Web服务的异步调用,二是引入Web服务批处理模式,三是压缩SOAP消息。
重点介绍在编程过程中如何使用异步Web服务以及异步调用和同步调用的差异点。
本文还示范了如何在项目中使用以上三种方法,以及各种方法所适合的应用场景。
JavaWeb服务简介
Web服务是一种面向服务架构的技术,通过标准的Web协议提供服务,目的是保证不同平台的应用服务可以互操作。
Web服务(WebService)是基于XML和HTTP通讯的一种服务,其通信协议主要基于SOAP,服务的描述通过WSDL、UDDI来发现和获得服务的元数据。
这种建立在XML标准和Internet协议基础上的Web服务是分布式计算的下一步发展方向,Web服务为那些由不同资源构建的商业应用程序之间的通信和协作带来了光明的前景,从而使它们可以彼此协作,而不受各自底层实现方案的影响。
JAX-RPC1.0是Java方面的Web服务的原始标准,但是由于JAX-RPC1.0对Web服务功能的认识有一定的局限,于是JAX-WS2.0应用而生。
JAX-WS2.0开发工作的主要目标是对各项标准进行更新,成功实现了业界对JAX-RPC1.X的各种期望。
此外,JAX-WS2.0直接支持XOP/MTOM,提高了系统附件传送能力以及系统之间的互操作性。
实例剖析Web服务性能瓶颈
通过以上简述不难体会到,Web服务以其XML+HTTP的松耦合、平台无关的特性,集万般宠爱于一身,必将成为未来数据共享的基础。
但与此同时我们也应当认识到世间完事万物均有其矛盾的两面性:
有优点,必将存在缺点,Web服务亦是如此。
就像当初JAVA大行其道的时候性能成为其致命诟病一样,Web服务也同样面临性能问题,似乎“性能问题”天生就是“平台无关”挥之不去的冤家。
但问题终归要解决,实践是检验和分析问题的唯一途径,让我们先来创建一个简单的Web服务再来审视和分析隐含其中的性能问题。
创建服务
创建服务JavaBean:
首先我们创建一个尽可能简单的书店服务Bean,服务的内容只有一个qryBooksByAuthor,即根据作者(Author)查询其名下的书籍(List
图1.书店服务Bean(BookStoreSrvBean)
服务Input-作者(Author)的实体类:
图2.作者实体类(Author)
服务出参Output-书籍(Book)列表的实体类:
图3.书籍实体类(Book)
至此我们的服务代码已经完成,我们不在此讨论此服务的业务合理性,创建此服务的目的只是举一个尽可能简单的实例以分析web服务的性能。
下面的任务就是开发Web服务了,手工编写及发布符合规范的Web服务过程极为繁琐,在此使用IBM的RationalSoftwareArchitect(后面简称RSA)来进行Web服务的服务器端以及客户端的开发。
发布Web服务
创建动态Web项目:
发布Web服务的前提当然需要一个J2EE的Web项目,打开RSA->File->New->DynamicWebProject,项目名称为testWebService,其余选项根据需要进行选择(注意需要选择加入Web项目到EAR)。
创建好的Web项目和EAR项目效果如下:
图4.Web项目以及应用项目的结构
创建Web服务:
选中导入的com.ibm.test.ws.srv.BookStoreSrvBean,右键New->Other->WebService来创建并发布Web服务。
创建的时候选择常用的JAX-WS标准,并选择生成WSDL文件。
由于Web服务的创建不是本文重点,此部分内容暂且省略。
服务创建完成之后就可以发布到上一步建好的Web项目中了。
创建客户端
使用RSA,客户端的创建工作将会非常简单:
右键点击上面生成的WSDL文件->WebServices->GenerateClient
图5.创建客户端界面
在此界面,根据实际情况选择server,JAX-WS标准以及Client代码的目标项目,然后点击下一步。
图6.输入客户端信息
此界面暂时使用默认配置,某些特殊选项将在后面章节进行描述。
客户端调用
由于JAX-WS规范大部分的stub调用代码是实时生成的,我们只需要修改客户端WSDL的port就可以用以下代码进行Web服务的调用。
这里修改WSDL端口的目的是让客户端调用RSA提供的TCP/IPMonitor的虚拟端口,这样我们就可以很轻易地看到Web服务实际的调用以及返回的SOAP消息了。
客户端调用代码如下:
图7.客户端调用代码
使用TCP/IPMonitor看到的SOAP消息如下:
图8.Web服务调用产生的SOAP消息
JavaWeb服务性能分析
从以上实例我们可以看到,Web服务的调用与传统的RPC还是有较大差异的。
最大的特点是调用双方使用XML格式的SOAP规范消息进行传输,这样以文本进行传输的好处是抛弃了私有协议,无论调用双方是何种平台,只要能够构造以及解析XML文本,并且存在双方都支持的传输协议,那么调用就成为了可能。
而XML的日益规范以及HTTP协议的普及更是给这两个必要条件提供了坚强的后盾,Web服务成为未来通用的服务提供标准已是不争的事实。
但是相信使用过Web服务的人都曾经经受过其性能不佳的窘境,原因为何我们结合刚才的实例可以分析出以下几点:
●SOAP文本消息转化导致效率低下
从刚才的TCP/IPMonitor监测到的request以及response的消息我们可以看到,在发送消息时,我们传入了Author对象,在实际的调用发生时,这个Author对象会被转化成XML格式的SOAP消息,此消息在到达Server端会被解析并重新构造成Server端的Author对象。
Response也是同理,BooksList也会经历XML序列化和反序列化的过程。
最糟糕的是,这种过程会在每一次调用的时候都会发生,这种构造以及解析的过程都会极大地消耗CPU,造成资源的消耗。
●SOAP文本消息传输导致传输内容膨胀
以request参数Author为例,必要的信息仅仅是”BruceEckel”这几个字节,但转化成XML消息后,可以从SOAP消息看到,多了很多SOAP规范的标签,这些信息会导致需要传输的内容急剧增大,几个字节很可能会变成几千字节。
当调用频度和参数内容增多的时候,这种传输内容的膨胀将不是一个可以忽略的影响,它不但会吃掉网络的带宽,还会给Server的数据吞吐能力造成负担,后果可想而知。
●同步阻塞调用在某些情况下导致性能低下
同步阻塞调用是指客户端在调用Web服务发送request后一直处于阻塞状态,客户端线程就会挂起,一直处于等待状态,不能进行其他任务的处理。
这样就会造成线程的浪费,如果相应线程占用了一些资源,也不能够及时释放。
这个问题在纯客户端访问Server端的情况下并不明显,但如果是两个Server端之间进行Web服务调用的话,阻塞模式就会成为调用Server端的性能瓶颈。
Web服务性能优化实践
使用异步方式调用web服务
先需要强调一点的是,这里的异步方式指的是客户端的异步,无论客户端是同步还是异步,都对服务端没有任何影响。
我们期望的理想结果是:
当客户端发送了调用请求后不必阻塞等待server端的返回结果。
最新的JAX-WS标准中增加了这一异步调用的特性,更好的消息是,RSA工具中也对JAX-WS的这一特性进行了支持,这样就极大地方便了我们进行异步调用客户端的创建。
其实讲客户端配置为异步模式极其简单,只要在RSA生成Client端代码时将‘Enableasynchronousinvocationforgeneratedclient’选中即可,如下图:
图9.异步客户端创建选项
这样在生成的客户端的BookStoreSrvBeanService中就会多了qryBooksByAuthorAsync的异步方法。
既然是异步方法,回调(CallBack)就是必不可少的,在下面的异步客户端测试代码中可以看到匿名内部类作为回调handler的具体使用方法:
图10.异步客户端调用示例代码
测试代码的输出结果如下:
图11.异步调用控制台输出
可以看到,当Web服务没有返回时,客户端仍然有机会做自己的输出:
“notdoneyet,candosomethingelse…”。
有些人可能会认为作为客户端此处的输出并无实际意义,但试想如果一个server作为客户端去访问一个Web服务,如果在服务等待期间能够有机会脱离阻塞状态执行自己需要的代码,甚至可以使用wait等方法释放被当前线程占用的资源,那么对于此server来说这将是一个对性能提升起到本质作用的因素。
使web服务支持批处理模式
●批处理模式简介
批处理顾名思义是采用一次性处理多条事务的方式来取代一次一条事务的传统处理方式。
JavaDatabaseConnectivty(JDBC)中提供了大量的批处理API用于优化数据库操作性能,例如Statement.executeBatch()可以一次性接收并执行多条SQL语句。
批处理思想可以方便的移植到Web服务调用场景以达到优化Web服务调用响应的目的。
通过实际Web服务调用时间戳分析不难看出网络通讯是Web服务性能的瓶颈之一,因此通过减少网络通讯开销来优化Web服务性能,批处理模式是其中较为直接的一种实现方式。
●批处理模式适应性
批处理模式虽然作用显著,但是也不适合所有场景。
使用批处理模式处理Web服务请求时需要考虑一下几点:
1.不同Web服务执行时间差异性
不同Web服务执行时间不尽相同,因此在同时处理多Web服务请求时需要考虑这种时间差异性。
一般情况下是等待最长处理时间的Web服务执行完毕后汇总所有Web服务执行结果从而返回到客户端,因此存在批处理多Web服务反而比顺序单次调用Web服务消耗更长时间可能性。
需要在采用批处理模式前对Web服务性能有清晰的了解,尽可能将性能参数相似的Web服务纳入批处理,而分别处理执行时间差异较大的Web服务。
一般建议将性能差异在30%以内的多Web服务可以考虑纳入批处理。
比方说AccountWebService中有一个获取用户账户列表的Web服务getUserAccounts,这个Web服务执行需要15秒,另外UserWebService中有一个获取用户目前pending的待处理通知getUserPendingNotifications,这个Web服务执行需要2秒时间,我们可以看到这两个Web服务执行时间差异较大,因此在这种情况下我们不建议将这两个Web服务纳入批处理。
而AccountWebService中有一个增加第三方用户账号的Web服务addThirdPartyNonHostAccount,该Web服务执行需要3秒,此时就就可以考虑能将getUserPendingNotificationsWeb服务和addThirdPartyNonHostAccount放在一个批处理中一次性调用处理。
2.不同Web服务业务相关性
一般情况下建议考虑将存在业务相关性的多Web服务放入批处理中,只有业务存在相关性的多Web服务才会涉及到减少调用次数以提高应用系统性能的需求。
比方说用户在增加第三方账号addThirdPartyNonHostAccount以后会默认自动发送一条pending的notification给用户用以提示用户来激活增加的账号,因此这种场景下可以完美的将addThirdPartyNonHostAccountWeb服务和getUserPendingNotificationsWeb服务放入一个批处理中,在用户增加完三方账号后系统自动刷新pendingnotification区域以提示用户激活账号。
UserWebService中有一个获取用户主账号的Web服务getUserHostAccounts和获取用户三方账号的Web服务getUserNonHostAccounts,MetaDataService中有一个获取国家金融机构假期数据的Web服务getFinacialAgencyHolidays,该Web服务明显和getUserHostAccounts,getUserNonHostAccounts不存在业务上相关性,因此不应该将它们纳入批处理。
3.尽量避免将存在依赖关系的多Web服务放入同一个批处理中
将多个存在依赖关系的多Web服务放入同一批处理中需要专门考虑、处理多Web服务彼此间的依赖关系,进而无法将方便的这些Web服务并发执行而不得不串行执行有依赖关系的Web服务,最悲观情况下批处理响应时间将是批处理中所有Web服务串行执行时间和。
原则上即使批处理中Web服务间存在依赖关系,通过动态指定依赖关系也可以实现多Web服务的批处理调用。
但是这样将大大增加批处理实现的技术复杂性,因此不建议如此操作。
4.多线程方式处理批处理Web服务请求
批处理模式在服务实现端一般通过多线程处理方法来并发处理多个Web服务调用请求。
通过集中的解析器解析批处理模式请求,之后针对每一个Web服务调用会启动一个单独的线程来处理此Web请求,同时会有一个总的线程管理器来调度不同Web服务执行线程,监控线程执行进度等。
在所有线程执行完成后汇总Web服务执行结果返回客户端。
●批处理实现方式
批处理实现方式一般有两种:
静态批处理模式,动态批处理模式:
静态批处理模式实现较为简单,但是相对缺乏灵活性。
静态批处理的核心思想就是在已有Web服务的基础上通过组合封装的方式来得到批处理的目的。
举例来说将系统中已有的Web服务请求结构组合成一个新的数据对象模型作为Web服务批处理请求结构,在客户端进行批处理调用时通过初始化批处理请求数据对象,并将特定的Web服务请求对象赋值给批处理请求对象属性的方式。
同理在服务实现端在生成批处理响应数据对象时也是通过将具体Web服务的响应组合起来生成并返回客户端。
动态批处理模式实现较为复杂,但也能提供更大的操作灵活性。
动态批处理模式一般需要应用采用Java反射API开发具有容器功能的批处理实现框架。
客户端可以动态的向容器中增加Web服务调用请求,比方说客户端可以动态的将addThirdPartyNonHostAccount,getUserPendingNotifications两个Web服务加入到这个容器中然后发起一个框架提供的批处理Web服务调用请求。
该批处理Web服务在实现端将解析容器并将其中的各个Web服务请求抽取解析并启动独立的线程来处理。
压缩SOAP
当WebServiceSOAP消息体比较大的时候,我们可以通过压缩soap来提高网络传输性能。
通过GZIP压缩SOAP消息,得到二进制数据,然后把二进制数据作为附件传输。
以前常规方法是把二进制数据Base64编码,但是Base64编码后的大小是二进制数据的1.33倍。
辛苦压缩的,被Base64给抵消差不多了。
是否可以直接传输二进制数据呢?
JAX-WS的MTOM是可以的,通过HTTP的MIME规范,SOAPmessage可以字符,二进制混合。
我们在client和server端各注册一个handler来处理压缩和解压。
由于压缩后的SOAP消息附件与消息体中的部分不是基于MTOM自动关联的,需要单独处理附件。
在生成client端和server端代码的时候需要enableMTOM。
Handler具体代码在本文代码附件中,test.TestClientHanlder,test.TestServerHanlder。
写好了handler了之后还要为service注册handler。
客户端handler样例代码如下:
客户端代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
publicbooleanhandleMessage(MessageContextarg0){
SOAPMessageContextct=(SOAPMessageContext)arg0;
booleanisRequestFlag=(Boolean)arg0
.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
SOAPMessagemsg=ct.getMessage();
if(isRequestFlag){
try{
SOAPBodybody=msg.getSOAPBody();
Nodeport=body.getChildNodes().item(0);
StringportContent=port.toString();
NodeListlist=port.getChildNodes();
for(inti=0;i port.removeChild(list.item(i)); } ByteArrayOutputStreamoutArr=newByteArrayOutputStream(); GZIPOutputStreamzip=newGZIPOutputStream(outArr); zip.write(portContent.getBytes()); zip.flush(); zip.close(); byte[]arr=outArr.toByteArray(); TestDataSourceds=newTestDataSource(arr); AttachmentPartattPart=msg.createAttachmentPart(); attPart.setDataHandler(newDataHandler(ds)); msg.addAttachmentPart(attPart); }catch(SOAPExceptione){ e.printStackTrace(); }catch(IOExceptione){ e.printStackTrace(); } } returntrue; } Web服务端handler样例代码如下: 服务端代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 publicbooleanhandleMessage(MessageContextarg0){ SOAPMessageContextct=(SOAPMessageContext)arg0; booleanisRequestFlag=(Boolean)arg0 .get(MessageContext.MESSAGE_OUTBOUND_PROPERTY); SOAPMessagemsg=ct.getMessage(); if(! isRequestFlag){ try{ Objectobj=ct.get("Attachments"); Attachmentsatts=(Attachments)obj; Listlist=atts.getContentIDList(); for(inti=1;i Stringid=(String)list.get(i); DataHandlerd=atts.getDataHandler(id); InputStreamin=d.getInputStream(); ByteArrayOutputStreamout=newByteArrayOutputStream(); GZIPInputStreamzip=newGZIPInputStream(in);
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Java Web 服务性能优化案例 服务 性能 优化 案例