LATEST VERSION: 1.1 - CHANGELOG
Redis for PCF v1.10

Quickstart Guide for App Developers

This topic provides some sample apps in various languages to demonstrate how to get Redis for Pivotal Cloud Foundry (PCF) up and running quickly. It also highlights the critical components of the apps that allow them to connect to a Redis instance. Credentials to connect to a Redis for PCF instance are passed to the apps as environment variables under VCAP_SERVICES.

Additionally, this topic includes advice for setting up Spring Sessions with Redis for PCF.

Feedback

If you have any feedback on this page or want more information (other quickstart guides, sample use cases), please send email to redis-feedback.

Quickstart Apps

All apps using Redis for PCF must parse and read the Redis for PCF instance credentials from the environment. The credentials are available to the app once a Redis for PCF instance is bound to it and are viewable by typing $cf env {app_name}.

Prerequisites for these examples include access to a Marketplace with p-redis or p.redis.

For reference, p.redis refers to the Redis service that provides On-Demand instances and p-redis refers to the Redis service that provides Dedicated-VM and Shared-VM instances. Any Redis for PCF service and plan works with the following examples. Available plans and instance types can be viewed with in the cf marketplace.

Quickstart Java App

This is a basic Java app with the capability to get and set keys in Redis and view configuration information. Prerequisites include Maven.

Here we use a cache-small plan of the p.redis service, but any p-redis or p.redis instance works.

$ git clone git@github.com:colrich/RedisForPCF-Java-Example.git java_redis_app
$ cd java_redis_app
$ mvn package
$ cf create-service p.redis cache-small redis_instance
$ cf push redis_example_app -p target/RedisExample-0.0.1-SNAPSHOT.jar
$ cf bind-service redis_example_app redis_instance
$ cf restage redis_example_app

You can then visit the app in your browser window. The app has three entry points:

  • “/” — Gets info about a bound Redis instance
  • “/set” — Sets a given key to a given value. E.g., {APP_URL}/set?kn=somekeyname&kv=valuetoset
  • “/get” — Gets the value stored at a given key. E.g., {APP_URL}/get?kn=somekeyname

In the application code, the snippet where VCAP_SERVICES is read and parsed is here:

@RequestMapping("/")
public RedisInstanceInfo getInfo() {
    LOG.log(Level.WARNING, "Getting Redis Instance Info in Spring controller...");
    // first we need to get the value of VCAP_SERVICES, the environment variable
    // where connection info is stored
    String vcap = System.getenv("VCAP_SERVICES");
    LOG.log(Level.WARNING, "VCAP_SERVICES content: " + vcap);


    // now we parse the json in VCAP_SERVICES
    LOG.log(Level.WARNING, "Using GSON to parse the json...");
    JsonElement root = new JsonParser().parse(vcap);
    JsonObject redis = null;
    if (root != null) {
        if (root.getAsJsonObject().has("p.redis")) {
            redis = root.getAsJsonObject().get("p.redis").getAsJsonArray().get(0).getAsJsonObject();
            LOG.log(Level.WARNING, "instance name: " + redis.get("name").getAsString());
        }
        else if (root.getAsJsonObject().has("p-redis")) {
            redis = root.getAsJsonObject().get("p-redis").getAsJsonArray().get(0).getAsJsonObject();
            LOG.log(Level.WARNING, "instance name: " + redis.get("name").getAsString());
        }
        else {
            LOG.log(Level.SEVERE, "ERROR: no redis instance found in VCAP_SERVICES");
        }
    }

    // then we pull out the credentials block and produce the output
    if (redis != null) {
        JsonObject creds = redis.get("credentials").getAsJsonObject();
        RedisInstanceInfo info = new RedisInstanceInfo();
        info.setHost(creds.get("host").getAsString());
        info.setPort(creds.get("port").getAsInt());
        info.setPassword(creds.get("password").getAsString());

        // the object will be json serialized automatically by Spring web - we just need to return it
        return info;
    }
    else return new RedisInstanceInfo();
}

Quickstart Node App

This is a basic node app with the capability to get and set keys in Redis and view configuration information. Prerequisites are the cf cli and access to a Marketplace with p-redis or p.redis.

Here we use a cache-small plan for the p.redis service, but any p-redis or p.redis instance works.

