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);
}
}
最後的效果: