JAVA SFTP連接上傳下載、SSH連接執行命令,自定義自動部署重啓服務。

1.背景

  某公司開發、測試兩套環境,暫未有一整套從開發到測試的部署工具,開發人員需要手動刪除本地jar包內容,使用filezilla上傳到開發、測試環境,再重啓服務,由於使用springcloud,服務還得等未知時間後才能被發現並可用。

2.改造

  目的主要爲了方便開發使用,提升開發效率,使用技術爲jsch,爲了快速實現,未做界面(後續效果好,可能會應用到部署生產環境中,可以省力),主要根據網上內容重寫了部分工具類,並根據需要進行了部分整合,花了1天左右時間完成,方便後續開發者使用。

3.jsch

 JSch 是SSH2的一個純Java實現。它允許你連接到一個sshd 服務器,使用端口轉發,X11轉發,文件傳輸等等。你可以將它的功能集成到你自己的 程序中。

maven依賴:

		<dependency>
			<groupId>com.jcraft</groupId>
			<artifactId>jsch</artifactId>
			<version>0.1.54</version>
		</dependency>

4.主要工具類

4.1 jar包刪除指定文件(刪除本地配置文件等)

package com.ybjdw.tool.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;

/**
 * author: zhanggw
 * 創建時間:  2020/4/15
 */
public class JarUtil {
    private static Logger logger = LoggerFactory.getLogger(JarUtil.class);

    public static void main(String[] args) {
        String jarName = "E:\\idea_workspace\\product\\target\\product-0.0.1-SNAPSHOT.jar";
        List<String> deleteKeys = new ArrayList<>();
        deleteKeys.add("BOOT-INF/classes/application.properties");
        deleteKeys.add("BOOT-INF/classes/logback.xml");
        delete(jarName, deleteKeys, true);
    }

    public static void delete(String jarName, List<String> deletes, Boolean deleteBak) {
        JarOutputStream jos = null;
        InputStream inputStream = null;
        JarFile bakJarFile = null;
        File bakFile = null;
        try{
            //先備份
            File oriFile = new File(jarName);
            if (!oriFile.exists()) {
                logger.error("{}未找到!", jarName);
                return;
            }
            //將文件名命名成備份文件
            String bakJarName = jarName.substring(0, jarName.length() - 3) + DateUtils.getNowDateStr("yyyyMMddHHmmssSSS") + ".jar";
            bakFile = new File(bakJarName);
            boolean isOK = oriFile.renameTo(bakFile);
            if (!isOK) {
                logger.error("文件重命名失敗!");
                return;
            }

            // 創建新jar包(刪除指定內容)
            bakJarFile = new JarFile(bakJarName);
            jos = new JarOutputStream(new FileOutputStream(jarName));
            Enumeration<JarEntry> entries = bakJarFile.entries();
            while (entries.hasMoreElements()) {
                JarEntry entry = entries.nextElement();
                if (!deletes.contains(entry.getName())) {
                    inputStream = bakJarFile.getInputStream(entry);
                    jos.putNextEntry(entry);
                    byte[] bytes = readStream(inputStream);
                    jos.write(bytes, 0, bytes.length);
                }
                else {
                    logger.debug("Delete內容" + entry.getName());
                }
            }
        }catch (Exception e){
            logger.error(e.getMessage());
        }finally {
            if(jos != null){
                try{
                    jos.flush();
                    jos.finish();
                    jos.close();
                }catch (Exception e){
                    logger.debug(e.getMessage());
                }
            }

            if(inputStream != null){
                try{
                    inputStream.close();
                }catch (Exception e){
                    logger.debug(e.getMessage());
                }
            }

            if(bakJarFile != null){
                try{
                    bakJarFile.close();
                }catch (Exception e){
                    logger.debug(e.getMessage());
                }
            }

            if(deleteBak){
                Boolean delFlag = bakFile.delete();
                if(delFlag !=null && !delFlag){
                    logger.debug("刪除jar包中內容失敗!");
                }
            }
        }
    }

    private static byte[] readStream(InputStream inStream) throws Exception {
        ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len;
        while ((len = inStream.read(buffer)) != -1) {
            outSteam.write(buffer, 0, len);
        }
        outSteam.close();
        inStream.close();
        return outSteam.toByteArray();
    }
}

4.2 SFTP上傳下載(備份服務器文件和上傳更新等)

package com.ybjdw.tool.utils;

import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;

/**
 * author: zhanggw
 * 創建時間:  2020/4/15
 */
public class SftpUtil {

    private Logger logger = LoggerFactory.getLogger(SftpUtil.class);

    // host_name sftp
    private Map<String, ChannelSftp> sftpMap = new HashMap<>();
    private Map<String, Session> sessionMap = new HashMap<>();

    public static void main(String[] args) {
        String localPath = "E:/idea_workspace/product/target/product-0.0.1-SNAPSHOT.jar";
        SftpUtil sftpUtil = new SftpUtil();
        String host = "192.168.0.105";
        String username = "product";
        ChannelSftp sftp = sftpUtil.createSftp("192.168.0.105", 22, "product", "product");
        sftpUtil.uploadFile(sftp, "/home/product/app/product-0.0.1-SNAPSHOT.jar", localPath, true);
        sftpUtil.closeSFtp(host, username);
    }

    public ChannelSftp createSftp(String host, int port, String username, String password){
        ChannelSftp channel = null;
        try{
            String mapKey = host + "_" + username;
            JSch jsch = new JSch();
            Session session = jsch.getSession(username, host, port);

            session.setPassword(password);
            session.setConfig("StrictHostKeyChecking", "no");
            session.connect(5000);
            sessionMap.put(mapKey, session);

            channel = (ChannelSftp)session.openChannel("sftp");
            channel.connect(5000);
            sftpMap.put(mapKey, channel);
        }catch (Exception e){
            logger.debug("sftp獲取連接異常:"+e.getMessage());
        }
        return channel;
    }

    public void downloadFile(ChannelSftp sftp, String ftpPath, String localDir, boolean sameName) {
        OutputStream outputStream = null;
        try {
            String localPath = localDir + "/";
            if(sameName){
                localPath = localPath + ftpPath.substring(ftpPath.lastIndexOf("/") + 1);
            }else{
                localPath = localPath + DateUtils.getNowDateStr("yyyyMMddHHmmss") + "_" + ftpPath.substring(ftpPath.lastIndexOf("/") + 1);
            }
            File localFile = new File(localPath);
            outputStream = new FileOutputStream(localFile);
            sftp.get(ftpPath, outputStream);
        } catch (Exception e) {
            logger.debug(e.getMessage());
        }finally {
            if(outputStream != null){
                try {
                    outputStream.close();
                    logger.debug("{}下載完畢,本地路徑:{}", ftpPath, localDir);
                } catch (IOException e) {
                    logger.debug(e.getMessage());
                }
            }
        }
    }

    public void closeSFtp(String host, String username){
        if(StringUtils.isBlank(host) && StringUtils.isBlank(username)){
            return;
        }

        String mapKey = host + "_" + username;
        ChannelSftp channelSftp = sftpMap.get(mapKey);
        if(channelSftp != null && !channelSftp.isClosed()){
            channelSftp.disconnect();
        }

        Session session = sessionMap.get(mapKey);
        if(session != null && session.isConnected()){
            session.disconnect();
        }
    }

    public void uploadFile(ChannelSftp sftp, String targetPath, String localPath, boolean overwrite){
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream(localPath);
            if(overwrite){
              sftp.put(inputStream, targetPath, ChannelSftp.OVERWRITE);
            }else{
                sftp.put(inputStream, targetPath);
            }
        } catch (Exception e) {
            logger.debug("sftp上傳文件異常:{}", e.getMessage());
        }finally {
            if(inputStream != null){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    logger.debug(e.getMessage());
                }
            }
            logger.debug("{}已上傳到{}",localPath,targetPath);
        }
    }
}

4.3 SSH連接及執行命令(執行腳本、重啓服務等)

package com.ybjdw.tool.utils;

import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.InputStream;

/**
 * author: zhanggw
 * 創建時間:  2020/4/16
 */
public class SSHUtil {
    private static Logger logger = LoggerFactory.getLogger(SSHUtil.class);

    public static void main(String[] args) {
        SSHUtil sshUtil = new SSHUtil();
        sshUtil.execCommand("dev", "pwd");
    }

    public String execCommand(String env, String cmd){
        String host = "192.168.0.105";
        int port = 22;
        String username = "root";
        String password = "123456";

        if("test".equals(env)){
            host = "120.xx.xx.xx";
            password = "xxxxxx";
        }

        String ret = execCommand(host, port, username, password, cmd);
        logger.debug("命令執行結果:{}", ret);
        return ret;
    }

    public String execCommand(String host, int port, String user, String password, String command){
        String out = "";
        try{
            JSch jsch = new JSch();
            Session session = jsch.getSession(user, host, port);
            session.setConfig("StrictHostKeyChecking", "no");
            session.setPassword(password);
            session.connect();

            ChannelExec channelExec = (ChannelExec) session.openChannel("exec");
            InputStream in = channelExec.getInputStream();
            channelExec.setCommand(command);
            channelExec.setErrStream(System.err);
            channelExec.connect();
            out = IOUtils.toString(in, "UTF-8");

            channelExec.disconnect();
            session.disconnect();
        }catch (Exception e){
            logger.debug(e.getMessage());
        }
        return out;
    }

}

最後的整合

package com.ybjdw.tool.utils;

import com.alibaba.fastjson.JSONObject;
import com.jcraft.jsch.ChannelSftp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;

/**
 * author: zhanggw
 * 創建時間:  2020/4/15
 */
public class AutoDeployUtil {
    private static Logger logger = LoggerFactory.getLogger(AutoDeployUtil.class);

    public static void main(String[] args) throws Exception{
        new AutoDeployUtil().testServiceUpdateAndReload("product");
    }

    public void testServiceUpdateAndReload(String serviceName){
        try{
            long startTime = System.currentTimeMillis();

            // 備份上傳
            String localWorkSpace = "E:/idea_workspace";
            String localBackupDir = "d:/tmp/serviceJar";
            AutoDeployUtil autoDeployUtil = new AutoDeployUtil();
            logger.debug("開始修改jar包內容!");
            autoDeployUtil.perfectLocalJar(localWorkSpace, serviceName);
            logger.debug("開始備份並上傳新jar包!");
            autoDeployUtil.backupAndUpload(localBackupDir,"dev",localWorkSpace,serviceName);

            SSHUtil sshUtil = new SSHUtil();
            // 開發環境重啓
            logger.debug("開始重啓開發環境!");
            String restartService = "sh restart_app.sh " + serviceName;
            sshUtil.execCommand("dev", restartService);

            // 傳輸到測試環境
            logger.debug("開始把新jar包傳送到測試環境!");
            String cmd = "sh sftp_test.sh "+serviceName;
            sshUtil.execCommand("dev", cmd);

            // 測試環境重啓
            logger.debug("開始重啓測試環境服務!");
            sshUtil.execCommand("test", restartService);
            logger.debug("開發測試環境服務已更新重啓,花費時間:{}秒", (System.currentTimeMillis()-startTime)/1000);

            JSONObject paramJson = new JSONObject();
            paramJson.put("userId", "1234");
            int sucTime = 0;
            for(int i=0; i < 10000000; i++){
                JSONObject retJson = httpTest("test", serviceName, "/testUri", paramJson,false);
                if(retJson.getBoolean("flag") != null){
                    sucTime++;
                }
                if(sucTime >= 60){
                    break;
                }
                Thread.sleep(100);
            }
            logger.debug("開發測試環境服務已可以使用,總花費時間:{}秒", (System.currentTimeMillis()-startTime)/1000);
        }catch (Exception e){
            logger.debug(e.getMessage());
        }
    }

    private static JSONObject httpTest(String env,String serviceName, String uri, JSONObject paramJson, boolean logFlag) {
        String url = HttpRequestUtils.getUrl(env,serviceName,uri);
        JSONObject data = new JSONObject();
        JSONObject reqParam = new JSONObject();
        data.putAll(paramJson);
        reqParam.put("data", data);

        HttpRequestUtils httpRequestUtils = new HttpRequestUtils();
        JSONObject response = httpRequestUtils.httpPost(url, reqParam, null);
        if(logFlag){
            logger.debug("響應結果:{}", response);
        }
        return response;
    }

    public void perfectLocalJar(String localWorkspace, String serviceName){
        String localJarPath = localWorkspace + "/" + serviceName + "/target/" + serviceName +"-0.0.1-SNAPSHOT.jar";
        List<String> deleteKeys = new ArrayList<>();
        deleteKeys.add("BOOT-INF/classes/application.properties");
        deleteKeys.add("BOOT-INF/classes/logback.xml");
        JarUtil.delete(localJarPath,deleteKeys,true);
        logger.debug("{}服務jar包已完善!", serviceName);
    }

    public void backupAndUpload(String localBackupDir, String env, String localWorkspace, String serviceName){
        logger.debug("{}環境{}服務開始備份並上傳!", env, serviceName);

        // 連接信息
        String host = "192.168.0.105";
        int port = 22;
        String username = "";
        String password = "";
        if("dev".equals(env)){
            username = serviceName;
            password = serviceName;
        }
        if("test".equals(env)){
            host = "120.xx.xx.xx";
            username = "root";
            password = "xxxxxx";
        }

        // 下載備份
        String ftpPath = "/home/"+serviceName+"/app/"+serviceName+"-0.0.1-SNAPSHOT.jar";
        String localPath = localBackupDir + "/" + env;
        SftpUtil sftpUtil = new SftpUtil();
        ChannelSftp sftp = sftpUtil.createSftp(host, port, username, password);
        sftpUtil.downloadFile(sftp, ftpPath, localPath, false);

        // 上傳jar
        String targetPath = "/home/"+serviceName+"/app/"+serviceName+"-0.0.1-SNAPSHOT.jar";
        String localJarPath = localWorkspace + "/" + serviceName + "/target/" + serviceName +"-0.0.1-SNAPSHOT.jar";
        sftpUtil.uploadFile(sftp, targetPath, localJarPath, true);

        // 關閉連接
        sftpUtil.closeSFtp(host, username);
    }

}

最後的效果:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章