Skip to content

Commit ef6644e

Browse files
authored
Merge pull request #234 from FunD-StockProject/fix/shortview-performance-improvement
Fix: 숏뷰 성능 개선 시도2
2 parents 6e8af47 + f6d673c commit ef6644e

File tree

3 files changed

+105
-31
lines changed

3 files changed

+105
-31
lines changed

src/main/java/com/fund/stockProject/shortview/controller/ShortViewController.java

Lines changed: 54 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -76,37 +76,61 @@ public ResponseEntity<List<ShortViewResponse>> getRecommendations(
7676
var latestScoreMap = shortViewService.getLatestScoresByStockIds(recommendedIds);
7777
var keywordsByStockId = shortViewService.getKeywordsByStockIds(recommendedIds, 3);
7878

79-
int priceConcurrency = 8;
80-
List<ShortViewResponse> responses = Flux.fromIterable(recommendedStocks)
81-
.flatMap(stock ->
82-
shortViewService.getRealTimeStockPrice(stock)
83-
.timeout(Duration.ofMillis(800))
84-
.filter(stockInfo -> {
85-
boolean valid = isValidPriceInfo(stockInfo);
86-
if (!valid) {
87-
log.warn("유효하지 않은 가격 정보로 제외합니다. stock_id: {}", stock.getId());
88-
}
89-
return valid;
90-
})
91-
.map(stockInfo -> ShortViewResponse.fromEntityWithPrice(
92-
stock,
93-
stockInfo,
94-
latestScoreMap.get(stock.getId()),
95-
keywordsByStockId.getOrDefault(stock.getId(), List.of())
96-
))
97-
.onErrorResume(e -> {
98-
log.warn("실시간 가격 조회 실패로 제외합니다. stock_id: {}, error: {}",
99-
stock.getId(), e.getMessage());
100-
return Mono.empty();
101-
}),
102-
priceConcurrency
103-
)
104-
.take(recommendTargetCount)
105-
.collectList()
106-
.block();
79+
List<ShortViewResponse> responses = new ArrayList<>();
80+
List<Stock> remainingStocks = new ArrayList<>();
10781

108-
if (responses == null) {
109-
responses = new ArrayList<>();
82+
for (Stock stock : recommendedStocks) {
83+
StockInfoResponse cachedInfo = shortViewService.getCachedRealTimeStockPrice(stock);
84+
if (isValidPriceInfo(cachedInfo)) {
85+
responses.add(ShortViewResponse.fromEntityWithPrice(
86+
stock,
87+
cachedInfo,
88+
latestScoreMap.get(stock.getId()),
89+
keywordsByStockId.getOrDefault(stock.getId(), List.of())
90+
));
91+
if (responses.size() >= recommendTargetCount) {
92+
break;
93+
}
94+
} else {
95+
remainingStocks.add(stock);
96+
}
97+
}
98+
99+
int neededCount = recommendTargetCount - responses.size();
100+
if (neededCount > 0 && !remainingStocks.isEmpty()) {
101+
int priceConcurrency = 8;
102+
List<ShortViewResponse> fetched = Flux.fromIterable(remainingStocks)
103+
.flatMap(stock ->
104+
shortViewService.getRealTimeStockPrice(stock)
105+
.timeout(Duration.ofMillis(600))
106+
.filter(stockInfo -> {
107+
boolean valid = isValidPriceInfo(stockInfo);
108+
if (!valid) {
109+
log.warn("유효하지 않은 가격 정보로 제외합니다. stock_id: {}", stock.getId());
110+
}
111+
return valid;
112+
})
113+
.map(stockInfo -> ShortViewResponse.fromEntityWithPrice(
114+
stock,
115+
stockInfo,
116+
latestScoreMap.get(stock.getId()),
117+
keywordsByStockId.getOrDefault(stock.getId(), List.of())
118+
))
119+
.onErrorResume(e -> {
120+
log.warn("실시간 가격 조회 실패로 제외합니다. stock_id: {}, error: {}",
121+
stock.getId(), e.getMessage());
122+
return Mono.empty();
123+
}),
124+
priceConcurrency
125+
)
126+
.take(neededCount)
127+
.take(Duration.ofMillis(1200))
128+
.collectList()
129+
.block();
130+
131+
if (fetched != null) {
132+
responses.addAll(fetched);
133+
}
110134
}
111135

112136
if (responses.isEmpty()) {

src/main/java/com/fund/stockProject/shortview/service/ShortViewService.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,4 +500,8 @@ public StockInfoResponse getRealTimeStockPriceSync(Stock stock) {
500500
public Mono<StockInfoResponse> getRealTimeStockPrice(Stock stock) {
501501
return securityService.getRealTimeStockPrice(stock);
502502
}
503+
504+
public StockInfoResponse getCachedRealTimeStockPrice(Stock stock) {
505+
return securityService.getCachedRealTimeStockPrice(stock);
506+
}
503507
}

src/main/java/com/fund/stockProject/stock/service/SecurityService.java

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
import com.fund.stockProject.stock.dto.response.StockOverseaVolumeRankResponse;
2727

2828
import lombok.RequiredArgsConstructor;
29+
import org.springframework.cache.Cache;
30+
import org.springframework.cache.CacheManager;
2931
import org.springframework.cache.annotation.Cacheable;
3032
import reactor.core.publisher.Mono;
3133

@@ -38,6 +40,8 @@ public class SecurityService {
3840
private final SecurityHttpConfig securityHttpConfig;
3941
private final WebClient webClient;
4042
private final ObjectMapper objectMapper;
43+
private final CacheManager cacheManager;
44+
private static final String STOCK_PRICE_CACHE = "stockPrice";
4145

4246
/**
4347
* 국내, 해외 주식 정보 조회
@@ -1011,14 +1015,56 @@ private Mono<List<PriceInfo>> parseFStockChartPriceOverseas(String response) {
10111015
}
10121016

10131017
public Mono<StockInfoResponse> getRealTimeStockPrice(Stock stock) {
1018+
StockInfoResponse cached = getCachedRealTimeStockPrice(stock);
1019+
if (cached != null) {
1020+
return Mono.just(cached);
1021+
}
1022+
10141023
return getSecurityStockInfoKorea(
10151024
stock.getId(),
10161025
stock.getSymbolName(),
10171026
stock.getSecurityName(),
10181027
stock.getSymbol(),
10191028
stock.getExchangeNum(),
10201029
getCountryFromExchangeNum(stock.getExchangeNum())
1021-
);
1030+
).doOnNext(response -> putStockPriceCache(stock, response));
1031+
}
1032+
1033+
public StockInfoResponse getCachedRealTimeStockPrice(Stock stock) {
1034+
Cache cache = cacheManager.getCache(STOCK_PRICE_CACHE);
1035+
if (cache == null || stock == null || stock.getSymbol() == null || stock.getExchangeNum() == null) {
1036+
return null;
1037+
}
1038+
1039+
String cacheKey = buildStockPriceCacheKey(stock);
1040+
if (cacheKey == null) {
1041+
return null;
1042+
}
1043+
1044+
return cache.get(cacheKey, StockInfoResponse.class);
1045+
}
1046+
1047+
private void putStockPriceCache(Stock stock, StockInfoResponse response) {
1048+
if (response == null || response.getPrice() == null || response.getPrice() <= 0) {
1049+
return;
1050+
}
1051+
1052+
Cache cache = cacheManager.getCache(STOCK_PRICE_CACHE);
1053+
if (cache == null) {
1054+
return;
1055+
}
1056+
1057+
String cacheKey = buildStockPriceCacheKey(stock);
1058+
if (cacheKey != null) {
1059+
cache.put(cacheKey, response);
1060+
}
1061+
}
1062+
1063+
private String buildStockPriceCacheKey(Stock stock) {
1064+
if (stock == null || stock.getSymbol() == null || stock.getExchangeNum() == null) {
1065+
return null;
1066+
}
1067+
return stock.getSymbol() + "_" + stock.getExchangeNum().name();
10221068
}
10231069

10241070
/**

0 commit comments

Comments
 (0)