【Linux】shell呼叫Java程式main方法通過crontab定時執行
最近一個專案需要寫一個batch定時讀取檔案往資料庫裡插入記錄,第一次寫,遇到好多問題,記錄一下。
先從shell如何呼叫java說起
建立如下目錄結構
batch 根目錄
├── bin shell放置目錄(如果直接放java class也放這裡)
│ ├── 1.sh
│ ├── test.sh
│ └── startup.sh
├── libs 引用jar包放置目錄
│ ├── commons-io-2.4.jar
│ ├── commons-lang3-3.3.2.jar
│ ├── ini4j-0.5.4.jar
│ ├── junit-3.8.1.jar
│ ├── log4j-1.2.17.jar
│ ├── postgresql-9.3-1102-jdbc41.jar
│ └── myjar.jar 自己的jar
├── lock 鎖檔案(空檔案)
└── logs 程式log目錄
├── cron.log
└── myjar.log
然後是shell指令碼
startup.sh
#!/bin/sh echo "" echo "----------------------------------" echo "start at `date '+%Y-%m-%d %H:%M:%S'` ..." JAVA_HOME=/usr/java/jdk1.8.0_45 MAIN_CLASS=com.superbatch.batch.DetectLogBatch SH_DIR=/usr/local/ee4one_batch/bin cd $SH_DIR dir="." temp="" libs="../libs/*" append(){ temp=$temp":"$1 } for file in $libs do append $file done export CLASSPATH=$dir:../$temp echo $CLASSPATH #nohup ${JAVA_HOME}/bin/java -classpath ${CLASSPATH} ${MAIN_CLASS} echo "finished at `date '+%Y-%m-%d %H:%M:%S'` ..." exit 0
第1行宣告shell型別
2-4行輸出一些時間資訊
6-8行分別定義了一些變數後面會用到
10行讓程式進入到自己shell指令碼的bin目錄,如果使用cron執行這很重要,因為libs資料夾定義的是相對路徑,而cron的預設執行路徑則找不到,如果手工進入到bin目錄下執行則無所謂
11-24行只做了一件事情,就是將bin目錄,batch目錄,libs目錄下的所有jar包,全部加入到classpath中,生成一長串字串如下:
.:../:../libs/commons-io-2.4.jar:../libs/commons-lang3-3.3.2.jar:../libs/ini4j-0.5.4.jar:../libs/junit-3.8.1.jar:../libs/log4j-1.2.17.jar:../libs/postgresql-9.3-1102-jdbc41.jar:../libs/myjar.jar
只不過是用了一個append()方法和一個for迴圈實現了字串的拼接,如果jar包不多的話手工寫上去也是完全可以的。
26行列印了一下生成的$CLASSPATH變數的值
29行是真正的呼叫java執行main()方法,要說的是 在前面如果加上“nohup ”的話,java的執行log就會直接輸出到當前目錄下的nohup.log檔案裡。如果是手工執行在最後加上“ $”的話程式將會後臺執行,也就是說關閉命令列視窗並不會中斷程式的執行,不過這種方法中shell的程序並不會等待java方法執行完成,而是立即完成。
-classpath 後面的$CLASSPATH是告訴java去什麼地方找到用到的jar包,我的程式是匯出的jar包,我也同樣把我的jar包放入到了這個目錄就可以了,如果是編譯的class檔案的話放到bin目錄下也可以。
32行輸出log
33行程式正常退出
如果是手工通過shell呼叫java那以上文字就已經搞定了
下面說如何通過crontab呼叫shell
在命令列下輸入
crontab -e
可以開啟cron任務的編輯介面
注意的是cron是區分使用者的,每個使用者的cron是單獨的
*/1 * * * * /usr/local/batch/bin/startup.sh
這樣寫的話就是每分鐘執行一次指定的shell了
前面的幾個* 分別是minute(分),hour(小時),day of month(日期),month(月份), day of week(星期),具體的可以查詢一下
這個只是每分鐘執行一次,下面的命令可以將執行的log輸入到指定檔案也就是shell中的echo
*/1 * * * * /usr/local/batch/bin/startup.sh >>/usr/local/batch/logs/cron.log
但是有好多時候,比如我的需求,如果資料量過大,上一個batch還沒執行完則不希望下一個batch也執行,本次不執行就可以了這個需求可以通過flock來實現
*/1 * * * * flock -xw 10 /usr/local/ee4one_batch/lock -c "sh /usr/local/ee4one_batch/bin/test.sh >> /usr/local/ee4one_batch/logs/cron.l
上面的命令前面就是用flock命令去給一個檔案上鎖,這個鎖是一個全域性鎖如果其它命令也來獲取這個檔案的鎖的而獲取不到的話則不會被執行,這個鎖檔案是什麼檔案無所謂。
後面的引數是獲取一個獨佔鎖,如果獲取不到,則等待10秒鐘
-s,--shared: 獲得一個共享鎖 -x,--exclusive:獲得一個獨佔鎖 -u,--unlock: 移除一個鎖,通常是不需要的,指令碼執行完會自動丟棄鎖 -n,--nonblock: 如果沒有立即獲得鎖,直接失敗而不是等待 -w,--timeout: 如果沒有立即獲得鎖,等待指定時間 -o,--close: 在執行命令前關閉檔案的描述符號。用於如果命令產生子程序時會不受鎖的管控 -c,--command: 在shell中執行一個單獨的命令 -h,--help 顯示幫助 -V,--version: 顯示版本一些引數貼出來
這樣,通過crontab,呼叫shell,實現java main()方法的定期執行,並且防止同一時間重複執行就完成了。
這樣做的好處是不用通過java來判斷多重執行,java判斷起來也不是很方便。
有一些小補充。
1,shell裡面進入目錄的那句話很重要,因為這個找了好久的原因。
2,執行shell的時候可以用sh -x startup.sh來執行輸出詳細執行log。
3,cron的執行log在/var/log/cron檔案裡面,可以檢視執行詳細。
4,我的java程式log是自己用log4j出力到logs下面的。