日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

DeferredResult – Spring MVC中的异步处理

發布時間:2023/12/3 javascript 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 DeferredResult – Spring MVC中的异步处理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

DeferredResult是一個可能尚未完成的計算的容器,它將在將來提供。 Spring MVC使用它來表示異步計算,并利用Servlet 3.0 AsyncContext異步請求處理。 簡要介紹一下它是如何工作的:

@RequestMapping("/") @ResponseBody public DeferredResult<String> square() throws JMSException {final DeferredResult<String> deferredResult = new DeferredResult<>();runInOtherThread(deferredResult);return deferredResult; }private void runInOtherThread(DeferredResult<String> deferredResult) {//seconds later in other thread...deferredResult.setResult("HTTP response is: 42"); }

通常,一旦離開控制器處理程序方法,請求處理即告完成。 但不能使用DeferredResult 。 Spring MVC(使用Servlet 3.0功能)將繼續響應,并保持空閑HTTP連接。 HTTP工作線程不再使用,但HTTP連接仍處于打開狀態。 稍后,其他線程將通過為其分配一些值來解析DeferredResult 。 Spring MVC將立即拾取此事件并將響應(在此示例中為“ HTTP響應:42” )發送到瀏覽器,從而完成請求處理。

您可能會在Future<V>和DeferredResult之間看到一些概念上的相似性–它們都代表計算,并且在將來的某個時間可用。 您可能想知道,為什么Spring MVC不允許我們簡單地返回Future<V>而是引入了新的專有抽象? 原因很簡單,再次顯示出Future<V>缺陷。 異步處理的全部要點是避免阻塞線程。 標準的java.util.concurrent.Future不允許在計算完成后注冊回調-因此,您要么需要分配一個線程來阻塞直到將來完成,要么使用一個線程來定期輪詢多個未來。 但是,后一種選擇會消耗更多的CPU并引入延遲。 但是來自番石榴的 出色ListenableFuture<V>似乎很合適? 的確如此,但是Spring沒有依賴于Guava,幸好將這兩個API橋接起來非常簡單。

但是首先請看一下實現自定義java.util.concurrent.Future<V>上一部分。 誠然,這并不像人們期望的那么簡單。 清理,處理中斷,鎖定和同步,維護狀態。 當我們需要的一切都像接收一條消息并從get()返回它一樣簡單時,就會有很多樣板。 讓我們嘗試改造以前的JmsReplyFuture實現,以實現更強大的ListenableFuture ,以便稍后在Spring MVC中使用它。

ListenableFuture只是擴展了標準 Future從而增加了注冊回調(偵聽器)的可能性。 因此,一個急切的開發人員只需坐下來,然后將Runnable偵聽器列表添加到現有實現中:

public class JmsReplyFuture<T extends Serializable> implements ListenableFuture<T>, MessageListener {private final List<Runnable> listeners = new ArrayList<Runnable>();@Overridepublic void addListener(Runnable listener, Executor executor) {listeners.add(listener);}//...

但這被大大簡化了。 當然,當將來完成或發生異常時,我們必須遍歷所有偵聽器。 如果添加偵聽器時未來已經解決,則必須立即調用該偵聽器。 此外,我們忽略了executor -根據API,每個偵聽器都可以使用提供給addListener()的不同線程池,因此我們必須存儲對: Runnable + Executor 。 最后但并非最不重要的一點addListener()不是線程安全的。 渴望的開發人員將在一兩個小時內解決所有問題。 再花兩個小時來修復同時引入的錯誤。 幾小時后的另一個小時,生產中又彈出了另一個“不可能的”錯誤。 我不急。 事實上,即使是上面最簡單的實現,我也懶得寫。 但是我很拼命,要在ListenableFuture上Ctrl + H (在IntelliJ IDEA中的子類型視圖)并瀏覽可用的骨骼實現樹。 AbstractFuture<V> –賓果游戲!

public class JmsReplyListenableFuture<T extends Serializable> extends AbstractFuture<T> implements MessageListener {private final Connection connection;private final Session session;private final MessageConsumer replyConsumer;public JmsReplyListenableFuture(Connection connection, Session session, Queue replyQueue) throws JMSException {this.connection = connection;this.session = session;this.replyConsumer = session.createConsumer(replyQueue);this.replyConsumer.setMessageListener(this);}@Overridepublic void onMessage(Message message) {try {final ObjectMessage objectMessage = (ObjectMessage) message;final Serializable object = objectMessage.getObject();set((T) object);cleanUp();} catch (Exception e) {setException(e);}}@Overrideprotected void interruptTask() {cleanUp();}private void cleanUp() {try {replyConsumer.close();session.close();connection.close();} catch (Exception e) {Throwables.propagate(e);}} }

就這樣,一切都可以編譯并運行。 與初始實現相比,代碼減少了近2 ListenableFuture并且我們獲得了更強大的ListenableFuture 。 大部分代碼已設置并清理。 AbstractFuture已經為我們實現了addListener() ,鎖定和狀態處理。 我們要做的就是在解決未來時調用set()方法(在我們的情況下,JMS答復到達)。 此外,我們最終會適當地支持異常。 以前我們只是簡單地忽略/重新拋出它們,而現在它們在訪問時已正確包裝并從get()拋出。 即使我們對ListenableFuture功能不感興趣, AbstractFuture仍然對我們有很大幫助。 我們免費獲得ListenableFuture 。

好的程序員喜歡編寫代碼。 更好的人喜歡刪除它 。 更少維護,更少測試,更少破壞。 有時我會驚訝于番石榴能提供多大的幫助。 上一次我使用大量的迭代器代碼。 數據是動態生成的,迭代器可以輕松生成數百萬個項目,因此我別無選擇。 有限的迭代器API和相當復雜的業務邏輯共同構成了無數管道代碼。 然后我找到了Iterators實用程序類 ,它拯救了我的生命。 我建議您打開Guava的JavaDoc并逐一檢查所有軟件包。 待會兒我會謝謝你的。

一旦有了自定義的ListenableFuture (顯然您可以使用任何實現),我們就可以嘗試將其與Spring MVC集成。 這是我們要實現的目標:

  • HTTP請求進來
  • 我們向JMS隊列發送請求
  • HTTP工作線程不再使用,它??可以處理其他請求
  • JMS偵聽器異步等待臨時隊列中的答復
  • 回復到達后,我們立即將其作為HTTP響應推送并完成連接。
  • 使用阻止Future第一個天真的實現:

    @Controller public class JmsController {private final ConnectionFactory connectionFactory;public JmsController(ConnectionFactory connectionFactory) {this.connectionFactory = connectionFactory;}@RequestMapping("/square/{value}")@ResponseBodypublic String square(@PathVariable double value) throws JMSException, ExecutionException, InterruptedException {final ListenableFuture<Double> responseFuture = request(value);return responseFuture.get().toString();}//JMS API boilerplateprivate <T extends Serializable> ListenableFuture<T> request(Serializable request) throws JMSException {Connection connection = this.connectionFactory.createConnection();connection.start();final Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);final Queue tempReplyQueue = session.createTemporaryQueue();final ObjectMessage requestMsg = session.createObjectMessage(request);requestMsg.setJMSReplyTo(tempReplyQueue);sendRequest(session.createQueue("square"), session, requestMsg);return new JmsReplyListenableFuture<T>(connection, session, tempReplyQueue);}private void sendRequest(Queue queue, Session session, ObjectMessage requestMsg) throws JMSException {final MessageProducer producer = session.createProducer(queue);producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);producer.send(requestMsg);producer.close();}}

    這種實現不是很幸運。 事實上,我們根本不需要Future ,因為我們幾乎沒有阻塞get() ,而是同步等待響應。 讓我們嘗試DeferredResult :

    @RequestMapping("/square/{value}") @ResponseBody public DeferredResult<String> square(@PathVariable double value) throws JMSException {final DeferredResult<String> deferredResult = new DeferredResult<>();final ListenableFuture<Double> responseFuture = request(value);Futures.addCallback(responseFuture, new FutureCallback<Double>() {@Overridepublic void onSuccess(Double result) {deferredResult.setResult(result.toString());}@Overridepublic void onFailure(Throwable t) {deferredResult.setErrorResult(t);}});return deferredResult; }

    復雜得多,但可擴展性也更大。 該方法幾乎不需要時間來執行,并且HTTP工作線程在準備處理另一個請求之后不久。 要做的最大觀察是onSuccess()和onFailure()由另一個線程執行,幾秒鐘甚至幾分鐘之后。 但是,HTTP工作線程池并未耗盡,并且應用程序仍保持響應狀態。

    這是一個教科書的例子,但是我們可以做得更好嗎? 首先嘗試將通用適配器從ListenableFuture寫入DeferredResult 。 這兩個抽象代表完全相同的事物,但是具有不同的API。 這很簡單:

    public class ListenableFutureAdapter<T> extends DeferredResult<String> {public ListenableFutureAdapter(final ListenableFuture<T> target) {Futures.addCallback(target, new FutureCallback<T>() {@Overridepublic void onSuccess(T result) {setResult(result.toString());}@Overridepublic void onFailure(Throwable t) {setErrorResult(t);}});} }

    我們只需擴展DeferredResult并使用ListenableFuture回調通知它。 用法很簡單:

    @RequestMapping("/square/{value}") @ResponseBody public DeferredResult<String> square(@PathVariable double value) throws JMSException {final ListenableFuture<Double> responseFuture = request(value);return new ListenableFutureAdapter<>(responseFuture); }

    但是我們可以做得更好! 如果ListenableFuture和DeferredResult非常相似,為什么ListenableFuture從控制器處理程序方法中返回ListenableFuture ?

    @RequestMapping("/square/{value}") @ResponseBody public ListenableFuture<Double> square2(@PathVariable double value) throws JMSException {final ListenableFuture<Double> responseFuture = request(value);return responseFuture; }

    好吧,這是行不通的,因為Spring無法理解ListenableFuture并且只會ListenableFuture 。 幸運的是,Spring MVC非常靈活,它使我們能夠輕松注冊新的所謂的 HandlerMethodReturnValueHandler 。 有12個這樣的內置處理程序,每當我們從控制器返回某個對象時,Spring MVC就會按預定義的順序檢查它們,然后選擇第一個可以處理給定類型的對象。 這樣的處理程序之一就是DeferredResultHandler (名稱說明了一切),我們將其用作參考:

    public class ListenableFutureReturnValueHandler implements HandlerMethodReturnValueHandler {public boolean supportsReturnType(MethodParameter returnType) {Class<?> paramType = returnType.getParameterType();return ListenableFuture.class.isAssignableFrom(paramType);}public void handleReturnValue(Object returnValue,MethodParameter returnType, ModelAndViewContainer mavContainer,NativeWebRequest webRequest) throws Exception {if (returnValue == null) {mavContainer.setRequestHandled(true);return;}final DeferredResult<Object> deferredResult = new DeferredResult<>();Futures.addCallback((ListenableFuture<?>) returnValue, new FutureCallback<Object>() {@Overridepublic void onSuccess(Object result) {deferredResult.setResult(result.toString());}@Overridepublic void onFailure(Throwable t) {deferredResult.setErrorResult(t);}});WebAsyncUtils.getAsyncManager(webRequest).startDeferredResultProcessing(deferredResult, mavContainer);}}

    用盡業力,安裝此處理程序并不像我希望的那樣簡單。 從技術上講,有WebMvcConfigurerAdapter.addReturnValueHandlers() ,如果對Spring MVC使用Java配置,我們可以輕松地覆蓋它。 但是此方法在處理程序鏈的末尾添加了自定義返回值處理程序,并且出于超出本文討論范圍的原因,我們需要在其開頭添加它(優先級更高)。 幸運的是,通過一點點黑客攻擊,我們也可以實現:

    @Configuration @EnableWebMvc public class SpringConfig extends WebMvcConfigurerAdapter {@Resourceprivate RequestMappingHandlerAdapter requestMappingHandlerAdapter;@PostConstructpublic void init() {final List<HandlerMethodReturnValueHandler> originalHandlers = new ArrayList<>(requestMappingHandlerAdapter.getReturnValueHandlers().getHandlers());originalHandlers.add(0, listenableFutureReturnValueHandler());requestMappingHandlerAdapter.setReturnValueHandlers(originalHandlers);}@Beanpublic HandlerMethodReturnValueHandler listenableFutureReturnValueHandler() {return new ListenableFutureReturnValueHandler();}}

    摘要

    在本文中,我們熟悉了稱為DeferredResult的將來/承諾抽象的另一種形式。 它用于推遲對HTTP請求的處理,直到完成一些異步任務。 因此, DeferredResult對于基于事件驅動系統,消息代理等之上的Web GUI而言非常有用。盡管它不如原始Servlet 3.0 API強大。 例如,我們無法在長時間運行的HTTP連接中流式傳輸多個事件(例如,新的推文)時– Spring MVC的設計更多地是針對請求-響應模式。

    我們還對Spring MVC進行了調整,以允許直接從控制器方法中從Guava中返回ListenableFuture 。 它使我們的代碼更加簡潔和富于表現力。

    參考: DeferredResult –在我們的JCG合作伙伴 Tomasz Nurkiewicz的NoBlogDefFound博客中,Spring MVC中的異步處理 。

    翻譯自: https://www.javacodegeeks.com/2013/03/deferredresult-asynchronous-processing-in-spring-mvc.html

    總結

    以上是生活随笔為你收集整理的DeferredResult – Spring MVC中的异步处理的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。