Web開発のしおりRepository

Webエンジニアリング関連の技術記事を掲載させていただいております。

Playframework (Scala) でRedisにさくっとアクセスする。non-blockingで。

はじめに

ScalaでRedisにアクセスする例です。ScalaでのRedisクライアントライブラリはいくつか選択肢はあるようですが、ここではJava製のLettuceを使います。

サンプルコードは以下にあります github.com

一見

先にPlayframeworkで実際にRedisにアクセスしているコードをお見せします。 Redisに値をSETしてからGETするだけのControllerのActionです。

@Singleton
class RedisController @Inject()(
  cc: ControllerComponents,
  redisConnection: StatefulRedisConnection[String, String] // RedisのコネクションをDI
)(implicit ec: ExecutionContext)
    extends AbstractController(cc) {

  def redis = Action.async {
    import scala.jdk.FutureConverters._ // JavaのCompletableFutureをScalaのFutureにするコンバータ
    val asyncCommands = redisConnection.async() // Redisのコマンドの非同期API
    for {
      _       <- asyncCommands.set("key", "Hello, Redis!").asScala // Redisに値をセット
      message <- asyncCommands.get("key").asScala // Redisから値をゲット
    } yield Ok(message)
  }
}

RedisController.scala - GitHub

アクセスすると "Hello, Redis!" と表示されます。

DI設定等は後述

Lettuceとは

公式ページ: https://lettuce.io/

特徴:

  • Java製 Redisクライアントライブラリ
  • ノンブロッキングIO (netty NIOベース)
  • 3種類のAPIを提供している
  • Redis ClusterやSentinelにも対応している。
  • Redis Pub/Subも使える

JavaのCompletableFutureはScalaのFutureに簡単に変換できます。 また、Reactive APIReactive Streamsの実装なのでAkka Streamなどとも相互変換可能です。

DI設定

Playframework標準のGoogle Guiceを使っている場合の設定例です。

まず、LettuceのRedisClientやStatefulRedisConnection (Redisへのコネクション)のプロバイダーを定義します。

// RedisClientのプロバイダー
@Singleton
class RedisClientProvider @Inject()(
  config: Configuration,
  lifecycle: ApplicationLifecycle
) extends Provider[RedisClient] {
  private val redisClient =
    RedisClient.create(config.get[String]("lettuce.redis-uri")) // RedisへのURIはapplication.confで設定する

  // アプリケーションの終了時のクリーンアップ
  lifecycle.addStopHook { () =>
    redisClient.shutdownAsync().asScala
  }

  override val get: RedisClient = redisClient
}


// StatefulRedisConnectionのプロバイダー
@Singleton
class StatefulRedisConnectionProvider {
  //  省略。RedisClientProviderと同様です
}

RedisClientProvider, StatefulRedisConnectionProvider - GitHub

application.conf - GitHub

次に、Google GuiceのModule設定をします。PlayframeworkはデフォルトではルートパッケージにModuleという名前のクラスを配置すると読み込まれます。

class Module extends AbstractModule {
  override def configure() = {
    bind(classOf[RedisClient])
      .toProvider(classOf[RedisClientProvider])
    bind(new TypeLiteral[StatefulRedisConnection[String, String]] {})
      .toProvider(classOf[StatefulRedisConnectionProvider])
  }
}

型パラメーター付きのクラスのDIを設定するときは、new TypeLiteral[< DIしたい型 >]とする必要があります。