read

Managing sessions outside of your app/JVM provides flexibility with deployments and it makes both blue/green and zero downtime deployments possible. Previously, this required setting up a session manager in Tomcat, but with Grails 3 it's much easier.

Since Grails 3 is based on SpringBoot, we can easily use the Spring Session project (along with spring-data-redis) to persist session information outside Tomcat and utilize Redis - a distributed key/value store.

Configuration:

First, lets add Spring Session and Redis to our build.gradle:

dependencies {
    compile "org.springframework.session:spring-session:1.1.1.RELEASE"
    compile "org.springframework.boot:spring-boot-starter-redis"
    ...
}

Next, tell Grails where the Redis server is located in application.yml:

spring:
    redis:
        host: localhost
        port: 6379
server:
    session-timeout: 3600 # session timeout in seconds

For Grails 3.1.x that's it! Grails 3.0.x needs a ordering fix

Verification

Start up your app and browse to a page. You should see the session stored in Redis:

$ grails run-app
# put something on the session,
#  eg: session.foo = "bar" in a controller
#  or login to your app if you have security setup
$ redis-cli keys '*'
1) "spring:session:expirations:1441301640000"
2) "spring:session:sessions:91dcfa08-00e1-4cab-98c2-34bf0da40184"

Conclusion

With two dependancies, one class, and a little yml we've added the ability to store sessions outside of Tomcat and share them with multiple JVMs!

You will now be able to:

  • Stop and start the app and still have the values for your session
  • Users will not be logged out between deployments
  • Orchestrate a blue/green or canary deployment without downtime
  • Ability to share sessions across multiple JVMs on the same or different servers and round-robbin between them without loosing the session.
  • Redis data is persisted to disk so sessions will survive a server restart

With Grails 3 we have all the power of SpringBoot. My key takeaway from this is to not limit yourself to just Grails Plugins with a Grails 3 app and to take a look at the SpringBoot ecosystem as well.

Checkout my talk at Gr8Conf US 2015 for this and a lot of other topics related to running and building a Grails app for production.

Notes

3.0.x

To fix an ordering issue in Grails 3.0.x add a new class to tie everything together under grails-app/init/<yourapp>/SessionConfig.groovy.

package <yourapp>

import org.springframework.boot.context.embedded.FilterRegistrationBean
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.Ordered
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession
import org.springframework.session.ExpiringSession
import org.springframework.session.web.http.SessionRepositoryFilter

@EnableRedisHttpSession
@Configuration
public class SessionConfig {

    /**
     * By Default SessionRepositoryFilter is registered as +50, but we need it to be before GrailsWebRequestFilter which is registered at +30. This
     * overrides the default order.
     * This override is not needed after SpringBoot 1.3.0.M5+ is included in Grails.
     */
    @Bean
    FilterRegistrationBean springSessionFilterRegistration(SessionRepositoryFilter<? extends ExpiringSession> filter) {
        new FilterRegistrationBean(filter:filter, order: Ordered.HIGHEST_PRECEDENCE + 15)
    }
}

You will need to register this bean in grails-app/conf/spring/resources.groovy or simply add @ComponentScan to your grails-app/init/<yourapp>/Application.groovy class:

...
import org.springframework.context.annotation.ComponentScan

@ComponentScan
class Application extends GrailsAutoConfiguration {
    static void main(String[] args) {
        GrailsApp.run(Application, args)
    }
}
Blog Logo

Eric Helgeson


Published

Image

Agile Orbit

Agile Development and DevOps

Back to Overview