【Linux】shell调用Java程序main方法通过crontab定时执行

时间:2023-01-31 21:25:48

最近一个项目需要写一个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下面的。