2013-03-12 2 views
7

ich besser bin versucht zu verstehen, wie die folgende Frühjahr Mvc 3.2 Anwendung funktioniert: https://github.com/rstoyanchev/spring-mvc-chatdie Spring MVC ist DeferredResult Klasse im Rahmen der Feder-mvc-chat Github Anwendung Legendes

Meine Frage ist, über die deferredResult Spring MVC class. Ich habe festgestellt, dass zu einer bestimmten Zeit so viele Einträge in der Karte chatRequests vorhanden sind, wie Benutzer mit der Chat-Anwendung verbunden sind.

Sagen Sie, es gibt 3 Benutzer verbunden mit der Chat-Anwendung. Sie werden sehen, wenn Benutzer # 3 eine Nachricht (siehe postMessage-Methode unten), dann die For-Schleife (in PostMessage-Methode) iteriert dreimal. Ich kann nicht herausfinden, warum das so ist.

Ich füge Beispielcode unten ein.

-Code für Controller:

@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-Code:

$(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()); 

}); 

und HTML-Seite:

<!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> 

Antwort

6

diskutierte ich dieses Thema ausführlich mit dem Autor von DeferredResult Klasse Frühling und hier ist der relevante Teil unseres Gesprächs:

Rossen Stoyanchev zu zitieren:

Grob gesprochen. Ein DeferredResult ist einer offenen Anfrage zugeordnet. Wenn die Anforderung abgeschlossen ist, wird die DeferredResult aus der Karte entfernt, und dann gibt der Client eine neue lange Abfrageanforderung, die

+1

Nun, ich denke, wenn Sie das mit dem Autor von "DeferredResult" besprochen haben, dann sollten Sie die gesamte Kommunikation irgendwo platzieren. Damit jeder mehr darüber erfährt. – rd22

+1

Ich denke [diese Reihe von Folien] (http://rstyanyanchev.github.io/spring-mvc-32-update/#8) von Rossen Stoyanchev wirft auch etwas Licht auf das Thema. –

11

Um zu verstehen, was DeferredResult Sie verstehen müssen, Servlets tut 3.0 Asynchrones Konzept

Mit Servlet 3.0 können Sie AsyncContext von Anfrage nehmen, speichern Sie es in Art von Sammlung.

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

als das Ergebnis Ihrer Application Container Thread wird freigegeben.

eine Operation auf separaten Thread Stellen und schreiben Sie an die Servlet Antwort führen zurück:

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

Von diesem Zeitpunkt Ihrer DeferredResult Werke absolut gleich.

Kleines Beispiel:

Betrachten wir nun, dass jede 5 Sekunden Sie ein Zitat von Dritten Service sind immer. Und Sie haben Kunden, die lange Ihren Server alle abfragen, um was aktualisiert zu bekommen.

Sie haben Ihre Controller-Methode:

/** 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; 
    } 

jetzt lassen Sie uns das Verfahren außerhalb des Controllers sehen, das Angebot bekommt und gibt Antwort an den Client.

function getQuoteAndUpdateClients(){ 

     String quote = getUpdatedQuoteFromThirdPartyService(); 

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

Hallo Danny. Danke für deine Antwort. Meine Frage bezog sich jedoch eher auf einen bestimmten Punkt im bereitgestellten Code, also siehe diesen Teil meines Beitrags: _Sagen Sie, dass 3 Benutzer mit der Chat-Anwendung verbunden sind. Wenn Benutzer 3 eine Nachricht veröffentlicht (siehe postMessage-Methode unten), wird die for-Schleife (in der postMessage-Methode) dreimal durchlaufen. Ich kann nicht herausfinden, warum das so ist. – balteo

1

Wenn ein Client eine neue DeferredResult Instanz fügt verbindet ein DeferredResult für das gespeichert wird Client in this.chatRequests. Wenn ein Client eine Nachricht sendet, durchläuft er alle DeferredResults (gelesene Clients), um ein Ergebnis festzulegen. Es ist nur logisch, dass dies dreimal passiert, wenn 3 Clients verbunden sind.

Verwandte Themen