by William Markito Oliveira
The right caching strategy can make a big difference in application performance.
Published July 2011Oracle Service Bus 11g provides built-in cache functionality that utilizes Oracle Coherence, offering first-class support for many caching strategies. This article describes how to use this functionality and explore a fail-over scenario with an example of how caching strategies can speed up your Web services and even prevent production outages.
This article is written with the assumption of knowledge in the following areas:
In our case study, we have a simple Web Service called CustomerService that is currently with some performance problems and for every request, it is spending nine seconds to respond. Here are the steps to create and simulate this “problematic” Web service:
1. Start Oracle Enterprise Pack Eclipse.
2. In the New Project window, select Web Service Project and click Next.
Figure 1 New Web Service Project
3. In the New Web Service Project window:
4. Right-click in the Customer project and create a new folder called schemas.
5. Create a new XML Schema CustomerType.xsd
Figure 2 New XML Schema
This schema will have the data structure the Web Service will use.
6. Copy and paste the following schema definition into the CustomerType.xsd file:
<schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.example.org/Customer"
xmlns:tns="http://www.example.org/Customer"
elementFormDefault="qualified">
<element name="customer" type="tns:CustomerType"></element>
<complexType name="CustomerType">
<sequence>
<element name="id" type="int"></element>
<element name="firstname" type="string"></element>
<element name="lastname" type="string"></element>
<element name="address" type="tns:AddressType"></element>
</sequence>
</complexType>
<complexType name="AddressType">
<sequence>
<element name="street" type="string"></element>
<element name="country" type="string"></element>
<element name="city" type="string"></element>
</sequence>
</complexType>
</schema>
Figure 3 Generate Java Classes from XML Schema using JAXB
compiling a schema...
com/oracle/examples/entities/AddressType.java
com/oracle/examples/entities/CustomerType.java
com/oracle/examples/entities/ObjectFactory.java
com/oracle/examples/entities/package-info.java
com/oracle/examples/entities/jaxb.properties
Package: com.oracle.examples.service
Name: Customer
import javax.jws.WebMethod;
import javax.jws.WebService;
import com.oracle.examples.entities.AddressType;
import com.oracle.examples.entities.CustomerType;
import com.oracle.examples.entities.ObjectFactory;
@WebService
public class Customer {
@WebMethod
public CustomerType getCustomer(int id) {
ObjectFactory factory = new ObjectFactory();
AddressType addressType = factory.createAddressType();
CustomerType customerType = factory.createCustomerType();
switch (id) {
case 1:
addressType.setCity("São Paulo");
addressType.setCountry("BRA");
addressType.setStreet("Marginal Pinheiros");
customerType.setId(1);
customerType.setFirstname("João");
customerType.setLastname("Silva");
customerType.setAddress(addressType);
break;
case 2:
addressType.setCity("San Francisco");
addressType.setCountry("USA");
addressType.setStreet("Some address");
customerType.setId(2);
customerType.setFirstname("John");
customerType.setLastname("Lock");
customerType.setAddress(addressType);
break;
default:
break;
}
// forcing slow down
try {
Thread.currentThread().sleep(9000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return customerType;
}
}
This Web service will return fixed values but of course you should do a real Customer search in a database using JPA and/or Oracle TopLink. Also, to simulate the slowness on our Web service there is a Thread.sleep() call that will cause our service to return only after nine seconds.
13. Deploy this project to a running instance of WebLogic Server and hit the URLs below to see the WSDL and test using WebLogic Test Client.
WSDL: http://localhost:7001/Customer/CustomerService?WSDL
TEST URL: http://localhost:7001/wls_utc/?wsdlUrl=http://localhost:7001/Customer/CustomerService?WSDL
Note: You can deploy this project to same server you will be running Oracle Service Bus.
14. When you invoke the service method getCustomer, you'll notice that it does indeed take nine seconds to execute.
Figure 4 Problematic Web Service Response
In the next section we will import the service into Oracle Service Bus and then learn how service caching can reduce the impact of this performance problem.
1. Open Oracle Service Bus Console: http://localhost:7001/sbconsole
2. Click in Project Explorer, start a Session and create a new project. Name it Customer for example.
3. Click in the Customer project and look for the Create Resource combo box. Select Resources from URL under Bulk.
4.Fill the form with the following information:
Figure 5 Import WSDL Resource into Oracle Service BUS
5. Click Next and then Import.
Figure 6 Business Service Creation
Figure 7 Request with performance problems
Figure 8 Web Service RESPONSE after nine seconds
Note that all service calls are really taking around nine seconds to execute. Sometimes it can take a little bit longer with an additional one or two seconds, but never less than nine seconds since this is the “response time” of our backend Web service.
In the next section we will demonstrate how Oracle Service Bus and Oracle Coherence are integrated and how you can escalate the solution using an out-of-process caching strategy.
Oracle Service Bus always had some mechanics of caching in order to provide XQuery and XML beans caching, in addition to object caching for Proxy and Business Services classes. But from 11g Release 1 onward, Oracle Service Bus offered an integrated solution for result caching with Oracle Coherence.
The result caching in Oracle Service Bus is available for Business Services. When you enable result caching, the Business Service will not reach the backend service and the response will come from the cache entry. This entry can expire and if the entry is expired, the next service call will indeed reach the backend service and update the cache entry.
Figure 9 How result caching works
To enable result caching for our Business Service follow these steps:
Figure 10 Enable Business Service Result Cache
Figure 11 Business Service Caching Settings
This will use the customer ID as our unique token for the cache entry. Also, set the Expiration Time for 10 minutes.
The first time you execute the service, it will take nine seconds to execute and it will populate our cache entry. Click Back in the Test Console and try to execute again; it will show the results immediately, directly from a cache entry in the embedded Oracle Coherence Server running within Oracle Service Bus. The cache Expiration Time (Time-to-Live) was set to 10 minutes in Step 5, so during this period your calls will not hit the backend Web service.
We have now provided one solution for our problematic service. But what if you want to see what’s going on with you cache, or want to know many entries you have in your cache server?
Oracle Coherence offers good APIs for JMX and Reports about cache servers, and that’s definitely the way to go for monitoring production environments with huge loads. But for development environments and other critical monitoring situations, you can use the Coherence Console.
Coherence Console is a self-contained command line utility that connects to specific Coherence Servers. It is a good tool to check which servers are participating in the cluster and for browsing the cache data. That’s the tool we will be using in the example to monitor our cache entries and Coherence servers.
In order to integrate Coherence Console and Oracle Service Cache you need to specify a few things:
#!/bin/sh # Middleware home
MDW=/opt/oracle/osb11114
# Service Bus installation folder
OSB=$MDW/Oracle_OSB
# Domain home
DM=/opt/oracle/domains/osb11gR1
# ----------------------------------------------------------------------------
if [ -f $JAVA_HOME/bin/java ]; then
JAVAEXEC=$JAVA_HOME/bin/java
else
JAVAEXEC=java
fi
OPTS="-Xms64m -Xmx64m
-Dtangosol.coherence.override=$DM/config/osb/coherence/osb-coherence-override.xml
-Dtangosol.coherence.cacheconfig=$DM/config/osb/coherence/osb-coherence-cache-config.xml
-Dtangosol.coherence.distributed.localstorage=false
-Dtangosol.coherence.cluster=OSB-cluster
-Dtangosol.coherence.localhost=localhost
-DOSB.coherence.cluster=OSB-cluster"
CP="$MDW/oracle_common/modules/oracle.coherence_3.6/coherence.jar"
CP=$CP:"$MDW/modules/features/weblogic.server.modules.coherence.server_10.3.4.0.jar"
CP=$CP:"$OSB/lib/osb-coherence-client.jar"
$JAVAEXEC -server -showversion $OPTS -cp $CP com.tangosol.net.CacheFactory $1
markito@anakin:~/Projects/PTS$ ./consoleOSB.sh
…
2011-06-17 16:14:35.612/0.240 Oracle Coherence 3.6.0.4 (thread=main, member=n/a): Loaded operational configuration from "jar:file:/opt/oracle/osb11114/oracle_common/modules/oracle.coherence_3.6/coherence.jar!/tangosol-coherence.xml"
2011-06-17 16:14:35.621/0.249 Oracle Coherence 3.6.0.4 (thread=main, member=n/a): Loaded operational overrides from
"file:/opt/oracle/domains/osb11gR1/config/osb/coherence/osb-coherence-override.xml"
2011-06-17 16:14:35.629/0.257 Oracle Coherence 3.6.0.4 (thread=main, member=n/a): Optional configuration override "/custom-mbeans.xml" is not specified
Oracle Coherence Version 3.6.0.4 Build 19111
Grid Edition: Development mode
Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
2011-06-17 16:14:36.127/0.755 Oracle Coherence GE 3.6.0.4 (thread=main, member=n/a): Local address "127.0.0.1" is a loopback address;
this cluster node will not connect to nodes located on different machines
2011-06-17 16:14:36.136/0.764 Oracle Coherence GE 3.6.0.4 (thread=main, member=n/a): TCMP bound to /127.0.0.1:7892 using SystemSocketProvider
2011-06-17 16:14:36.445/1.073 Oracle Coherence GE 3.6.0.4 (thread=Cluster, member=n/a): This Member(Id=4, Timestamp=2011-06-17 16:14:36.262,
Address=127.0.0.1:7892, MachineId=26733, Location=site:localdomain,machine:localhost,process:13130, Role=CoherenceConsole,
Edition=Grid Edition, Mode=Development, CpuCount=4, SocketCount=2) joined cluster "OSB-cluster" with senior Member(Id=1, Timestamp=2011-06-17 11:00:46.772,
Address=127.0.0.1:7890, MachineId=26733, Location=site:localdomain,machine:localhost,
process:5603, Role=OSB-node, Edition=Grid Edition, Mode=Development, CpuCount=4, SocketCount=2)
2011-06-17 16:14:36.454/1.082 Oracle Coherence GE 3.6.0.4 (thread=Cluster, member=n/a): Member 1 joined Service Cluster with senior member 1
2011-06-17 16:14:36.454/1.082 Oracle Coherence GE 3.6.0.4 (thread=Cluster, member=n/a): Member 1 joined Service Management with senior member 1
2011-06-17 16:14:36.454/1.082 Oracle Coherence GE 3.6.0.4 (thread=Cluster, member=n/a): Member 1 joined Service ORA-OSB-deployments with senior member 1
2011-06-17 16:14:36.457/1.085 Oracle Coherence GE 3.6.0.4 (thread=main, member=n/a): Started cluster Name=OSB-cluster
WellKnownAddressList(Size=2,
WKA{Address=127.0.0.1, Port=9999}
WKA{Address=127.0.0.1, Port=7890}
)
MasterMemberSet
(
ThisMember=Member(Id=4, Timestamp=2011-06-17 16:14:36.262, Address=127.0.0.1:7892, MachineId=26733,
Location=site:localdomain,machine:localhost,process:13130, Role=CoherenceConsole)
OldestMember=Member(Id=1, Timestamp=2011-06-17 11:00:46.772, Address=127.0.0.1:7890,
MachineId=26733, Location=site:localdomain,machine:localhost,process:5603, Role=OSB-node)
ActualMemberSet=MemberSet(Size=2, BitSetCount=2
Member(Id=1, Timestamp=2011-06-17 11:00:46.772, Address=127.0.0.1:7890, MachineId=26733,
Location=site:localdomain,machine:localhost,process:5603, Role=OSB-node)
Member(Id=4, Timestamp=2011-06-17 16:14:36.262, Address=127.0.0.1:7892, MachineId=26733,
Location=site:localdomain,machine:localhost,process:13130, Role=CoherenceConsole)
)
RecycleMillis=1200000
RecycleSet=MemberSet(Size=0, BitSetCount=0
)
)
TcpRing{Connections=[1]}
IpMonitor{AddressListSize=0}
2011-06-17 16:14:36.500/1.128 Oracle Coherence GE 3.6.0.4 (thread=Invocation:Management, member=4):
Service Management joined the cluster with senior service member 1
Map (?):
Map (?): cache /osb/service/ResultCache
2011-06-17 16:27:25.807/770.435 Oracle Coherence GE 3.6.0.4 <Info> (thread=main, member=4): Loaded cache configuration from "file:/opt/oracle/domains/osb11gR1/config/osb/coherence/osb-coherence-cache-config.xml"
2011-06-17 16:27:26.352/770.980 Oracle Coherence GE 3.6.0.4 <D5> (thread=DistributedCache:ORA-OSB-deployments, member=4): Service ORA-OSB-deployments joined the cluster with senior service member 1
<distributed-scheme>
<scheme-name>expiring-distributed</scheme-name>
<service-name>ORA-OSB-deployments</service-name>
<backing-map-scheme>
<local-scheme>
<scheme-ref>expiring-backing-map</scheme-ref>
</local-scheme>
</backing-map-scheme>
<autostart>true</autostart>
</distributed-scheme>
Map (/osb/service/ResultCache): size
0
Map (/osb/service/ResultCache): size
1
Map (/osb/service/ResultCache): list
PipelineResultCacheKey[BusinessService Customer/CustomerBS,getCustomer,1) = owner=BusinessService Customer/CustomerBS,value=[B@58c16b18
Now that we already have our service cache enabled, we will have a better throughput performance and can attend many more users in our system. However, remember that we are still using an in-process strategy, and that means we are using the same JVM memory for cache entries, WebLogic services like JDBC or JMS, and Oracle Service Bus objects, like transformations, Proxy Services and Business Services. If we increase our cache usage to a high volume, we can potentially run out of memory easily and compromise all other services in the Enterprise Service Bus, even the ones that are not using cache features.
To solve this situation you can use an external Coherence Server -- that is, a dedicated JVM with its own Coherence Server. This solution can increase the scalability of our overall architecture and a more complex Coherence Cluster infrastructure can take in place. It is out of the scope of this article discuss the configuration of Coherence Clusters, but it will give an idea of what can be achieved and then adjusted for your situation.
These are the steps to add a new Coherence Server using WebLogic Console:
Figure 12 Adding a new machine
Figure 13 Node Manager properties
Figure 14 Creating a New Coherence Server
/opt/oracle/osb11114/modules/features/weblogic.server.modules.coherence.server_10.3.4.0.jar:/opt/oracle/osb11114/coherence_3.6/lib/coherence.jar:/opt/oracle/osb11114/Oracle_OSB/lib/osb-coherence-client.jar
-Dtangosol.coherence.override=/opt/oracle/domains/osb11gR1/config/osb/coherence/osb-coherence-override.xml
-Dtangosol.coherence.cacheconfig=/opt/oracle/domains/osb11gR1/config/osb/coherence/osb-coherence-cache-config.xml
-Dtangosol.coherence.distributed.localstorage=true
-Dtangosol.coherence.cluster=OSB-cluster
-Dtangosol.coherence.localhost=localhost
-DOSB.coherence.cluster=OSB-cluster
Figure 15 Coherence Server settings
<!DOCTYPE coherence SYSTEM "coherence.dtd">
<coherence>
<cluster-config>
<!--
By specifying a well-known-address we disable the mutlicast listener.
This ensures that the Coherence cluster for OSB will be isolated to this machine only.
-->
<unicast-listener>
<well-known-addresses>
<socket-address id="1">
<address system-property="OSB.coherence.wka1">127.0.0.1</address>
<port system-property="OSB.coherence.wka1.port">7890</port>
</socket-address>
<socket-address id="2">
<address system-property="OSB.coherence.wka2">127.0.0.1</address>
<port system-property="OSB.coherence.wka2.port">9999</port>
</socket-address>
</well-known-addresses>
<address system-property="OSB.coherence.localhost">127.0.0.1</address>
<port system-property="OSB.coherence.localport">7890</port>
</unicast-listener>
<multicast-listener>
<time-to-live system-property="OSB.coherence.ttl">0</time-to-live>
</multicast-listener>
</cluster-config>
</coherence>
Here we are adding a new socket address to the Oracle Coherence cluster. For security and performance reasons, Oracle Coherence server that is bundled with Oracle Service Bus uses WKA and Unicast, so for every new node you add to the cluster this file needs to be updated or make sure that at least one of the nodes specified here is running when trying to start other nodes unlisted. In this case, we are specifying a new server listening in the localhost address (127.0.0.1) and port 9999.
-DOSB.coherence.wka2=localhost
-DOSB.coherence.wka2.port=9999
Congratulations, you have a new Coherence Server up and running integrated with Oracle Service Bus, and can manage your server through WebLogic Console with the help of a Node Manager. In the next sections we will test this integration and play with our cache server.
Now it is time to test our out-of-process strategy and execute our problematic Web service again. Remember that both servers -- the internal server bundled with Oracle Service Bus and the external server1 -- can still store cached data, because they have tangosol.coherence.distributed.localstorage property set to true.
To test your out-of-process cache you can do the following:
………
…
WellKnownAddressList(Size=2,
WKA{Address=127.0.0.1, Port=7890}
WKA{Address=127.0.0.1, Port=9999}
)
MasterMemberSet
(
ThisMember=Member(Id=3, Timestamp=2011-06-20 15:03:22.947, Address=127.0.0.1:7894, MachineId=8417, Location=site:localdomain,machine:localhost,process:11912, Role=CoherenceConsole)
OldestMember=Member(Id=1, Timestamp=2011-06-20 14:34:49.401, Address=127.0.0.1:7890, MachineId=8417, Location=site:localdomain,machine:localhost,process:10716, Role=OSB-node)
ActualMemberSet=MemberSet(Size=3, BitSetCount=2
Member(Id=1, Timestamp=2011-06-20 14:34:49.401, Address=127.0.0.1:7890, MachineId=8417, Location=site:localdomain,machine:localhost,process:10716, Role=OSB-node)
Member(Id=2, Timestamp=2011-06-20 14:47:55.749, Address=127.0.0.1:7892, MachineId=8417, Location=site:localdomain,machine:localhost,process:11562,member:server1, Role=WebLogicWebLogicCacheServer)
Member(Id=3, Timestamp=2011-06-20 15:03:22.947, Address=127.0.0.1:7894, MachineId=8417, Location=site:localdomain,machine:localhost,process:11912, Role=CoherenceConsole)
)
RecycleMillis=1200000
RecycleSet=MemberSet(Size=0, BitSetCount=0
)
)
TcpRing{Connections=[2]}
IpMonitor{AddressListSize=0}
Note that you now have two address in the Well Know Address List and three members in the Coherence cluster: OSB-node (internal), server1 (external) and CoherenceConsole (which is the console we are using).
Map (?): cache /osb/service/ResultCache
As we can see in the previous section, the external Coherence server (server1) is already taking care of cached data storage. But we are still doing cache in both Coherence servers, internal and external, and for a production environment it may be a good idea to disable the storage of internal Coherence server and free some JVM memory that can be used for other services like JDBC, JMS, or service processing. With this change all cached data will be stored only in the external Coherence server (server1).
To execute these settings follow the steps below:
"${JAVA_OPTIONS} ${JAVA_PROPERTIES}
-Dwlw.iterativeDev=${iterativeDevFlag} -Dwlw.testConsole=${testConsoleFlag}
-Dwlw.logErrorsToConsole=${logErrorsToConsoleFlag} "
JAVA_OPTIONS="${JAVA_OPTIONS} ${JAVA_PROPERTIES} -Dwlw.iterativeDev=${iterativeDevFlag} -Dwlw.testConsole=${testConsoleFlag}
-Dwlw.logErrorsToConsole=${logErrorsToConsoleFlag} -Dtangosol.coherence.distributed.localstorage=false "
/opt/oracle/jrockit-jdk1.6.0_20-R28.1.0-4.0.1/bin/java -jrockit -Xdebug -Xnoagent -
Xrunjdwp:transport=dt_socket,address=8453, server=y,suspend=n -Djava.compiler=NONE -Xms648m -Xmx768m -
Dweblogic.Name=AdminServer -Djava.security.policy=/opt/oracle/osb11114/wlserver_10.3/server/lib/weblogic.policy -
Xverify:none -da:org.apache.xmlbeans... -ea -da:com.bea... -da:javelin... -da:weblogic... -ea:com.bea.wli... -
ea:com.bea.broker... -ea:com.bea.sbconsole... -Dplatform.home=/opt/oracle/osb11114/wlserver_10.3 -
Dwls.home=/opt/oracle/osb11114/wlserver_10.3/server -Dweblogic.home=/opt/oracle/osb11114/wlserver_10.3/server -
Dcommon.components.home=/opt/oracle/osb11114/oracle_common -Djrf.version=11.1.1 -
Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.Jdk14Logger -Ddomain.home=/opt/oracle/domains/osb11gR1 -
Djrockit.optfile=/opt/oracle/osb11114/oracle_common/modules/oracle.jrf_11.1.1/jrocket_optfile.txt -
Doracle.server.config.dir=/opt/oracle/domains/osb11gR1/config/fmwconfig/servers/AdminServer -
Doracle.domain.config.dir=/opt/oracle/domains/osb11gR1/config/fmwconfig -
Digf.arisidbeans.carmlloc=/opt/oracle/domains/osb11gR1/config/fmwconfig/carml -
Digf.arisidstack.home=/opt/oracle/domains/osb11gR1/config/fmwconfig/arisidprovider -
Doracle.security.jps.config=/opt/oracle/domains/osb11gR1/config/fmwconfig/jps-config.xml -
Doracle.deployed.app.dir=/opt/oracle/domains/osb11gR1/servers/AdminServer/tmp/_WL_user -Doracle.deployed.app.ext=/- -
Dweblogic.alternateTypesDirectory=/opt/oracle/osb11114/oracle_common/modules/oracle.ossoiap_11.1.1,/opt/oracle/osb11114/oracle_common/modules/oracle.oamprovider_11.1.1
-Djava.protocol.handler.pkgs=oracle.mds.net.protocol -Dweblogic.jdbc.remoteEnabled=false -
Dem.oracle.home=/opt/oracle/osb11114/oracle_common -Djava.awt.headless=true -Dweblogic.management.discover=true -
Dwlw.iterativeDev= -Dwlw.testConsole= -Dwlw.logErrorsToConsole= -Dtangosol.coherence.distributed.localstorage=false -
Dweblogic.ext.dirs=/opt/oracle/osb11114/patch_wls1034/profiles/default/sysext_manifest_classpath:/opt/oracle/osb11114/patch_ocp360/profiles/default/sysext_manifest_classpath weblogic.Server
In this article was presented two different cache strategies and the benefits of using out-of-process caching with Oracle Coherence and Oracle Service Bus. There was an example of a Web service with performance problems exposed in Oracle Service Bus and through the use of result caching the response time improvement is clear. Also, the Oracle Coherence Console integration with Oracle Service Bus is an alternative for development and troubleshooting of service-caching solutions that shows the running servers and cached data information.
Out-of-process caching can drastically reduce the JVM memory footprint of an Oracle Service Bus domain and increase the scalability and fail-over of the architecture, and it is a recommended approach for projects that needs to scale safely.
William Markito Oliveira is a Senior Technologist at Oracle Platform Technology Solutions team in Brazil where he focuses in Middleware, SOA and Java technologies. He is also as contributor to the official Java EE 6 Tutorial providing write-ups and code examples about CDI, EJB 3.1 and JAX-RS.