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的簽名:
睡眠方法特別重要。 在指定的持續時間過去之前,它不會完成執行,并且像所有ZIO操作一樣,它是非阻塞的,因此在等待時間過去時,它實際上并沒有消耗任何線程。
我們可以使用sleep方法來實現本章前面介紹的延遲運算符:
Clock服務是ZIO中所有時間和計劃功能的基礎。 因此,每當重試,重復,計時或ZIO中內置的與時間和調度有關的其他功能時,您都將Clock服務視為環境的組成部分。
2.8.2 Console
控制臺服務提供了有關讀寫控制臺的功能。
到目前為止,在本書中,我們一直在通過使用ZIO.effect構造函數將Scala庫中的過程代碼轉換為ZIO效果與控制臺進行交互。 這有助于說明如何將程序轉換為ZIO,并證明ZIO自己的控制臺功能沒有“魔力”。
但是,直接包裝控制臺功能并不理想,因為我們無法為測試環境提供替代實現。 此外,控制臺服務還會為我們處理一些棘手的控制臺交互案例。 (例如,從控制臺讀取只能因IOException而失敗。)
控制臺服務上的關鍵方法是getStrLn,它類似于readLine()和putStrLn,它等效于println。 如果不想在將文本打印到控制臺之后添加換行符,則還有一個putStr方法。
控制臺服務通常在控制臺應用程序中使用,但在代碼中不如時鐘或隨機出現的多。
在本書的其余部分,我們將說明使用這些方法涉及控制臺應用程序的示例,而不是從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并確保它將在阻塞線程池上運行。
假設上述數據庫查詢不是基于基于回調的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(…))。
總結
以上是生活随笔為你收集整理的2.8 zio入门——标准ZIO服务的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c语言 for循环说课,《程序的循环结构
- 下一篇: 特种浓缩分离:无机陶瓷膜设备性能描述