Вступление
Java EE имеет ряд API и конструкций для поддержки асинхронного выполнения. Это жизненно важно с точки зрения масштабируемости и производительности.
Допустим, 2 модуля взаимодействуют друг с другом. Когда moduleA (отправитель) отправляет сообщение в moduleB (получатель) в синхронном режиме, связь происходит в контексте одного потока, то есть поток, который инициировал связь с moduleA, блокируется до тех пор, пока moduleB не ответит обратно.
Это было универсальное утверждение, но его можно распространить на контекст простых Java-методов, взаимодействующих друг с другом — в этом случае синхронный вызов от methodA к methodB будет выполняться в том же потоке, который будет заблокирован, пока methodB не вернет или не сгенерирует исключение ,
Зачем нам нужно асинхронное поведение в приложениях на основе Java EE?
Мы можем дополнительно экстраполировать эту точку на мир Java EE — будь то межсерверное взаимодействие, например, между веб-уровнем и уровнем EJB (сервлетами и EJB), или типичное взаимодействие клиент-сервер — браузер, взаимодействующий с конечной точкой RESTful, сервлетами и т. Д. — сервер поток, который отвечает на запрос клиента, всегда блокируется, пока компонент сервера не ответит обратно.
Вот где в игру вступает асинхронное выполнение — если поток сервера, обрабатывающий клиентский запрос, может быть освобожден / приостановлен, а фактическая бизнес-логика выполняется в отдельном потоке (отличном от исходного), производительность и масштабируемость могут быть значительно улучшены ! Например, если поток прослушивателя HTTP, выделенный для прослушивания клиентских запросов, освобождается немедленно, тогда он может выполнять запросы других клиентов, и бизнес-логика может выполняться в отдельном потоке контейнера, который затем может возвращать ответ с помощью соответствующих методов, таких как java.util.concurrent.Future объект или через обработчики обратного вызова, зарегистрированные клиентом. Подумайте с точки зрения конечного пользователя — отзывчивость имеет большое значение!
Погружение: асинхронные конструкции и API в Java EE
Давайте рассмотрим некоторые функции, связанные с Async (API) в Java EE. Это не исчерпывающий список, но он должен стать хорошей отправной точкой.
Различные спецификации Java EE имеют свои типичные способы и API для облегчения возможностей асинхронного подключения. Давайте рассмотрим следующие спецификации Java EE
- JAX-RS 2.0 (Java EE 7)
- Websocket 1.0 (Java EE 7)
- Утилиты параллелизма 1.0 (Java EE 7)
- EJB 3.1 (Java EE 6)
- Сервлет 3.0 (Java EE 6)
Примечание . Код, представленный ниже, представлен в виде фрагмента кода (по понятным причинам). С полными образцами можно ознакомиться здесь .
JAX-RS 2.0
Асинхронная обработка запросов — это новая функция в версии 2.0 JAX-RS (новая в Java EE 7). Чтобы выполнить запрос aysnc с использованием API-интерфейсов JAX-RS, необходимо внедрить ссылку на интерфейс javax.ws.rs.container.AsyncResponse в самом методе ресурсов JAX-RS. Этот параметр переводит выполнение запроса в асинхронный режим, и метод продолжает его выполнение. Метод возобновления объекта AsynResponse должен вызываться из отдельного потока после завершения выполнения бизнес-логики. Можно использовать функции утилиты параллелизма Java EE (обсуждаемые позже), такие как javax.enterprise.concurrent.ManagedExecutorService , чтобы инкапсулировать бизнес-логику в виде объекта Runnable и передать ее службе выполнения контейнера, которая позаботится обо всем остальном. Нет необходимости создавать неуправляемые изолированные потоки самостоятельно.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
@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"); } |
Клиентский API JAX-RS также имеет асинхронные возможности, но они не обсуждались в посте. На них определенно стоит посмотреть!
Websocket 1.0
API Websocket является совершенно новым дополнением к арсеналу Java EE (представлен в Java EE 7). Это облегчает двунаправленную (инициированную как сервером, так и клиентом) связь, которая также является дуплексной по своему характеру (клиент или сервер могут отправлять сообщения друг другу в любое время).
Для отправки асинхронных сообщений с помощью API Websocket необходимо использовать метод getAsyncRemote, доступный в интерфейсе javax.websocket.Session . Внутренне это не что иное, как экземпляр вложенного интерфейса javax.websocket.RemoteEnpoint — javax.websocket.RemoteEnpoint.Async . Вызов обычных методов sendXXX для этого приведет к тому, что процесс отправки будет выполняться в отдельном потоке. Это особенно полезно, когда вы рассматриваете обмен большими сообщениями или обработку большого количества клиентов веб-сокетов, которым сообщения должны быть отправлены. Метод wither возвращает объект java.util.concurrent.Future или можно зарегистрировать обратный вызов в форме реализации интерфейса javax.websocket.SendHandler .
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
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()); }); }); } |
Параллельные утилиты 1.0
Утилиты для параллелизма Java EE — еще одно замечательное дополнение к Java EE 7 . Он предоставляет стандартный способ порождения потоков — хорошая его часть заключается в том, что они управляются контейнером, а не просто изолированными / потерянными потоками, о которых контейнер не имеет контекстной информации.
В общем, Concurrency Utilities 1.0 предоставляют несколько стандартных конструкций для выполнения асинхронных задач в отдельных потоках. Это следующие: javax.enterprise.concurrent.ManagedExecutorService и javax.enterprise.concurrent.ManagedScheduledExecutorService .
Чтобы запустить новую задачу в отдельном потоке, можно использовать интерфейс ManagedExecutorService для отправки Runnable . В дополнение к реализации интерфейса Runnable класс также может реализовать интерфейс javax.enterprise.concurrent.ManagedTask и предоставить реализацию javax.enterprise.concurrent.ManagedTaskListener для прослушивания изменений жизненного цикла в задаче, переданной через ManagedExecutorService.
|
1
2
3
4
5
6
7
8
9
|
@Override protected 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"); } |
Сервлет 3.0
Асинхронный HTTP был введен в Servlet 3.0 (часть Java EE 6 ), который в основном предоставлял возможность выполнить запрос в отдельном потоке и приостановить исходный поток, который обрабатывал вызов клиента.
Ключевым игроком здесь является интерфейс javax.servlet.AsyncContext . Чтобы инициировать асинхронную обработку, вызывается метод startAsync интерфейса java.servlet.ServletRequest . Чтобы выполнить основную логику, объект java.lang.Runnable должен быть передан методу запуска интерфейса AsyncContext. Можно подключить прослушиватель, реализовав javax.servlet.AsyncListener для получения уведомлений об обратном вызове в течение определенного времени выполнения задачи Async.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
@Override public 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 1 final AsyncContext asyncContext = req.startAsync(); //step 2 asyncContext.addListener(new CustomAsyncHandler(asyncContext)); //step 3 asyncContext.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 4 asyncContext.complete(); } ); writer.println("EXITING ... " + MyAsyncServlet.class.getSimpleName() + "/doGet() and returning initial thread back to the thread pool"); } |
EJB 3.1
Как правило, (до EJB 3.1) EJB Message Driven Beans использовались для выполнения асинхронных требований. Компонент MDB прослушивает сообщения, отправленные в javax.jms.Destination ( очередь или тема ), и выполняет требуемую бизнес-логику — это может быть что угодно, от отправки сообщения электронной почты до инициирования задачи обработки заказа. Важно понимать, что клиент, который отправляет сообщение в очередь, в первую очередь, не знает о MDB ( отделен ) и не должен ждать / оставаться заблокированным до конца задания (получение по электронной почте или подтверждение обработки заказа). ).
В EJB 3.1 (часть Java EE 6 ) появилась аннотация javax.ejb.Asynchronous . Это может быть помещено в класс EJB Session bean (Stateless, Stateful или Singleton) (делает все методы асинхронными) или на самом уровне метода — в случае, если требуется детальное управление. Метод с аннотацией @Asynchronous может либо возвращать void (запустить и забыть), либо экземпляр java.util.concurrent.Future, если необходимо отслеживать результат асинхронного метода — это можно сделать, вызвав метод Future.get () — Следует отметить, что сам метод get является блокирующим по своей природе.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
@Asynchronous public 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. Эти API-интерфейсы и спецификации функционально богаты, и их все сложно описать в блоге! Я надеюсь, что это заинтересует вас и даст вам отправную точку для дальнейшего изучения.
Ура!
| Ссылка: | Java EE: асинхронные конструкции и возможности от нашего партнера JCG Абхишека Гупты в блоге Object Oriented .. |
