Рубрики
Без рубрики

Как Flexipool поддерживает как прокси-серверы соединений, так и декораторы

Автор оригинала: Vlad Mihalcea.

Пул Flexi контролирует использование пула соединений и поэтому ему необходимо перехватить вызов метода закрытия соединения. FlexyPool monitors connection pool usage and so it needs to intercept the connection close method call.

private static class ConnectionInvocationHandler 
    implements InvocationHandler {

    public static final String CLOSE_METHOD_NAME = "close";

    private final Connection target;
    private final ConnectionCallback callback;

    public ConnectionInvocationHandler(
        Connection target, 
        ConnectionCallback callback) {
        this.target = target;
        this.callback = callback;
    }

    @Override
    public Object invoke(
        Object proxy, 
        Method method, 
        Object[] args) throws Throwable {
        if (CLOSE_METHOD_NAME.equals(method.getName())) {
            callback.close();
        }
        return method.invoke(target, args);
    }
}

Как бы просто это ни было, вызов прокси-сервера выполняется медленнее, чем декоратор, который вызывает целевой метод с помощью прямого вызова. Поскольку все пулы подключений в любом случае используют прокси-серверы, добавление другого прокси-уровня только увеличивает накладные расходы на время вызова и поэтому теперь Flexipool также поддерживает декораторы соединений.

Регистратор соединений обертывает базовое соединение с базой данных, делегируя все вызовы фактическому экземпляру объекта. Как и его прокси-аналог, только метод close выполняет какую-либо дополнительную логику:

public class ConnectionDecorator 
    implements Connection {

    private final Connection target;

    private final ConnectionCallback callback;

    public ConnectionDecorator(
        Connection target, 
        ConnectionCallback callback) {
        this.target = target;
        this.callback = callback;
    }

    public Connection getTarget() {
        return target;
    }

    public ConnectionCallback getCallback() {
        return callback;
    }

    @Override
    public Statement createStatement() 
        throws SQLException {
        return target.createStatement();
    }

    @Override
    public void close() 
        throws SQLException {
        callback.close();
        target.close();
    }

    /**
     *  More methods omitted for brevity sake
     */

    public void setSchema(String schema) 
        throws SQLException {
        ReflectionUtils.invoke(
            target,
            ReflectionUtils.getMethod(
                target, 
                "setSchema", 
                String.class
            ),
            schema
        );
    }

    public String getSchema() 
        throws SQLException {
        return ReflectionUtils.invoke(
            target,
            ReflectionUtils.getMethod(
                target, 
                "getSchema"
            )
        );
    }

    public void abort(Executor executor) 
        throws SQLException {
        ReflectionUtils.invoke(
            target,
            ReflectionUtils.getMethod(
                target, 
                "abort", 
                Executor.class
            ),
            executor
        );
    }

    public void setNetworkTimeout(
        Executor executor, 
        int milliseconds) 
        throws SQLException {
        ReflectionUtils.invoke(
            target,
            ReflectionUtils.getMethod(
                target, 
                "setNetworkTimeout", 
                Executor.class, 
                int.class
            ),
            executor, 
            milliseconds
        );
    }

    public int getNetworkTimeout() 
        throws SQLException {
        return (Integer) 
            ReflectionUtils.invoke(
                target,
                ReflectionUtils.getMethod(
                    target, 
                    "getNetworkTimeout"
                )
            );
    }
}

Как вы, возможно, уже заметили, некоторые методы используют отражение Java вместо прямого вызова метода:

Эти методы были добавлены в Java 1.7, и прямой вызов завершится ошибкой при компиляции проекта с Java 1.6. These methods have been added to Java 1.7 and a direct call will fail when compiling the project with Java 1.6. Because Java 1.6 is the minimum requirement for most FlexyPool modules, these methods forward the incoming method call through a Java reflection invocation. Omitting these methods is not optional either because, on a 1.7 JVM, the Connection decorator will not have these methods and a class loading error will be thrown. Поскольку Java 1.6 является минимальным требованием для большинства модулей Flexipool, эти методы перенаправляют входящий вызов метода через вызов отражения Java. These methods have been added to Java 1.7 and a direct call will fail when compiling the project with Java 1.6.

В проектах, использующих не менее Java 1.7, пул Flexi также предлагает Java 7 ConnectionDecorator :

public class Java7ConnectionDecorator 
    extends ConnectionDecorator {

    public Java7ConnectionDecorator(
        Connection target, 
        ConnectionCallback callback) {
        super(target, callback);
    }

    @Override
    public void setSchema(String schema) 
        throws SQLException {
        getTarget().setSchema(schema);
    }

    @Override
    public String getSchema() 
        throws SQLException {
        return getTarget().getSchema();
    }

    @Override
    public void abort(Executor executor) 
        throws SQLException {
        getTarget().abort(executor);
    }

    @Override
    public void setNetworkTimeout(
        Executor executor, 
        int milliseconds) 
        throws SQLException {
        getTarget().setNetworkTimeout(executor, milliseconds);
    }

    @Override
    public int getNetworkTimeout() 
        throws SQLException {
        return getTarget().getNetworkTimeout();
    }
}

Этот класс не является частью основной библиотеки, поскольку включен в отдельный модуль, совместимый с Java 1.7. Чтобы использовать его, вам необходимо добавить следующую зависимость Maven:


    com.vladmihalcea.flexy-pool
    flexy-pool-core-java7
    ${flexy-pool.version}

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

До выпуска 1.2.4 поставщиком подключения по умолчанию был Jdk ConnectionProxyFactory , который использует динамические прокси. Prior to release 1.2.4

Фактическая версия декоратора решается во время выполнения, а механизм загрузки состоит из следующих компонентов:

Фактическая фабрика декоратора соединений решается следующим способом:

public ConnectionDecoratorFactory resolve() {
    int loadingIndex = Integer.MIN_VALUE;
    ConnectionDecoratorFactory connectionDecoratorFactory = null;
    Iterator 
        connectionDecoratorFactoryServiceIterator = serviceLoader.iterator();
    while (connectionDecoratorFactoryServiceIterator.hasNext()) {
        try {
            ConnectionDecoratorFactoryService connectionDecoratorFactoryService = 
                connectionDecoratorFactoryServiceIterator.next();
            int currentLoadingIndex = connectionDecoratorFactoryService.loadingIndex();
            if (currentLoadingIndex > loadingIndex) {
                ConnectionDecoratorFactory currentConnectionDecoratorFactory = 
                    connectionDecoratorFactoryService.load();
                if (currentConnectionDecoratorFactory != null) {
                    connectionDecoratorFactory = currentConnectionDecoratorFactory;
                    loadingIndex = currentLoadingIndex;
                }
            }
        } catch (LinkageError e) {
            LOGGER.info("Couldn't load ConnectionDecoratorFactoryService on the current JVM", e);
        }
    }
    if (connectionDecoratorFactory != null) {
        return connectionDecoratorFactory;
    }
    throw new IllegalStateException("No ConnectionDecoratorFactory could be loaded!");
}

Так же, как и в MetricsFactory , у каждой фабрики декораторов соединений есть связанный Поставщик услуг . Несколько таких поставщиков услуг могут быть загружены во время выполнения (служба декоратора соединений Java 1.6 по умолчанию или Java 1.7). Выбор производится на основе индекса (приоритет имеет последняя версия Java) и текущей поддержки версии JVM JDBC (декоратор соединений Java 1.7 не будет разрешен в среде выполнения Java 1.6).

Декораторы несут больше накладных расходов на настройку, чем прокси-сервер, но если вы хотите выжать последнее снижение производительности стоит рассмотреть преимущество прямого вызова метода.