5 Using Memcached Clients with Oracle Coherence

You can configure a memcached adapter to allow Coherence to be used as a distributed cache for memcached clients. A simple hello world client that is written using the spymemcached API is provided for demonstration purposes; howver any existing memcached client can be used to connect to Coherence.

This chapter includes the following sections:

Overview of the Oracle Coherence Memcached Adapter

The memcached adapter provides access to Coherence caches over the memcached binary protocol and allows Coherence to be used as a drop-in replacement for a memcached server. The adapter supports any memcached client API that supports the memcached binary protocol. This allows memcached clients that are written in many different programming languages to use Coherence.

The memcached adapter is located on a Coherence proxy server and is implemented as a Coherence*Extend-styled acceptor. Memcached clients connect to the acceptor, which manages the distributed cache operations on the cluster. The cache operations are performed as entry processor operations. The acceptor must first be enabled within a proxy service in order to interact with Coherence cached data. Additional features for securing memcached client communication and for sharing data with native Coherence clients are provided and can be configured as required.

Figure 5-1 shows a conceptual view of a memcached client connecting to the memcached acceptor located on a Coherence proxy server in order to use a distributed cache.

Figure 5-1 Conceptual View of a Memcached Client Connection

Description of Figure 5-1 follows
Description of "Figure 5-1 Conceptual View of a Memcached Client Connection"

Setting Up the Memcached Adapter

Memcached adapters are configured within a proxy service using a specific memcached acceptor. The acceptor configuration defines the socket address and the distributed cache for use by memcached clients.

This section includes the following topics:

Define the Memcached Adapter Socket Address

The memcached adapter uses a socket address (IP, or DNS name, and port) for clients to connect to. The socket address is configured in an operational override configuration file using the <address-provider> element. The address is then referenced from a proxy service definition using the configured id attribute. See address-provider in Developing Applications with Oracle Coherence.

The following example configures a socket address and uses 198.168.1.5 for the IP address, 9099 for the port, and memcached for the ID.

...
<cluster-config>
   <address-providers>
      <address-provider id="memcached">
         <socket-address>
            <address>198.168.1.5</address>
            <port>9099</port>
         </socket-address>
      </address-provider>
   </address-providers>
</cluster-config>
...

Define Memcached Adapter Proxy Service

A proxy service allows remote clients to interact with the caching services of a Coherence cluster without becoming cluster members. A proxy service for the memcached adapter includes a specific memcached acceptor that accepts memcached client requests on a defined socket address and then delegates the requests to a distributed cache.

Note:

The memcached adapter can only use a distributed cache.

To create a proxy service for memcached clients, edit the cache configuration file and add a <proxy-scheme> element and include the <memcached-acceptor> element within the <acceptor-config> element. The <memcached-acceptor> element must include the name of the cache to use and a reference to an address provider definition that defines the socket address to listen to for memcached client communication. See memcached-acceptor in Developing Applications with Oracle Coherence.

The following example creates a proxy service and defines a memcached acceptor. The example references the address provider that was defined in Define the Memcached Adapter Socket Address.

...
<caching-schemes>
   <proxy-scheme>
      <service-name>MemcachedProxyService</service-name>
      <acceptor-config>
         <memcached-acceptor>
            <cache-name>hello-example</cache-name>
            <address-provider>memcached</address-provider>
         </memcached-acceptor>
      </acceptor-config>
      <autostart>true</autostart>
   </proxy-scheme>
</caching-schemes>
...

The cache name refers to the hello-example cache. The cache name must resolve to a distributed cache. The following example shows the definition of the hello-example cache and the distributed scheme to which it maps.

