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