感觉http://www.ibm.com/developerworks/cn/webservices/1212_sunzg_java/1212_sunzg_java.html不错
做大数据库的B2C平台的webservice接口客户端(系统安装部署时会将数据库服务放在一台IBM服务器上,webservice接口客户端放在另一台IBM服务器上面。 )的问题及性能优化
1.你要弄清楚你一个订单的数据量有多大,就是几M,几k这种概念。
2.你两边的接口是局域网还是外网,网络质量怎么样?
3.如果发送测试发现慢的话,你要知道你的瓶颈在哪。如是读慢,还是写慢?还是网络传输入的问题。
如果不是网络传输慢,是读写慢的话,就要用concurrent包的队列,用专门的读写线程就行了。就是从数据库不断抓数据放队列,然后线程不断取包发。
(使用WEBSERVICE那么你的WEBSERVICE接口要尽可能地简单,处理任务时间要短,当然要考虑WEBSERVICE的集群。
如果你不使用WEBSERVICE那么建议你使用长连接模式(你会用WEBSERVICE一般访问用户会比较固定)。WEBSERVICE的开销在于创建连接,其传输消耗并不会太大。)
4.把一个批次的订单数据、文件(xml)先压缩后再传。根据我以前的经验,不管是文件,还是字符串都可 以压缩并进行base64,然后放到一个xml文件里。一个批次传一定量的订单,如1000个订单,而不要每次一条。(根据你一个订单的数据量定一个合理值)
5.如果还不行,加大硬件力度。
6.还是不行,就是webservice本身的问题了。直接ftp吧。
7.可以采用异步模式,客户端发送消息过去即认为成功,减少调用方法等待时间。但需要服务端支持才可以。
Java Web 服务性能分析
从以上实例我们可以看到,Web 服务的调用与传统的 RPC 还是有较大差异的。最大的特点是调用双方使用 XML 格式的 SOAP 规范消息进行传输,这样以文本进行传输的好处是抛弃了私有协议,无论调用双方是何种平台,只要能够构造以及解析 XML 文本,并且存在双方都支持的传输协议,那么调用就成为了可能。而 XML 的日益规范以及 HTTP 协议的普及更是给这两个必要条件提供了坚强的后盾,Web 服务成为未来通用的服务提供标准已是不争的事实。
但是相信使用过 Web 服务的人都曾经经受过其性能不佳的窘境,原因为何我们结合刚才的实例可以分析出以下几点:
- SOAP 文本消息转化导致效率低下
从刚才的 TCP/IP Monitor 监测到的 request 以及 response 的消息我们可以看到,在发送消息时,我们传入了 Author 对象,在实际的调用发生时,这个 Author 对象会被转化成 XML 格式的 SOAP 消息,此消息在到达 Server 端会被解析并重新构造成 Server 端的 Author 对象。Response 也是同理,Books List 也会经历 XML 序列化和反序列化的过程。最糟糕的是,这种过程会在每一次调用的时候都会发生,这种构造以及解析的过程都会极大地消耗 CPU,造成资源的消耗。
- SOAP 文本消息传输导致传输内容膨胀
以 request 参数 Author 为例,必要的信息仅仅是"Bruce Eckel”这几个字节,但转化成 XML 消息后,可以从 SOAP 消息看到,多了很多 SOAP 规范的标签,这些信息会导致需要传输的内容急剧增大,几个字节很可能会变成几千字节。当调用频度和参数内容增多的时候,这种传输内容的膨胀将不是一个可以忽略的影响,它不但会吃掉网络的带宽,还会给 Server 的数据吞吐能力造成负担,后果可想而知。
- 同步阻塞调用在某些情况下导致性能低下
同步阻塞调用是指客户端在调用 Web 服务发送 request 后一直处于阻塞状态,客户端线程就会挂起,一直处于等待状态,不能进行其他任务的处理。这样就会造成线程的浪费,如果相应线程占用了一些资源,也不能够及时释放。
这个问题在纯客户端访问 Server 端的情况下并不明显,但如果是两个 Server 端之间进行 Web 服务调用的话,阻塞模式就会成为调用 Server 端的性能瓶颈。
Web 服务性能优化实践
使用异步方式调用 web 服务
先需要强调一点的是,这里的异步方式指的是客户端的异步,无论客户端是同步还是异步,都对服务端没有任何影响。我们期望的理想结果是:当客户端发送了调用请求后不必阻塞等待 server 端的返回结果。最新的 JAX-WS 标准中增加了这一异步调用的特性,更好的消息是,RSA 工具中也对 JAX-WS 的这一特性进行了支持,这样就极大地方便了我们进行异步调用客户端的创建。
其实讲客户端配置为异步模式极其简单,只要在 RSA 生成 Client 端代码时将‘ Enable asynchronous invocation for generated client ’ 选中即可 , 如下图 :
图 9. 异步客户端创建选项
这样在生成的客户端的 BookStoreSrvBeanService 中就会多了 qryBooksByAuthorAsync 的异步方法。既然是异步方法,回调 (Call Back) 就是必不可少的,在下面的异步客户端测试代码中可以看到匿名内部类作为回调 handler 的具体使用方法 :
图 10. 异步客户端调用示例代码
测试代码的输出结果如下:
图 11. 异步调用控制台输出
可以看到,当 Web 服务没有返回时,客户端仍然有机会做自己的输出 :“not done yet, can do something else...”。有些人可能会认为作为客户端此处的输出并无实际意义,但试想如果一个 server 作为客户端去访问一个 Web 服务,如果在服务等待期间能够有机会脱离阻塞状态执行自己需要的代码,甚至可以使用 wait 等方法释放被当前线程占用的资源,那么对于此 server 来说这将是一个对性能提升起到本质作用的因素。
使 web 服务支持批处理模式
- 批处理模式简介
批处理顾名思义是采用一次性处理多条事务的方式来取代一次一条事务的传统处理方式。Java Database Connectivty (JDBC) 中提供了大量的批处理 API 用于优化数据库操作性能,例如 Statement.executeBatch() 可以一次性接收并执行多条 SQL 语句。批处理思想可以方便的移植到 Web 服务调用场景以达到优化 Web 服务调用响应的目的。通过实际 Web 服务调用时间戳分析不难看出网络通讯是 Web 服务性能的瓶颈之一,因此通过减少网络通讯开销来优化 Web 服务性能,批处理模式是其中较为直接的一种实现方式。
- 批处理模式适应性
批处理模式虽然作用显著,但是也不适合所有场景。使用批处理模式处理 Web 服务请求时需要考虑一下几点:
- 不同 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 秒,此时就就可以考虑能将 getUserPendingNotifications Web 服务和 addThirdPartyNonHostAccount 放在一个批处理中一次性调用处理。
- 不同 Web 服务业务相关性
一般情况下建议考虑将存在业务相关性的多 Web 服务放入批处理中,只有业务存在相关性的多 Web 服务才会涉及到减少调用次数以提高应用系统性能的需求。比方说用户在增加第三方账号 addThirdPartyNonHostAccount 以后会默认自动发送一条 pending 的 notification 给用户用以提示用户来激活增加的账号,因此这种场景下可以完美的将 addThirdPartyNonHostAccount Web 服务和 getUserPendingNotifications Web 服务放入一个批处理中,在用户增加完三方账号后系统自动刷新 pending notification 区域以提示用户激活账号。UserWebService 中有一个获取用户主账号的 Web 服务 getUserHostAccounts 和获取用户三方账号的 Web 服务 getUserNonHostAccounts,MetaDataService 中有一个获取国家金融机构假期数据的 Web 服务 getFinacialAgencyHolidays,该 Web 服务明显和 getUserHostAccounts,getUserNonHostAccounts 不存在业务上相关性,因此不应该将它们纳入批处理。
- 尽量避免将存在依赖关系的多 Web 服务放入同一个批处理中
将多个存在依赖关系的多 Web 服务放入同一批处理中需要专门考虑、处理多 Web 服务彼此间的依赖关系,进而无法将方便的这些 Web 服务并发执行而不得不串行执行有依赖关系的 Web 服务,最悲观情况下批处理响应时间将是批处理中所有 Web 服务串行执行时间和。原则上即使批处理中 Web 服务间存在依赖关系,通过动态指定依赖关系也可以实现多 Web 服务的批处理调用。但是这样将大大增加批处理实现的技术复杂性,因此不建议如此操作。
- 多线程方式处理批处理 Web 服务请求
批处理模式在服务实现端一般通过多线程处理方法来并发处理多个 Web 服务调用请求。通过集中的解析器解析批处理模式请求,之后针对每一个 Web 服务调用会启动一个单独的线程来处理此 Web 请求,同时会有一个总的线程管理器来调度不同 Web 服务执行线程,监控线程执行进度等。在所有线程执行完成后汇总 Web 服务执行结果返回客户端。
- 不同 Web 服务执行时间差异性
- 批处理实现方式
批处理实现方式一般有两种:静态批处理模式,动态批处理模式:
静态批处理模式实现较为简单,但是相对缺乏灵活性。静态批处理的核心思想就是在已有 Web 服务的基础上通过组合封装的方式来得到批处理的目的。举例来说将系统中已有的 Web 服务请求结构组合成一个新的数据对象模型作为 Web 服务批处理请求结构,在客户端进行批处理调用时通过初始化批处理请求数据对象,并将特定的 Web 服务请求对象赋值给批处理请求对象属性的方式。同理在服务实现端在生成批处理响应数据对象时也是通过将具体 Web 服务的响应组合起来生成并返回客户端。
动态批处理模式实现较为复杂,但也能提供更大的操作灵活性。动态批处理模式一般需要应用采用 Java 反射 API 开发具有容器功能的批处理实现框架。客户端可以动态的向容器中增加 Web 服务调用请求,比方说客户端可以动态的将 addThirdPartyNonHostAccount,getUserPendingNotifications 两个 Web 服务加入到这个容器中然后发起一个框架提供的批处理 Web 服务调用请求。该批处理 Web 服务在实现端将解析容器并将其中的各个 Web 服务请求抽取解析并启动独立的线程来处理。
压缩 SOAP
当 Web Service SOAP 消息体比较大的时候,我们可以通过压缩 soap 来提高网络传输性能。通过 GZIP 压缩 SOAP 消息,得到二进制数据,然后把二进制数据作为附件传输。以前常规方法是把二进制数据 Base64 编码,但是 Base64 编码后的大小是二进制数据的 1.33 倍。辛苦压缩的,被 Base64 给抵消差不多了。是否可以直接传输二进制数据呢? JAX-WS 的 MTOM 是可以的,通过 HTTP 的 MIME 规范, SOAP message 可以字符,二进制混合。我们在 client 和 server 端各注册一个 handler 来处理压缩和解压。 由于压缩后的 SOAP 消息附件与消息体中的部分不是基于 MTOM 自动关联的,需要单独处理附件。在生成 client 端和 server 端代码的时候需要 enable MTOM。 Handler 具体代码在本文代码附件中, test.TestClientHanlder, test.TestServerHanlder。 写好了 handler 了之后还要为 service 注册 handler。
客户端 handler 样例代码如下:
客户端代码
public boolean handleMessage(MessageContext arg0) { SOAPMessageContext ct = (SOAPMessageContext) arg0; boolean isRequestFlag = (Boolean) arg0 .get(MessageContext.MESSAGE_OUTBOUND_PROPERTY); SOAPMessage msg = ct.getMessage(); if (isRequestFlag) { try { SOAPBody body = msg.getSOAPBody(); Node port = body.getChildNodes().item(0); String portContent = port.toString(); NodeList list = port.getChildNodes(); for (int i = 0; i < list.getLength(); i++) { port.removeChild(list.item(i)); } ByteArrayOutputStream outArr = new ByteArrayOutputStream(); GZIPOutputStream zip = new GZIPOutputStream(outArr); zip.write(portContent.getBytes()); zip.flush(); zip.close(); byte[] arr = outArr.toByteArray(); TestDataSource ds = new TestDataSource(arr); AttachmentPart attPart = msg.createAttachmentPart(); attPart.setDataHandler(new DataHandler(ds)); msg.addAttachmentPart(attPart); } catch (SOAPException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } return true; }
Web 服务端 handler 样例代码如下:
服务端代码
public boolean handleMessage(MessageContext arg0) { SOAPMessageContext ct = (SOAPMessageContext) arg0; boolean isRequestFlag = (Boolean) arg0 .get(MessageContext.MESSAGE_OUTBOUND_PROPERTY); SOAPMessage msg = ct.getMessage(); if (!isRequestFlag) { try { Object obj = ct.get("Attachments"); Attachments atts = (Attachments) obj; List list = atts.getContentIDList(); for (int i = 1; i < list.size(); i++) { String id = (String) list.get(i); DataHandler d = atts.getDataHandler(id); InputStream in = d.getInputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream(); GZIPInputStream zip = new GZIPInputStream(in); byte[] arr = new byte[1024]; int n = 0; while ((n = zip.read(arr)) > 0) { out.write(arr, 0, n); } Document doc = DocumentBuilderFactory.newInstance() .newDocumentBuilder() .parse(new ByteArrayInputStream(out.toByteArray())); SOAPBody body = msg.getSOAPBody(); Node port = body.getChildNodes().item(0); port.appendChild(doc.getFirstChild().getFirstChild()); } } catch (SOAPException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (ParserConfigurationException e) { e.printStackTrace(); } } return true; }
在 web.xml 中 service-ref 部分添加 handler. Server 端 handler 也是同样添加。
配置代码
<handler-chains> <handler-chain> <handler> <handler-name>TestClientHandler</handler-name> <handler-class>test.TestClientHandler </handler-class> </handler> </handler-chain> </handler-chains>
结束语
以上三种解决方案是根据笔者的经验和分析,针对 Web 服务当前所面临的性能瓶颈进行提出的。并且,这几种解决方案在实际项目使用中都取得了比较好的效果。 综上所述, 在实际项目中,根据不同的需求采用上述方法中一个或者多个组合,可以使 Web 服务性能更加优化。