在Java中使用Callable和Future

banq發表於2018-11-21

最近,我們正在開發一個監控儀表板,我們想要ping幾個第三方伺服器,只是為了檢查它們的可用性,或者我們想要呼叫健康檢查。
我們正在對伺服器進行一系列api呼叫,這些伺服器的IP,使用者名稱和密碼被外部化為環境變數。
然後是我們的負載均衡器丟擲HTTP 504(閘道器超時)的挑戰,因為這些請求花了很多時間來返回結果。
因此,我們繼續使用Callable和Future同時進行這些呼叫以減少所需的時間。
在開發多執行緒應用程式的傳統方式中,我們建立執行緒併為其提供Runnable任務。Callable與Runnable非常相似,它有一個細微的區別,它可以返回結果或丟擲異常,而Runnable的run方法的返回型別為void。

Runnable runnable = new Runnable() {
    @Override
    public void run() {
        // this can neither return the result nor throw an exception
    }
}

但是,Callable可以返回任務的結果並且可以丟擲異常。您也可以指定結果型別。為簡單起見,我把它作為String,但只要call()方法返回它,它就可以是任何複雜的物件。

Callable<String> callable = new Callable<String>() {
    @Override
    public String call() throws Exception {
        return "this can return output OR can throw Exception";
    }
};


既然您有Callable或任務,您需要能夠執行該任務以獲得輸出。您可以使用ExecutorService使用它的submit()方法執行任務,或使用invokeAll()執行多個任務 ,您可以在其中提交可呼叫任務的集合。
現在是有趣的部分,submit()的返回型別是Future <T>,其中T是輸出(String,如上例所示),invokeAll()的返回型別是List <Future <T >>
Future表示非同步計算的結果。Java提供了檢查計算是否完成,等待它完成或從中檢索結果的方法。(它非常類似於javascript承諾,如果這使它易於理解)。因此,為了獲得未來的結果,我們只需要呼叫get()就可以了

ExecutorService executorService = Executors.newFixedThreadPool(10);
// callable from above code
Future<String> future = executorService.submit(callable);
String output = future.get();


下面是完整程式碼:

public class HealthCheckService {

  private HttpHandler httpHandler;
  private Config config;
  private ExecutorService executor;

  public HealthCheckService(HttpHandler httpHandler,Config config) {
    this.httpHandler = httpHandler;
    this.configuration = configuration;
    int size = config.servers.size();
    this.executor = Executors.newFixedThreadPool(size);
  }

  public List<HealthCheckResult> getHealthCheck() throws Exception {
    List<Callable<HealthCheck>> tasks = prepareTasks(config.uri);
    List<Future<HealthCheck>> futures = executor.invokeAll(tasks);
    List<HealthCheck> output = new ArrayList<>();
    for (Future<HealthCheckResult> future: futures) {
      output.add(future.get());
    }
    return output;
  }

  private List<Callable<HealthCheck>> prepareTasks(String api) {
    List<Callable<HealthCheckResult>> tasks = new ArrayList();
      for (String server: config.servers) {
        tasks.add(() -> httpHandler.getStatus(api));
      }
      return tasks;
  }
}


原始碼見:Github

相關文章