Avatar
0
Ngô Văn Hiếu Beginner
Hỏi về việc sử dụng Completablefuture trong dự án thực tế
Em chào anh Dũng và mọi người ạ!
Dạo gần đây em đang đọc phần về CompletableFuture trong cuốn Modern Java In Action. Sau khi tìm hiểu thì em thấy CompletableFuture khá hữu ích với các tác vụ liên quan đến I/O, chẳng hạn như gọi API từ các hệ thống bên ngoài.
Ví dụ, nếu cần gọi đồng thời hai API thì có thể viết như sau:
CompletableFuture<String> future1 =
        CompletableFuture.supplyAsync(api::callApi1);

CompletableFuture<String> future2 =
        CompletableFuture.supplyAsync(api::callApi2);
Theo cách hiểu của em, callApi1() callApi2() sẽ được thực thi song song trên các thread khác. Nhờ vậy, thread xử lý request không phải chờ từng API hoàn thành rồi mới gọi API tiếp theo, nên tổng thời gian phản hồi có thể được rút ngắn đáng kể.
Tuy nhiên, em cũng nghĩ rằng cách này cần quản lý thread cẩn thận, chẳng hạn sử dụng thread pool phù hợp. Nếu mỗi request đều tạo quá nhiều thread mới thì có thể gây lãng phí tài nguyên, thậm chí ảnh hưởng đến hiệu năng hoặc độ ổn định của server.
Em có một vài thắc mắc muốn xin ý kiến mọi người:
  • Trong thực tế, CompletableFuture có được sử dụng nhiều để xử lý các tác vụ I/O như thế này không? Các dự án production có áp dụng phổ biến không ạ?
  • Khi sử dụng CompletableFuture thì thường sẽ phải đánh đổi những gì? Hiện tại em mới thấy khá nhiều lợi ích nên muốn nghe thêm góc nhìn từ mọi người.
  • Theo kinh nghiệm của anh Dũng và mọi người thì ưu điểm, nhược điểm và những lưu ý quan trọng khi áp dụng CompletableFuture trong thực tế là gì ạ?
Em cảm ơn anh Dũng và mọi người!
  • Answer
Remain: 5
1 Answer
Avatar
tvd12 Enlightened
tvd12 Enlightened
The Best Answer
Hello Hiếu, sorry em anh trả lời trễ.
Ở bên trong thì CompletableFuture sử dụng kiểu
private static final Executor asyncPool = useCommonPool ?
        ForkJoinPool.commonPool() : new ThreadPerTaskExecutor();
Với code này thì Java core sẽ tự tính toán dùng thread thế nào cho hợp lý. Xem trong mã nguồn thì có vẻ như tối đa sẽ có 32.767 thread được tạo ra, đây là một số lượng thread rất lớn, nếu cần này thread được tạo ra cũng không phải là ý tưởng tốt vì có làm giảm hiệu quả của đa luồng. Vậy nên:
  1. Ưu điểm: Nếu các nghiệp vụ I/O của em xử lý rất nhanh thì CompletableFuture rất tuyệt vời, nó không cần chờ đợi tuần tự như vậy tốc độ có thể rất nhanh.
  2. Nhược điểm: Nhưng nếu nghiệp vụ I/O của em chậm, số lượng request call đồng thời tăng nhanh sẽ có hàng chục nghìn thread được ra, lúc này máy chủ sẽ đòi hỏi 1 lượng tài nguyên lớn để đáp ứng.
Theo anh thì cần phải giới hạn được số lượng thread, anh thường chỉ để tối đa vài trăm thread được sinh ra thôi nên rất ít khi anh dùng đến CompletableFuture.
  • 0
  • Reply
Đầu tiên cảm ơn anh đã phản hồi em ạ.
Từ câu trả lời của anh thì cho em hỏi thêm nếu gặp 1 task I/O mà cần xử lý đồng thời thì theo như em hiểu là chúng ta nên giới hạn số lượng thread tối đa trong thread pool, nếu đã giới hạn số lượng thread thì dùng cái CompletableFuture này vẫn ổn đúng không anh?
Tại vì em thấy trong 1 hệ thống microservice thì việc call 1 Api của service khác là 1 việc khá thường xuyên, mà việc gọi bất đồng bộ như này thì nó giúp giảm thời gian phản hồi của 1 request đáng kể (Theo em thì đây có thể là 1 trong các cách tối ưu thời gian phản hồi của 1 request chậm).
Em ví dụ việc giới hạn 200 thread.
ExecutorService executor =
    Executors.newFixedThreadPool(200);
CompletableFuture.supplyAsync(() -> callApi(), executor);
 –  Ngô Văn Hiếu 1783779415000
Đầu tiên cảm ơn anh đã phản hồi em ạ.
Từ câu trả lời của anh thì cho em hỏi thêm nếu gặp 1 task I/O mà cần xử lý đồng thời thì theo như em hiểu là chúng ta nên giới hạn số lượng thread tối đa trong thread pool, nếu đã giới hạn số lượng thread thì dùng cái CompletableFuture này vẫn ổn đúng không anh?
Tại vì em thấy trong 1 hệ thống microservice thì việc call 1 Api của service khác là 1 việc khá thường xuyên, mà việc gọi bất đồng bộ như này thì nó giúp giảm thời gian phản hồi của 1 request đáng kể (Theo em thì đây có thể là 1 trong các cách tối ưu thời gian phản hồi của 1 request chậm).
Em ví dụ việc giới hạn 200 thread.
ExecutorService executor =
    Executors.newFixedThreadPool(200);
CompletableFuture.supplyAsync(() -> callApi(), executor);
 –  Ngô Văn Hiếu 1783779524000
Ừ, nếu em đã giới hạn số lượng thread thì dùng CompletableFuture ok em ạ. Tuy nhiên em nên tạo ra lớp wrap vì lần nào gọi cũng phải truyền cái executor trông nó không được tự nhiên, một số người thì hay quên em ạ.
 –  tvd12 1783905895000
0%