1.maven環境jar包引入
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.11</artifactId>
<version>2.3.4</version>
</dependency>
<!-- java ssh -->
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.54</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.4</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.10</version>
</dependency>
2.使用CGLIB動態代理
Session類的原因,需要實現一個子類,且需要和session類在同一個包下面
package com.jcraft.jsch;
/**
* 解決Superclass has no null constructors but no arguments were given錯誤
*
* @author duhai
* @date 2019年12月12日
*/
public class SshSession extends Session {
/**
* 這樣創建無妨,最後使用的是session
* @throws JSchException
*/
public SshSession() throws JSchException {
super(new JSch(),"username", "host", 22);
}
public SshSession(final JSch jsch, final String username, final String host, final int port)
throws JSchException {
super(jsch, username, host, port);
}
}
3.使用代理攔截器歸還Session 資源
package com.spark.demo.sub.sshsession;
import java.lang.reflect.Method;
import com.jcraft.jsch.Session;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* CGLib代理的方法攔截器:<br/>
* method執行方法的爲目標對象: method.invoke(target, args);
*
* @author duhai
* @date 2019年12月12日
*/
public class SshSessionProxyFactoryBean implements MethodInterceptor {
// 維護目標對象
private Session target;
// 代理對象
private Session proxyObj;
// 資源池對象,規範資源用
private SshSessionFactoryBean sessionFactoryBean;
public SshSessionProxyFactoryBean(final Session target, final SshSessionFactoryBean sessionFactoryBean) {
this.target = target;
this.sessionFactoryBean = sessionFactoryBean;
}
/**
* 創建代理對象
*
* @param target
* @return
*/
public Object getInstance() {
// 1.工具類
Enhancer en = new Enhancer();
// 2.設置父類
en.setSuperclass(target.getClass());
// 3.設置回調函數
en.setCallback(this);
// 4.創建子類(代理對象)
proxyObj = (Session) en.create();
return proxyObj;
}
/**
* 攔截session的相關方法,disconnect的時候不斷開連接,歸還到seesion資源池
*
* @param obj
* @param method
* @param args
* @param proxy
* @return
* @throws Throwable
* @see net.sf.cglib.proxy.MethodInterceptor#intercept(java.lang.Object,
* java.lang.reflect.Method, java.lang.Object[],
* net.sf.cglib.proxy.MethodProxy)
*/
public Object intercept(final Object obj, final Method method, final Object[] args, final MethodProxy proxy)
throws Throwable {
if ("disconnect".equals(method.getName())) {
// 歸還Session 資源
sessionFactoryBean.back(proxyObj);
return new Object();
} else {
// 執行目標對象的方法
Object returnValue = method.invoke(target, args);
return returnValue;
}
}
}
4.ssh的session很吃資源,所以不能太多,需要資源池
package com.spark.demo.sub.sshsession;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SshSession;
/**
* SshSession 工場類
* @author duhai
* @date 2019年12月12日
*/
public class SshSessionFactoryBean {
public static final Logger logger = LoggerFactory.getLogger(SshSessionFactoryBean.class);
private static final String USER = "root";
private static final String PASSWORD = "*******";
private static final String HOST = "10.168.11.11";
private static final int DEFAULT_SSH_PORT = 22;
private int SESSION_PHY_NUM = 2;
private int SESSION_POOL_SIZE = 8;
// Sesssion 池
private Stack<Session> sesseionProxyPool = new Stack<Session>();
/**
* 類級的內部類,也就是靜態的成員式內部類,該內部類的實例與外部類的實例 沒有綁定關係,而且只有被調用到纔會裝載,從而實現了延遲加載
*/
private static class SshSessionFactoryBeanHolder {
/**
* 靜態初始化器,由JVM來保證線程安全
*/
private static SshSessionFactoryBean instance = new SshSessionFactoryBean();
}
/**
* 私有化構造方法
*/
private SshSessionFactoryBean() {
// 初始化Session池
intPool();
}
/**
* 獲取實體類
*
* @return
*/
public static SshSessionFactoryBean getInstance() {
return SshSessionFactoryBeanHolder.instance;
}
/**
* 取出session資源,歸還在intercept中
*
* @return
* @throws Exception
*/
public Session getObject() throws Exception {
return pop();
}
/**
* 初始化Session池,異步方式
*/
private void intPool() {
new Thread() {
@Override
public void run() {
logger.info("SshSession池 開始初始化!");
List<Session> list = new ArrayList<Session>();
for (int i = 0; i < SESSION_PHY_NUM; i++) {
try {
Session session = createSession();
list.add(session);
} catch (JSchException e) {
logger.error(e.getMessage());
e.printStackTrace();
}
}
for (int i = 0; i < SESSION_POOL_SIZE; i++) {
Session session = list.get(i % SESSION_PHY_NUM);
createProxySession(session);
}
logger.info("SshSession池 初始化成功!");
// 開啓一個心跳檢查,每10秒心跳一次
while (true) {
try {
logger.info("心跳檢查線程休眠10S");
Thread.sleep(10 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.info("開始執行Session連接心跳.....");
for (Session session : list) {
if (!session.isConnected()) {
logger.warn("一個Session連接失效,嘗試恢復連接");
try {
session.connect();
logger.info("Session嘗試恢復連接成功");
} catch (JSchException e) {
logger.error("Session嘗試恢復連接失敗!!!,建議重啓服務");
}
}
try {
logger.info("發送Session心跳");
session.sendKeepAliveMsg();
} catch (Exception e) {
logger.error("Session心跳錯誤,需要重連");
e.printStackTrace();
}
}
}
}
}.start();
}
/**
* 創建代理Session
*
* @param session
* @return
*/
private Session createProxySession(final Session session) {
Session proxySession = (Session) new SshSessionProxyFactoryBean(session, this).getInstance();
sesseionProxyPool.push(proxySession);
return proxySession;
}
/**
* 創建session
*
* @return
* @throws JSchException
*/
private Session createSession() throws JSchException {
JSch jsch = new JSch();
// Session session = jsch.getSession(USER, HOST, DEFAULT_SSH_PORT);
Session session = new SshSession(jsch, USER, HOST, DEFAULT_SSH_PORT);
session.setPassword(PASSWORD);
session.setConfig("StrictHostKeyChecking", "no");
try {
session.connect();
} catch (Exception e) {
String msg = "連接服務器失敗!";
logger.error(msg, e);
throw new RuntimeException(msg);
}
return session;
}
/**
* 對session stack的操作
*/
private Object lockObj = new Object();
/**
* 取出session資源
*
* @return
*/
public synchronized Session pop() {
if (sesseionProxyPool.isEmpty()) {
logger.warn("沒有Session,使線程休眠");
synchronized (lockObj) {
try {
lockObj.wait();
} catch (InterruptedException e) {
}
}
logger.warn("pop 被喚醒");
}
Session session = sesseionProxyPool.pop();
return session;
}
/**
* 歸還Session 資源
*
* @param session
* @param proxySession
*/
public void back(final Session proxySession) {
sesseionProxyPool.push(proxySession);
synchronized (lockObj) {
lockObj.notify();
}
}
public int getSesseionProxyPoolSize() {
return sesseionProxyPool.size();
}
}
5.測試
package com.spark.demo.sub.sshsession;
import java.io.IOException;
import java.io.InputStream;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
public class SshSessionTest {
public static void main(final String[] args) throws JSchException, IOException, InterruptedException {
SshSessionFactoryBean sshSessionFactoryBean = SshSessionFactoryBean.getInstance();
String command = "/usr/jdk1.8.0_121/bin/java -version";
// 啓動有個時間,間隔一下再跑命令
Thread.sleep(5000);
Session session = sshSessionFactoryBean.pop();
System.out.println("getSesseionProxyPoolSize:" + sshSessionFactoryBean.getSesseionProxyPoolSize());
Channel channel = session.openChannel("exec");
((ChannelExec) channel).setCommand(command);
channel.setInputStream(null);
((ChannelExec) channel).setErrStream(System.err);
InputStream in = channel.getInputStream();
channel.connect();
byte[] tmp = new byte[1024];
while (true) {
while (in.available() > 0) {
int i = in.read(tmp, 0, 1024);
if (i < 0) {
break;
}
System.out.print(new String(tmp, 0, i));
}
if (channel.isClosed()) {
if (in.available() > 0) {
continue;
}
System.out.println("exit-status: " + channel.getExitStatus());
break;
}
try {
Thread.sleep(1000);
} catch (Exception ee) {
}
}
channel.disconnect();
session.disconnect();
System.out.println("getSesseionProxyPoolSize:" + sshSessionFactoryBean.getSesseionProxyPoolSize());
System.out.println("exit....................");
}
}