背景:
作為一名開發(fā)人員,不可避免地需要部署項(xiàng)目。
在沒使用Jenkins之前,每次部署都使人感覺到很繁瑣思币。
那有沒有辦法自己弄一個程序來減輕一下部署的工作呢鹿响?
就是運(yùn)行一下程序,想要部署的項(xiàng)目就直接部署到服務(wù)器中谷饿。
準(zhǔn)備階段
一惶我、了解部署項(xiàng)目的步驟過程
將打包好的jar文件上傳到服務(wù)器上
查看項(xiàng)目是否在運(yùn)行,運(yùn)行的話就停止
啟動項(xiàng)目程序
二博投、前置條件:
一個簡單的springboot項(xiàng)目
一臺配置好環(huán)境的服務(wù)器
實(shí)際開發(fā):
一绸贡、簡單了解Jsch
JSch是Java Secure Channel的縮寫。JSch是一個SSH2的純Java實(shí)現(xiàn)毅哗。它允許你連接到一個SSH服務(wù)器听怕,并且可以使用端口轉(zhuǎn)發(fā),X11轉(zhuǎn)發(fā)虑绵,文件傳輸?shù)饶虿t,?dāng)然你也可以集成它的功能到你自己的應(yīng)用程序。
這里只需使用JSch實(shí)現(xiàn)的SFTP功能
SFTP是Secure File Transfer Protocol的縮寫翅睛,安全文件傳送協(xié)議声搁。可以為傳輸文件提供一種安全的加密方法捕发。SFTP 為 SSH的一部份疏旨,是一種傳輸文件到服務(wù)器的安全方式。SFTP是使用加密傳輸認(rèn)證信息和傳輸?shù)臄?shù)據(jù)扎酷,所以檐涝,使用SFTP是非常安全的。但是法挨,由于這種傳輸方式使用了加密/解密技術(shù)谁榜,所以傳輸效率比普通的FTP要低得多。
ChannelSftp類是JSch實(shí)現(xiàn)SFTP核心類凡纳,它包含了所有SFTP的方法
二惰爬、拆分步驟
要使用JSch將項(xiàng)目部署到服務(wù)器中,可拆分為如下步驟:
連接到服務(wù)器
創(chuàng)建新項(xiàng)目文件夾
將項(xiàng)目上傳到新創(chuàng)建的文件夾中
查看進(jìn)程并關(guān)閉正在運(yùn)行的項(xiàng)目程序
啟動項(xiàng)目程序
三惫企、具體程序代碼
- 添加依賴的jar包
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.54</version>
</dependency>
- 連接到服務(wù)器
public void connect() throws JSchException {
jsch = new JSch();
session = jsch.getSession(user, host, port);
session.setPassword(passwd);
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect();
Channel channel = session.openChannel("sftp");
channel.connect();
sftp = (ChannelSftp) channel;
log.info("連接到SFTP成功。host: " + host);
}
- 創(chuàng)建新項(xiàng)目文件夾
public void createDir(String createpath) {
try {
if (isDirExist(createpath)) {
sftp.cd(createpath);
return;
}
String pathArry[] = createpath.split("/");
StringBuffer filePath = new StringBuffer("/");
for (String path : pathArry) {
if (path.equals("")) {
continue;
}
filePath.append(path + "/");
if (isDirExist(filePath.toString())) {
sftp.cd(filePath.toString());
} else {
// 建立目錄
sftp.mkdir(filePath.toString());
// 進(jìn)入并設(shè)置為當(dāng)前目錄
sftp.cd(filePath.toString());
}
}
sftp.cd(createpath);
} catch (SftpException e) {
throw new RuntimeException("創(chuàng)建路徑錯誤:" + createpath);
}
}
- 將項(xiàng)目上傳到新創(chuàng)建的文件夾中
public void uploadFile(String local,String remote) throws Exception {
File file = new File(local);
if (file.isDirectory()) {
throw new RuntimeException(local + " is not a file");
}
InputStream inputStream = null;
try {
String rpath = remote.substring(0,remote.lastIndexOf("/")+1);
if (!isDirExist(rpath)){
log.info("目錄不存在陵叽,創(chuàng)建目錄:{}",rpath);
createDir(rpath);
}
inputStream = new FileInputStream(file);
sftp.setInputStream(inputStream);
sftp.put(inputStream, remote);
log.info("上傳文件完成");
} catch (Exception e) {
log.info("上傳文件失敗");
throw e;
}finally{
if(inputStream != null){
inputStream.close();
}
}
}
- 遠(yuǎn)程執(zhí)行Shell命令狞尔,執(zhí)行完后返回結(jié)果
public int execCmd(String command) throws Exception{
log.info( "開始執(zhí)行命令:" + command);
int returnCode = -1;
BufferedReader reader = null;
Channel channel = null;
channel = session.openChannel("exec");
((ChannelExec) channel).setCommand(command);
channel.setInputStream(null);
((ChannelExec) channel).setErrStream(System.err);
InputStream in = channel.getInputStream();
reader = new BufferedReader(new InputStreamReader(in));
channel.connect();
log.info("The remote command is:{}" ,command);
String buf ;
while ((buf = reader.readLine()) != null) {
log.info(buf);
}
reader.close();
// Get the return code only after the channel is closed.
if (channel.isClosed()) {
returnCode = channel.getExitStatus();
}
log.info( "Exit-status:" + returnCode );
/* StringBuffer buf = new StringBuffer( 1024 );
byte[] tmp = new byte[ 1024 ];
while ( true ) {
while ( in.available() > 0 ) {
int i = in.read( tmp, 0, 1024 );
if ( i < 0 ) break;
buf.append( new String( tmp, 0, i ) );
}
if ( channel.isClosed() ) {
res = channel.getExitStatus();
log.info( "Exit-status:" + res );
System.out.println( "Exit-status:" + res );
break;
}
TimeUnit.MILLISECONDS.sleep(100);
}
log.info( buf.toString() );*/
channel.disconnect();
return returnCode;
}
- 遠(yuǎn)程執(zhí)行Shell命令,每當(dāng)有一行結(jié)果就返回
public int shellCmd(String command) throws Exception{
long startTime=System.currentTimeMillis();
log.info( "開始執(zhí)行命令:" + command);
int returnCode = -1;
boolean isTimeOut = false;
ChannelShell channel=(ChannelShell) session.openChannel("shell");
InputStream in = channel.getInputStream();
channel.setPty(true);
channel.connect();
OutputStream os = channel.getOutputStream();
os.write((command + "\r\n").getBytes());
os.write("exit\r\n".getBytes());
os.flush();
log.info("The remote command is:{}" ,command);
byte[] tmp=new byte[1024];
while(true) {
while (in.available() > 0) {
int i = in.read(tmp, 0, 1024);
if (i < 0) break;
log.info(new String(tmp, 0, i));
}
if (channel.isClosed()) {
if (in.available() > 0) continue;
returnCode = channel.getExitStatus();
log.info("exit-status: " + channel.getExitStatus());
break;
}
long useTime=System.currentTimeMillis()-startTime;
if (useTime> 600000){
log.info("[error] Execution command timeout!" );
isTimeOut = true;
break;
}
try{Thread.sleep(1000);}catch(Exception ee){}
}
long endTime=System.currentTimeMillis();
float excTime=(float)(endTime-startTime)/1000;
log.info("執(zhí)行命令是否超時:{},共耗時:{}s",isTimeOut,excTime);
os.close();
in.close();
channel.disconnect();
session.disconnect();
return returnCode;
}
- 具體的執(zhí)行巩掺,如需要部署simple-auto-deploy.jar
public static void main(String[] args) {
JSchExecutor jSchUtil = new JSchExecutor( "123", "123123","192.168.243.18");
try {
jSchUtil.connect();
jSchUtil.execCmd("kill -9 `ps -ef | grep simple-auto-deploy.jar | grep -v grep | awk '{print $2}'`");
jSchUtil.uploadFile("C:\\mywork\\Workspaces\\IdeaProjects\\simple-auto-deploy\\target\\simple-auto-deploy.jar","/data/simple-auto-deploy/simple-auto-deploy.jar");
jSchUtil.shellCmd("nohup java -jar /data/simple-auto-deploy/simple-auto-deploy.jar >/dev/null &");
} catch (Exception e) {
e.printStackTrace();
}finally {
jSchUtil.disconnect();
}
}
總結(jié)
這樣一個簡單的自動部署就出來了偏序,運(yùn)行main方法,程序就自動部署到服務(wù)器中胖替。
代碼可參考:https://github.com/visionsws/learnJava 中的 simple-auto-deploy