最近一个项目需要写一个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
后面的参数是获取一个独占锁,如果获取不到,则等待10秒钟
一些参数贴出来
这样,通过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下面的。