<?xml version="1.0"?>
<cache-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xmlns="http://xmlns.oracle.com/coherence/coherence-cache-config"
              xsi:schemaLocation=
                "http://xmlns.oracle.com/coherence/coherence-cache-config
                coherence-cache-config.xsd">
 
   <caching-scheme-mapping>
      <cache-mapping>
         <cache-name>hello-example</cache-name>
         <scheme-name>distributed</scheme-name>
      </cache-mapping> 
   </caching-scheme-mapping>
      
   <caching-schemes>
      <distributed-scheme>
         <scheme-name>distributed</scheme-name>
         <service-name>MemcachedTest</service-name>
         <backing-map-scheme>
           <local-scheme/>
         </backing-map-scheme>
         <autostart>true</autostart>
      </distributed-scheme> 
         
      <proxy-scheme>
         <service-name>MemcachedProxyService</service-name>
         <acceptor-config>
            <memcached-acceptor>
               <cache-name>hello-example</cache-name>
               <address-provider>memcached</address-provider>
            </memcached-acceptor>
         </acceptor-config>
         <autostart>true</autostart>
      </proxy-scheme>
   </caching-schemes>
</cache-config>

Connecting to the Memcached Adapter

Memcached clients must specify the address and port of a proxy service for the memcached adapter. The proxy service address is used in place of the memcached server address. Refer to your memcached client documentation for details on how to specify the address of a memcached server.

The following example shows a simple hello world client that uses the spymemcached client API to connect to the proxy service for the memcached adapter that was defined in Setting Up the Memcached Adapter.

import net.spy.memcached.AddrUtil;
import net.spy.memcached.BinaryConnectionFactory;
import net.spy.memcached.MemcachedClient;

public class MemcachedExample {
   public static void main(String[] args) throws Exception {
      String key = "k1";
      String value = "Hello World!";
        
      MemcachedClient c = new MemcachedClient(
         new BinaryConnectionFactory(),
         AddrUtil.getAddresses("198.168.1.5:9099"));

      c.add(key, 0, value);
      System.out.println((String)c.get(key));
      c.shutdown();
   }
}

Securing Memcached Client Communication

The memcached adapter can use both authentication and authorization to restrict access to cluster resources. Authentication support is provided for the SASL (Simple Authentication and Security Layer) plain authentication. Authorization is implemented using Oracle Coherence*Extend-styled authorization, which relies on interceptor classes that provide fine-grained access for cache service operations. The memcached adapter authentication and authorization features reuses much of the existing security capabilities of Oracle Coherence: references are provided to existing content where applicable.

This section includes the following topics:

Performing Memcached Client Authentication

Memcached clients can use SASL plain authentication to provide a username and password when connecting to the memcached adapter. To use SASL plain authentication, you must create an IdentityAsserter implementation on the proxy. The memcached adapter calls the IdentityAsserter implementation and passes the com.tangosol.net.security.UsernameAndPassword object as a token. See Using Identity Tokens to Restrict Client Connections in Securing Oracle Coherence. Refer to your memcached client documentation for details on establishing a SASL plain connection.

In addition to an IdentityAsserter implementation, authentication must be enabled on a memcached adapter to use SASL plain authentication. To enable authentication, edit the proxy service definition in the cache configuration file and add a <memcached-auth-method> element, within the <memcached-acceptor> element, and set it to plain.

...
<caching-schemes>
   <proxy-scheme>
      <service-name>MemcachedProxyService</service-name>
      <acceptor-config>
         <memcached-acceptor>
            <cache-name>hello-example</cache-name>
            <memcached-auth-method>plain</memcached-auth-method>
            <address-provider>memcached</address-provider>
         </memcached-acceptor>
      </acceptor-config>
      <autostart>true</autostart>
   </proxy-scheme>
</caching-schemes>
...

Performing Memcached Client Authorization

The memcached adapter relies on the Oracle Coherence*Extend authorization framework to restrict which operations a memcached client performs on a cluster. See Implementing Extend Client Authorization in Securing Oracle Coherence.

Sharing Data Between Memcached and Coherence Clients

The memcached adapter stores entries in a cache using a binary format. If you intend to share the data with Coherence clients, then memcached clients must use a serialization format that Coherence clients also support. Coherence clients typically use Portable Object Format (POF), which is highlighted in this section. See Using Portable Object Format in Developing Applications with Oracle Coherence.

This section includes the following topics:

Configuring POF for Memcached Clients

To configure POF for Memcached clients:

  1. Edit the proxy service definition in the cache configuration file and add an <interop-enabled> element, within the <memcached-acceptor> element, and set it to true.
    ...
    <proxy-scheme>
       <service-name>MemcachedProxyService</service-name>
       <acceptor-config>
          <memcached-acceptor>
             <cache-name>hello-example</cache-name>
             <interop-enabled>true</interop-enabled>
             <address-provider>memcached</address-provider>
          </memcached-acceptor>
       </acceptor-config>
       <autostart>true</autostart>
    </proxy-scheme>
    ...
    
  2. Enable POF on the distributed cache that is used by the memcached acceptor.
    ...
    <distributed-scheme>
       <scheme-name>distributed</scheme-name>
       <service-name>MemcachedTest</service-name>
       <serializer>
          <instance>
             <class-name>com.tangosol.io.pof.ConfigurablePofContext</class-name>
             <init-params>
                <init-param>
                   <param-type>String</param-type>
                   <param-value>memcached-pof-config.xml</param-value>
                </init-param>
             </init-params>
          </instance>
       </serializer>
       <backing-map-scheme>
          <local-scheme/>
       </backing-map-scheme>
       <autostart>true</autostart>
    </distributed-scheme>
    
  3. Register POF types in the defined POF configuration file. For the above example, the POF configuration file is named memcached-pof-config.xml. The file must be found on the classpath before the coherence.jar file. The following example defines a POF user type for the PofUser object:
    <?xml version='1.0'?>
     
    <pof-config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://xmlns.oracle.com/coherence/coherence-pof-config"
       xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-pof-config
       coherence-pof-config.xsd">
       <user-type-list>
          <include>coherence-pof-config.xml</include>
     
          <!-- User types must be above 1000 -->
          <user-type>
             <type-id>1001</type-id>
             <class-name>memcached.PofUser</class-name>
          </user-type>
     
       </user-type-list>
    </pof-config>
    

Create a Memcached Client that Uses POF

Many memcached client libraries include the ability to plug in custom serializers. Refer to your memcached client documentation for details on how to plug in custom serializers. The following excerpt shows a spymemcached client that adds the PofUser object that was registered in step 3 and uses a spymemcached transcoder to plug in the POF serializer.

MemcachedClient client = m_client;
String key = "pofKey";
PofUser user = new PofUser("memcached", 1);
PofTranscoder<PofUser> tc = new PofTranscoder("memcached-pof-config.xml");
 
if (!client.set(key, 0, user, tc).get())
   {
      throw new Exception("failed to set value");
   }

The POF transcoder plug-in is defined as follows:

import com.tangosol.io.pof.ConfigurablePofContext;
import com.tangosol.util.Binary;
import com.tangosol.util.ExternalizableHelper;
 
import net.spy.memcached.CachedData;
import net.spy.memcached.compat.SpyObject;
import net.spy.memcached.transcoders.Transcoder;
 
 
public class PofTranscoder<T> extends SpyObject implements Transcoder<T>
    {
 
    public PofTranscoder(String sLocator)
        {
        m_ctx = new ConfigurablePofContext(sLocator);
        }
 
    @Override
    public boolean asyncDecode(CachedData arg0)
        {
        return Boolean.FALSE;
        }
 
    @Override
    public T decode(CachedData cachedData)
        {
        int nFlag = cachedData.getFlags();
        Binary bin = new Binary(cachedData.getData());
        return (T) ExternalizableHelper.fromBinary(bin, m_ctx);
        }
 
    @Override
    public CachedData encode(Object obj)
        {
 
        byte[] oValue = ExternalizableHelper.toByteArray(obj, m_ctx);
        return new CachedData(FLAG, oValue, CachedData.MAX_SIZE);
        }
 
    @Override
    public int getMaxSize()
        {
        return CachedData.MAX_SIZE;
        }
 
    protected ConfigurablePofContext m_ctx;
 
    protected static final int       FLAG = 4;