In this recipe, we will be using Java to talk to our memcached server. Java is a very powerful programming language and is famous for enterprise-class applications.
There are, of course, a variety of memcached clients written for Java. We have chosen the most powerful client that is not hard to use, that is spymemcached
.
The spymemcached
java library has artifacts published on the maven central repository. If you are using maven you will need to add this to your pom.xml
file (highlighted):
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.sample</groupId> <artifactId>spycache</artifactId> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version> <name>spycache</name> <url>http://maven.apache.org</url> <dependencies> <dependency> <groupId>net.spy</groupId> <artifactId>spymemcached</artifactId> <version>2.10.1</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies> </project>
In my project here (named spycache), we will be showing snippets written inside the src/main/java/com/sample/
directory and to run the application you will need to run the mvn
package.
Then after seeing BUILD SUCCESS you will need to run the application by using the following:
java -cp target/spycache-1.0-SNAPSHOT.jar com.sample.App
Let's start by adding the following snippet into our main function:
try { MemcachedClient client = new MemcachedClient(new InetSocketAddress("127.0.0.1", 11211)); client.set("city", 20, "Istanbul"); System.out.println((String)client.get("city")); client.shutdown(); } catch (IOException e) { e.printStackTrace(); }
We have just created a connection and did a simple set/get operation. Let's now store a more complex object:
class Employee implements Serializable{ private static final long serialVersionUID = 2620538145665245947L; private String name; private int age; public Employee(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Employee(\"" + name + "\", " + age + ")"; } }
Then, let's store and retrieve an instance of this class:
Employee sample = new Employee("Ihab", 26); client.set("engineer", 20, sample); System.out.println((Employee)client.get("engineer"));
The output will be like the following:
2013-10-29 19:22:26.928 INFO net.spy.memcached.MemcachedConnection: Added {QA sa=/127.0.0.1:11211, #Rops=0, #Wops=0, #iq=0, topRop=null, topWop=null, toWrite=0, interested=0} to connect queue
2013-10-29 19:22:26.935 INFO net.spy.memcached.MemcachedConnection: Connection state changed for sun.nio.ch.SelectionKeyImpl@680e62df
Employee("Ihab", 26)
2013-10-29 19:22:26.964 INFO net.spy.memcached.MemcachedConnection: Shut down memcached client
Basically, we are creating a MemcachedClient
object that creates our memcached connection, note that this object is quite smart and does automatic reconnect on connection failure. By default, we are using the plain-text protocol but in the second example we are using the new binary protocol which is far more efficient.
The second snippet also shows how to configure MemcachedClient
to connect to multiple memcached servers (cluster), automatic data distribution will be done for you.
The first method we used on the client object is set
which is actually asynchronous, this means that it works like fire and forget, it does not block the current thread until the actual set operation happens. Then, we used get
which is synchronous (the opposite), it blocks until data is retrieved and returns that data as Object
, that's why we need casting to get a reference of the correct object type.
Java has an already very stable data serialization mechanism, and that's exactly what we have used. Standard types are serializable by default but what happens if you are trying to serialize your custom Employee
object to memcached? You need to implement the serializable interface for that, then magically everything works as presented in the last recipe.
Spymemcached
offers an asynchronous API for get
as well, it's quite interesting actually and if you are familiar with java.util.concurrent.Future
you will find it very easy to follow and understand.
The idea is that client.asyncGet
returns a Future<Object>
which you can use to retrieve the value asynchronously. You can use this future object to get the actual value later and set a timeout on your get request, as follows:
Future<Object> fobject = client.asyncGet("engineer"); try { fobject.get(10, TimeUnit.SECONDS); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } catch (TimeoutException e) { fobject.cancel(false); }
What you can see here, is that we tried to get
the future and we set 10
seconds for our trial, if the timeout expired, we get a TimeoutException
, and then we can safely cancel the operation (by calling cancel()
on the future). This means that we are not interested in the operation anymore.
You can also catch multiple exceptions to handle different types of potential failures, such as InterruptedException
, which means that something interrupted the background thread, or ExecutionException
, which means that an exception was thrown while trying to execute the background job.