java web專案war包自動升級部署方案

邵磊發表於2017-07-10

前言

之前,我們公司部署以及升級都是由運維去管理的,聯想到很多開源平臺都支援自動升級,索性我也做個自動升級war的功能。
這裡沒有用docker映象發包,灰度發包等,只適用於單個tomcat的部署環境,支援docker單個tomcat容器。

分析

先簡單分析下war包自動升級流程:

  1. 檢查是否需要更新。
  2. 下載更新的war包到伺服器臨時目錄。(如後臺上傳則無需1,2步驟)
  3. 停止tomcat
  4. 清理tomcat下,webapps的war包解壓目錄、war包。
  5. 啟動tomcat

1,2步驟中沒有什麼坑,主要是3,4,5步驟,如果用java程式碼去執行,當tomcat服務關閉時,war包內的程式碼將停止,所以除非單獨寫個java程式跑才能繼續執行下面程式碼。但又覺得這種方式麻煩,對環境依賴太高,最終採用shell指令碼去執行3,4,5步驟,而java-web去呼叫這個shell指令碼即可。

實施

檢查更新

這一步比較簡單,這裡直接傳送一個請求,帶上版本號,如:
a.com/checkVersio…
返回是否需要更新,以及更新地址:

{
    "NeedUpdate": true,
    "downUrl": "http://a.com/1.0.war"
}複製程式碼

這裡使用httpclient去呼叫介面:

/*使用時注意字符集 "GBK""UTF-8"*/
public static String visitPost(String urlStr, String code) {
        try{
            URL url = new URL(urlStr);
            HttpURLConnection con = (HttpURLConnection)url.openConnection();
            con.setRequestMethod("GET");
            con.connect();
            BufferedReader reader = new BufferedReader(new InputStreamReader(con.getInputStream(),code));));
            String line;
            StringBuffer buffer = new StringBuffer();
            while((line = reader.readLine()) != null) {
                buffer.append(line);
            }
            reader.close();
            con.disconnect();
            String res = buffer.toString();
            return res;
        } catch(Exception e) {
            e.printStackTrace();
        }
        return null;
    }複製程式碼

類似方法有很多種,這裡不舉例。下載之後使用fastjson或者gson或者純string解析出內容即可。

下載檔案

java的下載檔案方法有很多種,都是以流的形式寫,程式碼量比較多,如果專案裡有框架的話,直接用就可以了,沒有的話,網上找一個。

    /**
     * 儲存附件
     * 1、儲存附件資訊到附件表
     * 2、儲存檔案到相應的目錄
     *
     * @param file 檔案實體
     */
    public boolean saveFile(FileEntity file) {
        BufferedOutputStream bos = null;
        FileOutputStream fos = null;
        byte[] bytes = file.getBytes();
        if (bytes != null) {
            try {
                fos = new FileOutputStream(file_Path + file.getName());
                bos = new BufferedOutputStream(fos);
                bos.write(bytes);
                bos.flush();
                IOUtils.closeQuietly(fos);
                IOUtils.closeQuietly(bos);
                return true;
            } catch (Exception e) {
                IOUtils.closeQuietly(fos);
                IOUtils.closeQuietly(bos);
                Log.error("儲存檔案失敗", e);
                return false;
            }
        }
        return false;
    }複製程式碼

啟動shell指令碼

runBatOrShell(packagename, System.getProperties().getProperty("os.name").indexOf("Windows") != -1)複製程式碼

這裡需要判斷Windows還是linux,true為Windows,否則為linux。引入包名引數是為了得到sh檔案及bat檔案。


    private String tomcat_Path = System.getProperty("catalina.base") + File.separator;//伺服器路徑

    /**
     * 執行方法指令碼
     * @param 路徑
     * @param 系統 
     */
    public boolean runBatOrShell( String name, boolean os) {
        Log.info("runBatOrShell   start:>>>>>>>>>>>>>>>>>>>>");
        String _path;
        try {
            _path = os ? this.getClass().getResource("/batshell/web.bat").getPath() + " " + file_Path + " "+ name + " " + tomcat_Path : "sh " + this.getClass().getResource("/batshell/web.sh").getPath() + " " + file_Path + " "+ name + " " + tomcat_Path;
            Log.info(_path);
            Process ps = Runtime.getRuntime().exec(_path);
            BufferedReader br = new BufferedReader(new InputStreamReader(ps.getInputStream(), "UTF-8"));//注意中文編碼問題
            String line;
            while ((line = br.readLine()) != null) {
                Log.info("runBatOrShell info=========>" + line);
            }
            br.close();
        } catch (IOException ioe) {
            Log.error("runBatOrShell error !!!!!!!!!!");
            ioe.printStackTrace();
            return false;
        }
        return true;
    }複製程式碼

這裡引入tomcat路徑是為了方便指令碼執行。
sh web.sh空格war檔案目錄空格檔名空格tomcat目錄
sh web.sh /usr/local/war/ 1.0.war /usr/local/tomcat/
bat檔案同理
web.bat d:/war/ 1.0.war d:/tomcat/
檔案目錄如下:

shell指令碼執行

#!/bin/sh
cd $1
echo $(date +%Y-%m-%d-%l:%M:%S) >>webvlog.txt;
echo $2>>webvlog.txt;
echo "正在關閉tomcat">>webvlog.txt;
sh $3/bin/shutdown.sh
echo "正在執行刪除war.war">>webvlog.txt;
rm $3/webapps/war.war;
echo "正在執行刪除war資料夾">>webvlog.txt;
rm -r $3/webapps/war;
echo "正在部署war">>webvlog.txt;
cp  $1$2 $3/webapps/war.war
echo "正在重啟tomcat">>webvlog.txt;
sh $3/bin/startup.sh
echo "部署成功">>webvlog.txt;複製程式碼

其中$1 $2 $3 這種變數標示執行時後面的傳值,其它程式碼echo就是註釋。webvlog.txt為部署日誌。

總結

這樣改造後,基本滿足目前現有需求,也簡化了部署步驟。但無法適用多tomcat環境中,很多場景有load balance負載均衡、有多個docker環境,此時,該方案便無法解決。

遺留問題

war包在升級時,可能會增加表、檢視等,所以還需要執行sql指令碼,sql指令碼升級方案下次分享。

相關文章