對于我的學習,我正在研究 Spring Boot REST API。我應該在收到請求時減少代碼的執行時間。所以,我認為使代碼異步是一個好主意。但是,不幸的是,我在使用 Spring 時遇到了一些問題,盡管在網上進行了幾個小時的研究以找到解決方案,但我什么也沒找到。讓我解釋 :
為了優化我的代碼,我決定使用@AsyncSpring Annotation。為此,我創建了一個如下所示的 AsyncConfiguration 類:
@Configuration
@EnableAsync
public class AsyncConfiguration {
@Bean(name = "asyncExecutor")
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(1);
executor.setQueueCapacity(50);
executor.setThreadNamePrefix("AsynchThread-");
executor.initialize();
return executor;
}
}
通常,該asyncExecutor()方法回傳ExecutorSpring 必須用于異步呼叫的類或子類。在這種情況下,它是一個ThreadPoolTaskExecutor. 而且我的大部分代碼都帶有注釋@Async以使用 my asyncExecutor,如下所示:
@Async("asyncExecutor")
public CompletableFuture<User> calculateRewards(User user) {
return CompletableFuture.supplyAsync(() -> {
logger.info("Calculating Reward for user : " user.getUserName());
List<VisitedLocation> userLocations = user.getVisitedLocations();
List<Attraction> attractions = gps.getAllAttraction();
for(VisitedLocation visitedLocation : userLocations) {
for(Attraction attraction : attractions) {
if(user.getUserRewards().stream().filter(r -> r.attraction.attractionName.equals(attraction.attractionName)).count() == 0) {
if(nearAttraction(visitedLocation, attraction)) {
user.addUserReward(new UserReward(visitedLocation, attraction, reward.getAttractionRewardPoints(attraction.attractionId, user.getUserId())));
}
}
}
}
return user;
});
}
但是,重點是:當我運行代碼時,Spring 不要使用我的asyncExecutor()bean。我怎么知道?首先,當我在終端中呼叫帶注釋的方法時,我看到的是:
2022-10-27 00:26:24.688 INFO 41436 --- [onPool-worker-4] c.T.T.service.TourGuideMainService : Calculating Reward for user : internalUser919
“ [onPool-worker-4]”是執行緒名稱,或者至少是執行緒名稱的結尾。但它不應該這樣命名。如果你看我asyncExecutor()上面的方法,你可以看到有一個executor.setThreadNamePrefix("AsynchThread-");. 如果代碼按預期作業,執行緒應該被稱為“AsynchThread-4”,但它不是。
其次,我決定在 Debug 模式下運行我的代碼,然后我進入 VS Code 的 Debug 選單,我發現了兩件事:
1 - 當我運行calculateRewards()同時進行 1000 次呼叫的壓力測驗時,只創建了 11 個執行緒。考慮到方法的執行時間和Executor默認calculateRewards()有它的事實(即),應該有超過 11 個執行緒;maxPoolSizeInteger.MAX_VALUE
2 - 創建執行緒時的全名是“[ForkJoinPool.commonPool-worker-4]”;
似乎 Spring 正在使用ForkJoinPool該類來創建執行緒,并且它從未考慮過我的 Executor 的配置。我不知道為什么會這樣,我從來沒有使用ForkJoinPool過,就像我說的,我在網上搜索時沒有找到任何東西。
那么,為什么 Spring 在我的代碼中使用ForkJoinPool而不是ThreadPoolTaskExecutorfor 異步方法?最重要的是:我該如何解決這個問題?
(我希望它是可以理解的......)
編輯 1:在一些隨機測驗期間,我發現,如果 Spring 似乎使用一些ForkJoinPool執行緒來執行我的代碼,它無論如何都會創建一個“AsynchThread”但不要使用它。這更令人困惑……-_-”
uj5u.com熱心網友回復:
這里的問題是你CompletableFuture.supplyAsync用來生產你的CompletableFuture.
此方法將派生在 ForkJoinPool.commonPool() 中運行的任務并在該任務中執行您的供應商。
由于 spring 將提供 executor 并異步運行整個方法,因此您不需要將supplyAsync函式與 lambda 一起使用。相反,您的 async 方法應如下所示(在您的服務內部):
@Service
public class MyService {
...
@Async("asyncExecutor")
public CompletableFuture<User> calculateRewards(User user) {
logger.info("Calculating Reward for user : " user.getUserName());
List<VisitedLocation> userLocations = user.getVisitedLocations();
List<Attraction> attractions = gps.getAllAttraction();
for(VisitedLocation visitedLocation : userLocations) {
for(Attraction attraction : attractions) {
if(user.getUserRewards().stream().filter(r -> r.attraction.attractionName.equals(attraction.attractionName)).count() == 0) {
if(nearAttraction(visitedLocation, attraction)) {
user.addUserReward(new UserReward(visitedLocation, attraction, reward.getAttractionRewardPoints(attraction.attractionId, user.getUserId())));
}
}
}
}
return CompletableFuture.completedFuture(user);
}
}
如果您將服務自動裝配到CommandLineRunner執行您的方法的服務中,您可以使用此運行器查看 spring 將使用Executor您在配置中定義的異步處理執行您的方法。
例如:
@Component
public class Runner implements CommandLineRunner {
private final MyService service;
public Runner(MyService service) {
this.service = service;
}
@Override
public void run(String... args) throws Exception {
CompletableFuture<User> user1 = service.calculateRewards(new User("user1"));
CompletableFuture<User> user2 = service.calculateRewards(new User("user2"));
CompletableFuture<User> user3 = service.calculateRewards(new User("user3"));
CompletableFuture.allOf(user1,user2,user3).join();
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/521731.html
