1. #! /bin/sh
    2. #################################################################################
    3. #【操作说明】: #
    4. # 1.启动命令:nohup sh watch_server_v4.sh >/dev/null 2>&1 & #
    5. # 2.需将 watch_server_v4.sh 放在 [最大盘]/ICC/dahua/fire/server_keeper 下。 #
    6. # 3.务必确保有且仅有一个守护进程正在运行。 #
    7. # 4.新增服务(如为jar包服务,仅需保证服务包中有且只有一个可执行jar包即可)无需进 #
    8. # 行任何操作,即被守护。 #
    9. # 5.启动守护进程后,务必确认当前路径下无 ERROR.log 产生。如有,请根据提示修复 #
    10. # 问题。 #
    11. # 6.由于服务做了冗余,默认不会启动 IFCSI 等服务,如需启动,请在 serverList.sh #
    12. # 中根据提示进行添加。 #
    13. # 7.请勿手动启动任何服务,否则将导致不可预知错误。 #
    14. #################################################################################
    15. #【更新日志 V4.1】 2020-7-22 23:39:52 刘迎光 #
    16. # 1.实现守护进程脚本的自动化,无需人为干预。 #
    17. # 2.优化脚本执行逻辑,新增服务后无需重启,即可被守护。 #
    18. # 3.增加对守护脚本存放路径的强校验(若路径错误,则生成 ERROR.log) #
    19. # 4.kafka 配置读取兼容性大幅度提高。 #
    20. # 5.定时清理日志文件。 #
    21. #################################################################################
    22. # 守护进程的文件名(不含后缀)
    23. watchPath=$(pwd)
    24. watchFileName=${0} # 文件名(含后缀)
    25. watchFileName=${watchFileName%.*} # 文件名(不含后缀)
    26. watchFilePath=${watchPath}/${watchFileName}.sh
    27. echo "守护进程的文件名(不含后缀):[[ ${watchFileName} ]]"
    28. # 新服务基础路径、日志文件名称
    29. basePath=$(
    30. cd ..
    31. pwd
    32. )
    33. time=$(date +'%Y-%m-%d %H:%M:%S')
    34. # 输出日志信息
    35. # args-1:输出内容
    36. # [args-2]:日志级别(可选参数,默认为:INFO。ERROR, WARN, INFO)
    37. function log_out() {
    38. # 当前执行操作的文件
    39. cur_file=$0
    40. # 日志内容
    41. logContent=$1
    42. # 日志级别
    43. LEVEL_CODE=$2
    44. # 将字符串转成大写,再进行字符串比较
    45. LEVEL_CODE=$(echo ${LEVEL_CODE} | tr [a-z] [A-Z])
    46. logTime=$(date +'%Y-%m-%d %H:%M:%S')
    47. # LEVEL_CODE 为空,则默认日志等级为 INFO
    48. if [[ ! -n "${LEVEL_CODE}" ]] || [[ "${LEVEL_CODE}" == I* ]]; then
    49. LEVEL_CODE=INFO
    50. LEVEL=1
    51. elif [[ "${LEVEL_CODE}" == W* ]]; then
    52. LEVEL_CODE=WARN
    53. LEVEL=2
    54. elif [[ "${LEVEL_CODE}" == D* ]]; then
    55. LEVEL_CODE=DEBUG
    56. LEVEL=3
    57. elif [[ "${LEVEL_CODE}" == E* ]]; then
    58. LEVEL_CODE=ERROR
    59. LEVEL=4
    60. else
    61. LEVEL_CODE=INFO
    62. LEVEL=1
    63. fi
    64. # logInfo="${logTime}:[${cur_file}][${LEVEL}] ${logContent}"
    65. logInfo="${logTime}:[${FUNCNAME[@]}][${LEVEL_CODE}] ${logContent}"
    66. echo "${logInfo}"
    67. # 根据等级输出日志
    68. if [[ ${LEVEL} -ge 1 ]]; then
    69. echo "${logInfo}" >>INFO.log
    70. fi
    71. if [[ ${LEVEL} -ge 2 ]]; then
    72. echo "${logInfo}" >>WARN.log
    73. fi
    74. if [[ ${LEVEL} -ge 3 ]]; then
    75. echo "${logInfo}" >>DEBUG.log
    76. fi
    77. if [[ ${LEVEL} -ge 4 ]]; then
    78. echo "${logInfo}" >>ERROR.log
    79. fi
    80. }
    81. # 调用示例
    82. # log_out "要输出的内容"
    83. # 校验文件位置是否正确,如果错误,则记录到日志中,并返回错误信息
    84. BASE_PATH=/ICC/dahua/fire
    85. if [[ ! ${basePath} =~ ${BASE_PATH} ]]; then
    86. log_out "守护进程存放位置 [[ ${basePath} ]] 可能有误,请检查脚本路径!!!" E
    87. # exit
    88. fi
    89. # 添加守护进程到开机启动中(服务器重启即会执行)
    90. FIND_S="source /etc/profile"
    91. FIND_STR="nohup sh ${watchFileName}.sh >/dev/null 2>&1 &"
    92. # 开机启动项文件位置
    93. FIND_FILE="/etc/rc.d/rc.local"
    94. # 移除开机启动项中的相关内容
    95. sed -i '/\/etc\/profile/d' ${FIND_FILE}
    96. sed -i '/watch_server/d' ${FIND_FILE}
    97. sed -i '/server_keeper/d' ${FIND_FILE}
    98. sed -i '/server/d' ${FIND_FILE}
    99. if [ $(grep -c "${FIND_S}" ${FIND_FILE}) -eq '0' ]; then
    100. chmod 777 /etc/rc.d/rc.local
    101. echo "source /etc/profile" >>/etc/rc.d/rc.local
    102. sleep 2
    103. fi
    104. if [ $(grep -c "watch_server" ${FIND_FILE}) -eq '0' ]; then
    105. chmod 777 /etc/rc.d/rc.local
    106. echo "cd ${watchPath}" >>/etc/rc.d/rc.local
    107. sleep 2
    108. chmod 777 /etc/rc.d/rc.local
    109. echo "nohup sh ${watchFileName}.sh >/dev/null 2>&1 &" >>/etc/rc.d/rc.local
    110. sleep 2
    111. fi
    112. JAVA_OPTS="-Xss512k -XX:MaxMetaspaceSize=1024M -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:CMSInitiatingOccupancyFraction=75 -XX:ParallelGCThreads=4 -XX:+HeapDumpOnOutOfMemoryError"
    113. # 检验服务是否启动的间隔时间
    114. sleepTime=10
    115. ###########################################################
    116. function start_kafka_zookeeper() {
    117. serverName=zookeeper
    118. serverPath=${basePath}/kafka
    119. # zookeeper_start_file=${serverPath}/bin/zookeeper-server-start.sh
    120. # zookeeper_stop_file=${serverPath}/bin/zookeeper-server-stop.sh
    121. # zookeeper_cfg_file=${serverPath}/config/zookeeper.properties
    122. #判断对应 Kafka zookeeper 进程是否已经启动
    123. if $(ps -ef | grep -v "grep" | grep ${serverPath} | grep -q "zookeeper.properties"); then
    124. log_out "[[ ${serverName} ]] 正常运行中,无需启动 ... "
    125. return
    126. fi
    127. cd ${serverPath}
    128. log_out "[[ ${serverPath} 下的 ${serverName} ]] 未运行,开始启动... " W
    129. nohup bin/zookeeper-server-start.sh config/zookeeper.properties >/dev/null 2>&1 &
    130. log_out "[[ ${serverPath} 下的 ${serverName} ]] 启动完成... " W
    131. cd ${watchPath}
    132. sleep 5
    133. }
    134. function start_kafka() {
    135. serverName=kafka
    136. serverPath=${basePath}/${serverName}
    137. #判断对应 kafka Server 进程是否已经启动
    138. if $(ps -ef | grep ${serverPath} | grep -q "kafka.Kafka"); then
    139. log_out "[[ ${serverName} ]] 正常运行中,无需启动 ... "
    140. return
    141. fi
    142. cd ${serverPath}
    143. log_out "[[ ${serverPath} 下的 ${serverName} ]] 未运行,开始启动... " W
    144. nohup bin/kafka-server-start.sh config/server.properties >/dev/null 2>&1 &
    145. log_out "[[ ${serverPath} 下的 ${serverName} ]] 启动完成... " W
    146. cd ${watchPath}
    147. }
    148. # 启动 zookeeper 和 kafka
    149. function start_zookeeperAndKafka() {
    150. serverName=kafka
    151. serverPath=${basePath}/${serverName}
    152. if [ ! -d "${serverPath}" ]; then
    153. log_out "[[ ${serverPath} 下的 ${serverName} ]] 不存在,无需启动"
    154. return
    155. fi
    156. # 获取端口(过滤注释行、行首空格(egrep -v "^\s*#|^\s*$"))
    157. kafka_port=$(egrep -v "^\s*#|^\s*$" ${serverPath}/config/server.properties | grep "listeners" | grep -v "advertised" | awk -F ':' '{print $3}')
    158. if [[ ! -n "${kafka_port}" ]]; then
    159. log_out "未找到 [[ ${serverPath} 下的 ${serverName} ]] 的端口 kafka_port !!!请检查 ${serverPath}/config/server.properties 中 是否包含 listeners = PLAINTEXT://:3998 配置" E
    160. return
    161. fi
    162. start_kafka_zookeeper
    163. start_kafka
    164. }
    165. # 启动 redis
    166. function start_redis() {
    167. serverName=redis
    168. serverPath=${basePath}/${serverName}
    169. if [ ! -d "${serverPath}" ]; then
    170. log_out "[[ ${serverPath} 下的 ${serverName} ]] 不存在,无需启动"
    171. return
    172. fi
    173. redis_file=${serverPath}/redis-server
    174. redis_conf=${serverPath}/redis.conf
    175. #判断对应 redis 进程是否已经启动
    176. if $(ps -ef | grep ${serverPath} | grep -q redis-server); then
    177. log_out "[[ ${serverName} ]] 正常运行中,无需启动 ... "
    178. return
    179. fi
    180. cd ${serverPath}
    181. log_out "[[ ${serverPath} 下的 ${serverName} ]] 未运行,开始启动... " W
    182. log_out "${redis_file} ${redis_conf} &"
    183. ${redis_file} ${redis_conf} &
    184. echo "[[ ${serverPath} 下的 ${serverName} ]] 启动完成... " W
    185. cd ${watchPath}
    186. }
    187. # MySQL 添加守护
    188. function start_mysql() {
    189. serverName=mysql
    190. serverPath=${basePath}/${serverName}
    191. if [ ! -d "${serverPath}" ]; then
    192. log_out "[[ ${serverPath} 下的 ${serverName} ]] 不存在,无需启动"
    193. return
    194. fi
    195. #判断对应 mysql 进程是否已经启动
    196. mysqlStatus=$(systemctl status mysqld | grep "Active: active")
    197. if [[ ${mysqlStatus} == *active* ]]; then
    198. # if [ -n ${mysqlStatus} ]; then
    199. log_out "[[ ${serverName} ]] 正常运行中,无需启动 ... "
    200. return
    201. fi
    202. log_out "[[ ${serverPath} 下的 ${serverName} ]] 未运行,开始启动... " W
    203. setenforce 0
    204. sed -i 's/SELINUX=enforcing SELINUX=disabled/g' /etc/selinux/config
    205. if [ -f "${serverPath}/../../../../firecontroldata/mysql/mysql.sock.lock" ]; then
    206. rm -rf ${serverPath}/../../../../firecontroldata/mysql/mysql.sock.lock
    207. else
    208. systemctl restart mysqld.service
    209. log_out "[[ ${serverPath} 下的 ${serverName} ]] 启动完成... " W
    210. fi
    211. }
    212. # 常用函数(启动 war 包服务)
    213. function startWarServer() {
    214. # 服务名称(new)
    215. serverName=$1
    216. serverPath=${basePath}/${serverName}
    217. numServer=$(ps -aux | grep ${serverPath}/bin | grep -v "grep" | wc -l)
    218. if [[ ${numServer} -eq 1 ]]; then
    219. log_out "[[ ${serverName} ]] 正常运行中,无需启动 ... "
    220. return
    221. fi
    222. cd ${serverPath}
    223. kill -9 $(ps -aux | grep ${serverPath}/bin | grep -v "grep" | awk -F ' ' '{print $2}')
    224. log_out "[[ ${serverPath} 下的 ${serverName} ]] 未运行,开始启动... " W
    225. bin/startup.sh
    226. log_out "[[ ${serverPath} 下的 ${serverName} ]] 启动完成... " W
    227. cd ${watchPath}
    228. }
    229. # 常用函数(启动 jar 包服务)
    230. function startJarServer() {
    231. # 标准路径:
    232. # ${basePath}
    233. # 服务名称(new)
    234. serverName=$1
    235. # jar 包名称
    236. jarName=$2
    237. # 限制内存大小(含单位:G)
    238. limitXmx=$3
    239. # eg. serverPath=/ICC/dahua/fire/DCPS_TCP
    240. serverPath=${basePath}/${serverName}
    241. # ${jarName} 为 NULL
    242. if [[ ! -n ${jarName} ]]; then
    243. log_out "${jarName} 为 NULL,选择从 ${serverName} 中读取 jarName。"
    244. jarNum=$(ls ${serverPath} | grep "jar$" | grep -iv "bak" | wc -l)
    245. # 可执行的 jar 包不为 1,则说明该服务存在异常
    246. if [[ ! ${jarNum} -eq 1 ]]; then
    247. log_out "${serverPath} 下 jar 包数量大于 1,请将无关 jar 包删除(或将其以 _bak 结尾命名)!!!" E
    248. return
    249. fi
    250. jarName=$(ls ${serverPath} | grep "jar$" | grep -iv "bak")
    251. fi
    252. jarPath=${serverPath}/${jarName}
    253. # 去除单位(:G)
    254. limitSize=${limitXmx%G*}
    255. # 默认限制内存大小:1G
    256. if [ ! -n "${limitSize}" ]; then
    257. limitXmx=1G
    258. elif [[ ${limitSize} -gt 5 ]]; then
    259. log_out "${limitXmx} 设置过大,请确认是否正确!!!" E
    260. fi
    261. log_out "basePath=${basePath},serverName=${serverName},jarName=${jarName},limitXmx=${limitXmx},serverPath=${serverPath},jarPath=${jarPath}"
    262. numServer=$(ps -aux | grep ${jarPath} | grep -v "grep" | wc -l)
    263. if [[ ${numServer} -eq 1 ]]; then
    264. log_out "[[ ${serverName} ]] 正常运行中,无需启动 ... "
    265. else
    266. cd ${serverPath}
    267. log_out "[[ ${serverPath} 下的 ${serverName} ]] 未运行,开始启动... " W
    268. nohup java -server -Xms512m -Xmx${limitXmx} ${JAVA_OPTS} -XX:HeapDumpPath=/logs/${serverName}.dump -jar ${jarPath} >/dev/null 2>&1 &
    269. log_out "[[ ${serverPath} 下的 ${serverName} ]] 启动完成... " W
    270. cd ${watchPath}
    271. fi
    272. }
    273. # 启动服务(war 包和 jar 包服务启动均使用该方法)
    274. function startServer() {
    275. serverName=$1
    276. serverPath=${basePath}/${serverName}
    277. if [ ! -d "${serverPath}" ]; then
    278. log_out "[[ ${serverPath} 下的 ${serverName} ]] 不存在,无需启动"
    279. return
    280. fi
    281. FIND_FILE=${basePath}/${serverName}/bin
    282. info="[[ ${serverPath} 下的 ${serverName} ]]"
    283. if [[ -d "${FIND_FILE}" ]]; then
    284. info=${info}",war 包启动"
    285. log_out "${info}"
    286. startWarServer ${serverName}
    287. else
    288. jarName=$2
    289. limitSize=$3
    290. info=${info}",jar 包名称:[[ ${jarName} ]] ,限制内存大小:[[ ${limitSize} ]] "
    291. log_out "${info}"
    292. startJarServer ${serverName} ${jarName} ${limitSize}
    293. fi
    294. }
    295. # 查询是否存在 serverList.sh,若不存在则创建,存在则跳过
    296. function mkDir() {
    297. file=$1
    298. if [ -f ${file} ]; then
    299. log_out "[[ ${serverPath} 下的 ${file} ]] 已存在,无需创建"
    300. return
    301. fi
    302. log_out "[[ ${serverPath} 下的 ${file} ]] 文件不存在,开始创建"
    303. # 创建文件
    304. touch ${file}
    305. # 往文件中添加初始化的文件列表
    306. echo "
    307. #! /bin/sh
    308. # 启动守护进程指令:nohup sh ${watchFileName}.sh >/dev/null 2>&1 &
    309. # [注意事项]
    310. # 0.默认服务(IFCS、FireAppServer、MessageServer、DCPS_TCP、DCPS_UDP、DCPS_TCP、ftpServer等)已自动守护,无需重复添加
    311. # 1.禁止手动启动任何服务,否则将会导致服务启动异常。
    312. # 2.启动守护进程后,务必观察当前文件夹下是否有 ERROR.log 文件产生。如有,务必根据提示修复存在问题。
    313. # 3.新增服务后即被守护,无需重启守护进程。
    314. # 4.MySQL、Redis、kafka 已被守护进程 ${watchFileName}.sh 守护,无需重复添加。
    315. ################ 示例 (始)################
    316. # 添加 war 包服务格式:服务名称
    317. IFCS
    318. # 添加 jar 包服务格式:服务名称 jar包 限制内存(选填,建议不填)
    319. DCPS_TCP DCPS.jar 1G
    320. DCPS_UDP DCPS.jar
    321. ################ 示例 (末)################
    322. # IFCSI IFCSI.war
    323. # IFCSI2.0 IFCSI.jar 2G
    324. # DCPS-HTTP_YD1.0 DCPS-HTTP.jar 1G
    325. # ONENET1.0 DCPS-HTTP.jar 1G
    326. # DCPS-HTTP_YD1.1 DCPS-HTTP.jar 1G
    327. # ONENET1.1 DCPS-HTTP.jar 1G
    328. # DCPS-HTTP_DX DCPS-HTTP.jar 1G
    329. # DXIOT DCPS-HTTP.jar 1G
    330. " >${file}
    331. log_out "创建 [[ ${file} ]] 文件结束"
    332. }
    333. # 清理文件(超过 10240000 行,即被清理)
    334. function cleanFile() {
    335. fileName=$1
    336. lineNum=$(cat ${filePath} | wc -l)
    337. MAX_LINE_NUM=10240000
    338. if [[ ${lineNum} -ge ${MAX_LINE_NUM} ]]; then
    339. echo "" >${fileName}
    340. log_out "[[ ${fileName} ]] 行数:${lineNum},超过 ${MAX_LINE_NUM},将其置空"
    341. fi
    342. }
    343. # 读取文件并启动服务(列表)
    344. function readFileAndStartServer() {
    345. # 服务列表的文件名称(eg. serverList.sh)
    346. serverListFile=$1
    347. log_out "服务列表文件:[[ ${serverListFile} ]]"
    348. # 读取文件(过滤掉 注释行、空行、行首的空格)
    349. egrep -v "^\s*#|^\s*$" ${serverListFile} | while read xxx; do
    350. serverName=$(echo ${xxx} | awk -F ' ' '{print $1}')
    351. jarName=$(echo ${xxx} | awk -F ' ' '{print $2}')
    352. limitSize=$(echo ${xxx} | awk -F ' ' '{print $3}')
    353. startServer ${serverName} ${jarName} ${limitSize}
    354. done
    355. }
    356. # 读取文件夹并启动服务
    357. function readDirAndStartServer() {
    358. # 获取文件夹列表(不显示文件,同时过滤掉 mysql、redis、kafka、sh、License、server_keeper、bak 等无关文件夹,同时默认不启动 IFCSI、ONENET、DXIOT等服务也一并过滤)
    359. dirArray=$(ls -F ${basePath} | grep '/$' | egrep -iv "IFCSI|IFCSI2.0|DCPS-HTTP_YD|TOPSAIL|DPSDK|ONENET|DX|IoT|mysql|redis|kafka|sh|License|server_keeper|ba")
    360. # 遍历,得到各文件夹名称
    361. # shellcheck disable=SC2068
    362. for dirName in ${dirArray[@]}; do
    363. # 也可以写成for element in ${dirArray[*]}
    364. echo "dirName:${dirName}"
    365. done
    366. # 调用执行服务的方法
    367. startServer ${dirName}
    368. }
    369. # 总的调用服务方法
    370. function main() {
    371. while true; do
    372. # 启动 zookeeper 和 kafka
    373. start_zookeeperAndKafka
    374. # 启动 redis
    375. start_redis
    376. # 启动 mysql
    377. start_mysql
    378. # 读取文件,启动服务
    379. readFileAndStartServer serverList.sh
    380. # 读取文件夹,启动服务
    381. readDirAndStartServer
    382. # cleanFile 文件名
    383. cleanFile INFO.log
    384. cleanFile DEBUG.log
    385. cleanFile WARN.log
    386. cleanFile ERROR.log
    387. log_out "~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ${sleepTime} 秒后开始下一轮检查 ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ " D
    388. sleep ${sleepTime}
    389. done
    390. }
    391. #####################################################
    392. #####################################################
    393. #####################################################
    394. #####################################################
    395. #####################################################
    396. #####################################################
    397. # 创建文件
    398. mkDir serverList.sh
    399. # 启动所有服务
    400. main
    401. exit 0