Das Verständnis der Spring MVC ist DeferredResult Klasse im Rahmen des spring-mvc-chat github-Anwendung
Ich versuche zu verstehen, wie die folgende spring mvc 3.2-Anwendung funktioniert: https://github.com/rstoyanchev/spring-mvc-chat
Meine Frage ist über die deferredResult Spring MVC Klasse. Ich habe bemerkt, dass zu einem gegebenen Zeitpunkt gibt es viele Einträge in der chatRequests
Karte als Benutzer eine Verbindung zum chat-Anwendung.
Sagen, es gibt 3 Benutzer verbunden um die chat-Anwendung. Sie werden sehen, dass, wenn user #3 sendet eine Nachricht (siehe postMessage-Methode unten), dann die for-Schleife (in der postMessage-Methode) wiederholt dreimal. Ich kann nicht herausfinden, warum das so ist.
Ich bin auch Beispiel-code unten.
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>
Du musst angemeldet sein, um einen Kommentar abzugeben.
Um zu verstehen, was DeferredResult tun, müssen Sie verstehen, Servlet 3.0 Asynchronen Konzept.
Verwendung von Servlet 3.0 können Sie nehmen AsyncContext von der Anfrage, speichern Sie es in Art Sammlung.
als Ergebnis Ihrer Anwendung-Container-Thread veröffentlicht.
Machen, einen Vorgang auf einem separaten thread und Schreibe Ergebnis zurück an das Servlet Antwort:
Ab diesem Punkt Ihr
DeferredResult
funktioniert absolut die gleiche.Kleines Beispiel:
Nun bedenkt, dass jeder 5 Sekunde, Sie bekommen ein Angebot von Diensten Dritter.
Und Sie haben Kunden, die long-polling Ihre server alle in Ordnung zu gibts was.
Haben Sie Ihre Controller-Methode:
nun lasst uns sehen, die Methode außerhalb der Steuerung, die bekommt Zitat und gibt response an den client.
Ich besprach dieses Thema ausführlich mit dem Autor von Frühlings DeferredResult Klasse und hier ist der relevante Teil unseres Gesprächs:
Zitieren Rossen Stoyanchev:
DeferredResult
dann sollten Sie die gesamte Kommunikation an irgendeinen Ort. So bekommt jeder mehr darüber wissen.Wenn sich ein client verbindet, wird ein DeferredResult gespeichert, die Clients in diesem.chatRequests. Wenn ein client sendet eine Nachricht, es durchläuft alle DeferredResults (Lesen-clients), um eine Ergebnis. Es ist nur logisch, dass das passiert 3 mal, wenn es gibt 3 clients verbunden.