Статьи

EJB 3.x: модели жизненного цикла и параллелизма (часть 1)

Сведения о жизненном цикле Java EE и деталях, связанных с параллелизмом, могут быть не новы для опытных профессионалов, но для новичков это может занять некоторое время.

совпадение

Что касается EJB, понимание их жизненного цикла (и связанных сценариев параллелизма) крайне важно для обеспечения надлежащего использования и разработки решения с использованием EJB. Злоупотреблять ими легко!

Жизненный цикл бобов

Жизненный цикл бобов

Я быстро расскажу о бинах без гражданства и о состоянии в этом посте и пока пропущу бобы Лимы!

  • Stateful Session Beans — жизненный цикл + обработка параллелизма
  • Бины без сохранения состояния — только модель параллелизма, поскольку я кратко рассмотрел жизненный цикл в одной из моих предыдущих публикаций .

Каковы различные состояния в жизненном цикле сессионного компонента с состоянием?

  • Не существует
  • готовы
  • Пассивированная

Как меняются штаты? Что их вызывает?

Вот быстрый снимок в виде таблицы и диаграмма высокого уровня. Для более подробной информации читайте дальше. , ,

Диаграмма состояний жизненного цикла бина сеанса без сохранения состояния

Диаграмма состояний жизненного цикла бина сеанса без сохранения состояния

Примечание : DNE — не существует, R — готово, P — пассивировано, SFSB — сессионный компонент с сохранением состояния

Государственный переход Триггеры Callbacks
DNE к R При первом обращении к экземпляру SFSB через JNDI или DI @PostConstruct
R в DNE Контейнер закрывается, клиент вызывает метод, аннотированный @Remove, бин достигает порога простоя, обозначенного DD или @StatefulTimeout @PreDestroy
R к P Контейнер EJB пассивирует неиспользуемые компоненты и удаляет их из активной памяти на основе определенных алгоритмов. @PrePassivate
P к DNE Компонент достигает порога простоя, обозначенного DD или @StatefulTimeout Примечание : аннотированный метод @PreDestroy НЕ вызывается
P к R Когда клиент вызывает экземпляр SFSB после его пассивации, но время еще не истекло @PostActivate

Примечание . Если SFSB генерирует исключение во время обработки запроса, его экземпляр уничтожается, т.е. он переходит в состояние DNE. В этом случае аннотированный метод @PreDestroy не вызывается

Теперь, когда у нас есть некоторое представление о жизненном цикле SFSB, давайте попробуем взглянуть на то, как эти бины ведут себя под нагрузкой, т.е. когда приложение используется несколькими пользователями одновременно, что приводит к одновременному доступу экземпляров SFSB.

Сессионные компоненты Stateful: управление параллелизмом

Безопасность потоков — одна из основных функций EJB. Следует отметить, что эта безопасность потока бесплатна и не требует каких-либо связанных с параллелизмом конструкций, которые должны быть закодированы самим разработчиком компонента (есть несколько исключений ). Что касается SFSB, контейнер EJB гарантирует, что только один поток может получить доступ к экземпляру компонента в определенный момент времени.

В этом примере мы пытаемся симулировать одновременный доступ к одному экземпляру SFSB, вызывая тестовый сервлет через JMeter . Сервлет вводит компонент через DI и вызывает для него метод. Метод SFSB просто использует Thread.sleep (), чтобы притворяться, будто он что-то выполняет.

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
package com.abhirockzz.wordpress.ejb.lifecycle.stateful;
 
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ejb.Stateful;
 
@Stateful
public class MyStatefulBean {
 
    public MyStatefulBean() {
    }
 
    public void act() {
        System.out.println("Entered MyStatefulBean/act() on " + new Date().toString() + " . SFSB instance " + this.hashCode() + " Thread : " + Thread.currentThread().getName());
        try {
            Thread.sleep(2000);
        } catch (InterruptedException ex) {
            Logger.getLogger(MyStatefulBean.class.getName()).log(Level.SEVERE, null, ex);
        }
 
        System.out.println("Exit MyStatefulBean/act() on " + new Date().toString() + " . SFSB instance " + this.hashCode() + " Thread : " + Thread.currentThread().getName());
 
    }
}
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
package com.abhirockzz.wordpress.ejb.lifecycle.stateful;
 
