参考:
spring-framework-reference.pdf:17.3 Implementing Controllers的Asynchronous Request Processing
Adding Long Polling to an Existing Web Application:https://spring.io/blog/2012/05/14/spring-mvc-3-2-preview-adding-long-polling-to-an-existing-web-application
关于DeferredResult的应用场景,spring-framework-reference.pdf有一段话:A second option is for the controller to return an instance of DeferredResult.In this case the return value will also be produced from a separate thread. However, that thread is not known to Spring MVC.For example the result may be produced in response to some external event such as a JMS message,a scheduled task.
前面介绍spring amqp股票交易的第二点:由客户端先初始化,服务端处理的请求-回复"股票交易"的交互.
在前面的例子
1.先post请求到controller的trade方法,异步发送消息到amqp的服务端,就马上直接返回到页面前端.
2.因为是异步,没法马上得到股票响应信息的,所以写了个传统轮询每隔2s去再拿股票响应信息,直到拿到这个信息,轮询才停止.
这个过程,页面与服务端(controller)的请求来来回回好几次.
如果使用DeferredResult,是非常适合的,流程如下:
1.先post请求到controller的trade方法,异步发消息到amqp的服务端,这时response是打开的,还没有提交.
2.amqp的服务端接到客户端发过来的请求,处理(ServerHandler.handleMessage(TradeRequest))完再发送回客户端,客户端的监听器接收再处理(QuoteController.handleTrade(TradeResponse),我个人把这个处理提取到ClientHandler.handleMessage(TradeResponse)).
3.在这个方法,把结果通过DeferredResult.setResult()设置,这时服务端的response开始提交了,再返回到页面.
5.回到amqp客户端处理ClientHandler.handleMessage(TradeResponse):
spring-framework-reference.pdf:17.3 Implementing Controllers的Asynchronous Request Processing
Adding Long Polling to an Existing Web Application:https://spring.io/blog/2012/05/14/spring-mvc-3-2-preview-adding-long-polling-to-an-existing-web-application
关于DeferredResult的应用场景,spring-framework-reference.pdf有一段话:A second option is for the controller to return an instance of DeferredResult.In this case the return value will also be produced from a separate thread. However, that thread is not known to Spring MVC.For example the result may be produced in response to some external event such as a JMS message,a scheduled task.
前面介绍spring amqp股票交易的第二点:由客户端先初始化,服务端处理的请求-回复"股票交易"的交互.
在前面的例子
1.先post请求到controller的trade方法,异步发送消息到amqp的服务端,就马上直接返回到页面前端.
2.因为是异步,没法马上得到股票响应信息的,所以写了个传统轮询每隔2s去再拿股票响应信息,直到拿到这个信息,轮询才停止.
这个过程,页面与服务端(controller)的请求来来回回好几次.
如果使用DeferredResult,是非常适合的,流程如下:
1.先post请求到controller的trade方法,异步发消息到amqp的服务端,这时response是打开的,还没有提交.
2.amqp的服务端接到客户端发过来的请求,处理(ServerHandler.handleMessage(TradeRequest))完再发送回客户端,客户端的监听器接收再处理(QuoteController.handleTrade(TradeResponse),我个人把这个处理提取到ClientHandler.handleMessage(TradeResponse)).
3.在这个方法,把结果通过DeferredResult.setResult()设置,这时服务端的response开始提交了,再返回到页面.
这个过程,从页面到controller只发了一次请求.
下面是大概的代码修改过程.
1.从页面发起的post请求:
$('#tradeForm').submit(function () {
$('#messages').text('');
$.post($('#tradeForm').attr("action"), $('#tradeForm').serialize(),
function(response) {
if (response && response.requestId) {
$('#confirmations').append($.mustache(confirmation, {response : response}));
} else {
$('#messages').html("<p>Invalid response.</p>");
}
});
return false;
})
2.按前文修改spring mvc支持处理异步请求.即在AbstractDispatcherServletInitializer修改filter和servlet的asyncSupport为true(其中servlet的asyncSupport已默认为true).
3.Controller接收页面请求处理的方法.
@RequestMapping(value = "/trade", method = RequestMethod.POST)
@ResponseBody
public DeferredResult<TradeResponse> trade(@ModelAttribute TradeRequest tradeRequest) {
String ticker = tradeRequest.getTicker();
Long quantity = tradeRequest.getQuantity();
if (quantity == null || quantity <= 0 || !StringUtils.hasText(ticker)) {
return null;
}
// Fake rest of request while UI is basic
tradeRequest.setAccountName("ACCT-123");
tradeRequest.setBuyRequest(true);
tradeRequest.setOrderType("MARKET");
final String requestId=UUID.randomUUID().toString();
tradeRequest.setRequestId(requestId);
tradeRequest.setUserName("Joe Trader");
tradeRequest.setUserName("Joe");
this.stockServiceGateway.send(tradeRequest);
DeferredResult<TradeResponse> result = new DeferredResult<TradeResponse>(30000l,null);
final Map<String, DeferredResult> resultMap=clientHandler.getSuspendedTradeRequests();
resultMap.put(requestId, result);
result.onCompletion(new Runnable() {
@Override
public void run() {
resultMap.remove(requestId);
}
});
return result;
}
这里就是向amqp服务端发送消息,DeferredResult还没有调用setResult方法,response先不提交.注意DeferredResult这个构造函数,请求时间超过第一个参数设置的时间,则返回第二个参数作为结果.对于onCompletion回调,当异步请求完成时(包括超时和网络错误),这个方法将被调用,这个方法用于检测一个不可用的DeferredResult实例是非常有用的.
4.amqp服务端的处理修改,将ExecutionVenueServiceStub.executeTradeRequest()方法的response.setRequestId(request.getId());改为response.setRequestId(request.getRequestId());这个requestId是我全程跟踪这个服票交易消息是属于那一个请求的.5.回到amqp客户端处理ClientHandler.handleMessage(TradeResponse):
private Map<String, DeferredResult> suspendedTradeRequests = new ConcurrentHashMap<String, DeferredResult>();
public Map<String, DeferredResult> getSuspendedTradeRequests() {
return suspendedTradeRequests;
}
public void handleMessage(TradeResponse response) {
logger.info("Client received: " + response);
DeferredResult<TradeResponse> deferredResult=suspendedTradeRequests.get(response.getRequestId());
//以防重启spring mvc服务器,延迟的信息被侦察到延时处理,从map里get为null
if (deferredResult!=null){
deferredResult.setResult(response);
}
}
这里得到相应的DeferredResult,再调用setResult,就会把response提交,回到页面显示.