前言
關(guān)于自動化部署java項目的方案有很多,就比如說比較知名的使用 Jenkins 實現(xiàn)自動化部署,還有比如說使用 IDEA 中的插件 Alibaba Cloud Toolkit 實現(xiàn)自動化部署;其他的方式我也沒太去了解,我現(xiàn)在要做的是使用java自定義部署項目
使用第三方的服務(wù)或插件實現(xiàn)部署所存在的問題
關(guān)于 Jenkins 我學(xué)習(xí)也使用了一會, Alibaba Cloud Toolkit 也試著用了一會,個人感覺用著有以下幾點問題
1.Jenkins 安裝及配置方面有點繁瑣(對于小型項目來說沒必要,對于大型的分布式項目來說會比較有用)
2.Jenkins 需要安裝到服務(wù)器上,使用的時候會占用大量的服務(wù)器資源 (除非你專門給Jenkins配一個服務(wù)器或者裝自己電腦上)
3.上手需要一定的時間,不同人熟悉需要的時間不同
4.因為這些東西并不是自己做的,沒法完全按照自己的想法進行部署,遇到一些疑問解決起來比較麻煩耗時間
正因如此我就想能不能按照自己的項目部署方式來定制一套自動化部署的功能
自動化部署java項目
java項目部署方式
我的項目使用的是 spring boot,打包后也就只有2個jar包需要部署到服務(wù)器, 業(yè)務(wù)量決定了是否需要分布式。
流程
1. maven 項目執(zhí)行 clean 再 package 或者點擊 IDEA 的 clean 再 package
2. 在 target 目錄下找到需要部署的jar包上傳至服務(wù)器 (同時備份這次上傳的jar包)
3. 啟動項目
代碼實現(xiàn)
我直接將執(zhí)行代碼放在項目的 test 中執(zhí)行
打包
mvn package 執(zhí)行的時候默認會去調(diào)用 test 中的測試方法,為避免影響打包結(jié)果我選擇了修改pom,當然了,你也可以選擇修改打包命令
解決方法參考:https://blog.csdn.net/hjseo_seg/article/details/123657734
pom 中加入 或 修改以下代碼
<plugin>
<groupId>org.apache.maven.plugin</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.5</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
或者修改打包命令為
mvn package -Dmaven.test.skip=true
使用 java 執(zhí)行 cmd 進行打包
java執(zhí)行cmd參考:https://blog.csdn.net/weixin_43916074/article/details/123118256
執(zhí)行 cmd 命令時內(nèi)容前需要加 cmd /c
例如原 cmd 命令:mvn package
修改后 cmd 命令:cmd /c mvn package
/**
* 執(zhí)行cmd命令
* @param cmd cmd命令
*/
public static void cmd(String cmd) {
Runtime run = Runtime.getRuntime();
try {
Process p = run.exec(cmd);
InputStream ins = p.getInputStream();
new Thread(() -> {
String line = null;
byte[] b = new byte[1024];
int num = 0;
try {
while ((num = ins.read(b)) != -1) {
System.out.print(byteToStr(b));
}
} catch (Exception e) {
e.printStackTrace();
}
}).start();
p.waitFor();
} catch (Exception e) {
e.printStackTrace();
}
}
public static String byteToStr(byte[] buffer) {
try {
int length = 0;
for (int i = 0; i < buffer.length; ++i) {
if (buffer[i] == 0) {
length = i;
break;
}
}
return new String(buffer, 0, length, "gb2312");
} catch (Exception e) {
return "";
}
}
上傳jar包到服務(wù)器指定路徑
java實現(xiàn)上傳文件到服務(wù)器參考自:http://t.zoukankan.com/kuangzhisen-p-8662816.html
先在pom中加入相關(guān)依賴
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.51</version>
</dependency>
java實現(xiàn)使用ssh上傳文件到Linux服務(wù)器
這里我就直接以用戶名密碼的方式進行連接,如果要通過其他方式登錄那就需要自己再稍微改一下了
/**
* 上傳文件到服務(wù)器
* @param host 主機地址
* @param port ssh端口
* @param username 用戶名
* @param password 密碼
* @param localFile 本地文件
* @param serverPath 上傳到服務(wù)器的路徑
* @throws Exception
*/
public static void sshSftp(String host, int port, String username, String password, String localFile, String serverPath)
throws Exception {
Session session = null;
ChannelSftp channel = null;
JSch jsch = new JSch();
if (port <= 0) {
//連接服務(wù)器,采用默認端口
session = jsch.getSession(username, host);
} else {
//采用指定的端口連接服務(wù)器
session = jsch.getSession(username, host, port);
}
//如果服務(wù)器連接不上,則拋出異常
if (session == null) {
throw new Exception("session is null");
}
//設(shè)置第一次登陸的時候提示,可選值:(ask | yes | no)
session.setConfig("StrictHostKeyChecking", "no");
// 如果出現(xiàn)了 com.jcraft.jsch.JSchException: Algorithm negotiation fail ,就加入下面這條
// session.setConfig("kex", "diffie-hellman-group1-sha1,diffie-hellman-group14-sha1,diffie-hellman-group-exchange-sha1,diffie-hellman-group-exchange-sha256");
//設(shè)置登陸主機的密碼
session.setPassword(password);//設(shè)置密碼
//設(shè)置登陸超時時間
session.connect();
try {
//創(chuàng)建sftp通信通道
channel = (ChannelSftp) session.openChannel("sftp");
channel.connect(1000);
ChannelSftp sftp = (ChannelSftp) channel;
//進入服務(wù)器指定的文件夾
sftp.cd(serverPath);
//列出服務(wù)器指定的文件列表
for (Object o : sftp.ls("*")) {
System.out.println(o);
}
//以下代碼實現(xiàn)從本地上傳一個文件到服務(wù)器,如果要實現(xiàn)下載,對換以下流就可以了
File file = new File(localFile);
OutputStream outstream = sftp.put(file.getName());
InputStream instream = new FileInputStream(file);
byte[] b = new byte[1024];
int n;
while ((n = instream.read(b)) != -1) {
outstream.write(b, 0, n);
}
outstream.flush();
outstream.close();
instream.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
session.disconnect();
if (channel != null) {
channel.disconnect();
}
}
}
遠程執(zhí)行Linux命令啟動項目
java實現(xiàn)遠程執(zhí)行Linux參考自:https://blog.csdn.net/qq_16504067/article/details/103471898
這里我就直接以用戶名密碼的方式進行連接,如果要通過其他方式登錄那就需要自己再稍微改一下了
默認執(zhí)行單條命令,如要執(zhí)行多條命令可在每條命令結(jié)尾加 \n 表示換行執(zhí)行下條命令,
例如:“pwd \n” + “cd /app/server \n” + “pwd”
/**
* 遠程執(zhí)行Linux命令
* @param host 主機地址
* @param port 端口
* @param username 用戶名
* @param password 密碼
* @param sh 執(zhí)行內(nèi)容
*/
public static void shell(String host, int port, String username, String password, String sh) {
JSch jSch = new JSch();
Session session = null;
ChannelExec channelExec = null;
BufferedReader inputStreamReader = null;
BufferedReader errInputStreamReader = null;
try {
// 1. 獲取 ssh session
session = jSch.getSession(username, host, port);
session.setPassword(password);
session.setTimeout(3000);
session.setConfig("StrictHostKeyChecking", "no");
// 如果出現(xiàn)了 com.jcraft.jsch.JSchException: Algorithm negotiation fail ,就加入下面這條
//session.setConfig("kex", "diffie-hellman-group1-sha1,diffie-hellman-group14-sha1,diffie-hellman-group-exchange-sha1,diffie-hellman-group-exchange-sha256");
session.connect(); // 獲取到 ssh session
// 2. 通過 exec 方式執(zhí)行 shell 命令
channelExec = (ChannelExec) session.openChannel("exec");
channelExec.setCommand(sh);
channelExec.connect(); // 執(zhí)行命令
// 3. 獲取標準輸入流
inputStreamReader = new BufferedReader(new InputStreamReader(channelExec.getInputStream()));
// 4. 獲取標準錯誤輸入流
errInputStreamReader = new BufferedReader(new InputStreamReader(channelExec.getErrStream()));
// 5. 記錄命令執(zhí)行 log
String line = null;
while ((line = inputStreamReader.readLine()) != null) {
System.out.println(line);
}
// 6. 記錄命令執(zhí)行錯誤 log
String errLine = null;
while ((errLine = errInputStreamReader.readLine()) != null) {
System.err.println(errLine);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (inputStreamReader != null) {
inputStreamReader.close();
}
if (errInputStreamReader != null) {
errInputStreamReader.close();
}
if (channelExec != null) {
channelExec.disconnect();
}
if (session != null) {
session.disconnect();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
main方法與完整代碼
首先執(zhí)行 cmd 命令 mvn clean 與 mvn package,再設(shè)置本地的jar包路徑上傳jar包,執(zhí)行相關(guān)命令,例如我每次部署都習(xí)慣先備份一遍今天的jar包,最后執(zhí)行事先準備好的shell腳本啟動項目文章來源:http://www.zghlxwxcb.cn/news/detail-410908.html
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @Author: asus
* @Date: 1919/8/10 11:45:14
*/
public class ReleasePackage {
public static void main(String[] args) {
long start = System.currentTimeMillis();
// 打包
long s = System.currentTimeMillis();
System.out.println("==================== 清理 ====================");
cmd("cmd /c mvn clean");
System.out.println("清理耗時:" + (System.currentTimeMillis() - s) + "ms");
s = System.currentTimeMillis();
System.out.println("==================== 打包 ====================");
cmd("cmd /c mvn package");
System.out.println("打包耗時:" + (System.currentTimeMillis() - s) + "ms");
// 服務(wù)器地址
String host = "11.4.5.14";
// ssh端口默認22
int port = 24;
// 用戶名
String username = "1919";
// 密碼
String password = "1145141919810";
// 上傳文件到服務(wù)器
try {
s = System.currentTimeMillis();
String localFile = "C:\\ho\\admin\\target\\admin.jar";
String serverPath = "/app/ho";
System.out.println("上傳 " + localFile + " 到 " + serverPath);
sshSftp(host, port, username, password, localFile, serverPath);
System.out.println(localFile + "=== 上傳完成 ===");
System.out.println("上傳文件1耗時:" + (System.currentTimeMillis() - s) + "ms");
s = System.currentTimeMillis();
localFile = "C:\\ho\\user\\target\\user.jar";
System.out.println("上傳 " + localFile + " 到 " + serverPath);
sshSftp(host, port, username, password, localFile, serverPath);
System.out.println(localFile + "=== 上傳完成 ===");
System.out.println("上傳文件2耗時:" + (System.currentTimeMillis() - s) + "ms");
// 備份jar包并啟動項目
s = System.currentTimeMillis();
System.out.println("===== 開始啟動 =====");
String date = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
String sh = "cd /app/ho \n" +
"mkdir /app/ho/backup/" + date + "\n" +
"\\cp admin.jar backup/" + date + "/\n" +
"\\cp user.jar backup/" + date + "/\n" +
"/app/ho/run.sh \n";
shell(host, port, username, password, sh);
System.out.println("===== 操作完成 =====");
System.out.println("命令執(zhí)行耗時:" + (System.currentTimeMillis() - s) + "ms");
} catch (Exception e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("操作總耗時:" + ((end - start) / 1000) + "s" + ((end - start) % 1000) + "ms");
}
/**
* 執(zhí)行cmd命令
* 參考自 https://blog.csdn.net/weixin_43916074/article/details/123118256
*
* @param cmd cmd命令
*/
public static void cmd(String cmd) {
Runtime run = Runtime.getRuntime();
try {
Process p = run.exec(cmd);
InputStream ins = p.getInputStream();
new Thread(() -> {
String line = null;
byte[] b = new byte[1024];
int num = 0;
try {
while ((num = ins.read(b)) != -1) {
System.out.print(byteToStr(b));
}
} catch (Exception e) {
e.printStackTrace();
}
}).start();
p.waitFor();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 遠程執(zhí)行Linux命令
* 參考自 https://blog.csdn.net/qq_16504067/article/details/103471898
*
* @param host 主機地址
* @param port 端口
* @param username 用戶名
* @param password 密碼
* @param sh 執(zhí)行內(nèi)容
*/
public static void shell(String host, int port, String username, String password, String sh) {
JSch jSch = new JSch();
Session session = null;
ChannelExec channelExec = null;
BufferedReader inputStreamReader = null;
BufferedReader errInputStreamReader = null;
try {
// 1. 獲取 ssh session
session = jSch.getSession(username, host, port);
session.setPassword(password);
session.setTimeout(3000);
session.setConfig("StrictHostKeyChecking", "no");
// 如果出現(xiàn)了 com.jcraft.jsch.JSchException: Algorithm negotiation fail ,就加入下面這條
// session.setConfig("kex", "diffie-hellman-group1-sha1,diffie-hellman-group14-sha1,diffie-hellman-group-exchange-sha1,diffie-hellman-group-exchange-sha256");
session.connect(); // 獲取到 ssh session
// 2. 通過 exec 方式執(zhí)行 shell 命令
channelExec = (ChannelExec) session.openChannel("exec");
channelExec.setCommand(sh);
channelExec.connect(); // 執(zhí)行命令
// 3. 獲取標準輸入流
inputStreamReader = new BufferedReader(new InputStreamReader(channelExec.getInputStream()));
// 4. 獲取標準錯誤輸入流
errInputStreamReader = new BufferedReader(new InputStreamReader(channelExec.getErrStream()));
// 5. 記錄命令執(zhí)行 log
String line = null;
while ((line = inputStreamReader.readLine()) != null) {
System.out.println(line);
}
// 6. 記錄命令執(zhí)行錯誤 log
String errLine = null;
while ((errLine = errInputStreamReader.readLine()) != null) {
System.err.println(errLine);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (inputStreamReader != null) {
inputStreamReader.close();
}
if (errInputStreamReader != null) {
errInputStreamReader.close();
}
if (channelExec != null) {
channelExec.disconnect();
}
if (session != null) {
session.disconnect();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 上傳文件到服務(wù)器
* 參考自 http://t.zoukankan.com/kuangzhisen-p-8662816.html
*
* @param host 主機地址
* @param port ssh端口
* @param username 用戶名
* @param password 密碼
* @param localFile 本地文件
* @param serverPath 上傳到服務(wù)器的路徑
* @throws Exception
*/
public static void sshSftp(String host, int port, String username, String password, String localFile, String serverPath)
throws Exception {
Session session = null;
ChannelSftp channel = null;
JSch jsch = new JSch();
if (port <= 0) {
//連接服務(wù)器,采用默認端口
session = jsch.getSession(username, host);
} else {
//采用指定的端口連接服務(wù)器
session = jsch.getSession(username, host, port);
}
//如果服務(wù)器連接不上,則拋出異常
if (session == null) {
throw new Exception("session is null");
}
//設(shè)置第一次登陸的時候提示,可選值:(ask | yes | no)
session.setConfig("StrictHostKeyChecking", "no");
// 如果出現(xiàn)了 com.jcraft.jsch.JSchException: Algorithm negotiation fail ,就加入下面這條
// session.setConfig("kex", "diffie-hellman-group1-sha1,diffie-hellman-group14-sha1,diffie-hellman-group-exchange-sha1,diffie-hellman-group-exchange-sha256");
//設(shè)置登陸主機的密碼
session.setPassword(password);//設(shè)置密碼
//設(shè)置登陸超時時間
session.connect();
try {
//創(chuàng)建sftp通信通道
channel = (ChannelSftp) session.openChannel("sftp");
channel.connect(1000);
ChannelSftp sftp = (ChannelSftp) channel;
//進入服務(wù)器指定的文件夾
sftp.cd(serverPath);
//列出服務(wù)器指定的文件列表
for (Object o : sftp.ls("*")) {
System.out.println(o);
}
//以下代碼實現(xiàn)從本地上傳一個文件到服務(wù)器,如果要實現(xiàn)下載,對換以下流就可以了
File file = new File(localFile);
OutputStream outstream = sftp.put(file.getName());
InputStream instream = new FileInputStream(file);
byte[] b = new byte[1024];
int n;
totalLength = file.length();
finishLength = 0;
startProcess();
while ((n = instream.read(b)) != -1) {
outstream.write(b, 0, n);
finishLength += 1024;
}
pd.join();
outstream.flush();
outstream.close();
instream.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
session.disconnect();
if (channel != null) {
channel.disconnect();
}
}
}
private static String byteToStr(byte[] buffer) {
try {
int length = 0;
for (int i = 0; i < buffer.length; ++i) {
if (buffer[i] == 0) {
length = i;
break;
}
}
return new String(buffer, 0, length, "gb2312");
} catch (Exception e) {
return "";
}
}
private static Thread pd = new Thread();
private static long totalLength = 0L;
private static long finishLength = 0L;
private static void startProcess() {
pd = new Thread(() -> {
while (finishLength < totalLength) {
System.out.print(getProcess(totalLength, finishLength));
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(getProcess(totalLength, finishLength));
});
pd.start();
}
private static String getProcess(long total, long finish) {
StringBuilder str = new StringBuilder();
str.append("\r上傳中[");
int finishP = finish < total ? (int) (finish * 100 / total) : 100;
for (int i = 0; i < 100; i++) {
if (i > finishP) {
str.append("-");
} else if (i == finishP) {
str.append(">");
} else {
str.append("=");
}
}
str.append("]").append(finishP).append("%");
return str.toString();
}
}
文章來源地址http://www.zghlxwxcb.cn/news/detail-410908.html
總結(jié)
這樣就可以通過使用 java 的一個 main 方法進行自動化部署,實現(xiàn)了打包在本地部署自動化的想法,不需要熟悉額外的部署服務(wù),本方法非常適合小型項目的部署,而且可以根據(jù)自己的需求在這基礎(chǔ)上進行定制
到了這里,關(guān)于純java的方式實現(xiàn)自定義自動化部署java項目的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!