$ git clone git@github.com:colrich/RedisForPCF-Node-Example.git node_redis_app
$ cd node_redis_app
$ cf create-service p.redis cache-small redis_instance
$ cf push redis_example_app
$ cf bind-service redis_example_app redis_instance
$ cf restage redis_example_app

You can then visit the app in your browser window. The app has three entry points:

  • “/” — Gets info about bound redis instance
  • “/set” — Sets a given key to a given value. E.g., {APP_URL}/set?kn=somekeyname&kv=valuetoset
  • “/get” — Gets the value stored at a given key. E.g., {APP_URL}/get?kn=somekeyname

In the application code, the snippet where VCAP_SERVICES is read and parsed is here:

// parses the VCAP_SERVICES env var and looks for redis service instances
function getVcapServices() {
  var vcstr = process.env.VCAP_SERVICES;
  if (vcstr != null && vcstr.length > 0 && vcstr != '{}') {
    console.log("found VCAP_SERVICES: " + vcstr)

    var vcap = JSON.parse(vcstr);
    if (vcap != null) {
      if (vcap.hasOwnProperty("p.redis")) {
        console.log("found redis instance: " + vcap["p.redis"][0].name);
        return vcap["p.redis"][0]
      }
      else if (vcap.hasOwnProperty("p-redis")) {
        console.log("found redis instance: " + vcap["p-redis"][0].name);
        return vcap["p-redis"][0]
      }
      else {
        console.log("ERROR: no redis service bound!")
      }
    }
    else {
      console.log("ERROR: no redis service bound!")
    }
  }
  else {
    console.log("ERROR: VCAP_SERVICES does not contain a redis block")
  }
  return null
}

// pulls the necessary connection info out of the parsed VCAP_SERVICES block for
// the redis connection
function getRedisInfo(vcap) {
  var info = {}
  info["host"] = vcap["credentials"]["host"]
  info["port"] = vcap["credentials"]["port"]
  info["password"] = vcap["credentials"]["password"]
  return info
}


// set the port to listen on; for apps in PCF it's important to listen on $PORT (usually 8000)
app.set('port', (process.env.PORT || 8080))


// this method looks in VCAP_SERVICES for a redis service instance and outputs the
// host / port / password info to the response
app.get('/', function(request, response) {
  console.log("Getting Redis connection info from the environment...")

  var vcap = getVcapServices()
  if (vcap != null) {
    var info = getRedisInfo(vcap)
    console.log("connection info: " + info.host + " / " + info.port + " / " + info.password)
    response.send("connection info: " + info.host + " / " + info.port + " / " + info.password)
  }
  else {
    console.log("ERROR: VCAP_SERVICES does not contain a redis block or no redis bound")
    response.send("ERROR: VCAP_SERVICES does not contain a redis block or no redis bound")
  }
})

Quickstart Ruby App

This is a basic ruby app with the capability to get and set keys in Redis and view configuration information. Here we use an instance of the dedicated_vm service, but any p-redis or p.redis instance works.

$ git clone git@github.com:pivotal-cf/cf-redis-example-app.git ruby_redis_app
$ cd ruby_redis_app
$ cf create-service p-redis dedicated-vm redis_instance
$ cf push redis_example_app --no-start
$ cf bind-service redis_example_app redis_instance
$ cf start redis_example_app"

You can then get, set, and delete keys:

$ export APP=redis-example-app.my-cloud-foundry.com
$ curl -X PUT $APP/foo -d 'data=bar'
success
$ curl -X GET $APP/foo
bar
$ curl -X DELETE $APP/foo
success

In the application code, the method where VCAP_SERVICES is read is here:

def redis_credentials
  service_name = ENV['service_name'] || "redis"

  if ENV['VCAP_SERVICES']
    all_pivotal_redis_credentials = CF::App::Credentials.find_all_by_all_service_tags(['redis', 'pivotal'])
    if all_pivotal_redis_credentials && all_pivotal_redis_credentials.first
      all_pivotal_redis_credentials && all_pivotal_redis_credentials.first
    else
      redis_service_credentials = CF::App::Credentials.find_by_service_name(service_name)
      redis_service_credentials
    end
  end
end

The method where VCAP_SERVICES is parsed is here:

