29 июня 2019
В последнее время у меня была необычно высокая загрузка ЦП на случайном экземпляре Wildfly cluster.
Дамп потока показывает причину проблемы:
Found one Java-level deadlock:
=============================
"Thread-1 (ActiveMQ-server-org.apache.activemq.artemis.core.server.impl.ActiveMQServerImpl$2@46fcd20a-1114701218)":
waiting to lock Monitor@0x00007f45f02e20f8 (Object@0x0000000603407950, a org/apache/activemq/artemis/core/server/cluster/impl/ClusterConnectionImpl$MessageFlowRecordImpl),
which is held by "Thread-29 (ActiveMQ-client-global-threads-2129186403)"
"Thread-29 (ActiveMQ-client-global-threads-2129186403)":
waiting to lock Monitor@0x00007f46203b5518 (Object@0x00000004cc79a7b8, a org/apache/activemq/artemis/core/server/impl/QueueImpl),
which is held by "Thread-1 (ActiveMQ-server-org.apache.activemq.artemis.core.server.impl.ActiveMQServerImpl$2@46fcd20a-1114701218)"
Found a total of 1 deadlock.
Ниже приведена часть официальной документации:
"Если для thread-pool-max-size установлено положительное целое число больше, чем 0, пул потоков будет ограничен. Если поступают запросы и в пуле нету свободных потоков, запросы будут блокироваться до тех пор, пока поток не станет доступным. Рекомендуется использовать пул ограниченных потоков с осторожностью, поскольку это может привести к тупиковым ситуациям, если верхняя граница настроена слишком низко."
По-этому решение будет таковым:
/subsystem=messaging-activemq/server=default:write-attribute(name=thread-pool-max-size,value=-1)
28 июня 2019
Для загрузки классов во время выполнения java используют механизм ClassLoader, который основан на нескольких основных принципах:
parent-first delegation
, - наследник ClassLoader будет использоваться, если родитель не может найти или загрузить класс. Это поведение можно изменить на child-first
, переписав ClassLoader.loadClass(...)
;Основные сценарии использования пользовательского ClassLoader:
Итак, давайте посмотрим, как выглядит использование пользовательского ClassLoader с точки зрения исходного кода:
List<File> jars = Arrays.asList(new File("/tmp/jars").listFiles());
URL[] urls = new URL[files.size()];
for (int i = 0; i < jars.size(); i++) {
try {
urls[i] = jars.get(i).toURI().toURL();
} catch (Exception e) {
e.printStackTrace();
}
}
URLClassLoader childClassLoader = new URLClassLoader(urls, ClassLoader.getSystemClassLoader());
Затем загрузите класс с пользовательским ClassLoader:
Class.forName("org.kostenko.examples.core.classloader.ClassLoaderTest", true , childClassLoader);
Обратите внимение! Если ваши загруженные библеотеки используют некоторые ресурсы, такие как свойства или что-то ещё, вам нужно продоставить загрузчик класса котекста:
Thread.currentThread().setContextClassLoader(childClassLoader);
Кроме того, вы можете использовать пользовательские ClassLoaders для загрузки служб с помощью Java Service Provider Interface(SPI)
ServiceLoader<MyProvider> serviceLoader = ServiceLoader.load(MyProvider.class, childClassLoader);
...
27 мая 2019
Хорошо разработанный API - очень важен, так как его будут использовать не только вы, но и кто-то другой, если вы ожидаете. По-этому, что бы построить читаемый и хорошо написанный DLS(domain specific language) на Java, обычно используют паттерн Builder с несколькими простыми правилами:
Окей, давайте посмотрим, как это выглядит с точки зрения исходного кода...
Точка входа Design API
public class MyAPI {
public static UserBuilder.Registration asUser() {
return new UserBuilder.User();
}
}
Design builder, использующий интерфейсы для ограничения доступности значений, зависит от текущего шага.
public class UserBuilder {
public static class User implements Registration, Login, Password, Apply {
private String login;
private String password;
@Override
public Login doRegistration() {
return this;
}
@Override
public Password withLogin(String login) {
this.login = login;
return this;
}
@Override
public Apply withPassword(String password) {
this.password = password;
return this;
}
@Override
public void apply() {
// ...
}
}
public interface Registration {
Login doRegistration();
}
public interface Login {
Password withLogin(String login);
}
public interface Password {
Apply withPassword(String password);
}
public interface Apply {
void apply();
}
}
Тогда использование, без попытки ошибиться, выглядит вот так:
asUser().doRegistration()
.withLogin("login").withPassword("password")
.apply();
02 мая 2019
Загрузил новое видео на YouTube про кластеризацию доменного режима Wildfly.
15 апреля 2019
Wildfly Application Server предоставляет нам два возможных режима настройки кластерной среды для приложений Java EE.
standalone.xml
.контроллер домена
. С помощью контроллера домена вы можете управлять группой серверов. Также вы можете управлять группами. Каждая группа серверов может иметь их собственные конфигурации, развертывания и т.д. Конфигурация находится в файлах domain.xml
и host.xml
.С версии 10 Wildfly добавляет поддержку для использования подсистемы Undertow
в качестве баланса нагрузки. Итак, теперь все, что нам нужно для создания кластерной инфраструктуры Java EE, это только Wildfly. Давай сделаем это.
Скачайте последнюю версию сервера приложений с https://wildfly.org/downloads/ и после распакуйте дистрибутиву. Чтобы запустить Wildfly в доменном режим, пожалуйста выполните:
kostenko@kostenko:/opt/wildfly-16.0.0.Final/bin$ ./domain.sh
Подключитесь к Wildfly CLI консоле
kostenko@kostenko:/opt/wildfly-16.0.0.Final/bin$ ./jboss-cli.sh -c
[domain@localhost:9990 /]
По умолчанию в Wildfly предварительно настроены группы серверов main-server-group
иother-server-group
, поэтому нам нужно очистить существующие серверы:
:stop-servers(blocking=true)
/host=master/server-config=server-one:remove
/host=master/server-config=server-two:remove
/host=master/server-config=server-three:remove
/server-group=main-server-group:remove
/server-group=other-server-group:remove
Создайте новую группу серверов и серверов, используя профиль full-ha
, чтобы включить поддержкуmod_cluster
:
/server-group=backend-servers:add(profile=full-ha, socket-binding-group=full-ha-sockets)
/host=master/server-config=backend1:add(group=backend-servers, socket-binding-port-offset=100)
/host=master/server-config=backend2:add(group=backend-servers, socket-binding-port-offset=200)
#start the backend servers
/server-group=backend-servers:start-servers(blocking=true)
#add system properties (so we can tell them apart)
/host=master/server-config=backend1/system-property=server.name:add(boot-time=false, value=backend1)
/host=master/server-config=backend2/system-property=server.name:add(boot-time=false, value=backend2)
Далее настройте группу серверов для балансировщика нагрузки.
/server-group=load-balancer:add(profile=load-balancer, socket-binding-group=load-balancer-sockets)
/host=master/server-config=load-balancer:add(group=load-balancer)
/socket-binding-group=load-balancer-sockets/socket-binding=modcluster:write-attribute(name=interface, value=public)
/server-group=load-balancer:start-servers
Теперь давайте разработаем простую конечную точку JAX-RS, чтобы показать, как она работает:
@Path("/clusterdemo")
@Stateless
public class ClusterDemoEndpoint {
@GET
@Path("/serverinfo")
public Response getServerInfo() {
return Response.ok().entity("Server: " + System.getProperty("server.name")).build();
}
}
Создайте проект и задеплойте его в группе backend-servers
:
[domain@localhost:9990 /] deploy ee-jax-rs-examples.war --server-groups=backend-servers
И проверьте результат на http://localhost:8080/ee-jax-rs-examples/clusterdemo/serverinfo
:
Теперь мы можем легко добавить серверы в группу во время выполнения, и запросы будут автоматически балансироваться:
[domain@localhost:9990 /] /host=master/server-config=backend3:add(group=backend-servers, socket-binding-port-offset=300)
[domain@localhost:9990 /] /host=master/server-config=backend3/system-property=server.name:add(boot-time=false, value=backend3)
[domain@localhost:9990 /] /server-group=backend-servers/:start-servers(blocking=true)
Это всё!
Код этого блога доступен на GitHub: Demo application, Wildlfly CLI