2.8 zio入门——标准ZIO服务

2.8 标准ZIO服务

在本章的前面,我们讨论了ZIO环境类型,但是我们还没有使用它来编写任何程序。 在本书的后面,我们将深入介绍环境,并说明该环境如何为依赖项注入问题提供全面的解决方案。
现在,我们将讨论ZIO为每个应用程序提供的基本服务以及如何使用它们。
服务提供了定义明确的接口,可以在测试环境和生产环境中以不同的方式实现这些接口。ZIO根据平台为所有应用程序提供四到五个不同的默认服务:
1.Clock。 提供与时间和计划有关的功能。 如果您正在访问当前时间或计划在将来某个时间进行计算,则使用此时间。
2. Console. 提供与控制台输入和输出相关的功能。
3. System. 提供用于获取系统和环境变量的功能的能力。
4. Random. 提供用于生成随机值的功能。
5. Blocking. 用于工作负载优化,在单独的Executor上运行阻塞任务。 由于Scala.js不支持阻止,因此该服务仅在JVM上可用。

由于此基本系统功能是作为服务提供的,因此您可以轻松测试使用这些服务的任何代码,而无需实际与生产实现进行交互。

例如,随机服务使我们能够生成随机数。该服务的Live实现只是委托给scala.util.Random。但这可能并不总是我们想要的实现。 scala.util.Random是不确定的,在生产中可能有用,但会使我们更难测试程序。为了测试随机服务,我们可能要使用纯功能的伪随机数生成器,该生成器在给定相同初始种子的情况下始终会生成相同的值。这样,如果测试失败,我们可以重现失败并进行调试。
ZIO Test是用于测试ZIO应用程序的工具包,我们将在下一章中对其进行讨论,它提供了一个TestRandom来完成此任务。实际上,ZIO Test提供了每个标准服务的测试实现,并且您可以想象也希望提供其他实现。

ZIO提供了针对阻塞任务提供了特定的Executor的默认实现,但是您可以通过多种其他方式针对特定的使用模式调整Executor。

通过根据定义良好的接口定义功能,我们将具体实现推迟到以后。如您将看到的,将这些服务与ZIO一起使用非常容易,但是与此同时,高级用户具有极大的灵活性来提供这些服务(或您在自己的应用程序中定义的服务)的自定义实现。

使用服务的第二个较小的好处是,您可以使用该功能来记录效果中正在使用的功能。如果我们看到一个带有ZIO [Clock,Nothing,Unit]类型签名的效果,我们可以得出结论,这种效果(不会失败)正在使用与时间或调度相关的功能(可能不是与随机数有关的功能)。

但是,这种好处较小,因为只有在您的团队对接口编码(而不是实现)有严格的纪律时,这才是正确的。因为效果构造函数可以将任何副作用代码包装到ZIO值中,所以没有什么可以阻止我们编写如下代码:

val int: ZIO[Any, Nothing, Int] = ZIO.effectTotal(scala.util.Random.nextInt())

在此代码段中,我们将生成随机数,但这不会反映在类型签名中,因为我们直接包装了scala.util.Random,而不是在Random服务上使用方法。 不幸的是,编译器无法为我们进行检查,因此必须确保开发人员对接口的代码必须通过代码审查来实施。

因此,我们考虑了“查看”计算使用什么功能的能力,作为使用服务的次要和可选的好处。 主要的好处就是能够在测试和生产环境中插入不同的实现。现在,让我们详细讨论每种标准的ZIO服务。

2.8.1 Clock

Clock服务提供与时间和计划有关的功能。 这包括几种以不同方式获取当前时间的方法(currentTime以指定的TimeUnit返回当前时间,currentDateTime以返回当前OffsetDateTime,nanoTime以纳米为单位获取当前时间)。
此外,时钟服务还包括一种睡眠方法,可用于睡眠一定时间。
以下代码段显示了nanoTime和sleep的签名:

  import zio.duration._
  package object clock {
    def nanoTime: URIO[Clock, Long]
    def sleep(duration: => Duration): URIO[Clock, Unit]
  }