import java.io.IOException;
import java.util.Date;
import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
@WebServlet(name = "SFSBTestServlet", urlPatterns = {"/SFSBTestServlet"})
public class SFSBTestServlet extends HttpServlet {
 
    public SFSBTestServlet() {
    }
 
    @Inject
    MyStatefulBean mySFSB;
 
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        System.out.println("Entered SFSBTestServlet/doGet() on " + new Date().toString() + " . Servlet instance " + this.hashCode() + " Thread : " + Thread.currentThread().getName());
        mySFSB.act();
    }
 
}

HTTP GET запрос через JMeter

HTTP GET запрос через JMeter

Моделирование параллельных запросов через JMeter

Моделирование параллельных запросов через JMeter

наблюдения

  • Поскольку сам сервлет не является потокобезопасным, на самом деле несколько потоков будут входить в метод doGet ()
  • Единственный экземпляр SFSB (очевидный через результат hashCode) при одновременном доступе (см. Имена потоков в записанных инструкциях)
  • Только один поток сможет получить доступ к экземпляру SFSB — другие потоки ждут своей очереди, пока метод SFSB не вернется. Эта задержка заметна через лог-операторы на консоли
Консольные журналы

Консольные журналы

А как насчет бобов без гражданства?

Эти бобы по своей природе являются потокобезопасными . Почему ? Это связано с тем, что по умолчанию контейнер обеспечивает обслуживание каждого нового запроса новым экземпляром компонента. Помните, что клиент может получить ссылку на bean-компонент без состояния тремя возможными способами — DI, JNDI или через удаленный интерфейс (RMI). Во всех этих случаях это контейнер (прокси), который перехватывает вызов — таким образом, даже если несколько потоков, по-видимому, обращаются к одному и тому же экземпляру компонента, на самом деле это не один и тот же!

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.abhirockzz.wordpress.ejb.lifecycle.stateless;
 
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ejb.Stateless;
 
@Stateless
public class MyStatelesslBean {
 
    public void act() {
 
        System.out.println("Entered MyStatelesslBean/act() on " + new Date().toString() + " . SLSB instance " + this.hashCode() + " Thread : " + Thread.currentThread().getName());
        try {
            Thread.sleep(2000);
        } catch (InterruptedException ex) {
            Logger.getLogger(MyStatelesslBean.class.getName()).log(Level.SEVERE, null, ex);
        }
 
        System.out.println("Exit MyStatelesslBean/act() on " + new Date().toString() + " . SLSB instance " + this.hashCode() + " Thread : " + Thread.currentThread().getName());
    }
}
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
package com.abhirockzz.wordpress.ejb.lifecycle.stateless;
 
import java.io.IOException;
import java.util.Date;
import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
@WebServlet(name = "SLSBTestServlet", urlPatterns = {"/SLSBTestServlet"})
public class SLSBTestServlet extends HttpServlet {
 
    @Inject
    MyStatelesslBean slsb;
 
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
 
        System.out.println("Entered SLSBTestServlet/doGet() on " + new Date().toString() + " . Servlet instance " + this.hashCode() + " Thread : " + Thread.currentThread().getName());
 
        slsb.act();
 
    }
 
}

наблюдения

  • Поскольку сам сервлет не является потокобезопасным, на самом деле несколько потоков будут входить в метод doGet ()
  • Контейнер выбирает различные экземпляры SLSB (что видно из результата hashCode ) для управления параллельными запросами (см. Имена потоков в записанных инструкциях).
  • Несмотря на одновременные запросы, каждый поток запросов обслуживается новым экземпляром
Консольные журналы

Консольные журналы

На этом пока все! Я планирую рассказать о бинах Singleton Session в следующем посте. Оставайтесь в курсе . , , ,

Спасибо за чтение!