< Sergii Kostenko's blog

Проблемы с подключением JMX Wildfly (очень медленно и прекращается)

19 августа 2019

JMX (Java Management Extensions ) - это технология, которая предоставляет нам возможность мониторинга приложеий (серверов приложения) с помощью MBeans (Managed Bean) объектов.
Список поддерживаемых MBeans можно получить с помощью инструмента JConsole, который уже включен в JDK. Поскольку JMX не обеспечивает строго определенный протокол коммуникации - реализация может отличаться в зависимости от производителя.
Например, для подключения к серверу приложений Wildfly вам необходимо использовать включенный в дистрибутиву скрипт jconsole.sh:

<WFLY_HOME>/bin/jconsole.sh

или добавить <WFLY_HOME>/bin/client/jboss-client.jar в classpath:

jconsole J-Djava.class.path=$JAVA_HOME\lib\tools.jar;$JAVA_HOME\lib\jconsole.jar;jboss-client.jar

По умолчанию Wildfly использует timeout = 60s для удаленных JMX-соединений, после того как это соединение будет разорвано:
jconsole terminated connection
Чтобы изменить значение timeout, используйте свойство org.jboss.remoting-jmx.timeout:

./jconsole.sh -J-Dorg.jboss.remoting-jmx.timeout=300

Но увеличение таймаутов не всегда является хорошим решением.
Итак, давайте искать причину медленной работы. Чтобы создать список MBeans, jconsole рекурсивно запрашивает ВСЕ MBean, что может быть очень медленным в случае большого количества развертываний и большого количества логгеров. (Reported issue: WFCORE-3186). Частичное решение заключается в уменьшении количества логов файлов путем измениния rotating типа из periodic-size-rotating-file-handler на size-rotating-file-handler.

Другой причиной крайне медленной работы может бытьBatch subsystem (JBeret). Он хранит много рабочей информации в своих таблицах (в памяти или на удаленной БД, зависит от конфигурации). Если эти таблицы достаточно большие - это может негативно сказаться на производительность сервера.
Так что, если вам не нужны эти данные, просто периодически очищайте их (например, каждое повторное развертывание, если вы делаете это достаточно часто):

TRUNCATE TABLE PARTITION_EXECUTION CASCADE;
TRUNCATE TABLE STEP_EXECUTION CASCADE;
TRUNCATE TABLE JOB_EXECUTION CASCADE;
TRUNCATE TABLE JOB_INSTANCE CASCADE;  

С другой точки зрения, получений ВСЕХ MBeans не самое хорошое решение. Поэтому, просто используйте инструменты, которые позволят вам найти MBeans по пути.

Comments


Jakarta EE мультимодульный шаблон приложения

08 августа 2019

В этом посту я поделюсь простым и полезным gradle шаблоном для создания мультимодульного Jakarta EE приложения. Мы реализуем один из типичных, который состоит из REST контроллера (module1) и некоторой основной логики (module2). Общая картина архитектуры нашего приложения выглядит вот так:

EE multi module application

Итак, давайте сделаем инициализацию проекта с помощью следующего gradle шаблона:
settings.gradle:

rootProject.name = 'ee-application-multi-module-gradle-template'
include 'module1'
include 'module2:module2-api', 'module2:module2-core'

root build.gradle:

defaultTasks 'clean', 'build'
subprojects {
    ext.libraryVersions = [
        javaee                  : '8.0',
    ]
    defaultTasks 'clean', 'build'
    repositories {
        jcenter()
    }
}

Выше, мы описали начальную структуру приложения, где module1 является плоским подпроектом для нашего контроллера, а module2 является нашей основной логикой, которая состоит из подпроектов API и Core. В качестве контроллера, мы будем использовать основную логику API, и мы решили разделить приложение на модули (что означает отсутсвие общеорганизационных архивов) - наши подпроекты должны быть достаточно простыми:

module1 build.gradle:

apply plugin: 'war'
dependencies {
    compile project(':module2:module2-api')
    providedCompile "javax:javaee-api:${libraryVersions.javaee}"
}

module2:module2-api:

apply plugin: 'java'
dependencies {
}

module2:module2-core:

apply plugin: 'war'
dependencies {
    compile project(':module2:module2-api')
    providedCompile "javax:javaee-api:${libraryVersions.javaee}"
}

На самом деле, это всё!
Теперь мы можем реализовать наш контроллер следующим образом:

