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-соединений, после того как это соединение будет разорвано:
Чтобы изменить значение 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 по пути.
08 августа 2019
В этом посту я поделюсь простым и полезным gradle шаблоном для создания мультимодульного Jakarta EE приложения. Мы реализуем один из типичных, который состоит из REST контроллера (module1) и некоторой основной логики (module2). Общая картина архитектуры нашего приложения выглядит вот так:
Итак, давайте сделаем инициализацию проекта с помощью следующего 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
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)
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">