Guava文件翻譯之 Service

devos發表於2016-01-22

概覽

Guava的介面代表了一個有執行狀態的物件,有啟動和停止的方法。比如網路伺服器,RPC伺服器,以及計時器等,都可以實現Service介面。掌管像這樣的服務的狀態,需要正確地管理啟動和關閉,因此會是繁瑣的,特別是牽扯到多執行緒和排程。Guava提供了一個基本的骨架,可以幫你管理狀態邏輯,以及同步的細節

一個Service的通常的生命週期是

 

一個已經停止的Service是不能被重啟的。如果這個服務在啟動或者執行、停止時失敗,那麼它會轉入Service.State.FAILED狀態。

一個服務可以被使用 startAsync()來非同步地啟動,這個方法會返回'this'引用,以方便地使用方法鏈。只有這個服務是NEW狀態的時候才能呼叫。所以,你的程式應該有個唯一的地方來啟動每一個服務。

停止服務是類似的,使用非同步的stopAsync()方法。但是不像startAsync()方法,它可以安全地呼叫多次。這使得可以它可以應對在關閉伺服器可能遇到的競爭問題。

服務也提供了幾種方法來等待服務狀態轉換的完成。

  • 非同步地使用addListener()。addListener()允許你新增一個Service.Listener,它會在服務的服務狀態轉換時被呼叫。需要注意的是,如果在這個listener被新增時,伺服器已經不是NEW的狀態。,那麼已經發生過的狀態轉換是不會在這個listener上重放。
  • 非同步地使用awaitRunning()。它是不可中斷的(uniterruptible),不會丟擲受檢異常,會在服務啟動完畢時返回。如果服務啟動失敗,會丟擲一個IllegalStateException。類似的是 awaitTerminated(),呼叫它等待服務到達終止狀態(TERMINATED or FAILED)。這兩個方法都有過載的方法,允許指定超時時間。

 Service介面是微妙的。我們不建議直接實現它。你可以使用我們在guava提供幾個虛類中的一個作為你的實現的基礎。每個基類提供了一個特定的執行緒模型。

實現

AbstractIdleService

 AbstractIdleService骨架實現了一個Service,這個服務不需要在“running”狀態執行任何動作,因此它在執行時也不需要一個執行緒,但是它仍然需要執行啟動和停止的動作。實現這樣的服務只需要繼承AbstractIdleService,並且實現 startUp()以及shutDown()方法,

protected void startUp() {
  servlets.add(new GcStatsServlet());
}
protected void shutDown() {}

需要指出的是,任何對GcStatsServlet的查詢已經執行於一個執行緒中了。因此,在這個服務執行時,我們不需要它執行任務操作。

AbstractExecutionThreadService

一個AbstractExecutionThreadService在同一個執行緒中執行啟動,動行和關閉操作。你必須重寫run() 方法,而且這個方法必須對關閉請求作為響應。比如,你或許會在一個工作迴圈中執行這些動作:

public void run() {
  while (isRunning()) {
    // perform a unit of work
  }
}

或者,你可以覆蓋 triggerShutdown()方法,只要它能夠使得run()方法返回。覆蓋startUp()shutdown()方法是可選的,但是服務的狀態會由這個骨架替你管理。

protected void startUp() {
  dispatcher.listenForConnections(port, queue);
}
protected void run() {
  Connection connection;
  while ((connection = queue.take() != POISON)) {
    process(connection);
  }
}
protected void triggerShutdown() {
  dispatcher.stopListeningForConnections(queue);
  queue.put(POISON);
}

需要注意的是start()呼叫了你的startUp()方法,為你建立了一個執行緒,並且在這個執行緒中呼叫run()方法。stop()方法呼叫了triggerShutdown()方法,並且等待執行緒停止。(譯註:重點是run()方法不是在呼叫start()的執行緒中執行的)

AbstractScheduledService

一個AbstractScheduledService在它動行時執行一些週期性的任務。它的子類覆蓋runOneIteration()來指定每個週期的任務,同時也需要覆蓋我們熟悉的startUpshutDown() 方法

為了描述怎麼排程執行,你需要實現scheduler()方法。通常而言,你會使用已經AbstractScheduledService.Scheduler提供的幾個排程器中的一個,newFixedRateSchedule(initialDelay, delay, TimeUnit)或者newFixedDelaySchedule(initialDelay, delay, TimeUnit),與我們熟知的ScheduledExecutorService中的方法相類。自定義的排程器可以用來實現,請參見Javadoc.

AbstractService

如果你需要自己管理執行緒,就需要直接覆蓋AbstractService。通常來說,以上提到的幾種實現就夠用了,但是,有些情況下是建議實現AbstractService的,比如,你對有自己的執行緒語義的東西進行建模,把它做成一個Service,你就會有自己特殊的執行緒相關的需求。

為了實現AbstractService,你需要實現這兩個方法

  • doStart():doStart()在第一次呼叫startAsync()時被直接呼叫,你的doStart()方法應該完成所有初始化工作,並且在啟動成功時呼叫,或者啟動失敗時呼叫。
  • doStop():doStop()在第一次呼叫stopAsync()時被呼叫,你的doStop()方法應該關閉你的服務,並且如果關閉成功,最終呼叫notifyStopped(),或者關閉失敗,就呼叫notifyFailed()

你的doStartdoStop方法應該執行得很快。如果你需要進行昂貴的初始化工作,比如讀取檔案,開啟網路連線,或者進行任何可能會阻塞的工作,你需要移動這些工作到另一個執行緒去做。

使用SerivceManager

Service的骨架實現之外,Guava還提供了一個ServiceManager類,它使得一些跟多個Service的實現相關操作操作更簡單一些。使用Service的集合建立一個新的ServiceManager。然後你就可以管理它們了:

或者檢查這些服務:

  • isHealthy() 在所有服務都在狀態時返回true
  • 將會返回一個所有服務的一致的snapshot,以它們的狀態作為索引。
  • startupTimes()將會返回從受管理的Service到它花了多少毫秒啟動的map.Guava保證這個返回的map將按照啟動時間(startup time)排序。

別然我們建議服務的生命週期通過ServiceManager來管理,但是通過其它機制進行的狀態轉換不會影響它的方法的正確性。比如,如果一些服務不是通過startAsync()啟動的,監聽器仍然會在合適的時機被呼叫,並且 awaitHealthy()也會照常工作。ServiceManager的唯一要求是在ServiceManager構造時,所有的Serivce都在NEW狀態。

相關文章