我试图用http4s创建基于Http4sServlet的webapp。以下代码无法编译。
import cats.effect._
import org.http4s.servlet.BlockingServletIo
import org.http4s.servlet.Http4sServlet
import scala.concurrent.ExecutionContext.global
import org.http4s.implicits._
class UserSvcServlet
extends Http4sServlet[IO](service = UserSvcServer.start
, servletIo = BlockingServletIo(4096, Blocker.liftExecutionContext(global)))(IOApp)
错误信息:
[error] /home/developer/scala/user-svc/src/main/scala/io/databaker/UserSvcServlet.scala:12:54: Cannot find implicit value for ConcurrentEffect[[+A]cats.effect.IO[A]].
[error] Building this implicit value might depend on having an implicit
[error] s.c.ExecutionContext in scope, a Scheduler, a ContextShift[[+A]cats.effect.IO[A]]
[error] or some equivalent type.
[error] extends Http4sServlet[IO]( service = UserSvcServer.stream
[error] ^
[error] /home/developer/scala/user-svc/src/main/scala/io/databaker/UserSvcServlet.scala:13:36: Cannot find an implicit value for ContextShift[[+A]cats.effect.IO[A]]:
[error] * import ContextShift[[+A]cats.effect.IO[A]] from your effects library
[error] * if using IO, use cats.effect.IOApp or build one with cats.effect.IO.contextShift
[error] , servletIo = BlockingServletIo(4096, Blocker.liftExecutionContext(global)))
[error] ^
[error] two errors found
[error] (Compile / compileIncremental) Compilation failed
[error] Total time: 1 s, completed May 29, 2020, 8:45:00 PM
錯誤訊息: UserSvcServer
的实现方式如下。
import org.http4s.HttpApp
import cats.effect.{ConcurrentEffect, ContextShift, Timer}
import org.http4s.implicits._
import org.http4s.server.middleware.Logger
object UserSvcServer {
def start[F[_] : ConcurrentEffect](implicit T: Timer[F], C: ContextShift[F]): HttpApp[F] = {
val helloWorldAlg = HelloWorld.impl[F]
val httpApp = UserSvcRoutes.helloWorldRoutes[F](helloWorldAlg).orNotFound
Logger.httpApp(true, true)(httpApp)
}
}
如何隐式导入ContextShift?
Context shift只是猫的包装器,它是在 ExecutionContext
. 你可以明确地创建一个 陈述 在文档中。
implicit val cs: ContextShift[IO] = IO.contextShift(ExecutionContext.global)
你通常会在你的应用的入口处创建一个上下文转移(可能在主方法中)。
如果您的应用程序使用 IOApp
它已经在范围内隐含了contextShift。它将使用执行上下文,它的线程数等于你计算机的可用处理器数。
如果你想在你的应用程序中 "深入 "使用创建的contextShift,你可以把它作为一个隐式参数传递。
def doSomething(implicit cs: ContextShift[IO]): IO[Unit] = ???
所以为了让你的代码正常工作, 你需要确保方法或类调用Constructor of UserSvcServlet
对contextShift有隐含。(implicit cs: ContextShift[IO])
.
你也可以把它放在单独的对象中。
object AppContextShift {
implicit val cs: ContextShift[IO] = IO.contextShift(ExecutionContext.global)
implicit val t: Timer[IO] = IO.timer(ExecutionContext.global) //you will probably also need timer eventually
}
然后你可以在需要contextShift的时候导入它。
import AppContextShift._
顺便说一下,为Blocker使用一个全局执行上下文并不是一个好主意,Blocked是用来阻止操作的,而且用它和 ExecutionContext.global
可能会导致您的应用程序中的线程饥饿。
最常见的方法是使用从缓存线程池创建的阻塞器。
Blocker.liftExecutorService(Executors.newCachedThreadPool())