使用guava的cache实现ssh connection的缓存

1、业务需求

java后台需要根据前端传入的IP地址和密码使用ssh连接到linux虚拟机。为了简化系统,假设连接使用的用户名固定,这样只需要提供IP和密码就可以连接到linux机器。
所以目前这个系统需要缓存大量的ssh connection,不然每次执行命令都需要从新连接linux将会很慢。

2、解决方案

2.1 不使用缓存

每次都是新建connection。这样是真的慢,也很没有必要。

2.2 目前的方案

每一个前端请求缓存一个connection。我想了好久,找了好久也找不到一个方案,当然自己也写不出来。只好用这个方案了。每次如果要执行ssh命令,就先到request域中找,找到直接返回,找不到就新建一个connection并放入request中。这样同一个请求可以达到复用connecton,在退出后还可以使用监听器关闭connection。
弊端:复用性不高,每次request销毁后悔判断一下,当前request域中是否有connection。

2.3 新的方法

学习guava无意中想到了这里的cache正好可以用来存储connection。
他满足:

  1. 缓存connection
  2. 移除时的监听器
  3. 缓存淘汰策略满足
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>sshcache</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>sshcache</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>

        <dependency>
            <groupId>ch.ethz.ganymed</groupId>
            <artifactId>ganymed-ssh2</artifactId>
            <version>build210</version>
        </dependency>

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>23.3-jre</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

package com.example.sshcache.util;

import ch.ethz.ssh2.Connection;
import com.google.common.base.Joiner;
import com.google.common.cache.*;

import java.io.IOException;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

public class SshConnectionUtil {

    private static LoadingCache<String,Connection> sshLoadingCache = null;
    static {
        sshLoadingCache = CacheBuilder.newBuilder()
                .expireAfterAccess(5,TimeUnit.MINUTES)
                .maximumSize(1000)
                .removalListener(new RemovalListener<String, Connection>(){
                    @Override
                    public void onRemoval(RemovalNotification<String, Connection> removalNotification) {
                        if(!Objects.isNull(removalNotification.getValue())){
                            removalNotification.getValue().close();
                            removalNotification.setValue(null);
                        }
                        String key = removalNotification.getKey();
                        System.out.println("ip:" + key.substring(0,key.indexOf(',')) + " removed");

                    }
                } )
                .build(new CacheLoader<String, Connection>() {
                    @Override
                    public Connection load(String key) {
                        //从SQL或者NoSql 获取对象
                        //Iterable<String> split = Splitter.on(',').split(key);
                        String[] split = key.split(",");
                        String username = split.length == 3 ? split[2] : "root";
                        String password = split[1];
                        String ip = split[0];


                        Connection connection = new Connection(ip, 22);
                        boolean b = false;
                        try {
                            connection.addConnectionMonitor(v -> {
                                System.out.println(v);
                            });
                            b = connection.authenticateWithPassword(username, password);
                            if(b) {
                                return connection;
                            }else {
                                return null;
                            }
                        }catch (IOException e){
                            e.printStackTrace();
                        }catch (Exception e){
                            e.printStackTrace();
                        }finally {
                            if(!b){
                                connection.close();
                            }
                        }
                        return null;
                    }
        });
    }



    public static Connection getConnection(String ip,String password,String username){
        String join = Joiner.on(',').skipNulls().join(ip, password, username);
        try {
            Connection connection = sshLoadingCache.get(join);
            return connection;
        }catch (ExecutionException e){
            e.printStackTrace();
        }
        return null;
    }


    public static void main(String[] args) {
        Connection root = SshConnectionUtil.getConnection("192.168.11.110", "123456", "root");
        System.out.println(root);
    }

}

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