![]() ![]() |
Quality of service will become an important attribute in distinguishing your service from others. Quality of service will correlate strongly to your service's over-all popularity. Performance is one aspect of quality of service and can be measured by latency and throughput. Latency is the roundtrip time, from sending a request to receiving a response. Throughput is the number of requests a Web service handles in a specified period.
Over time, many Web services will bump into limitations in the underlying transport protocols, such as HTTP. HTTP is a stateless protocol that only attempts best-effort delivery. This may create many problems for your service, because it does not guarantee that packets will arrive in the order in which they were sent, that all packets will make it to their destination, or that enough bandwidth is available. Several emerging protocols, such as Direct Internet Message Encapsulation (DIME), Reliable HTTP (HTTPR), and Blocks Extensible Exchange Protocol (BEEP), address latency and guaranteed delivery. Adoption of these protocols may take time.
In the meantime, if your service is not response-time sensitive, consider message queuing. Web services can use message queuing implementations based on Java Message Service (described in detail later) for invocations. A messaging approach will provide your service with a reliable, adaptable mechanism for asynchronous exchange of data throughout your enterprise. A message queue can make sure that a message is delivered once and only once. It will also deliver messages to the target as they arrive, without the receiver's having to request them.
One of the biggest slowdowns in performance is related to an XML parser's speed when reading a SOAP message. By its nature, XML does not allow for much size optimization. Because SOAP is based on XML, it has this limitation and more. Unlike regular XML, a SOAP message must include all typing information. Besides parsing this, the XML parser may also need to perform multiple parsing passes to extract the SOAP envelope and body from the SOAP packet.
Many XML parsers are bloated and require significant CPU and memory resources. Some support features such as validating whether a document is well formed, type checking, conversion, and so on. Many of the SOAP stacks are implementations of the Document Object Model (DOM), which is inherently slow to parse large messages. You could consider a nonvalidating, SAX-based SOAP implementation, which will reduce memory overhead and increase throughput.
XML has the side effect of increasing data-in many cases, by five times or more. Because SOAP uses XML as its payload, the best way to increase performance is to consider compressing the XML. In a Java-based Web service, this can be handled easily with a ZippedOutputStream, if your downstream Web services can also use this format. One of the factors to consider when compressing XML is to do it only when the CPU overhead required for compression is less than the network latency.
The best way to determine the performance characteristics of your service is to use a service proxy. Service proxies are used to hide any communications details from a client and are similar to stubs in Java RMI. Service proxies contain code specific to the bindings in the service interface. Listing 16.1 shows the WSDL for a simple timer service that returns the time it took to call another service.
![]() |
<?xml version="1.0" encoding="UTF-8"?> <definitions name="TimerService" targetNamespace="http://www.flutebank.com/TimerService" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://www.flutebank.com/TimerService" xmlns:xsd="http://www.w3.org/1999/XMLSchema"> <message name="IntimerRequest"> <part name="meth1_inType1" type="xsd:string"/> </message> <message name="OuttimerResponse"> <part name="meth1_outType" type="xsd:string"/> </message> <portType name="TimerService"> <operation name="timer"> <input message="IntimerRequest"/> <output message="OuttimerResponse"/> </operation> </portType> <binding name="TimerServiceBinding" type="TimerService"> <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/> <operation name="time"> <soap:operation soapAction="urn:timerservice-service"/> <input> <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:timerservice-service" use="encoded"/> </input> <output> <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:timerservice-service" use="encoded"/> </output> </operation> </binding> <service name="TimerService"> <documentation>Java Web services Architecture</documentation> <port binding="TimerServiceBinding" name="TimerServicePort"> <soap:address location="http://www.flutebank.com:8080/soap/servlet/rpcrouter"/> </port> </service> </definitions>
![]() |
Many Web service toolkits will automatically generated a service proxy from WSDL. Creating a timer service requires modifying the generated code. Listing 16.2 contains modified code that simply wraps the call to the Web service through the invoke method.
![]() |
import java.net.*; import java.util.*; import org.apache.soap.*; import org.apache.soap.encoding.*; import org.apache.soap.rpc.*; import org.apache.soap.util.xml.*; import com.flutebank.Timer; public class TimerServiceProxy { private Call call = new Call(); private URL url = null; private String SOAPActionURI = ""; private SOAPMaptimeRegistry smr = call.getSOAPMaptimeRegistry(); public TimerServiceProxy() throws MalformedURLException { call.setTargetObjectURI("urn:timerservice-service"); call.setEncodingStyleURI("http://schemas.xmlsoap.org/soap/encoding/"); this.url = new URL("http://www.flutebank.com:8080/soap/servlet/rpcrouter"); this.SOAPActionURI = "urn:timerservice-service"; } public synchronized void setEndPoint(URL url) { this.url = url; } public synchronized URL getEndPoint() { return url; } public synchronized String time (String meth1_inType1) throws SOAPException { if (url == null) { throw new SOAPException(Constants.FAULT_CODE_CLIENT, "A URL must be specified"); } call.setMethodName("time"); Vector params = new Vector(); Parameter meth1_inType1Param = new Parameter("meth1_inType1", String.class, meth1_inType1, null); params.addElement(meth1_inType1Param); call.setParams(params); // Start an instance of the flutebank Timer Timer timer = new Timer(); timer.start(); Response res = call.invoke(url, SOAPActionURI); // Stop the Timer timer.stop(); // Calculate the difference in time System.out.println("Response Time="+ timer.getDifference()); // Check for any errors in the response if (res.generatedFault()) { Fault fault = resp.getFault(); throw new SOAPException(fault.getFaultCode(), fault.getFaultString()); } else { Parameter retValue = res.getReturnValue(); return (String)retValue.getValue(); } } }
![]() |
Quantifying performance bottlenecks in your service infrastructure will become a necessity. Timing each step in a Web service that uses a chain of Web services could also use the above approach.
![]() ![]() |