2013-03-12 7 views
7

मैं बेहतर कैसे निम्नलिखित वसंत MVC 3.2 आवेदन काम करता है समझने के लिए कोशिश कर रहा हूँ के संदर्भ में वसंत MVC के DeferredResult वर्ग को समझना: https://github.com/rstoyanchev/spring-mvc-chatवसंत-MVC-चैट GitHub आवेदन

मेरा प्रश्न deferredResult Spring MVC class बारे में है। मैंने देखा कि किसी दिए गए समय पर chatRequests मानचित्र में कई प्रविष्टियां हैं क्योंकि चैट एप्लिकेशन से जुड़े उपयोगकर्ता हैं।

कहें कि 3 उपयोगकर्ता चैट अनुप्रयोग में से जुड़े हुए हैं। आप देखेंगे कि जब उपयोगकर्ता # 3 कोई संदेश पोस्ट करता है (नीचे पोस्ट मैसेज विधि देखें), तो लूप के लिए (पोस्टमेसेज विधि में) तीन बार पुनरावृत्त करता है। मैं नहीं समझ सकता कि वह क्यों है।

मैं नीचे नमूना कोड शामिल कर रहा हूँ। नियंत्रक के लिए

कोड:

@Controller 
@RequestMapping("/mvc/chat") 
public class ChatController { 

    private final ChatRepository chatRepository; 
    private final Map<DeferredResult<List<String>>, Integer> chatRequests = new ConcurrentHashMap<DeferredResult<List<String>>, Integer>(); 

    @Autowired 
    public ChatController(ChatRepository chatRepository) { 
     this.chatRepository = chatRepository; 
    } 

    @RequestMapping(method = RequestMethod.GET) 
    @ResponseBody 
    public DeferredResult<List<String>> getMessages(@RequestParam int messageIndex) { 

     final DeferredResult<List<String>> deferredResult = new DeferredResult<List<String>>(null, Collections.emptyList()); 
     this.chatRequests.put(deferredResult, messageIndex); 

     deferredResult.onCompletion(new Runnable() { 
      @Override 
      public void run() { 
       chatRequests.remove(deferredResult); 
      } 
     }); 

     List<String> messages = this.chatRepository.getMessages(messageIndex); 
     if (!messages.isEmpty()) { 
      deferredResult.setResult(messages); 
     } 

     return deferredResult; 
    } 

    @RequestMapping(method = RequestMethod.POST) 
    @ResponseBody 
    public void postMessage(@RequestParam String message) { 

     this.chatRepository.addMessage(message); 

     // Update all chat requests as part of the POST request 
     // See Redis branch for a more sophisticated, non-blocking approach 

     for (Entry<DeferredResult<List<String>>, Integer> entry : this.chatRequests.entrySet()) { 
      List<String> messages = this.chatRepository.getMessages(entry.getValue()); 
      entry.getKey().setResult(messages); 
     } 
    } 
} 

Javascript कोड:

$(document).ready(function() { 

    function ChatViewModel() { 

     var that = this; 

     that.userName = ko.observable(''); 
     that.chatContent = ko.observable(''); 
     that.message = ko.observable(''); 
     that.messageIndex = ko.observable(0); 
     that.activePollingXhr = ko.observable(null); 


     var keepPolling = false; 

     that.joinChat = function() { 
      if (that.userName().trim() != '') { 
       keepPolling = true; 
       pollForMessages(); 
      } 
     } 

     function pollForMessages() { 
      if (!keepPolling) { 
       return; 
      } 
      var form = $("#joinChatForm"); 


      that.activePollingXhr($.ajax({url: form.attr("action"), type: "GET", data: form.serialize(), cache: false, 
       success: function(messages) { 
        console.log(messages); 
        for (var i = 0; i < messages.length; i++) { 
         that.chatContent(that.chatContent() + messages[i] + "\n"); 
         that.messageIndex(that.messageIndex() + 1); 
        } 
       }, 
       error: function(xhr) { 
        if (xhr.statusText != "abort" && xhr.status != 503) { 
         resetUI(); 
         console.error("Unable to retrieve chat messages. Chat ended."); 
        } 
       }, 
       complete: pollForMessages 
      })); 
      $('#message').focus(); 
     } 

     that.postMessage = function() { 
      if (that.message().trim() != '') { 
       var form = $("#postMessageForm"); 
       $.ajax({url: form.attr("action"), type: "POST", 
        data: "message=[" + that.userName() + "] " + $("#postMessageForm input[name=message]").val(), 
        error: function(xhr) { 
         console.error("Error posting chat message: status=" + xhr.status + ", statusText=" + xhr.statusText); 
        } 
       }); 
       that.message(''); 
      } 
     } 

     that.leaveChat = function() { 
      that.activePollingXhr(null); 
      resetUI(); 
      this.userName(''); 
     } 

     function resetUI() { 
      keepPolling = false; 
      that.activePollingXhr(null); 
      that.message(''); 
      that.messageIndex(0); 
      that.chatContent(''); 
     } 

    } 

    //Activate knockout.js 
    ko.applyBindings(new ChatViewModel()); 

}); 

और html पृष्ठ:

