The Web services model [1] is attracting the attention of many companies. Web services are applications that can be accessed via open standard technologies such as HTTP [2] and XML [3]. Web services enable data exchange independent of programming language, platform, and transport protocol by using SOAP (Simple Object Access Protocol) [4] as a messaging format. Using this feature of Web services, applications on different platforms can be combined. Therefore, it is important that Web services be loosely coupled and dynamically bound.
Since the current focus is on the interoperability of Web services, the issues of operating in the real world, that is to say issues such as performance have not been much discussed yet. In this paper, we describe a cache architecture for Web services.
Because Web services are accessed via XML messages, the key of a cache entry can be a request XML message. However, a semantically identical XML request message can be represented differently. If simple literal expressions are used for the keys of cache entries, we may store the same cache entry many times.
There are many examples of semantically identical but literally different messages. One example is white space between elements. Another example is differences of namespace prefixes for the same namespace URI. For example, even if same service is called using SOAP, different request messages are generated by each client implementation. Listing 1 and 2 show request messages to invoke the same service using Apache-SOAP and MS-SOAP respectively.
<?xml version='1.0' encoding='UTF-8'?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance" xmlns:xsd="http://www.w3.org/1999/XMLSchema"> <SOAP-ENV:Body> <ns1:GetLastTradePrice xmlns:ns1="Some-URI" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> <symbol xsi:type="xsd:double">DIS</symbol> </ns1:GetLastTradePrice> </SOAP-ENV:Body> </SOAP-ENV:Envelope> |
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Body> <SOAPSDK1:GetLastTradePrice xmlns:SOAPSDK1="Some-URI"> <symbol>DIS</symbol> </SOAPSDK1:GetLastTradePrice> </SOAP-ENV:Body> </SOAP-ENV:Envelope> |
Figure 1 illustrates a cache architecture, which uses canonicalized XML messages. In a typical case, a request message is sent, and the Reverse Proxy performs canonicalization to find a cached entry. If found and still valid, the Reverse Proxy returns the response message from the Cache Table without accessing the backend Application Server.
Our proxy can accept messages that are not canonicalized as well as canonicalized messages. Accordingly, the Requestor Nodes do not have to be specialized for this architecture. Rather, when request messages are intended for our architecture, the Reverse Proxy simply exhibits better performance.
The Cache Table manages cached entries, each of which includes a request and a response message. As in Table 1, the entry also includes hash values of the request and response messages. The Proxy Cache utilizes the request hash value to retrieve entries from the Cache Table more quickly. The response hash is used to check if the response message stored by the requestor is identical to the one stored by the Reverse Proxy.
Column Name | Note |
---|---|
Request URL | URL for a request |
Response | XML document for a response |
Request-hash | Hash value (20bytes) for request |
Response-hash | Hash value (20bytes) for request |
Using canonicalization, semantically equivalent XML messages are transformed into a literal expression. A standard specification, C14N, for XML canonicalization has been proposed by W3C [7]. Although C14N defines a generic method for canonicalization, for our purpose it often fails.�@For example, we can define a canonicalization rule that transforms Listing 3 to Listing 4. The actual rules here are: namespace qualifiers should be a concatenation of "ns" and a number, and all spaces in each line should be eliminated.
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> <SOAP-ENV:Body> <m:GetLastTradePrice xmlns:m="urn:stock-quote"> <symbol>IBM</symbol> </m:GetLastTradePrice> </SOAP-ENV:Body> </SOAP-ENV:Envelope> |
<ns1:Envelope xmlns:ns1="http://schemas.xmlsoap.org/soap/envelope/" ns1:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> <ns1:Body> <ns2:GetLastTradePrice xmlns:ns2="urn:stock-quote"> <symbol>IBM</symbol> </ns2:GetLastTradePrice> </ns1:Body> </ns1:Envelope> |
A Canonicalized Template is useful for simplifying the generation of canonicalized messages at Requestor Nodes. As shown in Listing 5, a canonicalized template contains variables that are indicated by a string surrounded with "$" symbols. When the original document includes "$" symbols, they are replaced by "$$" symbols. A variable name cannot include the "$" symbol. Substituted strings must be composed of character data as defined in XML 1.0:
Character data ::= [^<&]* - ([^<&]* ']]>' [^<&]*)
<ns1:Envelope xmlns:ns1="http://schemas.xmlsoap.org/soap/envelope/" ns1:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> <ns1:Body> <ns2:GetLastTradePrice xmlns:ns2="urn:stock-quote"> <symbol>$symbol$</symbol> </ns2:GetLastTradePrice> </ns1:Body> </ns1:Envelope> |
The requestor sends an unmodified request message. The Proxy Cache performs canonicalization, calculates a hash value, and retrieves any entries with that hash value from the Cache Table.
Canonicalized request messages are sent. The Proxy Cache calculates the hash value of the message, and retrieves any entries with that hash value from the Cache Table. Compared to Pattern-1, we do not need the canonicalization process, and thus avoid XML parsing.
The requestor performs canonicalization, calculates the hash value and includes it in the HTTP header. The Proxy Cache directly retrieves from the Cache Table any entries with that hash value. Compared with Pattern-2, we do not need any calculation of the hash value.
The requestor also manages the response messages, so it includes hash values of request and response messages. The Proxy Cache first finds any entry using the hash value from the request message, and returns an HTTP No Content response if one is found.
Here we describe how Proxy Cache in Figure 1 is implemented. The Reverse Proxy is implemented on top of Jakarta-Tomcat3.3 [5], a servlet engine for Java. The Proxy Cache is implemented as a Java program, and the entries are kept in memory. For the hashing algorithm, we use the SHA-1 algorithm, which is widely used for hashing. With SHA-1, any XML document is hashed to a 160-bit binary value. In our experiments, we use an extremely simple echo application as a provider application.
The following supporting software was used for our prototype:
SOAP Engine: | Apache SOAP2.2, Apache Axis-alpha2 [6] |
XML Parser: | Apache Xerces2.0.0beta3 [6] |
Here are the specifications of the reverse proxy machine:
CPU: | Pentium III 850MHz |
Memory: | 512MB |
Hard Disc: | 6GB |
OS: | Windows 2000 Service Pac 2 |
Java VM: | IBM JDK1.3.0 |
We experimented by sending various size messages of Patterns 1, 2, 3, and 4. For comparison, we also tried sending the same messages directly to Apache-SOAP and Apache-Axis. Table 2 shows the result of these experiments, and Figure 2 shows a graph based on Table 2. The number in each cell indicates how many messages the reverse proxy could handle each second, i.e. TPS (transactions per second)
414byte | 2kbyte | 5kbyte | 10kbyte | 20kbyte | 50kbyte | |
---|---|---|---|---|---|---|
SOAP2.2 | 115 | 103 | 80 | 54 | 34 | 20 |
Axis-alpha2 | 170 | 140 | 97 | 64 | 39 | 20 |
pattern-1 | 316 | 273 | 198 | 135 | 87 | 47 |
pattern-2 | 782 | 653 | 451 | 338 | 213 | 119 |
pattern-3 | 813 | 772 | 612 | 485 | 289 | 170 |
pattern-4 | 841 | 816 | 726 | 633 | 324 | 187 |
From Table 2 and Figure 2, it is difficult to understand how fast our architecture is compared to direct access to the server. Figure 3 shows the ratio between direct access and each of the patterns.
We put the reverse proxy in front of the application server. When the reverse proxy deals with non-canonicalized messages (i.e. pattern-1), the reverse proxy performance is approximately twice that for direct access. When the reverse proxy receives canonicalized messages (i.e. Patterns 2, 3, and 4), the reverse proxy performance is five times faster than direct access. When the reverse proxy deals with small messages, there is no big difference among Patterns 2, 3, and 4. However, when the reverse proxy is handling large messages, the differences become clear. When the message size is large, it is obvious that Patterns 3 and 4 are advantageous.
In real world applications, we expect a lower number for direct access because the application in application server is almost doing nothing. Real applications may require more processes, for example accessing databases. Therefore, in using a cache for a real application, the ratio of each pattern to direct access is expected to be higher than the result here.
The cache architecture for Web services and the experiments for performance evaluation are still preliminary. Although there are many research reports on caching Web content, as far as we know, no research has started addressing Web services. Therefore our goal is to provide an initial outlook for this research area.
In this paper, we have described a cache architecture for Web services, and given the result of initial experiments. Our architecture uses XML canonicalization and canonicalized templates in order to effectively apply existing cache technologies to Web services. Our experiments show that when we canonicalize requests, the performance is approximately doubled. When we use canonicalized templates, the performance improves approximately fivefold.