睡眠方法特别重要。 在指定的持续时间过去之前,它不会完成执行,并且像所有ZIO操作一样,它是非阻塞的,因此在等待时间过去时,它实际上并没有消耗任何线程。
我们可以使用sleep方法来实现本章前面介绍的延迟运算符:

  import zio.clock._
  import zio.duration._
  def delay[R, E, A](zio: ZIO[R, E, A])(duration: Duration): ZIO[R with Clock, E, A] = clock.sleep(duration) *> zio

Clock服务是ZIO中所有时间和计划功能的基础。 因此,每当重试,重复,计时或ZIO中内置的与时间和调度有关的其他功能时,您都将Clock服务视为环境的组成部分。

2.8.2 Console

控制台服务提供了有关读写控制台的功能。
到目前为止,在本书中,我们一直在通过使用ZIO.effect构造函数将Scala库中的过程代码转换为ZIO效果与控制台进行交互。 这有助于说明如何将程序转换为ZIO,并证明ZIO自己的控制台功能没有“魔力”。
但是,直接包装控制台功能并不理想,因为我们无法为测试环境提供替代实现。 此外,控制台服务还会为我们处理一些棘手的控制台交互案例。 (例如,从控制台读取只能因IOException而失败。)
控制台服务上的关键方法是getStrLn,它类似于readLine()putStrLn,它等效于println。 如果不想在将文本打印到控制台之后添加换行符,则还有一个putStr方法。

  package object console {
    val getStrLn : ZIO [Console, IOException, String]

    def putStr(line : => String) : URIO [Console, Unit]

    def putStrLn(line : => String) : URIO [Console, Unit]
  }

控制台服务通常在控制台应用程序中使用,但在代码中不如时钟或随机出现的多。
在本书的其余部分,我们将说明使用这些方法涉及控制台应用程序的示例,而不是从Scala标准库转换方法。

2.8.3 System

系统服务提供了获取系统和环境变量的功能。

  package object system {
    def env(variable : String) : IO [SecurityException, Option [String]]

    def property(prop : String) : IO [Throwable, Option [String]]
  }

System服务上的两个主要方法是env(访问指定的环境变量)和property(访问指定的系统属性)。如果不存在指定的环境变量或属性,还有其他方法可用于获取所有环境变量或系统属性.又或者指定默认值。
与控制台服务一样,系统服务通常在应用程序或某些库(例如,处理配置的库)中使用更多,但在通用代码中并不常见。

2.8.4 Random

随机服务提供与随机数生成相关的功能。 随机服务公开与scala.util.Random基本上相同的接口,但是所有方法都返回功能效果。 因此,如果您熟悉标准库中的random.nextInt(6)之类的代码,那么使用Random服务应该会很舒服。
随机服务有时在调度中的通用代码中使用,例如在某些效果的重复之间添加随机延迟时。

2.8.5 Blocking

阻塞服务支持 在针对阻塞任务进行了优化的执行器上 运行阻塞效果。 默认情况下,ZIO运行时针对异步和计算绑定任务进行了优化,并具有少量固定数量的线程来执行所有工作。
尽管此选择可以优化异步和CPU任务的吞吐量,但是这意味着,如果您在ZIO的默认线程上运行阻塞的I / O操作,则可能会耗尽它们。 因此,至关重要的是,阻塞任务必须在单独的阻塞线程池上运行,该池针对这些工作负载进行了优化。
阻塞服务有几种支持此用例的方法。
最基本的是blocking操作,它会消费一个effect并确保它将在阻塞线程池上运行。

  import zio.blocking._
  object blocking {
    def blocking[R <: Blocking, E, A](zio: ZIO[R, E, A]): ZIO[R, E, A] =
      ???
  }

假设上述数据库查询不是基于基于回调的API来实现的,而是同步等待直到结果可用(从而阻塞了调用线程)。 在这种情况下,我们想确保通过使用blocking操作在阻塞线程池上执行效果,如以下代码片段所示:

  import zio.blocking._
  def getUserById(id: Int): IO[Unit, String] =
    ???
  def getUserByIdBlocking(id: Int): ZIO[Blocking, Unit, String] =
    blocking(getUserById(id))

请注意,阻塞服务现在是类型签名中的依赖项,它说明返回的效果涉及阻塞IO,而其先前的签名则没有。
如果要直接从阻塞的副作用构造一个effect,则可以使用effectBlocking构造函数,它等效于blocking(ZIO.effect(…))。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值