<!DOCTYPE html> 
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> 
<head> 
    <title>Chat</title> 
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 
</head> 
<body> 
    <h1>Chat</h1> 

    <form id="joinChatForm" th:action="@{/mvc/chat}" data-bind="visible: activePollingXhr() == null"> 
     <p> 
      <label for="user">User: </label> 
      <input id="user" name="user" type="text" data-bind="value: userName"/> 
      <input name="messageIndex" type="hidden" data-bind="value: messageIndex"/> 
      <button id="start" type="submit" data-bind="click: joinChat">Join Chat</button> 
     </p> 
    </form> 

    <form id="leaveChatForm" th:action="@{/mvc/chat}" data-bind="visible: activePollingXhr() != null"> 
     <p> 
      You're chatting as <strong data-bind="text: userName"></strong> 
      <button id="leave" type="submit" data-bind="click: leaveChat">Leave Chat</button> 
     </p> 
    </form> 

    <div data-bind="visible: activePollingXhr() != null"> 
     <textarea rows="15" cols="60" readonly="readonly" data-bind="text: chatContent"></textarea> 
    </div> 

    <form id="postMessageForm" th:action="@{/mvc/chat}" data-bind="visible: activePollingXhr() != null"> 
     <p> 
      <input id="message" name="message" type="text" data-bind="value: message" /> 
      <button id="post" type="submit" data-bind="click: postMessage">Post</button> 
     </p> 
    </form> 
</body> 
<script type="text/javascript" src="../../../resources/js/jquery-1.7.2.min.js" th:src="@{/resources/js/jquery-1.7.2.min.js}"></script> 
<script type="text/javascript" src="../../../resources/js/knockout-2.0.0.js" th:src="@{/resources/js/knockout-2.0.0.js}"></script> 
<script type="text/javascript" src="../../../resources/js/chat.js" th:src="@{/resources/js/chat.js}"></script> 

</html> 

उत्तर

6

मैं वसंत के DeferredResult वर्ग के लेखक के साथ विस्तार से इस विषय पर चर्चा की और यहां हमारी बातचीत के प्रासंगिक हिस्सा है:

रोसन Stoyanchev के शब्दों में:

मोटे तौर पर कहा। एक DeferredResult एक खुले अनुरोध से जुड़ा हुआ है। जब अनुरोध पूरा करता है, DeferredResult नक्शे से हटा दिया जाता है, और फिर, ग्राहक एक नया लंबे पोलिंग अनुरोध है, जो एक ग्राहक जोड़ता है जब एक DeferredResult कि के लिए भंडारित किया जाता है एक नया DeferredResult उदाहरण

+1

ठीक है, मुझे लगता है कि अगर आपने इसे 'DeferredResult' के लेखक के साथ चर्चा की है तो आपको पूरे संचार को कुछ जगह रखना चाहिए। ताकि हर किसी को इसके बारे में और जान सकें। – rd22

+1

मुझे लगता है कि [स्लाइड का यह सेट] (http://rstoyanchev.github.io/spring-mvc-32-update/#8) रॉसेन स्टोयाचेव से विषय पर कुछ प्रकाश भी फेंकता है। –

11

यह समझने के लिए क्या DeferredResult कर रहा है आप सर्वलेट 3.0 समझने की जरूरत में Async अवधारणा।

सर्वलेट 3.0 का उपयोग करके आप अनुरोध से AsyncContext ले सकते हैं, इसे संग्रह के प्रकार में संग्रहीत कर सकते हैं।

AsyncContext aCtx = request.startAsync(request, response); 

परिणाम के रूप में अपने आवेदन कंटेनर थ्रेड जारी किया जाएगा।

अलग थ्रेड पर कुछ आपरेशन करते हैं और परिणाम वापस सर्वलेट जवाबी कार्रवाई के लिए लिखें:

aCtx.getResponse().getWriter().print(result); 

अपने DeferredResult काम करता है बिल्कुल उसी के उस बिंदु से।

छोटे उदाहरण:

अब विचार है कि हर 5 सेकंड आप तृतीय पक्ष सेवा की एक बोली हो रही है। और आपके पास ऐसे ग्राहक हैं जो अपडेट करने के लिए आपके सर्वर को लंबे समय तक मतदान कर रहे हैं।

/** put deferred result to some HashSet. This is the same logic as you 
     store async context in servlet 3.0, those are clients who are waiting for    
     response 
    **/ 
    @RequestMapping(value="/getQuote.do", method=RequestMethod.GET) 
    @ResponseBody 
    public DeferredResult<String> getQuote(){ 
     final DeferredResult<String> deferredResult = new DeferredResult<String>(); 

     someMap.put(deferredResult); 
     return deferredResult; 
    } 

अब के नियंत्रक जो बोली हो जाता है और ग्राहक के लिए प्रतिक्रिया लौटाता के बाहर विधि देखते हैं:

आप अपने नियंत्रक विधि है।

function getQuoteAndUpdateClients(){ 

     String quote = getUpdatedQuoteFromThirdPartyService(); 

     for (DeferredResult<String> deferredResult: someMap){ 
       deferredResult.setResult(quote); 
     } 
} 
+0

हाय डैनी। आपके जवाब के लिए धन्यवाद। हालांकि, मेरा प्रश्न प्रदान किए गए कोड में एक विशिष्ट बिंदु के बारे में था, यानी मेरी पोस्ट के इस हिस्से को देखें: _Say चैट एप्लिकेशन से जुड़े 3 उपयोगकर्ता हैं। आप देखेंगे कि जब उपयोगकर्ता # 3 एक संदेश पोस्ट करता है (नीचे पोस्ट मैसेज विधि देखें), तो लूप के लिए (पोस्ट मैसेज विधि में) तीन बार पुनरावृत्त करता है। मैं यह नहीं समझ सकता कि यह क्यों है ._ – balteo

1

कहते जारी करता है इस .chatRequests में ग्राहक। जब कोई ग्राहक कोई संदेश पोस्ट करता है, तो यह परिणाम सेट करने के लिए सभी DeferredResults (क्लाइंट पढ़ें) के माध्यम से loops। यह केवल तार्किक है कि 3 क्लाइंट जुड़े होने पर यह 3 बार होता है।