Skip to content

Commit 18f9e08

Browse files
committed
fix news scheduler
1 parent 28b74d1 commit 18f9e08

5 files changed

Lines changed: 242 additions & 176 deletions

File tree

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,25 @@
11
package com.phantoms.phantomsbackend.common.config;
22

3-
import org.springframework.scheduling.annotation.EnableScheduling;
43
import org.springframework.context.annotation.Configuration;
4+
import org.springframework.scheduling.annotation.EnableScheduling;
5+
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
6+
import org.springframework.context.annotation.Bean;
7+
8+
import java.util.concurrent.ThreadPoolExecutor;
59

610
@Configuration
711
@EnableScheduling
812
public class SchedulingConfig {
13+
14+
@Bean
15+
public ThreadPoolTaskScheduler taskScheduler() {
16+
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
17+
scheduler.setPoolSize(5); // 核心线程数5,避免单线程阻塞
18+
scheduler.setThreadNamePrefix("FF14-Scheduler-");
19+
scheduler.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
20+
scheduler.setWaitForTasksToCompleteOnShutdown(true);
21+
scheduler.setAwaitTerminationSeconds(60);
22+
return scheduler;
23+
}
24+
925
}

src/main/java/com/phantoms/phantomsbackend/common/utils/FF14CrystalNewsUtils.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,11 @@ public List<NewsItem> fetchCrystalNews() {
118118
String coverUrl = cover.getString("url");
119119

120120
if (coverUrl != null && !coverUrl.isEmpty()) {
121+
// 修复URL格式,确保有正确的协议前缀
122+
if (!coverUrl.startsWith("http://") && !coverUrl.startsWith("https://")) {
123+
coverUrl = "https:" + coverUrl;
124+
}
125+
121126
if ("502*282".equals(size)) {
122127
imageUrl = coverUrl;
123128
break;

src/main/java/com/phantoms/phantomsbackend/service/scheduler/FF14CrystalNewsScheduler.java

Lines changed: 70 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -74,66 +74,81 @@ public void initCache() {
7474

7575
@Scheduled(fixedRate = 5 * 60 * 1000)
7676
public void fetchAndSendFF14CrystalNews() {
77-
logger.info("开始获取FF14水晶世界新闻列表");
77+
long start = System.currentTimeMillis();
78+
logger.info("开始获取FF14水晶世界新闻列表(耗时监控)");
7879

7980
try {
80-
List<FF14CrystalNewsUtils.NewsItem> newsList = ff14CrystalNewsUtils.fetchCrystalNews();
81-
82-
if (newsList.isEmpty()) {
83-
logger.warn("未获取到FF14水晶世界新闻");
84-
return;
85-
}
86-
87-
List<String> currentIds = newsList.stream()
88-
.map(FF14CrystalNewsUtils.NewsItem::getId)
89-
.collect(Collectors.toList());
90-
91-
List<String> cachedIds = new ArrayList<>();
92-
try {
93-
Object cachedIdsObj = redisUtil.get(FF14_CRYSTAL_NEWS_CACHE_KEY);
94-
if (cachedIdsObj instanceof List) {
95-
cachedIds = (List<String>) cachedIdsObj;
96-
}
97-
} catch (Exception e) {
98-
logger.warn("Redis读取缓存失败,使用内存缓存: {}", e.getMessage());
99-
cachedIds = new ArrayList<>(inMemoryCache);
100-
}
101-
102-
List<String> finalCachedIds = cachedIds;
103-
List<FF14CrystalNewsUtils.NewsItem> newNewsList = newsList.stream()
104-
.filter(news -> !finalCachedIds.contains(news.getId()))
105-
.collect(Collectors.toList());
106-
107-
if (newNewsList.size() > 5) {
108-
logger.warn("检测到缓存丢失,新新闻数量 {} 条超过阈值,使用最新新闻更新缓存", newNewsList.size());
81+
// 任务超时控制:超过30秒则中断
82+
java.util.concurrent.CompletableFuture.runAsync(() -> {
10983
try {
110-
redisUtil.set(FF14_CRYSTAL_NEWS_CACHE_KEY, currentIds);
111-
inMemoryCache = new ArrayList<>(currentIds);
112-
logger.info("缓存已更新,共 {} 条水晶世界新闻ID", currentIds.size());
84+
List<FF14CrystalNewsUtils.NewsItem> newsList = ff14CrystalNewsUtils.fetchCrystalNews();
85+
86+
if (newsList.isEmpty()) {
87+
logger.warn("未获取到FF14水晶世界新闻");
88+
return;
89+
}
90+
91+
List<String> currentIds = newsList.stream()
92+
.map(FF14CrystalNewsUtils.NewsItem::getId)
93+
.collect(Collectors.toList());
94+
95+
List<String> cachedIds = new ArrayList<>();
96+
try {
97+
Object cachedIdsObj = redisUtil.get(FF14_CRYSTAL_NEWS_CACHE_KEY);
98+
if (cachedIdsObj instanceof List) {
99+
cachedIds = (List<String>) cachedIdsObj;
100+
}
101+
} catch (Exception e) {
102+
logger.warn("Redis读取缓存失败,使用内存缓存: {}", e.getMessage());
103+
cachedIds = new ArrayList<>(inMemoryCache);
104+
}
105+
106+
List<String> finalCachedIds = cachedIds;
107+
List<FF14CrystalNewsUtils.NewsItem> newNewsList = newsList.stream()
108+
.filter(news -> !finalCachedIds.contains(news.getId()))
109+
.collect(Collectors.toList());
110+
111+
if (newNewsList.size() > 5) {
112+
logger.warn("检测到缓存丢失,新新闻数量 {} 条超过阈值,使用最新新闻更新缓存", newNewsList.size());
113+
try {
114+
redisUtil.set(FF14_CRYSTAL_NEWS_CACHE_KEY, currentIds);
115+
inMemoryCache = new ArrayList<>(currentIds);
116+
logger.info("缓存已更新,共 {} 条水晶世界新闻ID", currentIds.size());
117+
} catch (Exception e) {
118+
logger.warn("Redis更新缓存失败,仅更新内存缓存: {}", e.getMessage());
119+
inMemoryCache = new ArrayList<>(currentIds);
120+
}
121+
return;
122+
}
123+
124+
if (!newNewsList.isEmpty()) {
125+
logger.info("发现 {} 条FF14水晶世界新新闻", newNewsList.size());
126+
sendNewsToGroup(newNewsList);
127+
} else {
128+
logger.debug("没有FF14水晶世界新新闻");
129+
}
130+
131+
try {
132+
redisUtil.set(FF14_CRYSTAL_NEWS_CACHE_KEY, currentIds);
133+
inMemoryCache = new ArrayList<>(currentIds);
134+
} catch (Exception e) {
135+
logger.warn("Redis更新缓存失败,仅更新内存缓存: {}", e.getMessage());
136+
inMemoryCache = new ArrayList<>(currentIds);
137+
}
138+
113139
} catch (Exception e) {
114-
logger.warn("Redis更新缓存失败,仅更新内存缓存: {}", e.getMessage());
115-
inMemoryCache = new ArrayList<>(currentIds);
140+
logger.error("获取FF14水晶世界新闻失败", e);
116141
}
117-
return;
118-
}
119-
120-
if (!newNewsList.isEmpty()) {
121-
logger.info("发现 {} 条FF14水晶世界新新闻", newNewsList.size());
122-
sendNewsToGroup(newNewsList);
123-
} else {
124-
logger.debug("没有FF14水晶世界新新闻");
125-
}
126-
127-
try {
128-
redisUtil.set(FF14_CRYSTAL_NEWS_CACHE_KEY, currentIds);
129-
inMemoryCache = new ArrayList<>(currentIds);
130-
} catch (Exception e) {
131-
logger.warn("Redis更新缓存失败,仅更新内存缓存: {}", e.getMessage());
132-
inMemoryCache = new ArrayList<>(currentIds);
133-
}
134-
135-
} catch (Exception e) {
136-
logger.error("获取FF14水晶世界新闻失败", e);
142+
}).orTimeout(30, java.util.concurrent.TimeUnit.SECONDS)
143+
.exceptionally(e -> {
144+
logger.error("定时任务执行超时/异常", e);
145+
return null;
146+
}).join();
147+
148+
} catch (Throwable e) {
149+
logger.error("定时任务主流程异常", e);
150+
} finally {
151+
logger.info("获取FF14水晶世界新闻列表结束,耗时 {}ms", System.currentTimeMillis() - start);
137152
}
138153
}
139154

src/main/java/com/phantoms/phantomsbackend/service/scheduler/FF14GlobalNewsScheduler.java

Lines changed: 75 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -74,71 +74,86 @@ public void initCache() {
7474

7575
@Scheduled(fixedRate = 5 * 60 * 1000)
7676
public void fetchAndSendFF14GlobalNews() {
77-
logger.info("开始获取FF14国际服新闻列表");
77+
long start = System.currentTimeMillis();
78+
logger.info("开始获取FF14国际服新闻列表(耗时监控)");
7879

7980
try {
80-
List<FF14GlobalNewsUtils.NewsItem> newsList = ff14GlobalNewsUtils.fetchGlobalNews();
81-
82-
if (newsList.isEmpty()) {
83-
logger.warn("未获取到FF14国际服新闻");
84-
return;
85-
}
86-
87-
List<String> currentIds = newsList.stream()
88-
.map(FF14GlobalNewsUtils.NewsItem::getId)
89-
.collect(Collectors.toList());
90-
91-
// 获取缓存的新闻ID,增加异常处理
92-
List<String> cachedIds = new ArrayList<>();
93-
try {
94-
Object cachedIdsObj = redisUtil.get(FF14_GLOBAL_NEWS_CACHE_KEY);
95-
if (cachedIdsObj instanceof List) {
96-
cachedIds = (List<String>) cachedIdsObj;
97-
}
98-
} catch (Exception e) {
99-
logger.warn("Redis读取缓存失败,使用内存缓存: {}", e.getMessage());
100-
cachedIds = new ArrayList<>(inMemoryCache);
101-
}
102-
103-
List<String> finalCachedIds = cachedIds;
104-
List<FF14GlobalNewsUtils.NewsItem> newNewsList = newsList.stream()
105-
.filter(news -> !finalCachedIds.contains(news.getId()))
106-
.collect(Collectors.toList());
107-
108-
// 如果新新闻数量超过5条,判定为缓存丢失
109-
if (newNewsList.size() > 5) {
110-
logger.warn("检测到缓存丢失,新新闻数量 {} 条超过阈值,使用最新新闻更新缓存", newNewsList.size());
111-
// 更新缓存为当前所有新闻ID,不发送消息
81+
// 任务超时控制:超过30秒则中断
82+
java.util.concurrent.CompletableFuture.runAsync(() -> {
11283
try {
113-
redisUtil.set(FF14_GLOBAL_NEWS_CACHE_KEY, currentIds);
114-
inMemoryCache = new ArrayList<>(currentIds);
115-
logger.info("缓存已更新,共 {} 条国际服新闻ID", currentIds.size());
84+
List<FF14GlobalNewsUtils.NewsItem> newsList = ff14GlobalNewsUtils.fetchGlobalNews();
85+
86+
if (newsList.isEmpty()) {
87+
logger.warn("未获取到FF14国际服新闻");
88+
return;
89+
}
90+
91+
List<String> currentIds = newsList.stream()
92+
.map(FF14GlobalNewsUtils.NewsItem::getId)
93+
.collect(Collectors.toList());
94+
95+
// 获取缓存的新闻ID,增加异常处理
96+
List<String> cachedIds = new ArrayList<>();
97+
try {
98+
Object cachedIdsObj = redisUtil.get(FF14_GLOBAL_NEWS_CACHE_KEY);
99+
if (cachedIdsObj instanceof List) {
100+
cachedIds = (List<String>) cachedIdsObj;
101+
}
102+
} catch (Exception e) {
103+
logger.warn("Redis读取缓存失败,使用内存缓存: {}", e.getMessage());
104+
cachedIds = new ArrayList<>(inMemoryCache);
105+
}
106+
107+
List<String> finalCachedIds = cachedIds;
108+
List<FF14GlobalNewsUtils.NewsItem> newNewsList = newsList.stream()
109+
.filter(news -> !finalCachedIds.contains(news.getId()))
110+
.collect(Collectors.toList());
111+
112+
// 如果新新闻数量超过5条,判定为缓存丢失
113+
if (newNewsList.size() > 5) {
114+
logger.warn("检测到缓存丢失,新新闻数量 {} 条超过阈值,使用最新新闻更新缓存", newNewsList.size());
115+
// 更新缓存为当前所有新闻ID,不发送消息
116+
try {
117+
redisUtil.set(FF14_GLOBAL_NEWS_CACHE_KEY, currentIds);
118+
inMemoryCache = new ArrayList<>(currentIds);
119+
logger.info("缓存已更新,共 {} 条国际服新闻ID", currentIds.size());
120+
} catch (Exception e) {
121+
logger.warn("Redis更新缓存失败,仅更新内存缓存: {}", e.getMessage());
122+
inMemoryCache = new ArrayList<>(currentIds);
123+
}
124+
return;
125+
}
126+
127+
if (!newNewsList.isEmpty()) {
128+
logger.info("发现 {} 条FF14国际服新新闻", newNewsList.size());
129+
sendNewsToGroup(newNewsList);
130+
} else {
131+
logger.debug("没有FF14国际服新新闻");
132+
}
133+
134+
// 更新缓存,增加异常处理
135+
try {
136+
redisUtil.set(FF14_GLOBAL_NEWS_CACHE_KEY, currentIds);
137+
// 同时更新内存缓存
138+
inMemoryCache = new ArrayList<>(currentIds);
139+
} catch (Exception e) {
140+
logger.warn("Redis更新缓存失败,仅更新内存缓存: {}", e.getMessage());
141+
inMemoryCache = new ArrayList<>(currentIds);
142+
}
143+
116144
} catch (Exception e) {
117-
logger.warn("Redis更新缓存失败,仅更新内存缓存: {}", e.getMessage());
118-
inMemoryCache = new ArrayList<>(currentIds);
145+
logger.error("获取FF14国际服新闻失败", e);
119146
}
120-
return;
121-
}
122-
123-
if (!newNewsList.isEmpty()) {
124-
logger.info("发现 {} 条FF14国际服新新闻", newNewsList.size());
125-
sendNewsToGroup(newNewsList);
126-
} else {
127-
logger.debug("没有FF14国际服新新闻");
128-
}
129-
130-
// 更新缓存,增加异常处理
131-
try {
132-
redisUtil.set(FF14_GLOBAL_NEWS_CACHE_KEY, currentIds);
133-
// 同时更新内存缓存
134-
inMemoryCache = new ArrayList<>(currentIds);
135-
} catch (Exception e) {
136-
logger.warn("Redis更新缓存失败,仅更新内存缓存: {}", e.getMessage());
137-
inMemoryCache = new ArrayList<>(currentIds);
138-
}
139-
140-
} catch (Exception e) {
141-
logger.error("获取FF14国际服新闻失败", e);
147+
}).orTimeout(30, java.util.concurrent.TimeUnit.SECONDS)
148+
.exceptionally(e -> {
149+
logger.error("定时任务执行超时/异常", e);
150+
return null;
151+
}).join();
152+
153+
} catch (Throwable e) {
154+
logger.error("定时任务主流程异常", e);
155+
} finally {
156+
logger.info("获取FF14国际服新闻列表结束,耗时 {}ms", System.currentTimeMillis() - start);
142157
}
143158
}
144159

0 commit comments

Comments
 (0)