Java EE:异步构造和功能
介紹
Java EE具有許多API和構(gòu)造以支持異步執(zhí)行。 從可伸縮性和性能的角度來看,這是至關(guān)重要的。
讓我們假設(shè)2個模塊相互交互。 當(dāng)模塊A (發(fā)送方)以同步方式向模塊B (接收方)發(fā)送消息時,通信將在單個線程的上下文中進(jìn)行,即,從模塊A發(fā)起通信的線程將被阻塞,直到模塊B作出響應(yīng)為止。
這是一個通用的聲明,但可以擴(kuò)展到一個簡單的Java方法相互交融的背景下-在這種情況下,從了methodA到的methodB 同步調(diào)用將在同一個 線程執(zhí)行這將被阻塞,直到的methodB返回或拋出一個異常。
為什么在基于Java EE的應(yīng)用程序中需要異步行為?
我們可以進(jìn)一步將這一點(diǎn)推算到Java EE領(lǐng)域-服務(wù)器之間的通信,例如Web層和EJB層(Servlet和EJB)之間的通信,或者是典型的客戶端服務(wù)器交互-瀏覽器與RESTful端點(diǎn),Servlet等的交互-服務(wù)器響應(yīng)客戶端請求的線程始終阻塞,直到服務(wù)器組件響應(yīng)。
異步執(zhí)行在這里發(fā)揮作用-如果可以釋放/暫停處理客戶請求的服務(wù)器線程 ,并且在單獨(dú)的線程中執(zhí)行實際的業(yè)務(wù)邏輯(與原始線程不同),則可以極大地提高性能和可伸縮性。 ! 例如,如果分配給偵聽客戶端請求的HTTP偵聽器線程被立即釋放,則可以自由地處理來自其他客戶端的請求,并且可以在單獨(dú)的容器線程中執(zhí)行業(yè)務(wù)邏輯,然后該容器線程可以通過適當(dāng)?shù)姆椒?#xff08;例如, java.util.concurrent.Future對象或通過客戶端注冊的回調(diào)處理程序 。 從最終用戶的角度考慮- 響應(yīng)非常重要!
深入研究:Java EE中的異步構(gòu)造和API
讓我們看一下Java EE中的一些異步相關(guān)功能(API)。 這不是一個詳盡的列表,但是應(yīng)該是一個很好的起點(diǎn)。
不同的Java EE規(guī)范具有促進(jìn)異步功能的典型方式和API。 讓我們探索以下Java EE規(guī)范
- JAX-RS 2.0(Java EE 7)
- Websocket 1.0(Java EE 7)
- 并發(fā)實用工具1.0(Java EE 7)
- EJB 3.1(Java EE 6)
- Servlet 3.0(Java EE 6)
注意 :下面提供的代碼為摘要形式(出于明顯的原因)。 完整的樣本可以在這里訪問 。
JAX-RS 2.0
請求的異步處理是JAX-RS 2.0版 (Java EE 7中的新增功能 )的一項新功能 。 為了使用JAX-RS API執(zhí)行aysnc請求,需要在JAX-RS資源方法本身中注入對javax.ws.rs.container.AsyncResponse接口的引用。 此參數(shù)將請求執(zhí)行置于異步模式,然后該方法繼續(xù)執(zhí)行。 業(yè)務(wù)邏輯執(zhí)行完成后,需要從單獨(dú)的線程中調(diào)用AsynResponse對象上的resume方法。 可以利用Java EE并發(fā)實用程序功能(稍后討論),例如javax.enterprise.concurrent.ManagedExecutorService ,以將業(yè)務(wù)邏輯封裝為Runnable對象,并將其提交給容器的執(zhí)行者服務(wù),其余部分由該服務(wù)執(zhí)行。 無需自己產(chǎn)生不受管理的隔離線程。
@Path("/{id}")@GET@Produces("application/xml")public void asyncMethod(@Suspended AsyncResponse resp, @PathParam("id") String input) {System.out.println("Entered MyAsyncRESTResource/asyncMethod() executing in thread: "+ Thread.currentThread().getName());mes.execute(() -> {System.out.println("Entered Async zone executing in thread: "+ Thread.currentThread().getName());System.out.println("Simulating long running op via Thread sleep() started on "+ new Date().toString());try {Thread.sleep(5000);} catch (InterruptedException ex) {Logger.getLogger(MyAsyncRESTResource.class.getName()).log(Level.SEVERE, null, ex);}System.out.println("Completed Long running op on "+new Date().toString());System.out.println("Exiting Async zone executing in thread: "+ Thread.currentThread().getName());//creating a dummy instance of our model class (Student)Student stud = new Student(input, "Abhishek", "Apr-08-1987");resp.resume(Response.ok(stud).build());});System.out.println("Exit MyAsyncRESTResource/asyncMethod() and returned thread "+Thread.currentThread().getName()+" back to thread pool");}JAX-RS客戶端API也具有異步功能,但本文未對此進(jìn)行討論。 他們絕對值得一看!
Websocket 1.0
Websocket API是Java EE工具庫(Java EE 7中引入)的全新添加 。 它促進(jìn)了雙向 (服務(wù)器和客戶端發(fā)起的)通信的本質(zhì)上也是全雙工的(客戶端或服務(wù)器都可以在任何時候相互發(fā)送消息)。
為了使用Websocket API發(fā)送異步消息,需要使用javax.websocket.Session接口上可用的getAsyncRemote方法。 在內(nèi)部,這不過是javax.websocket.RemoteEnpoint – javax.websocket.RemoteEnpoint.Async的嵌套接口的實例。 對此調(diào)用常規(guī)的sendXXX方法將導(dǎo)致發(fā)送過程在單獨(dú)的線程中執(zhí)行。 當(dāng)您考慮交換大消息或處理需要向其發(fā)送消息的大量Websocket客戶端時,這特別有用。 枯萎的方法返回一個java.util.concurrent.Future對象,或者可以以javax.websocket.SendHandler接口實現(xiàn)的形式注冊一個回調(diào)。
public void sendMsg(@Observes Stock stock) {System.out.println("Message receieved by MessageObserver --> "+ stock);System.out.println("peers.size() --> "+ peers.size());peers.stream().forEach((aPeer) -> {//stock.setPrice();aPeer.getAsyncRemote().sendText(stock.toString(), (result) -> {System.out.println("Message Sent? " + result.isOK());System.out.println("Thread : " + Thread.currentThread().getName());});});}并發(fā)實用工具1.0
Java EE并發(fā)實用程序是Java EE 7的另一個重要補(bǔ)充 。 它提供了一種標(biāo)準(zhǔn)的生成線程的方式–好的方面是,這些線程是容器管理的 , 而不僅僅是容器沒有上下文信息的孤立/孤立線程 。
通常,并發(fā)實用工具1.0提供了一些用于在單獨(dú)線程中執(zhí)行異步任務(wù)的標(biāo)準(zhǔn)構(gòu)造。 它們?nèi)缦?#xff1a; javax.enterprise.concurrent.ManagedExecutorService 和 javax.enterprise.concurrent.ManagedScheduledExecutorService 。
為了在單獨(dú)的線程中啟動新任務(wù),可以使用ManagedExecutorService接口提交Runnable 。 除了實現(xiàn)Runnable接口之外,類還可以實現(xiàn)javax.enterprise.concurrent.ManagedTask接口并提供javax.enterprise.concurrent.ManagedTaskListener實現(xiàn),以便偵聽通過ManagedExecutorService提交的任務(wù)的生命周期更改。
@Overrideprotected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {System.out.println("Enter AConcurrencyUtilsExample/doGet executing in thread "+ Thread.currentThread().getName());System.out.println("initiating task . . . ");mes.execute(new AManagedTask());System.out.println("Exit AConcurrencyUtilsExample/doGet and returning thread "+ Thread.currentThread().getName() +" back to pool");}Servlet 3.0
Servlet 3.0( Java EE 6的一部分)中引入了異步HTTP,它基本上提供了在單獨(dú)的線程中執(zhí)行請求并掛起處理客戶端調(diào)用的原始線程的功能。
這里的關(guān)鍵角色是javax.servlet.AsyncContext接口。 為了啟動異步處理,調(diào)用java.servlet.ServletRequest接口的startAsync方法。 為了執(zhí)行核心邏輯,需要將java.lang.Runnable對象提交給AsyncContext接口的start方法。 可以選擇通過實現(xiàn)javax.servlet.AsyncListener來附加偵聽器,以便在Async任務(wù)執(zhí)行的特定時間接收回調(diào)通知。
@Overridepublic void doGet(HttpServletRequest req, HttpServletResponse resp) {PrintWriter writer = null;try {writer = resp.getWriter();} catch (IOException ex) {Logger.getLogger(MyAsyncServlet.class.getName()).log(Level.SEVERE, null, ex);}//System.out.println("entered doGet()");writer.println("ENTERING ... " + MyAsyncServlet.class.getSimpleName() + "/doGet()");writer.println("Executing in Thread: " + Thread.currentThread().getName());//step 1final AsyncContext asyncContext = req.startAsync();//step 2asyncContext.addListener(new CustomAsyncHandler(asyncContext));//step 3asyncContext.start(() -> {PrintWriter logger = null;try {logger = asyncContext.getResponse().getWriter();} catch (IOException ex) {Logger.getLogger(MyAsyncServlet.class.getName()).log(Level.SEVERE, null, ex);}logger.println("Long running Aync task execution started : " + new Date().toString());logger.println("Executing in Thread: " + Thread.currentThread().getName());try {Thread.sleep(5000);} catch (InterruptedException e) {Logger.getLogger(MyAsyncServlet.class.getName()).log(Level.SEVERE, null, e);}logger.println("Long task execution complete : " + new Date().toString());logger.println("Calling complete() on AsyncContext");//step 4asyncContext.complete();});writer.println("EXITING ... " + MyAsyncServlet.class.getSimpleName() + "/doGet() and returning initial thread back to the thread pool");}EJB 3.1
通常,(在EJB 3.1之前)EJB消息驅(qū)動Bean用于滿足與異步相關(guān)的要求。 MDB bean偵聽發(fā)送到j(luò)avax.jms.Destination ( 隊列或Topic )的消息,并執(zhí)行所需的業(yè)務(wù)邏輯–從發(fā)送電子郵件到啟動訂單處理任務(wù),這可能是任何事情。 要了解的重要一點(diǎn)是,首先將消息發(fā)送到Queue的客戶端不知道 MDB(已解耦 ),并且不必等待/保持阻塞,直到作業(yè)結(jié)束(電子郵件收據(jù)或訂單處理確認(rèn)) )。
EJB 3.1( Java EE 6的一部分)引入了javax.ejb.Asynchronous批注。 可以將其放在EJB會話bean(無狀態(tài),有狀態(tài)或單例) 類 (使所有方法異步)上,也可以放在方法級別本身上(以防需要精細(xì)控制)。 如果需要跟蹤異步方法的結(jié)果,則帶有@Asynchronous批注的方法可以返回void (失火并忘記)或java.util.concurrent.Future的實例–這可以通過調(diào)用Future.get()來完成–需要注意的是, get方法本身實際上是阻塞的。
@Asynchronouspublic Future<String> asyncEJB2(){System.out.println("Entered MyAsyncEJB/asyncEJB2()");System.out.println("MyAsyncEJB/asyncEJB2() Executing in thread: "+ Thread.currentThread().getName());System.out.println("Pretending as if MyAsyncEJB/asyncEJB2() is doing something !");try {Thread.sleep(5000);} catch (InterruptedException ex) {java.util.logging.Logger.getLogger(MyAsyncEJB.class.getName()).log(Level.SEVERE, null, ex);}System.out.println("Exiting MyAsyncEJB/asyncEJB2()");return new AsyncResult("Finished Executing on "+ new Date().toString());}這是Java EE功能的相當(dāng)簡短的預(yù)覽。 這些API和規(guī)范功能豐富,很難通過博客文章涵蓋所有這些API和規(guī)范! 我希望這能激起您的興趣,并為您提供進(jìn)一步探索的起點(diǎn)。
干杯!
翻譯自: https://www.javacodegeeks.com/2014/08/java-ee-asynchronous-constructs-and-capabilities.html
總結(jié)
以上是生活随笔為你收集整理的Java EE:异步构造和功能的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 被ddos攻击了怎么查(被ddos 怎么
- 下一篇: 鳞翅目动物的诅咒:玩java.time