Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -169,24 +169,38 @@ private boolean bulkInsert(List<NewsEsDocument> docs, String section) throws IOE
}

/**
* ํ‚ค์›Œ๋“œ๋กœ ๋‰ด์Šค ๊ธฐ์‚ฌ ๊ฒ€์ƒ‰
* ์–ด์ œ ๋‚ ์งœ ๊ธฐ์ค€์œผ๋กœ ๋‰ด์Šค ๊ธฐ์‚ฌ ๊ฒ€์ƒ‰์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
*
* <p>{@code combinedTokens} ํ•„๋“œ์— ๋Œ€ํ•ด match ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ณ ,
* ์ ์ˆ˜(score) ๊ธฐ์ค€์œผ๋กœ ๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌ๋œ ์ƒ์œ„ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.</p>
* {@code publishedAt} ํ•„๋“œ๊ฐ€ ์–ด์ œ(00:00~24:00)์ธ ๋ฐ์ดํ„ฐ๋งŒ ํ•„ํ„ฐ๋งํ•ฉ๋‹ˆ๋‹ค.
* ๊ฒฐ๊ณผ๋Š” ์ ์ˆ˜(score) ๊ธฐ์ค€์œผ๋กœ ๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌ๋˜์–ด ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค.</p>
*
* @param keyword ๊ฒ€์ƒ‰ ํ‚ค์›Œ๋“œ
* @param size ์ตœ๋Œ€ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ์ˆ˜
* @return ๊ฒ€์ƒ‰๋œ ๋‰ด์Šค ๋„ํ๋จผํŠธ ๋ฆฌ์ŠคํŠธ
* @param size ์ตœ๋Œ€ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ์ˆ˜
* @return ์–ด์ œ ๋‚ ์งœ ์ค‘ ํ‚ค์›Œ๋“œ๋ฅผ ํฌํ•จํ•˜๋Š” ๋‰ด์Šค ๋„ํ๋จผํŠธ ๋ฆฌ์ŠคํŠธ
* @throws IOException Elasticsearch ๊ฒ€์ƒ‰ ์‹คํŒจ ์‹œ ๋ฐœ์ƒ
*/
public List<NewsEsDocument> searchByKeyword(String keyword, int size) throws IOException {
LocalDate today = LocalDate.now();
LocalDate yesterday = today.minusDays(1);

String from = yesterday.atStartOfDay().toString(); // ์–ด์ œ 00:00:00
String to = today.atStartOfDay().toString(); // ์˜ค๋Š˜ 00:00:00

SearchResponse<NewsEsDocument> response = elasticsearchClient.search(s -> s
.index("news-index-nori")
.size(size)
.query(q -> q
.match(m -> m
.field("combinedTokens")
.query(keyword)
.bool(b -> b
.must(m -> m.match(mm -> mm
.field("combinedTokens")
.query(keyword)
))
.filter(f -> f.range(r -> r
.field("publishedAt")
.gte(JsonData.of(from))
.lt(JsonData.of(to))
))
)
)
.sort(sort -> sort
Expand Down Expand Up @@ -235,4 +249,46 @@ public List<StringTermsBucket> getTopKeywordsForDateRange(LocalDate gte, LocalDa
.buckets()
.array();
}

// ======================= Deprecated =========================

/**
* Hot Fix
*
* Deprecated At 2025-07-24
* By ๊น€์›์ค‘
*
*/
// /**
// * ํ‚ค์›Œ๋“œ๋กœ ๋‰ด์Šค ๊ธฐ์‚ฌ ๊ฒ€์ƒ‰
// *
// * <p>{@code combinedTokens} ํ•„๋“œ์— ๋Œ€ํ•ด match ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ณ ,
// * ์ ์ˆ˜(score) ๊ธฐ์ค€์œผ๋กœ ๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌ๋œ ์ƒ์œ„ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.</p>
// *
// * @param keyword ๊ฒ€์ƒ‰ ํ‚ค์›Œ๋“œ
// * @param size ์ตœ๋Œ€ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ์ˆ˜
// * @return ๊ฒ€์ƒ‰๋œ ๋‰ด์Šค ๋„ํ๋จผํŠธ ๋ฆฌ์ŠคํŠธ
// * @throws IOException Elasticsearch ๊ฒ€์ƒ‰ ์‹คํŒจ ์‹œ ๋ฐœ์ƒ
// */
// public List<NewsEsDocument> searchByKeyword(String keyword, int size) throws IOException {
// SearchResponse<NewsEsDocument> response = elasticsearchClient.search(s -> s
// .index("news-index-nori")
// .size(size)
// .query(q -> q
// .match(m -> m
// .field("combinedTokens")
// .query(keyword)
// )
// )
// .sort(sort -> sort
// .score(sc -> sc.order(SortOrder.Desc))
// ),
// NewsEsDocument.class
// );
//
// return response.hits().hits().stream()
// .map(Hit::source)
// .filter(Objects::nonNull)
// .toList();
// }
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ public IntermediateBatchRedisService(@Qualifier("redisSession1Template") RedisTe

private static final String BATCH_KEY_PREFIX = "IntermediateBatch:";

public boolean isBatchAlreadyDone(String key) {
return redisSession1Template.hasKey(key);
}

public void markBatchDone(String key) {
redisSession1Template.opsForValue().set(key, "done", Duration.ofHours(12)); // 12
}

/**
* ์ค‘๊ฐ„ ๋ฐฐ์น˜ ์‹คํ–‰ ํšŸ์ˆ˜๋ฅผ ์กฐํšŒ ๋ฉ”์„œ๋“œ
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,63 +27,132 @@ public class NewsMonitoringService {
private final JobLauncher jobLauncher;
private final Job newsDataSaveJob_Monitoring;

/**
* ๋งค ์ •๊ฐ๋งˆ๋‹ค ์‹คํ–‰ ๋˜๋Š” ๋ชจ๋‹ˆํ„ฐ๋ง ์Šค์ผ€์ค„๋Ÿฌ
*
* ๊ฐ ์„น์…˜ ๋ณ„ ํ˜„์žฌ ๋ช‡๊ฐœ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ชจ์˜€๋Š”์ง€ ๋ชจ๋‹ˆํ„ฐ๋ง
* 9000๊ฐœ๊ฐ€ ๋„˜๋Š” ์„น์…˜ ๋ฐœ๊ฒฌ ์‹œ, ์ฆ‰์‹œ DB์— Batch ์‹ค์‹œ
*
*/
@Scheduled(cron = "0 0 * * * *") // ๋งค ์‹œ๊ฐ„ ์ •๊ฐ
//@Scheduled(cron = "0 */5 * * * *") // 5๋ถ„
// @Scheduled(cron = "0 */3 * * * *") // 3๋ถ„
//@Scheduled(cron = "0 */3 * * * *") // 3๋ถ„
//@Scheduled(cron = "0 */2 * * * *") // 2๋ถ„
//@Scheduled(cron = "0 * * * * *") // 1๋ถ„
public void monitoring(){
public void monitoring() {

/* ๋‚ ์งœ & ์‹œ๊ฐ„ ํ™•์ธ */
LocalDate day = LocalDate.now(); // ์˜ค๋Š˜์˜ ๋‚ ์งœ
LocalDateTime time = LocalDateTime.now(); // ํ˜„์žฌ ๋‚ ์งœ์™€ ์‹œ๊ฐ„(๋ถ„ ๋‹จ์œ„ ํฌํ•จ)
log.info("// ======================= ์˜ค๋Š˜์˜ ๋‚ ์งœ : {} =========================", day); // ์˜ค๋Š˜์˜ ๋‚ ์งœ ๋กœ๊ทธ ํ™•์ธ
log.info("// ======================= ์˜ค๋Š˜์˜ ๋‚ ์งœ : {} =========================", day);
log.info("ํ˜„์žฌ ์‹œ๊ฐ„ : {}", time);

// ์ž์ •(00์‹œ)์ด๋ฉด ์Šคํ‚ต
if (time.getHour() == 0) {
log.info("[SKIP] ์ž์ • 00์‹œ์—๋Š” ๋ชจ๋‹ˆํ„ฐ๋ง ์ž‘์—…์„ ์‹คํ–‰ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.");
return;
}

/* ๋‚ ์งœ ํฌ๋งคํŒ… ์ž‘์—… */
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); //์›ํ•˜๋Š” ํฌ๋งท ์ง€์ •
String dateTo = day.format(formatter); //๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜
String dateFrom = day.format(formatter); //๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String dateStr = day.format(formatter);

/* ์„น์…˜ ๋ณ„ ๋ˆ„์  ๋ฐ์ดํ„ฐ ์ˆ˜ ๋ชจ๋‹ˆํ„ฐ๋ง ๋กœ์ง */
for (String section : sections) {

// ํ˜„์žฌ ์„น์…˜์˜ ํ˜„ ์‹œ๊ฐ ๋ˆ„์  Total_Item ์ˆ˜ ํ™•์ธ
int total_items = newsMonitoringManager.getTotalItems(section, dateFrom, dateTo);
int total_items = newsMonitoringManager.getTotalItems(section, dateStr, dateStr);
String batchDoneKey = "batch_done:" + dateStr + ":" + section; // ํ•˜๋ฃจ 1ํšŒ ์ฒดํฌ์šฉ
String batchCountKey = "batch_count:" + dateStr + ":" + section; // ํ•˜๋ฃจ ํšŸ์ˆ˜ ์ œํ•œ์šฉ

/* ๋‰ด์Šค ๋ฐ์ดํ„ฐ 9000๊ฐœ ์ด์ƒ ์‹œ ์ค‘๊ฐ„ ๋ฐฐ์น˜ + Redis ๊ธฐ๋ก */
if(total_items >= 9000 && total_items <= 18000){
/* =================== 9000 ์ด์ƒ 18000 ์ดํ•˜์ผ ๊ฒฝ์šฐ =================== */
if (total_items >= 9000 && total_items <= 18000) {

// ํ•ด๋‹น ์„น์…˜์ด ์ด์ „์—๋„ ์‚ฌ์ „ Batch ์ž‘์—…์ด ์‹คํ–‰ ๋˜์—ˆ๋Š”์ง€ ํ™•์ธ (Redis์—์„œ ํšŸ์ˆ˜ ์กฐํšŒ)
int n = intermediateBatchRedisService.getBatchCount(section);
log.info("{} ์„น์…˜ ๊ธฐ์กด ์ค‘๊ฐ„ ๋ฐฐ์น˜ ํ˜„ํ™ฉ : ์ด {}ํšŒ ์ค‘๊ฐ„ ๋ฐฐ์น˜ ",section, n);
// ํ•˜๋ฃจ ํ•œ ๋ฒˆ๋งŒ ์ˆ˜ํ–‰: ์ด๋ฏธ ํ–ˆ๋Š”์ง€ Redis์— ํ™•์ธ
if (intermediateBatchRedisService.isBatchAlreadyDone(batchDoneKey)) {
log.info("[SKIP] {} ์„น์…˜์€ ์ด๋ฏธ {}์— ์ค‘๊ฐ„ ๋ฐฐ์น˜๊ฐ€ ์ˆ˜ํ–‰๋จ", section, dateStr);
continue;
}

// ํ˜„์žฌ total_item์ด ๊ธฐ์ค€(9000๊ฐœ)์„ ๋„˜์œผ๋ฉด Job Launcher๋กœ Batch ์ž‘์—… ์ง„ํ–‰ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
runNewsBatch(section);
int prevCount = intermediateBatchRedisService.getBatchCount(batchCountKey);
log.info("{} ์„น์…˜ ๊ธฐ์กด ์ค‘๊ฐ„ ๋ฐฐ์น˜ ํ˜„ํ™ฉ : ์ด {}ํšŒ ์ค‘๊ฐ„ ๋ฐฐ์น˜", section, prevCount);

// ์ค‘๊ฐ„ ๋ฐฐ์น˜๊ฐ€ ์ง„ํ–‰๋œ ์„น์…˜ ๋ฐฐ์น˜ ํ˜„ํ™ฉ ๊ธฐ๋ก (Redis์— ํšŸ์ˆ˜ ์ฆ๊ฐ€)
intermediateBatchRedisService.incrementBatchCount(section);
runNewsBatch(section); // ๋ฐฐ์น˜ ์‹คํ–‰

} /* 2๋ฒˆ์งธ ์ค‘๊ฐ„ ๋ฐฐ์น˜ ๋ถ€ํ„ฐ ์ฃผ์˜ ๋ฉ”์‹œ์ง€ ํ˜ธ์ถœ */
else if(total_items >= 18000){
intermediateBatchRedisService.incrementBatchCount(batchCountKey); // ํšŸ์ˆ˜ ์ฆ๊ฐ€
intermediateBatchRedisService.markBatchDone(batchDoneKey); // ํ•˜๋ฃจ 1ํšŒ ์™„๋ฃŒ ๊ธฐ๋ก

// Job Launcher๋กœ Batch ์ž‘์—… ์ง„ํ–‰ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
runNewsBatch(section);
}

// ํ˜„์žฌ total_item์ด ๊ธฐ์ค€(18000๊ฐœ)์„ ๋„˜์œผ๋ฉด Job Launcher๋กœ Batch ์ž‘์—… ์ง„ํ–‰ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
log.info("[#์ฃผ์˜#] {}์„น์…˜์˜ total_items์ˆ˜ : {}",section,total_items); // ์ถ”ํ›„, ํ•˜๋ฃจ์— 3๋ฒˆ ์ด์ƒ์˜ ์ค‘๊ฐ„ ํ˜ธ์ถœ์ด ์ผ์–ด๋‚˜๋Š” ๊ฒƒ์— ๋Œ€ํ•œ ๋ฐฉ์–ด์ฝ”๋“œ๋„ ์ž‘์„ฑํ•  ๊ฒƒ
/* =================== 18000 ์ดˆ๊ณผ์ผ ๊ฒฝ์šฐ =================== */
else if (total_items > 18000) {

int count = intermediateBatchRedisService.getBatchCount(batchCountKey);

if (count >= 3) {
log.warn("[SKIP] {} ์„น์…˜์€ ์ด๋ฏธ {}์— 3ํšŒ ์ด์ƒ ๋ฐฐ์น˜๋จ", section, dateStr);
continue;
}

runNewsBatch(section); // ๋ฐฐ์น˜ ์‹คํ–‰

intermediateBatchRedisService.incrementBatchCount(batchCountKey); // ํšŸ์ˆ˜ ์ฆ๊ฐ€
log.warn("[#์ฃผ์˜#] {} ์„น์…˜์˜ total_items ์ˆ˜ : {} (๋ˆ„์  {}ํšŒ ๋ฐฐ์น˜๋จ)", section, total_items, count + 1);
}
}
}

// ======================= deprecated =========================

/**
* ๋งค ์ •๊ฐ๋งˆ๋‹ค ์‹คํ–‰ ๋˜๋Š” ๋ชจ๋‹ˆํ„ฐ๋ง ์Šค์ผ€์ค„๋Ÿฌ
*
* ๊ฐ ์„น์…˜ ๋ณ„ ํ˜„์žฌ ๋ช‡๊ฐœ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ชจ์˜€๋Š”์ง€ ๋ชจ๋‹ˆํ„ฐ๋ง
* 9000๊ฐœ๊ฐ€ ๋„˜๋Š” ์„น์…˜ ๋ฐœ๊ฒฌ ์‹œ, ์ฆ‰์‹œ DB์— Batch ์‹ค์‹œ
*
*/
// @Scheduled(cron = "0 0 * * * *") // ๋งค ์‹œ๊ฐ„ ์ •๊ฐ
// //@Scheduled(cron = "0 */5 * * * *") // 5๋ถ„
// // @Scheduled(cron = "0 */3 * * * *") // 3๋ถ„
// //@Scheduled(cron = "0 */2 * * * *") // 2๋ถ„
// //@Scheduled(cron = "0 * * * * *") // 1๋ถ„
// public void monitoring(){
//
// /* ๋‚ ์งœ & ์‹œ๊ฐ„ ํ™•์ธ */
// LocalDate day = LocalDate.now(); // ์˜ค๋Š˜์˜ ๋‚ ์งœ
// LocalDateTime time = LocalDateTime.now(); // ํ˜„์žฌ ๋‚ ์งœ์™€ ์‹œ๊ฐ„(๋ถ„ ๋‹จ์œ„ ํฌํ•จ)
// log.info("// ======================= ์˜ค๋Š˜์˜ ๋‚ ์งœ : {} =========================", day); // ์˜ค๋Š˜์˜ ๋‚ ์งœ ๋กœ๊ทธ ํ™•์ธ
// log.info("ํ˜„์žฌ ์‹œ๊ฐ„ : {}", time);
//
// /* ๋‚ ์งœ ํฌ๋งคํŒ… ์ž‘์—… */
// DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); //์›ํ•˜๋Š” ํฌ๋งท ์ง€์ •
// String dateTo = day.format(formatter); //๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜
// String dateFrom = day.format(formatter); //๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜
//
// /* ์„น์…˜ ๋ณ„ ๋ˆ„์  ๋ฐ์ดํ„ฐ ์ˆ˜ ๋ชจ๋‹ˆํ„ฐ๋ง ๋กœ์ง */
// for (String section : sections) {
//
// // ํ˜„์žฌ ์„น์…˜์˜ ํ˜„ ์‹œ๊ฐ ๋ˆ„์  Total_Item ์ˆ˜ ํ™•์ธ
// int total_items = newsMonitoringManager.getTotalItems(section, dateFrom, dateTo);
//
// /* ๋‰ด์Šค ๋ฐ์ดํ„ฐ 9000๊ฐœ ์ด์ƒ ์‹œ ์ค‘๊ฐ„ ๋ฐฐ์น˜ + Redis ๊ธฐ๋ก */
// if(total_items >= 9000 && total_items <= 18000){
//
// // ํ•ด๋‹น ์„น์…˜์ด ์ด์ „์—๋„ ์‚ฌ์ „ Batch ์ž‘์—…์ด ์‹คํ–‰ ๋˜์—ˆ๋Š”์ง€ ํ™•์ธ (Redis์—์„œ ํšŸ์ˆ˜ ์กฐํšŒ)
// int n = intermediateBatchRedisService.getBatchCount(section);
// log.info("{} ์„น์…˜ ๊ธฐ์กด ์ค‘๊ฐ„ ๋ฐฐ์น˜ ํ˜„ํ™ฉ : ์ด {}ํšŒ ์ค‘๊ฐ„ ๋ฐฐ์น˜ ",section, n);
//
// // ํ˜„์žฌ total_item์ด ๊ธฐ์ค€(9000๊ฐœ)์„ ๋„˜์œผ๋ฉด Job Launcher๋กœ Batch ์ž‘์—… ์ง„ํ–‰ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
// runNewsBatch(section);
//
// // ์ค‘๊ฐ„ ๋ฐฐ์น˜๊ฐ€ ์ง„ํ–‰๋œ ์„น์…˜ ๋ฐฐ์น˜ ํ˜„ํ™ฉ ๊ธฐ๋ก (Redis์— ํšŸ์ˆ˜ ์ฆ๊ฐ€)
// intermediateBatchRedisService.incrementBatchCount(section);
//
// } /* 2๋ฒˆ์งธ ์ค‘๊ฐ„ ๋ฐฐ์น˜ ๋ถ€ํ„ฐ ์ฃผ์˜ ๋ฉ”์‹œ์ง€ ํ˜ธ์ถœ */
// else if(total_items >= 18000){
//
// // Job Launcher๋กœ Batch ์ž‘์—… ์ง„ํ–‰ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
// runNewsBatch(section);
//
// // ํ˜„์žฌ total_item์ด ๊ธฐ์ค€(18000๊ฐœ)์„ ๋„˜์œผ๋ฉด Job Launcher๋กœ Batch ์ž‘์—… ์ง„ํ–‰ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
// log.info("[#์ฃผ์˜#] {}์„น์…˜์˜ total_items์ˆ˜ : {}",section,total_items); // ์ถ”ํ›„, ํ•˜๋ฃจ์— 3๋ฒˆ ์ด์ƒ์˜ ์ค‘๊ฐ„ ํ˜ธ์ถœ์ด ์ผ์–ด๋‚˜๋Š” ๊ฒƒ์— ๋Œ€ํ•œ ๋ฐฉ์–ด์ฝ”๋“œ๋„ ์ž‘์„ฑํ•  ๊ฒƒ
//
// }
// }
// }

// ======================= Spring Batch Job Launcher (Batch Starter Method) =========================

/**
Expand Down