Статьи

Пул ssh-соединений с использованием Apache KeyedObjectPool

Я нашел org.apache.commons.pool чрезвычайно полезным и надежным, но недостаточно хорошо задокументированным. Итак, я постараюсь немного помочь, объясняя, как использовать Apache KeyedObjectPool . Что такое KeyedObjectPool ? Это карта, которая содержит пул экземпляров нескольких типов. К каждому типу можно получить доступ с помощью произвольного ключа. В этом примере я создам пул ssh-соединений JSch и в качестве ключа буду использовать простой объект-установщик геттера, называемый ServerDetails. В основном для каждого сервера я хочу иметь пул из 10 повторно используемых соединений SSH. Поэтому первое, что нужно сделать, — это создать Sessionfactory, класс, отвечающий за создание фактического объекта, который вы хотите сохранить в пуле. В нашем примере это было бы соединение ssh.

Sessionfactory необходимо расширить BaseKeyedPoolableObjectFactory <K, V>, где K — это тип ключей в этом пуле, а V — это тип объектов, хранящихся в этом пуле. All you need to do is implement the метод makeObject, в котором вам нужно фактически создать объект в пуле, и destroyObject, где, очевидно, вам нужно реализовать код, когда объект освобождается и помещается обратно в пул.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package org.grep4j.core.command.linux;
import org.apache.commons.pool.BaseKeyedPoolableObjectFactory;
import org.grep4j.core.model.ServerDetails;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.UserInfo;
/**
 * This class is used to handle ssh Session inside the pool.
 *
 * @author Marco Castigliego
 *
 */
public class SessionFactory extends BaseKeyedPoolableObjectFactory<ServerDetails, Session> {
 
        /**
         * This creates a Session if not already present in the pool.
         */
        @Override
        public Session makeObject(ServerDetails serverDetails) throws Exception {
                Session session = null;
                try {
                        JSch jsch = new JSch();
                        session = jsch.getSession(serverDetails.getUser(), serverDetails.getHost(), serverDetails.getPort());
                        session.setConfig('StrictHostKeyChecking', 'no'); //
                        UserInfo userInfo = new JschUserInfo(serverDetails.getUser(), serverDetails.getPassword());
                        session.setUserInfo(userInfo);
                        session.setTimeout(60000);
                        session.setPassword(serverDetails.getPassword());
                        session.connect();
                } catch (Exception e) {
                        throw new RuntimeException(
                                        'ERROR: Unrecoverable error when trying to connect to serverDetails :  ' + serverDetails, e);
                }
                return session;
        }
 
        /**
         * This is called when closing the pool object
         */
        @Override
        public void destroyObject(ServerDetails serverDetails, Session session) {
                session.disconnect();
        }
}

Второе, что вам нужно сделать, это создать реальный объект пула с ключами. В нашем примере мы создаем синглтон, который содержит StackKeyedObjectPool. Число 10 ограничивает количество «спящих» экземпляров в пуле. Если 11 клиентов попытаются получить ssh-соединение для одного и того же сервера, 11-й будет ждать, пока один из первых 10 не освободит его соединение.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package org.grep4j.core.command.linux;
import org.apache.commons.pool.KeyedObjectPool;
import org.apache.commons.pool.impl.StackKeyedObjectPool;
import org.grep4j.core.model.ServerDetails;
import com.jcraft.jsch.Session;
/**
 * Pool controller. This class exposes the org.apache.commons.pool.KeyedObjectPool class.
 *
 * @author Marco Castigliego
 *
 */
public class StackSessionPool {
 
        private KeyedObjectPool<ServerDetails, Session> pool;
 
        private static class SingletonHolder {
                public static final StackSessionPool INSTANCE = new StackSessionPool();
        }
 
        public static StackSessionPool getInstance() {
                return SingletonHolder.INSTANCE;
        }
 
        private StackSessionPool()
        {
                startPool();
        }
 
        /**
         *
         * @return the org.apache.commons.pool.KeyedObjectPool class
         */
        public KeyedObjectPool<ServerDetails, Session> getPool() {
                return pool;
        }
 
        /**
         *
         * @return the org.apache.commons.pool.KeyedObjectPool class
         */
        public void startPool() {
                pool = new StackKeyedObjectPool<ServerDetails, Session>(new SessionFactory(), 10);
        }
}

Как это использовать, это просто и понятно. Чтобы получить ssh-соединение из пула, нам просто нужно позвонить:

1
StackSessionPool.getInstance().getPool().borrowObject(serverDetails)

где serverDetails — наш ключ (нам нужен пул ssh-соединений на сервер).

Когда соединение больше не нужно, мы помещаем его обратно в пул с помощью:

1
StackSessionPool.getInstance().getPool().returnObject(serverDetails, session);
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package org.grep4j.core.command.linux;
 
import org.grep4j.core.command.ExecutableCommand;
import org.grep4j.core.model.ServerDetails;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.Session;
/**
 * The SshCommandExecutor uses the net.schmizz.sshj library to execute remote
 * commands.
 *
 * <ol>
 * <li>Establish a connection using the credential in the {@link serverDetails}</li>
 * <li>Opens a session channel</li>
 * <li>Execute a command on the session</li>
 * <li>Closes the session</li>
 * <li>Disconnects</li>
 * </ol>
 *
 * @author Marco Castigliego
 *
 */
public class JschCommandExecutor extends CommandExecutor {
 
        public JschCommandExecutor(ServerDetails serverDetails) {
                super(serverDetails);
        }
 
        @Override
        public CommandExecutor execute(ExecutableCommand command) {
                Session session = null;
                Channel channel = null;
                try {
 
                        session = StackSessionPool.getInstance().getPool()
                                        .borrowObject(serverDetails);
                        //...do stuff
                } catch (Exception e) {
                        throw new RuntimeException(
                                        'ERROR: Unrecoverable error when performing remote command '
                                                        + e.getMessage(), e);
                } finally {
                        if (null != channel && channel.isConnected()) {
                                channel.disconnect();
                        }
                        if (null != session) {
                                try {
                                        StackSessionPool.getInstance().getPool()
                                                        .returnObject(serverDetails, session);
                                } catch (Exception e) {
                                        e.printStackTrace();
                                }
                        }
                }
 
                return this;
        }
}

Не забудьте закрыть пул, когда он вам больше не нужен, с помощью StackSessionPool.getInstance (). GetPool (). Close ();

Ссылка: пул ssh-соединений с использованием Apache KeyedObjectPool от нашего партнера по JCG Марко Кастильего из блога « Удалить дублирование и исправить дурные имена» .