def redis_client
  @client ||= Redis.new(
    host: redis_credentials.fetch('host'),
    port: redis_credentials.fetch('port'),
    password: redis_credentials.fetch('password'),
    timeout: 30
  )
end

Spring Session with Redis for PCF

One common use case of Redis for PCF is management of a user’s session information with Spring Session. Spring Session provides an API and implementations with which to manage sessions.

This topic describes how to use Redis for PCF as the backend with Spring Session to manage user session information.

This documentation is adopted from the Spring Session docs and quickstart guide and extends to include instructions for use with Redis for PCF.

Setting Up Spring Session

Updating Dependencies

To use Spring Session, update your dependencies to include spring-session-data-redis. The below example is for Maven.

pom.xml

  <dependencies>
          <!-- ... -->
          <dependency>
                  <groupId>org.springframework.session</groupId>
                  <artifactId>spring-session-data-redis</artifactId>
                  <version>1.3.1.RELEASE</version>
                  <type>pom</type>
          </dependency>
          <dependency>
                  <groupId>biz.paluch.redis</groupId>
                  <artifactId>lettuce</artifactId>
                  <version>3.5.0.Final</version>
          </dependency>
          <dependency>
                  <groupId>org.springframework</groupId>
                  <artifactId>spring-web</artifactId>
                  <version>4.3.4.RELEASE</version>
          </dependency>
  </dependencies>

Spring Java Configuration

After adding the required dependencies, we can create our Spring configuration.

The Spring configuration is responsible for creating a Servlet Filter that replaces the HttpSession implementation with an implementation backed by Spring Session. Add the following Spring Configuration:

@EnableRedisHttpSession (1)
public class Config {

        @Bean
        public LettuceConnectionFactory connectionFactory() {
                return new LettuceConnectionFactory(); (2)
        }
}
1 The @EnableRedisHttpSession annotation creates a Spring Bean with the name of springSessionRepositoryFilter that implements Filter. The filter is what is in charge of replacing the HttpSession implementation to be backed by Spring Session. In this instance Spring Session is backed by Redis.
2 We create a RedisConnectionFactory that connects Spring Session to the Redis Server. We configure the connection to connect to localhost on the default port (6379) For more information on configuring Spring Data Redis, refer to the reference documentation.

Java Servlet Container Initialization

Our Spring Configuration created a Spring Bean named springSessionRepositoryFilter that implements Filter. The springSessionRepositoryFilter bean is responsible for replacing the HttpSession with a custom implementation that is backed by Spring Session.

In order for our Filter to do its magic:

  • Spring needs to load our Config class.

  • We need to ensure that our Servlet Container (i.e. Tomcat) uses our springSessionRepositoryFilter for every request.

Fortunately, Spring Session provides a utility class named AbstractHttpSessionApplicationInitializer, which helps us confirm that these two requirements are met.

The example below shows how to extend AbstractHttpSessionApplicationInitializer:

src/main/java/sample/Initializer.java

public class Initializer extends AbstractHttpSessionApplicationInitializer { (1)

        public Initializer() {
                super(Config.class); (2)
        }
}

The name of our class (Initializer) does not matter. What is important is that we extend AbstractHttpSessionApplicationInitializer. Doing this achieves the following:

  • It ensures that the Spring Bean by the name springSessionRepositoryFilter is registered with our Servlet Container for every request.

  • It provides a mechanism to easily ensure that Spring loads our Config.

Configuring Redis for PCF as a Backend

At this stage, Spring Session is now configured to use a Redis instance. To use a Redis for PCF instance, create a session-replication tag for it.

$ cf update-service INSTANCE_NAME -t session-replication

Other Considerations

The RedisHttpSessionConfiguration tries to use the Redis CONFIG command. The CONFIG command is not available due to security recommendations.

This feature can be disabled by exposing ConfigureRedisAction.NO_OP as a bean:


@Bean
public static ConfigureRedisAction configureRedisAction() {
    return ConfigureRedisAction.NO_OP;
}

However, disabling the configuration means that Redis cannot send namespace notifications. This functionality is critical for apps that require SessionDestroyedEvent to be fired to clean up resources, such as for WebSocket apps to ensure open WebSockets are closed when the HttpSession expires.

If you want a workaround for this use case, send email to redis-feedback.

Create a pull request or raise an issue on the source for this page in GitHub