使用java開發(fā)簡單的自動部署

背景:

作為一名開發(fā)人員,不可避免地需要部署項(xiàng)目。

在沒使用Jenkins之前,每次部署都使人感覺到很繁瑣思币。

那有沒有辦法自己弄一個程序來減輕一下部署的工作呢鹿响?

就是運(yùn)行一下程序,想要部署的項(xiàng)目就直接部署到服務(wù)器中谷饿。

準(zhǔn)備階段

一惶我、了解部署項(xiàng)目的步驟過程
  1. 將打包好的jar文件上傳到服務(wù)器上

  2. 查看項(xiàng)目是否在運(yùn)行,運(yùn)行的話就停止

  3. 啟動項(xiàng)目程序

二博投、前置條件:
  1. 一個簡單的springboot項(xiàng)目

  2. 一臺配置好環(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ù)器中,可拆分為如下步驟:

  1. 連接到服務(wù)器

  2. 創(chuàng)建新項(xiàng)目文件夾

  3. 將項(xiàng)目上傳到新創(chuàng)建的文件夾中

  4. 查看進(jìn)程并關(guān)閉正在運(yùn)行的項(xiàng)目程序

  5. 啟動項(xiàng)目程序

三惫企、具體程序代碼
  1. 添加依賴的jar包

<dependency>

   <groupId>com.jcraft</groupId>

   <artifactId>jsch</artifactId>

   <version>0.1.54</version>

  </dependency>

  1. 連接到服務(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);

    }

  1. 創(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);

        }

    }

  1. 將項(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();

            }

        }

    }

  1. 遠(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;

    }

  1. 遠(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;

    }

  1. 具體的執(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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末研儒,一起剝皮案震驚了整個濱河市豫缨,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌端朵,老刑警劉巖好芭,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異冲呢,居然都是意外死亡舍败,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進(jìn)店門敬拓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來邻薯,“玉大人,你說我怎么就攤上這事乘凸〔薰睿” “怎么了?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵营勤,是天一觀的道長灵嫌。 經(jīng)常有香客問我,道長冀偶,這世上最難降的妖魔是什么醒第? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮进鸠,結(jié)果婚禮上稠曼,老公的妹妹穿的比我還像新娘。我一直安慰自己客年,他們只是感情好霞幅,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著量瓜,像睡著了一般司恳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上绍傲,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天扔傅,我揣著相機(jī)與錄音,去河邊找鬼烫饼。 笑死猎塞,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的杠纵。 我是一名探鬼主播荠耽,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼比藻!你這毒婦竟也來了铝量?” 一聲冷哼從身側(cè)響起倘屹,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎慢叨,沒想到半個月后纽匙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡插爹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年哄辣,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赠尾。...
    茶點(diǎn)故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡力穗,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出气嫁,到底是詐尸還是另有隱情当窗,我是刑警寧澤,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布寸宵,位于F島的核電站崖面,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏梯影。R本人自食惡果不足惜巫员,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望甲棍。 院中可真熱鬧简识,春花似錦、人聲如沸感猛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽陪白。三九已至颈走,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間咱士,已是汗流浹背立由。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留序厉,地道東北人锐膜。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像脂矫,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子霉晕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,047評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理庭再,服務(wù)發(fā)現(xiàn)捞奕,斷路器,智...
    卡卡羅2017閱讀 134,662評論 18 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,823評論 6 342
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,171評論 25 707
  • 悄悄地拄轻,是一個不眠夜颅围, 心情撥動了特殊的音弦, 腦袋里回蕩著舒心的樂恨搓, 心里的鬼胎尚不在人間院促, 感性和天真又浪漫天邊。
    稻草翁揮帽閱讀 164評論 0 5
  • 同屬“天朝三少”,洗車小哥無疑是比快遞小哥辉浦、外賣小哥更為Bug的一種存在弄抬。外賣小哥無非是風(fēng)一樣的男子,野蠻沖撞過路...
    UCen宇晨閱讀 329評論 0 0