< Sergii Kostenko's blog

Взаимная блокировка подсистемы Wildfly ActiveMQ

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)

Comments


Как использовать пользовательский ClassLoader для загрузки JAR-файлов во время выполнения

28 июня 2019

Для загрузки классов во время выполнения java используют механизм ClassLoader, который основан на нескольких основных принципах:

Основные сценарии использования пользовательского 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);
...

Comments


Разработка DSL API на Java

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();

Comments


Моё видео про кластеризацию доменного режима Wildfly

02 мая 2019

Загрузил новое видео на YouTube про кластеризацию доменного режима Wildfly.

Comments


Кластер доменного режима Wildfly и балансировка нагрузки из коробки

15 апреля 2019

Wildfly Application Server предоставляет нам два возможных режима настройки кластерной среды для приложений Java EE.

  1. Автономный режим (Standalone mode) - каждый автономный экземпляр имеет свой собственный интерфейс управления и конфигурацию. Вы можете управлять одним экземпляром за раз. Конфигурация помещена в файл standalone.xml.
  2. Доменный режим (Domain mode) - все экземпляры Wildfly управляются с помощью специального процесса оркестровки, называемого контроллер домена. С помощью контроллера домена вы можете управлять группой серверов. Также вы можете управлять группами. Каждая группа серверов может иметь их собственные конфигурации, развертывания и т.д. Конфигурация находится в файлах domain.xml и host.xml.
    Пример группы серверов Wildfly:
    wildfly-cluster-domain-mode

С версии 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 :
wildfly-cluster-domain-mode-1-2

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

[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)

wildfly-cluster-domain-mode-1-2-3

Это всё!
Код этого блога доступен на GitHub: Demo application, Wildlfly CLI

Comments