#${cluster_dir}/job_history.sh.template
#爬取 mapreduce.jobhistory.webapp.address 页面 Job History 数据脚本模板
#版权声明:不定期更新维护地址,看这里→_→ https://blog.youkuaiyun.com/qq_16592497/article/details/81391319
#脚本模板中涉及需要被替换的变量
#URL=${URL}
#operation_dir=${operation_dir}
#logstash_input_file=${logstash_input_file}
#爬取页面信息存储到以下文件中
file_name=job_history.jmx
################################################################################
#curl爬取网址的异常处理,若连接超时则意味着需要替换网址或者移除监控,可从页面无数据发现
#curl爬取网页信息并存储到指定位置,-s 静默模式 --connect-timeout 设置连接超时时间(秒)
curl -s --connect-timeout 10 ${URL} > ${operation_dir}/${file_name}
#if判断上一条语句是否执行成功,若失败则退出脚本
if [[ $? -ne 0 ]]; then
#exit退出shell脚本,返回状态值 1 表示失败
exit 1
fi
################################################################################
#sed编辑文件保留以'['开头的行,即每条任务的信息
sed -i '/^\[/!d' ${operation_dir}/${file_name}
#awk获取以 "," 分隔的第 4 列信息,sed提取出任务名赋值给对应变量
jobs=`awk -F \",\" '{ print $4 }' ${operation_dir}/${file_name} | sed -r 's/.*>(.+)<.*/\1/'`
#for循环遍历提取每个任务的信息
for job in $jobs; do
#if判断该任务信息是否已经采集过,若以任务名命名的文件存在则使用continue跳过此次循环,进行下次循环
if [[ -e ${operation_dir}/${job} ]]; then
continue
else
#curl爬取 Job Overview 页面信息存储为以任务名命名的文件
curl -s ${URL}/job/${job} > ${operation_dir}/${job}
#sed编辑文件去掉所有行之前的空格、除了以<a开头的其他HTML标签行
sed -i 's/^[ ]*//g;/^<[^a]/d' ${operation_dir}/${job}
#sed获取对应信息的下一行内容即其值,赋值给对应变量
user_name=`sed -n '/^User Name:/{n;p}' ${operation_dir}/${job}`
queue=`sed -n '/^Queue:/{n;p}' ${operation_dir}/${job}`
state=`sed -n '/^State:/{n;p}' ${operation_dir}/${job}`
submit_time=`sed -n '/^Submitted:/{n;p}' ${operation_dir}/${job}`
start_time=`sed -n '/^Started:/{n;p}' ${operation_dir}/${job}`
finish_time=`sed -n '/^Finished:/{n;p}' ${operation_dir}/${job}`
map_total=`sed -n '/>Map</{n;p}' ${operation_dir}/${job}`
reduce_total=`sed -n '/>Reduce</{n;p}' ${operation_dir}/${job}`
#第一个sed获取对应行的内容,第二个sed过滤出其值赋值给对应变量
map_failed=`sed -n '/m\/FAILED/p' ${operation_dir}/${job} | sed -r 's|.*>(.+)<.*|\1|'`
reduce_failed=`sed -n '/r\/FAILED/p' ${operation_dir}/${job} | sed -r 's|.*>(.+)<.*|\1|'`
#转换字符串为时间戳,便于求时间差
submit_time_timestamp=`date -d "${submit_time}" +%s`
start_time_timestamp=`date -d "${start_time}" +%s`
finish_time_timestamp=`date -d "${finish_time}" +%s`
#等待时间为开始时间减提交时间,执行时间为结束时间减开始时间
waitting_time_s=$[ ${start_time_timestamp} - ${submit_time_timestamp} ]
execution_time_s=$[ ${finish_time_timestamp} - ${start_time_timestamp} ]
#curl爬取 Job Configuration 页面信息覆盖之前文件中的信息
curl -s ${URL}/conf/${job} > ${operation_dir}/${job}
#sed获取 mapreduce.workflow.name 的下一行内容即其值,赋值给对应变量
#sql=`sed -n '/^mapreduce.workflow.name/{n;p}' ${operation_dir}/${job}`
#后来发现完整的SQL不止占一行,故改为如下方式获取SQL,赋值给变量时会自动把内容拼接为一行,以空格分隔
#sql=`sed -n '/mapreduce.workflow.name/,/<tr>/p' ${operation_dir}/${job} | sed 's/^[ ]*//g;/^</d;/^$/d;1d' | sed '$d'`
#后来又发现获取到的SQL内容含有HTML实体需要替换,故改为如下方式
#sed获取 mapreduce.workflow.name 到 <tr> 之间的行即SQL的信息存储为 ${job}_sql 文件
sed -n '/mapreduce.workflow.name/,/<tr>/p' ${operation_dir}/${job} > ${operation_dir}/${job}_sql
#sed编辑文件删除所有行之前的空格、HTML标签行、空行、第一行(即 mapreduce.workflow.name 这句话)
sed -i 's/^[ ]*//g;/^</d;/^$/d;1d' ${operation_dir}/${job}_sql
#sed编辑文件删除最后一行,因为有一行标记信息,不属于SQL且因为行号变化不能和上一条语句合并执行
sed -i '$d' ${operation_dir}/${job}_sql
###############################################################################################
#替换爬取到的SQL中HTML实体为其表示的字符
#第一个grep过滤出HTML实体(&#十进制ASCII码值;),第二个grep过滤出十进制ASCII码值
asciis=`grep -oE "&#[0-9]+;" ${operation_dir}/${job}_sql | grep -oE [0-9]+`
#for循环替换HTML实体为其表示的字符
for ascii in $asciis; do
#sed编辑文件替换HTML实体为其表示的字符,echo和awk组合将十进制ASCII码值替换为其表示的字符
sed -i "s/&#$ascii;/`echo $ascii | awk '{printf("%c", $1)}'`/g" ${operation_dir}/${job}_sql
done
#sed编辑文件替换大于小于号
sed -i 's/>/>/g' ${operation_dir}/${job}_sql
sed -i 's/</</g' ${operation_dir}/${job}_sql
###############################################################################################
#############################################################################################
#sed编辑文件将多行合并为一行
#sed默认只按行处理,N可以让其读入下一行,再对\n进行替换,这样就可以将两行并做一行。
#但是怎么将所有行并作一行呢?可以采用sed的跳转功能。:a 在代码开始处设置一个标记a,在代码执行到结尾处时
#利用跳转命令t a重新跳转到标号a处,重新执行代码,这样就可以递归的将所有行合并成一行。
sed -i ':a;N;s/\n/ /;t a' ${operation_dir}/${job}_sql
#sed编辑文件替换多个空格为一个
sed -i 's/[ ][ ]*/ /g' ${operation_dir}/${job}_sql
#############################################################################################
#cat文件内容赋值给变量
sql=`cat ${operation_dir}/${job}_sql`
#echo拼接变量为一条记录,追加输到指定文件中等待 Logstash 采集
#后来发现 ${sql} 变量中的 * 会显示当前目录下的文件名列表,所以加双引号屏蔽 * 的含义
echo ${job} ${user_name} ${queue} ${state} ${submit_time} ${start_time} ${finish_time} ${waitting_time_s} \
${execution_time_s} ${map_total} ${reduce_total} ${map_failed} ${reduce_failed} "${sql}" >> ${logstash_input_file}
#清空文件内容,只保留文件名,目的是减少不必要的空间占用
cat /dev/null > ${operation_dir}/${job}
#删除文件减少不必要的空间占用
rm ${operation_dir}/${job}_sql
fi
done