@Path("/")
@Stateless
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class TestEndpoint {

    @EJB(lookup = TestService.TEST_SERVICE_JNDI)
    TestService testService;

    @GET
    @Path("/test")
    public Response test() {
        SomethingDto something = testService.doSomething();
        return Response.ok().entity(something.getMessage()).build();
    }

В свою очередь, основная логика API содержиться в Interface и DTO:
TestService.java:

public interface TestService {

  String TEST_SERVICE_NAME = "test-service";
  String TEST_SERVICE_JNDI ="java:global/module2-core/" + TEST_SERVICE_NAME;

  SomethingDto doSomething();
}

SomethingDto.java:

public class SomethingDto implements Serializable{
  ...
}

В конце концов, основная логика Core состоит из логики, реализующей API:
TestServiceImpl.java

@Remote(TestService.class)
@Stateless(name = TestService.TEST_SERVICE_NAME)
public class TestServiceImpl implements TestService {

    @PersistenceContext
    EntityManager entityManager;

    @Override
    public SomethingDto doSomething() {
        TestEntity entity = entityManager.find(TestEntity.class, Long.MAX_VALUE);
        return new SomethingDto("Hello Jakarta EE world!");
    }
}

Описанная архитектура приложений Jakarta EE позволяет нам наслаждаться всеми возможностями ЕЕ с абсолютно прозрачными взаимодействиями между модулями и в то же время оставаться близким к дизайну микросервисов - поскольку у нас нет ограничений в использований одного контейнера для всех модулей.

Исходный код, доступен на GitHub

Comments


Как споймать 'убитые' сигналы в Java

07 августа 2019

Вы можете отправлять разные сигналы в ваше приложение, используя команду kill -l. По умолчанию kill будет отправлять сигнао TERM. Java по умолчанию ловит несколько типов сигналов, например

kill -3 <PID>

будет дампать трассировку стека Java к стандартным ошибкам

Кроме того, вы можете поймать сигнал внутри вашего приложения, как

public class App {
    public static void main(String... s) throws Exception {

        Signal.handle(new Signal("HUP"), signal -> {
            System.out.println(signal.getName() + " (" + signal.getNumber() + ")");
        });

        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(1000l);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
}

Список доступных сигналов, которые вы можете получить, выполнив команду kill -l:

kostenko@kostenko:~$ kill -l
 1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL	 5) SIGTRAP
 6) SIGABRT	 7) SIGBUS	 8) SIGFPE	 9) SIGKILL	10) SIGUSR1
11) SIGSEGV	12) SIGUSR2	13) SIGPIPE	14) SIGALRM	15) SIGTERM
16) SIGSTKFLT	17) SIGCHLD	18) SIGCONT	19) SIGSTOP	20) SIGTSTP
21) SIGTTIN	22) SIGTTOU	23) SIGURG	24) SIGXCPU	25) SIGXFSZ
26) SIGVTALRM	27) SIGPROF	28) SIGWINCH	29) SIGIO	30) SIGPWR
31) SIGSYS	34) SIGRTMIN	35) SIGRTMIN+1	36) SIGRTMIN+2	37) SIGRTMIN+3
38) SIGRTMIN+4	39) SIGRTMIN+5	40) SIGRTMIN+6	41) SIGRTMIN+7	42) SIGRTMIN+8
43) SIGRTMIN+9	44) SIGRTMIN+10	45) SIGRTMIN+11	46) SIGRTMIN+12	47) SIGRTMIN+13
48) SIGRTMIN+14	49) SIGRTMIN+15	50) SIGRTMAX-14	51) SIGRTMAX-13	52) SIGRTMAX-12
53) SIGRTMAX-11	54) SIGRTMAX-10	55) SIGRTMAX-9	56) SIGRTMAX-8	57) SIGRTMAX-7
58) SIGRTMAX-6	59) SIGRTMAX-5	60) SIGRTMAX-4	61) SIGRTMAX-3	62) SIGRTMAX-2
63) SIGRTMAX-1	64) SIGRTMAX

Обратите внимание! Не все сигналы могут быть перехвачены в приложении, некоторые из них зарезервированы в ОС. Например, если вы попытается обратать kill -SIGKILL (kill -9), то вы получите:

Exception in thread "main" java.lang.IllegalArgumentException: Signal already used by VM or OS: SIGKILL
	at java.base/jdk.internal.misc.Signal.handle(Signal.java:173)
	at jdk.unsupported/sun.misc.Signal.handle(Signal.java:157)

Comments


Wildfly. Настройка показателей балансировки нагрузки

06 августа 2019

Ранее я писал о Кластер доменного режима Wildfly и балансировка нагрузки из коробки. Но что, если мы хотим выполнить балансировку зависящей от конкретного поведения сервера?

Подсистема Wildfly mod_cluster предоставляет нам несколько предопределенных типов показателей для определения наилучшей балансировки запросов:

Также вы можете настроить weight (влияние метрики на другие метрики) и свойства capacity ;
Ниже приведен пример того, как изменить значение по умолчанию на основе балансировки ЦП на балансировку на основе busyness + ЦП:

/subsystem=modcluster/mod-cluster-config=configuration/dynamic-load-provider=configuration/load-metric=cpu:remove()
/subsystem=modcluster/mod-cluster-config=configuration:add-metric(type=busyness,weight=2)
/subsystem=modcluster/mod-cluster-config=configuration:add-metric(type=cpu,weight=1)

Если этих предопределенных типов недостаточно, вы можете обеспечить custom-load-metric, реализовав org.jboss.modcluster.load.metric.impl.AbstractLoadMetric. Для возможности использования вашей пользовательской метрики,- вам необходимо скопировать упакованный JAR-файл в модуль modcluster и обновитьmodule.xml. Теперь вы можете использовать свою собственную метрику с вашей конфигурацией, как:

 <custom-load-metric class="org.kostenko.examples.wldfly.modcluster.MyBalancingMetric">  

Comments