The Lift modules in this project allow Lift utilize a clustered servlet container.
By design, a page served by Lift can only be further serviced with Ajax calls and comet updates by the application server instance which served the page. While this provides a strong layer of security for Lift applications, it has a drawback that if the Lift application server were to fail, all pages served must be reloaded against a new instance.
This project provides tooling to deploy Lift in a clustered mode which allows peer instances of the Lift application server to service pages served by a failed instance. Please read Deployment Considerations below for the implications of running Lift in this manner.
Firstly, the lift-cluster-jetty9
module gives you a configurable bootstrap for running Jetty 9 embedded in your Lift application.
This bootstrap can be optionally configured for Jetty clustering, which is implemented by storing session state in a SQL DB.
Secondly, the lift-cluster-kryo
library utilizes Twitter chill which in turn uses Kryo for serializing the LiftSession
object into the container's session.
With both modules configured for clustering, a Lift server can get killed and replaced while continuing to serve existing pages with the LiftSession
deserialized from the cluster.
Other containers, backend stores, and serialization libraries can readily be supported in the existing design of this solution.
You can configure an existing Lift project to utilize container clustering with the lift-cluster-kryo
and lift-cluster-jetty9
.
Add both lift-cluster-kryo
and lift-cluster-jetty9
as dependencies in your build.sbt
.
libraryDependencies ++= {
val liftVersion = "3.2.0-SNAPSHOT"
val liftEdition = liftVersion.value.replaceAllLiterally("-SNAPSHOT", "").split('.').take(2).mkString(".")
val clusterVersion = "0.0.2"
Seq(
// Other dependencies ...
"net.liftmodules" %% ("lift-cluster-kryo_"+liftEdition) % clusterVersion % "compile",
"net.liftmodules" %% ("lift-cluster-jetty9_"+liftEdition) % clusterVersion % "compile"
)
}
First you need to define an entry point (i.e. a main
method) which can configure and start Jetty.
See the below simplistic example.
import net.liftmodules.cluster.jetty9._
import net.liftweb.common.Loggable
import net.liftweb.util.{LoggingAutoConfigurer, Props, StringHelpers}
import util.Properties
object Start extends App with Loggable {
startLift()
def startLift(): Unit = {
LoggingAutoConfigurer().apply()
val endpoint = Some(SqlEndpointConfig.forMySQL("host", 3306, "my_db", "user", "password", "extra" -> "param"))
val clusterConfig = Some(Jetty9ClusterConfig(
workerName = "node1",
jdbcDriver = DriverMariaDB,
sqlEndpointConfig = endpoint
))
val startConfig = Jetty9Config(
port = port,
contextPath = contextPath,
clusterConfig = clusterConfig
)
Jetty9Starter.start(startConfig)
}
}
Secondly, you need to configure and initialize LiftCluster
in your Boot
class
package bootstrap.liftweb
class Boot {
def boot {
// Other stuff...
val clusterConfig = LiftClusterConfig(
serializer = KryoSerializableLiftSession.serializer
)
LiftCluster.init(clusterConfig)
}
}
Utilizing lift-cluster places some constraints on the way you can write your Lift application code. The two primary considerations are session serialization/deserialization and comet rehydration.
The lift-cluster-kryo module serializes each LiftSession
instance in the servlet container.
(More precisely, we wrap the LiftSession
instance such that when Java serialization is utilized by the container, we use Kryo to do the dirty work).
TODO: Watch for serialization exceptions
TODO: Watch for null pointer exceptions
TODO: Beware of state in comets
TODO DB-per cluster
TODO Canary releases
Need help? Hit us up on the Lift Google group. We'd love to help you out and hear about what you're building.
As with any open source project, contributions are greatly appreciated. If you find an issue or have a feature idea, we'd love to know about it! Any of the following will help this effort tremendously.
- Issue a Pull Request with the fix/enhancement and unit tests to validate the changes. OR
- Open an issue to let us know about what you've discovered.
Below is the recommended procedure for git:
- Fork it
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push the branch (
git push origin my-new-feature
) - Create new Pull Request
Please include as much as you are able, such as tests, documentation, updates to this README, etc.
- 0.0.1: First release which seems to work.
Modules affected:
lift-cluster-common
,lift-cluster-kryo
, andlift-cluster-jetty9
. - 0.0.2: Added
PatienceConfig
andhost
as config parameters forJettyStart
. Modules affected:lift-cluster-jetty9
.
This project and updates to the Lift project were developed in collaboration with and support from Fractal Industries Inc.
lift-cluster is licensed under APL 2.0.
Copyright 2017 net.liftweb
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.