2017-12-24 4 views
0

내 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); 
     } 
    } 

} 

답변

0

IQuoteService 및 구현 TickerService의 변경된 구현 원자 참고로 concurrenHashMap을 사용 : 그것은 이전처럼

@Autowired 
private ApplicationConstants Constants; 
private ConcurrentHashMap<AtomicReference<String>,AtomicReference<Quote>> 
quotes = new ConcurrentHashMap<AtomicReference<String>,AtomicReference<Quote>>(); 
private ConcurrentHashMap<AtomicReference<String>,AtomicReference<LocalDateTime>> quoteExpirationQueue = new ConcurrentHashMap<AtomicReference<String>,AtomicReference<LocalDateTime>>(); 
private final RestTemplate restTemplate; 

코드가 정확하게 작동과 새로운 구현이되도록 "해야한다"고 존재와 값에 대한 갱신은 완전히 쓰여지 기 전에 부분적으로 읽히지 않으며, 얻어진 값은 일관성이 있어야합니다. 필자는 사운드 예제를 찾지 못했고이 주제에 대한 답변을 얻지 못했습니다.이를 테스트하고 발견 한 문제를 게시 할 것입니다.