This topic provides some sample apps in various languages to demonstrate how you can get started with Redis for VMware Tanzu Application Service. 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 Tanzu Application Service 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 Tanzu Application Service.

Quickstart apps

All apps using Redis for Tanzu Application Service must parse and read the Redis for Tanzu Application Service instance credentials from the environment. The credentials are available to the app once a Redis for Tanzu Application Service 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 shared-VM instances. Any service offering and plan work with the following examples. You can view available plans and instance types in the 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 an on-demand-cache plan of the p.redis service, but a p-redis instance also works.

$ git clone [email protected]:colrich/RedisForPCF-Java-Example.git java_redis_app
$ cd java_redis_app
$ mvn package
$ cf create-service p.redis on-demand-cache 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. For example, {APP_URL}/set?kn=somekeyname&kv=valuetoset
  • "/get" — Gets the value stored at a given key. For example, {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 an on-demand-cache plan for the p.redis service, but a p-redis instance also works.

$ git clone [email protected]:colrich/RedisForPCF-Node-Example.git node_redis_app
$ cd node_redis_app
$ cf create-service p.redis on-demand-cache 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. For example, {APP_URL}/set?kn=somekeyname&kv=valuetoset
  • "/get" — Gets the value stored at a given key. For example, {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, 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 shared-VM service, but any p-redis or p.redis instance works.

$ git clone [email protected]:pivotal-cf/cf-redis-example-app.git ruby_redis_app
$ cd ruby_redis_app
$ cf create-service p-redis shared-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 Tanzu Application Service

One common use case of Redis for Tanzu Application Service 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 Tanzu Application Service as the backend with Spring Session to manage user session information.

This documentation is adopted from the Spring Session docs and extends to include instructions for use with Redis for Tanzu Application Service. The document is also adopted from this Spring Session - Spring Boot guide.

Setting up Spring Session

Updating dependencies

To use Spring Session, update your dependencies to include spring-session-data-redis. The following 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 Tanzu Application Service as a back end

At this stage, Spring Session is now configured to use a Redis instance. To use a Redis for Tanzu Application Service 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 deactivated by exposing ConfigureRedisAction.NO_OP as a bean:


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

However, deactivating 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.

check-circle-line exclamation-circle-line close-line
Scroll to top icon