내 Rest API가 정상적으로 작동합니다. 그러나 스크립트를 통해 테스트했지만 아직 보지 못했지만 동시성 문제가 걱정됩니다. 필자의 연구에서, Atomic Values와 concurrentHasMap을 함께 사용하여 더티 읽기가 발생하지 않도록하는 것과 관련된 일부 자료를 발견했습니다. 내 질문은 두 가지입니다. 첫째, 내 구현을 고려할 때 걱정해야합니까? 둘째로, 내가해야만한다면, 실제로해야한다면, 원자 가치를 구현하는 가장 현명한 방법은 무엇일까요? RestTemplate에 대한 래퍼 클래스를 삭제하고 속도에 대한 촉매로 각도 4 컴포넌트에 문자열을 단순히 전달하는 것을 고려해 보았습니다. 그러나 다른 곳에서는 값 객체를 사용할 수 있으므로 주저합니다. 아래의 구현을 참조하십시오.concurrentHasMap 및 Atomic Values
@Service
@EnableScheduling
public class TickerService implements IQuoteService {
@Autowired
private ApplicationConstants Constants;
private ConcurrentHashMap<String,Quote> quotes = new ConcurrentHashMap<String, Quote>();
private ConcurrentHashMap<String,LocalDateTime> quoteExpirationQueue = new ConcurrentHashMap<String, LocalDateTime>();
private final RestTemplate restTemplate;
public TickerService(RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.build();
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public Quote getQuote(String symbol) {
if (this.quotes.containsKey(symbol)){
Quote q = (Quote)this.quotes.get(symbol);
//Update Expiration
LocalDateTime ldt = LocalDateTime.now();
this.quoteExpirationQueue.put(symbol, ldt.plus(Constants.getQuoteExpirationMins(),ChronoUnit.MINUTES));
return q;
} else {
QuoteResponseWrapper qRes = this.restTemplate.getForObject(Constants.getRestURL(symbol), QuoteResponseWrapper.class, symbol);
ArrayList<Quote> res = new ArrayList<Quote>();
res = qRes.getQuoteResponse().getResult();
//Add to Cache
quotes.put(symbol, res.get(0));
//Set Expiration
LocalDateTime ldt = LocalDateTime.now();
this.quoteExpirationQueue.put(symbol, ldt.plus(Constants.getQuoteExpirationMins(),ChronoUnit.MINUTES));
return res.get(0);
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
public ConcurrentHashMap<String,Quote> getQuotes(){
return this.quotes;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@Scheduled(fixedDelayString = "${application.quoteRefreshFrequency}")
public void refreshQuotes(){
if (quoteExpirationQueue.isEmpty()) {
return;
}
LocalDateTime ldt = LocalDateTime.now();
//Purge Expired Quotes
String expiredQuotes = quoteExpirationQueue.entrySet().stream().filter(x -> x.getValue().isBefore(ldt)).map(p -> p.getKey()).collect(Collectors.joining(","));
if (!expiredQuotes.equals("")) {
this.purgeQuotes(expiredQuotes.split(","));
}
String allQuotes = quoteExpirationQueue.entrySet().stream().filter(x -> x.getValue().isAfter(ldt)).map(p -> p.getKey()).collect(Collectors.joining(","));
List<String> qList = Arrays.asList(allQuotes.split(","));
Stack<String> stack = new Stack<String>();
stack.addAll(qList);
// Break Requests Into Manageable Chunks using property file settings
while (stack.size() > Constants.getMaxQuoteRequest()) {
String qSegment = "";
int i = 0;
while (i < Constants.getMaxQuoteRequest() && !stack.isEmpty()) {
qSegment = qSegment.concat(stack.pop() + ",");
i++;
}
logger.debug(qSegment.substring(0, qSegment.lastIndexOf(",")));
this.updateQuotes(qSegment);
}
// Handle Remaining Request Delta
if (stack.size() < Constants.getMaxQuoteRequest() && !stack.isEmpty()) {
String rSegment = "";
while (!stack.isEmpty()){
rSegment = rSegment.concat(stack.pop() + ",");
}
logger.debug(rSegment);
this.updateQuotes(rSegment.substring(0, rSegment.lastIndexOf(",")));
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private void updateQuotes(String symbols) {
if (symbols.equals("")) {
return;
}
System.out.println("refreshing -> " + symbols);
QuoteResponseWrapper qRes = this.restTemplate.getForObject(Constants.getRestURL(symbols), QuoteResponseWrapper.class, symbols);
for (Quote q : qRes.getQuoteResponse().getResult()) {
this.quotes.put(q.getSymbol(), q);
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
private void purgeQuotes(String[] symbols) {
for (String q : symbols) {
System.out.println("purging -> " + q);
this.quotes.remove(q);
this.quoteExpirationQueue.remove(q);
}
}
}