Skip to content

Commit

Permalink
Merge branch 'master' of github.com:Netflix/EVCache
Browse files Browse the repository at this point in the history
  • Loading branch information
smadappa committed Jun 29, 2016
2 parents ac14d5b + 857adac commit 5ee6245
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 84 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ final public class EVCacheImpl implements EVCache {

stats = EVCacheMetricsFactory.getStats(appName, cacheName);
_metricName = (_cacheName == null) ? _appName : _appName + "." + _cacheName;
_metricPrefix = (_cacheName == null) ? _appName : _appName + "-" + _cacheName + "-";
_metricPrefix = ( (_cacheName == null) ? _appName : _appName + "-" + _cacheName ) + "-";
this._poolManager = poolManager;
this._pool = poolManager.getEVCacheClientPool(_appName);
final EVCacheConfig config = EVCacheConfig.getInstance();
Expand Down Expand Up @@ -674,7 +674,7 @@ public Future<Boolean>[] touch(String key, int timeToLive) throws EVCacheExcepti
if (touchTTLSummary == null) this.touchTTLSummary = EVCacheConfig.getInstance().getDistributionSummary(_appName + "-TouchData-TTL");
if (touchTTLSummary != null) touchTTLSummary.record(timeToLive);

if (touchCounter == null) this.touchCounter = EVCacheMetricsFactory.getCounter(_appName, _cacheName, _metricPrefix + "-TouchCall", DataSourceType.COUNTER);
if (touchCounter == null) this.touchCounter = EVCacheMetricsFactory.getCounter(_appName, _cacheName, _metricPrefix + "TouchCall", DataSourceType.COUNTER);
if (touchCounter != null) touchCounter.increment();
if (event != null) {
event.setCanonicalKeys(Arrays.asList(canonicalKey));
Expand Down
24 changes: 24 additions & 0 deletions evcacheproxy/README
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
REST API Service is for the clients that are Non-Java clients such as Python , JavaScript , Shell etc., and store key , value in memcached.Service supports basic REST (CRUD) Operations for evcache.

PRE-REQUISTES:
1. memcached must be deployed and running.

Basic REST Operations :



HTTP Method: GET
URI : http://[hostname]/evcrest/v1.0/{cacheName}/{key}
Action : Retrieve key value
Response : 200 Success, 404 Not found

HTTP Method: POST
URI : http://[hostname]/evcrest/v1.0/{cacheName}/{key}?ttl=[ttl-for-key]
Action : store key value in evcache ( binary)
Response : 202 Success, 400 Bad Request

HTTP Method: DELETE
URI : http://[hostname]/evcrest/v1.0/{cacheName}/{key}
Action : Delete key from cache
Response : 200 Success

Original file line number Diff line number Diff line change
@@ -1,144 +1,136 @@
package com.netflix.evcache.service.resources;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Future;

import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.inject.Inject;
import com.netflix.evcache.EVCache;
import com.netflix.evcache.EVCacheException;
import com.netflix.evcache.service.transcoder.RESTServiceTranscoder;
import net.spy.memcached.CachedData;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Future;


/**
* Created by senugula on 3/22/16.
*/
@Path("/evcache")
@Path("/evcrest/v1.0")
public class EVCacheRESTService {

private Logger logger = LoggerFactory.getLogger(EVCacheRESTService.class);

private final EVCache.Builder builder;
private final Map<String, EVCache> evCacheMap;
private final RESTServiceTranscoder evcacheTranscoder = new RESTServiceTranscoder();

@Inject
public EVCacheRESTService(EVCache.Builder builder) {
this.builder = builder;
this.evCacheMap = new HashMap<>();
}

@GET
@Path("get/{appId}/{key}")
@Produces("text/plain")
public Response getOperation(@PathParam("appId") String appId,
@PathParam("key") String key) {
if(logger.isDebugEnabled()) logger.debug("Get for application " + appId + " for Key " + key);
final EVCache evCache = getEVCache(appId);
@POST
@Path("{appId}/{key}")
@Consumes({MediaType.APPLICATION_OCTET_STREAM})
@Produces(MediaType.TEXT_PLAIN)
public Response setOperation(final InputStream in, @PathParam("appId") String appId, @PathParam("key") String key,
@QueryParam("ttl") String ttl) {
try {
final Object _response = evCache.get(key);
if(_response == null) {
return Response.ok("Key " + key + " was not found.").build();
} else {
return Response.ok(_response).build();
byte[] bytes = IOUtils.toByteArray(in);
final EVCache evcache = getEVCache(appId);
if (ttl == null) {
return Response.status(400).type("text/plain").entity("Please specify ttl for the key " + key + " as query parameter \n").build();
}
int timeToLive = Integer.valueOf(ttl).intValue();
Future<Boolean>[] _future = evcache.set(key, bytes, timeToLive);
if (_future.equals(Boolean.TRUE)) {
if (logger.isDebugEnabled()) logger.debug("set key is successful \n");
}
return Response.ok("Set Operation for Key - " + key + " was successful. \n").build();
} catch (EVCacheException e) {
e.printStackTrace();
return Response.serverError().build();
} catch (Throwable t) {
return Response.serverError().build();
}
return Response.serverError().build();
}

@GET
@Path("set/{appId}/{key}/{ttl}/{value}")
@Produces("text/plain")
public Response setOperation(@PathParam("appId") String appId, @PathParam("key") String key,
@PathParam("ttl") String ttl, @PathParam("value") String value) {
if(logger.isDebugEnabled()) logger.debug("Set for application " + appId + " for Key " + key + " value " + value + " with ttl " + ttl );
@PUT
@Path("{appId}/{key}")
@Consumes({MediaType.APPLICATION_OCTET_STREAM})
@Produces(MediaType.TEXT_PLAIN)
public Response putOperation(final InputStream in, @PathParam("appId") String appId, @PathParam("key") String key,
@QueryParam("ttl") String ttl) {
try {
byte[] bytes = IOUtils.toByteArray(in);
final EVCache evcache = getEVCache(appId);
if (ttl == null) {
return Response.status(400).type("text/plain").entity("Please specify ttl for the key " + key + " as query parameter \n").build();
}
int timeToLive = Integer.valueOf(ttl).intValue();
Future<Boolean>[] _future = evcache.set(key, value, timeToLive);
Future<Boolean>[] _future = evcache.set(key, bytes, timeToLive);
if (_future.equals(Boolean.TRUE)) {
if(logger.isDebugEnabled()) logger.debug("set key is successful");
if (logger.isDebugEnabled()) logger.debug("set key is successful \n");
}
return Response.ok("Set Operation for Key - " + key + " was successful.").build();
} catch (Exception e) {
return Response.ok("Set Operation for Key - " + key + " was successful. \n").build();
} catch (EVCacheException e) {
e.printStackTrace();
return Response.serverError().build();
} catch (Throwable t) {
return Response.serverError().build();
}
return Response.serverError().build();
}

@GET
@Path("delete/{appId}/{key}")
@Consumes(MediaType.APPLICATION_JSON)
@Produces("text/plain")
public Response delete(@PathParam("appId") String appId, @PathParam("key") String key) {
if(logger.isDebugEnabled()) logger.debug("Get for application " + appId + " for Key " + key);
final EVCache evCache = getEVCache(appId);
@Path("{appId}/{key}")
@Produces({MediaType.APPLICATION_OCTET_STREAM})
public Response getOperation(@PathParam("appId") String appId,
@PathParam("key") String key) {
if (logger.isDebugEnabled()) logger.debug("Get for application " + appId + " for Key " + key);
try {
Future<Boolean>[] _future = evCache.delete(key);
if (_future.equals(Boolean.TRUE)) {
if(logger.isDebugEnabled()) logger.debug("set key is successful");
final EVCache evCache = getEVCache(appId);
CachedData cachedData = evCache.get(key, evcacheTranscoder);
if (cachedData == null) {
return Response.status(404).type("text/plain").entity("Key " + key + " Not Found in cache " + appId + "\n").build();
}
return Response.ok("Deleted Operation for Key - " + key + " was successful.").build();
} catch (EVCacheException e) {
e.printStackTrace();
}
return Response.serverError().build();
}

@POST
@Path("set/{appId}/{key}/{ttl}")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces(MediaType.TEXT_PLAIN)
public Response postSetOperation(@PathParam("appId") String appId, @PathParam("key") String key,
@PathParam("ttl") String ttl, @FormParam("value") String value) {
if(logger.isDebugEnabled()) logger.debug("Post for application " + appId + " for Key " + key + " value " + value);
final EVCache evcache = getEVCache(appId);
try {
int timeToLive = Integer.valueOf(ttl).intValue();
Future<Boolean>[] _future = evcache.set(key, value, timeToLive);
if (_future.equals(Boolean.TRUE)) {
if(logger.isDebugEnabled()) logger.debug("set key is successful");
byte[] bytes = cachedData.getData();
if (bytes == null) {
return Response.status(404).type("text/plain").entity("Key " + key + " Not Found in cache " + appId + "\n").build();
} else {
return Response.status(200).type("application/octet-stream").entity(bytes).build();
}
return Response.ok("Set Operation for Key - " + key + " was successful.").build();
} catch (EVCacheException e) {
e.printStackTrace();
return Response.serverError().build();

}
return Response.serverError().build();
}


@DELETE
@Path("delete/{appId}/{key}")
@Path("{appId}/{key}")
@Consumes(MediaType.APPLICATION_JSON)
@Produces("text/plain")
public Response deleteOperation(@PathParam("appId") String appId, @PathParam("key") String key) {
if(logger.isDebugEnabled()) logger.debug("Get for application " + appId + " for Key " + key);
if (logger.isDebugEnabled()) logger.debug("Get for application " + appId + " for Key " + key);
final EVCache evCache = getEVCache(appId);
try {
Future<Boolean>[] _future = evCache.delete(key);
if (_future.equals(Boolean.TRUE)) {
if(logger.isDebugEnabled()) logger.debug("set key is successful");
if (logger.isDebugEnabled()) logger.debug("set key is successful");
}
return Response.ok("Deleted Operation for Key - " + key + " was successful.").build();
return Response.ok("Deleted Operation for Key - " + key + " was successful. \n").build();
} catch (EVCacheException e) {
e.printStackTrace();
return Response.serverError().build();
}
return Response.serverError().build();
}

private EVCache getEVCache(String appId) {
Expand All @@ -148,4 +140,4 @@ private EVCache getEVCache(String appId) {
evCacheMap.put(appId, evCache);
return evCache;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.netflix.evcache.service.transcoder;

import net.spy.memcached.CachedData;
import net.spy.memcached.transcoders.Transcoder;

/**
* Created by senugula on 6/23/16.
*/
public class RESTServiceTranscoder implements Transcoder<CachedData> {

public RESTServiceTranscoder() {

}

public boolean asyncDecode(CachedData d) {
return false;
}

public CachedData decode(CachedData d) {
return d;
}

public CachedData encode(CachedData o) {
return o;
}

public int getMaxSize() {
return CachedData.MAX_SIZE;
}
}
52 changes: 52 additions & 0 deletions evcacheproxy/src/main/resources/evcacheproxy.properties
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,55 @@ aws.id.registrationRequired=false
log4j.rootLogger=ERROR
log4j.logger.httpclient=ERROR


netflix.appinfo.name=evcacheproxy
netflix.appinfo.port=7001
netflix.appinfo.securePort=7002
netflix.appinfo.healthCheckUrlPath=/healthcheck
netflix.appinfo.statusUrlPath=/Status
netflix.appinfo.vipAddress=${netflix.appinfo.name}:${netflix.appinfo.port}
netflix.datacenter=cloud
#netflix.environment=${@environment}
#eureka.environment=${@environment}

eureka.serviceUrl.default=http://${@region}.discovery${NETFLIX_ENVIRONMENT}.netflix.net:7001/discovery/v2/

eureka.us-east-1.availabilityZones=us-east-1c,us-east-1d,us-east-1e
eureka.us-west-1.availabilityZones=us-west-1a,us-west-1b,us-west-1c
eureka.us-west-2.availabilityZones=us-west-2a,us-west-2b,us-west-2c
eureka.eu-west-1.availabilityZones=eu-west-1a,eu-west-1b,eu-west-1c

eureka.shouldUseDns=true

eureka.registration.enabled=true
eureka.shouldFetchRegistry=true

#Discovery Route 53 information
eureka.eurekaServer.domainName=discovery${NETFLIX_ENVIRONMENT}.netflix.net
eureka.eurekaServer.port=7001
eureka.eurekaServer.context=discovery/v2

#Users are going to complain about the verbosity because of discovery making periodic calls, hence disable
netflix.route53.verboseHttpOutput=false

eureka.shouldFilterOnlyUpInstances=false

## --------------------------
## Bootstrap Fallback Configs
## --------------------------

eureka.bootstrap.fallback.dnsbased.serviceurl=http://discoverybootstrapfallback.${EC2_REGION}.dyn${NETFLIX_ENVIRONMENT}.netflix.net:7001/v2/

## -----------------------------------
## Need to set to cloud to use AwsInfo
## -----------------------------------


eureka.datacenter=cloud

eureka.appinfo.name=evcacheproxy
eureka.appinfo.port=7001
eureka.appinfo.securePort=7002
eureka.appinfo.healthCheckUrlPath=/healthcheck
eureka.appinfo.statusUrlPath=/Status
eureka.appinfo.vipAddress=${netflix.appinfo.name}:${netflix.appinfo.port}

0 comments on commit 5ee6245

Please sign in to comment.