Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 4 of 6
interface IHttpResponseCache {
IHttpResponse put(String key, IHttpResponse response) throws IOException;
void remove(String key) throws IOException;
IHttpResponse get(String key) throws IOException;
}
class RemoteHttpResponseCache implements IHttpResponseCache {
private MemcachedClient memCachedClient;
public RemoteHttpResponseCache(InetSocketAddress... cacheServers) throws IOException {
memCachedClient = new MemcachedClient(Arrays.asList(cacheServers));
}
public IHttpResponse put(String key, IHttpResponse response) throws IOException {
byte[] bodyData = response.getBlockingBody().readBytes();
memCachedClient.set(key, 3600, bodyData);
return null;
}
public IHttpResponse get(String key) throws IOException {
byte[] bodyData = (byte[]) memCachedClient.get(key);
if (bodyData != null) {
return new HttpResponse(200, "text/plain", bodyData);
} else {
return null;
}
}
public void remove(String key) throws IOException {
memCachedClient.delete(key);
}
}
Listing 2 and the rest of this article's example code also uses the xLightweb HTTP library. Listing 3 shows an example business service implementation. The onRequest(...) method -- similar to the Servlet API's goGet(...) or doPost(...) method -- is called each time a request header is received. The exchange.send() method sends the response.
class MyRequestHandler implements IHttpRequestHandler {
public void onRequest(IHttpExchange exchange) throws IOException {
IHttpRequest request = exchange.getRequest();
int customerId = request.getRequiredIntParameter("id");
long amount = request.getRequiredLongParameter("amount");
//...
// perform some operations
//..
String response = ...
// and return the response
exchange.send(new HttpResponse(200, "text/plain", response));
}
}
class Server {
public static void main(String[] args) throws Exception {
HttpServer httpServer = new HttpServer(8180, new MyRequestHandler());
httpServer.run();
}
}
Based on the HttpResponse cache, a simple caching solution can be implemented that caches the HTTP response for an HTTP request. If the same request
is received twice, the corresponding response can be taken from the cache, without calling the business service. This requires
intercepting the request-handling flow. This can be done by the interceptor shown in Listing 4.
class CacheInterceptor implements IHttpRequestHandler {
private IHttpResponseCache cache;
public CacheInterceptor(IHttpResponseCache cache) {
this.cache = cache;
}
public void onRequest(final IHttpExchange exchange) throws IOException {
IHttpRequest request = exchange.getRequest();
// check if request is cacheable (Cache-Control header, ...)
// ...
boolean isCacheable = ...
// if request is not cacheable forward it to the next handler of the chain
if (!isCacheable) {
exchange.forward(request);
return;
}
// create the cache key
StringBuilder sb = new StringBuilder(request.getRequestURI());
TreeSet<String> sortedParamNames = new TreeSet<String>(request.getParameterNameSet());
for (String paramName : sortedParamNames) {
sb.append(URLEncoder.encode(paramName) + "=");
List<String> paramValues = Arrays.asList(request.getParameterValues(paramName));
Collections.sort(paramValues);
for (String paramValue : paramValues) {
sb.append(URLEncoder.encode(paramValue) + ", ");
}
}
final String cacheKey = URLEncoder.encode(sb.toString());
// is request in cache?
IHttpResponse cachedResponse = cache.get(cacheKey);
if (cachedResponse != null) {
IHttpResponse response = HttpUtils.copy(cachedResponse);
response.setHeader("X-Cached", "true");
exchange.send(response);
// .. no -> forward it to the next handler of the chain
} else {
// define a intermediate response handler to intercept and copy the response
IHttpResponseHandler respHdl = new IHttpResponseHandler() {
@InvokeOn(InvokeOn.MESSAGE_RECEIVED)
public void onResponse(IHttpResponse response) throws IOException {
cache.put(cacheKey, HttpUtils.copy(response));
exchange.send(response); // forward the response to the client
}
public void onException(IOException ioe) throws IOException {
exchange.sendError(ioe); // forward the error to the client
}
};
// forward the request to the next handler of the chain
exchange.forward(request, respHdl);
}
}
}
class Server {
public static void main(String[] args) throws Exception {
RequestHandlerChain handlerChain = new RequestHandlerChain();
handlerChain.addLast(new CacheInterceptor(new RemoteHttpResponseCache(new InetSocketAddress(cachSrv1, 11211), new InetSocketAddress(cachSrv2, 11211))));
handlerChain.addLast(new MyRequestHandler());
HttpServer httpServer = new HttpServer(8180, handlerChain);
httpServer.run();
}
}
The CacheInterceptor in Listing 4 uses the memcached-based implementation to cache responses, based on the hashcode of dedicated header attributes. If the cache contains a response
for this hashcode, the request is not forwarded to the business-service handler. Instead, the response is returned from the
cache. If the cache does not contain a response, the request is forwarded by adding a response handler to intercept the response
flow. If a response is received from the business-service handler, the response is added to the cache. (Note that Listing
4 does not show cache invalidation. Often dedicated business operations require the cache entry to be invalidated.)
spymemcached, an improved memcached client for Java.
More