play framework与play-redis集成后得到NPE
Got NPE after integrating play framework with play-redis
将play-redis(https://github.com/KarelCemus/play-redis)与play framework集成后,收到请求报错:
[20211204 23:20:48.350][HttpErrorHandler.scala:272:onServerError][E] Error while handling error
java.lang.NullPointerException: null
at play.api.http.HttpErrorHandlerExceptions$.convertToPlayException(HttpErrorHandler.scala:377)
at play.api.http.HttpErrorHandlerExceptions$.throwableToUsefulException(HttpErrorHandler.scala:367)
at play.api.http.DefaultHttpErrorHandler.onServerError(HttpErrorHandler.scala:264)
at play.core.server.Server$$anonfun$handleErrors.applyOrElse(Server.scala:109)
at play.core.server.Server$$anonfun$handleErrors.applyOrElse(Server.scala:105)
at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:35)
at play.core.server.Server$.getHandlerFor(Server.scala:129)
at play.core.server.AkkaHttpServer.handleRequest(AkkaHttpServer.scala:317)
at play.core.server.AkkaHttpServer.$anonfun$createServerBinding(AkkaHttpServer.scala:224)
at akka.stream.impl.fusing.MapAsync$$anon.onPush(Ops.scala:1297)
at akka.stream.impl.fusing.GraphInterpreter.processPush(GraphInterpreter.scala:541)
at akka.stream.impl.fusing.GraphInterpreter.processEvent(GraphInterpreter.scala:495)
at akka.stream.impl.fusing.GraphInterpreter.execute(GraphInterpreter.scala:390)
at akka.stream.impl.fusing.GraphInterpreterShell.runBatch(ActorGraphInterpreter.scala:625)
at akka.stream.impl.fusing.GraphInterpreterShell$AsyncInput.execute(ActorGraphInterpreter.scala:502)
at akka.stream.impl.fusing.GraphInterpreterShell.processEvent(ActorGraphInterpreter.scala:600)
at akka.stream.impl.fusing.ActorGraphInterpreter.akka$stream$impl$fusing$ActorGraphInterpreter$$processEvent(ActorGraphInterpreter.scala:775)
at akka.stream.impl.fusing.ActorGraphInterpreter$$anonfun$receive.applyOrElse(ActorGraphInterpreter.scala:790)
at akka.actor.Actor.aroundReceive(Actor.scala:537)
at akka.actor.Actor.aroundReceive$(Actor.scala:535)
at akka.stream.impl.fusing.ActorGraphInterpreter.aroundReceive(ActorGraphInterpreter.scala:691)
at akka.actor.ActorCell.receiveMessage(ActorCell.scala:579)
at akka.actor.ActorCell.invoke(ActorCell.scala:547)
at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:270)
at akka.dispatch.Mailbox.run(Mailbox.scala:231)
at akka.dispatch.Mailbox.exec(Mailbox.scala:243)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)
我确定原因一定是 play-redis
因为没有它应用程序运行顺利。特别是,我使用配置提供程序的自定义实现,因为需要通过调用名称服务的 rest API 来获取 ip 和端口。
@Singleton
class CustomRedisInstance @Inject() (
config: Configuration,
polarisExtensionService: PolarisExtensionService,
@NamedCache("redisConnection") redisConnectionCache: AsyncCacheApi)(implicit
asyncExecutionContext: AsyncExecutionContext)
extends RedisStandalone
with RedisDelegatingSettings {
val pathPrefix = "play.cache.redis"
def name = "play"
private def defaultSettings =
RedisSettings.load(
// this should always be "play.cache.redis"
// as it is the root of the configuration with all defaults
config.underlying,
"play.cache.redis")
def settings: RedisSettings = {
RedisSettings
.withFallback(defaultSettings)
.load(
// this is the path to the actual configuration of the instance
//
// in case of named caches, this could be, e.g., "play.cache.redis.instances.my-cache"
//
// in that case, the name of the cache is "my-cache" and has to be considered in
// the bindings in the CustomCacheModule (instead of "play", which is used now)
config.underlying,
"play.cache.redis")
}
def host: String = {
val connectionInfoFuture = getConnectionInfoFromPolaris
Try(Await.result(connectionInfoFuture, 10.seconds)) match {
case Success(extractedVal) => extractedVal.host
case Failure(_) => config.get[String](s"$pathPrefix.host")
case _ => config.get[String](s"$pathPrefix.host")
}
}
def port: Int = {
val connectionInfoFuture = getConnectionInfoFromPolaris
Try(Await.result(connectionInfoFuture, 10.seconds)) match {
case Success(extractedVal) => extractedVal.port
case Failure(_) => config.get[Int](s"$pathPrefix.port")
case _ => config.get[Int](s"$pathPrefix.port")
}
}
def database: Option[Int] = Some(config.get[Int](s"$pathPrefix.database"))
def password: Option[String] = Some(config.get[String](s"$pathPrefix.password"))
}
但是play-redis
本身没有错误日志。经过所有这些阅读手册和示例的艰苦工作,结果证明我应该转向 Jedis
或 Lettuce
?现在没希望了。
原因是我想使用Redis
和Caffeine
会造成冲突,正如文档所说,需要将default-cache
重命名为redis
application.conf
:
play.modules.enabled += play.api.cache.redis.RedisCacheModule
# provide additional configuration in the custom module
play.modules.enabled += services.CustomCacheModule
play.cache.redis {
# do not bind default unqualified APIs
bind-default: false
# name of the instance in simple configuration,
# i.e., not located under `instances` key
# but directly under 'play.cache.redis'
default-cache: "redis"
source = custom
host = 127.0.0.1
# redis server: port
port = 6380
# redis server: database number (optional)
database = 0
# authentication password (optional)
password = "#########"
refresh-minute = 10
}
所以在CustomCacheModule
中,NamedCacheImpl
的输入参数需要从play
改为redis
。
class CustomCacheModule extends AbstractModule {
override def configure(): Unit = {
// NamedCacheImpl's input used to be "play"
bind(classOf[RedisInstance]).annotatedWith(new NamedCacheImpl("redis")).to(classOf[CustomRedisInstance])
()
}
}
将play-redis(https://github.com/KarelCemus/play-redis)与play framework集成后,收到请求报错:
[20211204 23:20:48.350][HttpErrorHandler.scala:272:onServerError][E] Error while handling error
java.lang.NullPointerException: null
at play.api.http.HttpErrorHandlerExceptions$.convertToPlayException(HttpErrorHandler.scala:377)
at play.api.http.HttpErrorHandlerExceptions$.throwableToUsefulException(HttpErrorHandler.scala:367)
at play.api.http.DefaultHttpErrorHandler.onServerError(HttpErrorHandler.scala:264)
at play.core.server.Server$$anonfun$handleErrors.applyOrElse(Server.scala:109)
at play.core.server.Server$$anonfun$handleErrors.applyOrElse(Server.scala:105)
at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:35)
at play.core.server.Server$.getHandlerFor(Server.scala:129)
at play.core.server.AkkaHttpServer.handleRequest(AkkaHttpServer.scala:317)
at play.core.server.AkkaHttpServer.$anonfun$createServerBinding(AkkaHttpServer.scala:224)
at akka.stream.impl.fusing.MapAsync$$anon.onPush(Ops.scala:1297)
at akka.stream.impl.fusing.GraphInterpreter.processPush(GraphInterpreter.scala:541)
at akka.stream.impl.fusing.GraphInterpreter.processEvent(GraphInterpreter.scala:495)
at akka.stream.impl.fusing.GraphInterpreter.execute(GraphInterpreter.scala:390)
at akka.stream.impl.fusing.GraphInterpreterShell.runBatch(ActorGraphInterpreter.scala:625)
at akka.stream.impl.fusing.GraphInterpreterShell$AsyncInput.execute(ActorGraphInterpreter.scala:502)
at akka.stream.impl.fusing.GraphInterpreterShell.processEvent(ActorGraphInterpreter.scala:600)
at akka.stream.impl.fusing.ActorGraphInterpreter.akka$stream$impl$fusing$ActorGraphInterpreter$$processEvent(ActorGraphInterpreter.scala:775)
at akka.stream.impl.fusing.ActorGraphInterpreter$$anonfun$receive.applyOrElse(ActorGraphInterpreter.scala:790)
at akka.actor.Actor.aroundReceive(Actor.scala:537)
at akka.actor.Actor.aroundReceive$(Actor.scala:535)
at akka.stream.impl.fusing.ActorGraphInterpreter.aroundReceive(ActorGraphInterpreter.scala:691)
at akka.actor.ActorCell.receiveMessage(ActorCell.scala:579)
at akka.actor.ActorCell.invoke(ActorCell.scala:547)
at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:270)
at akka.dispatch.Mailbox.run(Mailbox.scala:231)
at akka.dispatch.Mailbox.exec(Mailbox.scala:243)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)
我确定原因一定是 play-redis
因为没有它应用程序运行顺利。特别是,我使用配置提供程序的自定义实现,因为需要通过调用名称服务的 rest API 来获取 ip 和端口。
@Singleton
class CustomRedisInstance @Inject() (
config: Configuration,
polarisExtensionService: PolarisExtensionService,
@NamedCache("redisConnection") redisConnectionCache: AsyncCacheApi)(implicit
asyncExecutionContext: AsyncExecutionContext)
extends RedisStandalone
with RedisDelegatingSettings {
val pathPrefix = "play.cache.redis"
def name = "play"
private def defaultSettings =
RedisSettings.load(
// this should always be "play.cache.redis"
// as it is the root of the configuration with all defaults
config.underlying,
"play.cache.redis")
def settings: RedisSettings = {
RedisSettings
.withFallback(defaultSettings)
.load(
// this is the path to the actual configuration of the instance
//
// in case of named caches, this could be, e.g., "play.cache.redis.instances.my-cache"
//
// in that case, the name of the cache is "my-cache" and has to be considered in
// the bindings in the CustomCacheModule (instead of "play", which is used now)
config.underlying,
"play.cache.redis")
}
def host: String = {
val connectionInfoFuture = getConnectionInfoFromPolaris
Try(Await.result(connectionInfoFuture, 10.seconds)) match {
case Success(extractedVal) => extractedVal.host
case Failure(_) => config.get[String](s"$pathPrefix.host")
case _ => config.get[String](s"$pathPrefix.host")
}
}
def port: Int = {
val connectionInfoFuture = getConnectionInfoFromPolaris
Try(Await.result(connectionInfoFuture, 10.seconds)) match {
case Success(extractedVal) => extractedVal.port
case Failure(_) => config.get[Int](s"$pathPrefix.port")
case _ => config.get[Int](s"$pathPrefix.port")
}
}
def database: Option[Int] = Some(config.get[Int](s"$pathPrefix.database"))
def password: Option[String] = Some(config.get[String](s"$pathPrefix.password"))
}
但是play-redis
本身没有错误日志。经过所有这些阅读手册和示例的艰苦工作,结果证明我应该转向 Jedis
或 Lettuce
?现在没希望了。
原因是我想使用Redis
和Caffeine
会造成冲突,正如文档所说,需要将default-cache
重命名为redis
application.conf
:
play.modules.enabled += play.api.cache.redis.RedisCacheModule
# provide additional configuration in the custom module
play.modules.enabled += services.CustomCacheModule
play.cache.redis {
# do not bind default unqualified APIs
bind-default: false
# name of the instance in simple configuration,
# i.e., not located under `instances` key
# but directly under 'play.cache.redis'
default-cache: "redis"
source = custom
host = 127.0.0.1
# redis server: port
port = 6380
# redis server: database number (optional)
database = 0
# authentication password (optional)
password = "#########"
refresh-minute = 10
}
所以在CustomCacheModule
中,NamedCacheImpl
的输入参数需要从play
改为redis
。
class CustomCacheModule extends AbstractModule {
override def configure(): Unit = {
// NamedCacheImpl's input used to be "play"
bind(classOf[RedisInstance]).annotatedWith(new NamedCacheImpl("redis")).to(classOf[CustomRedisInstance])
()
}
}