
目录一、app.sh对焦一主要功能概述二代码展示优点分析三改进空间思考四改进和优化增强安全性改进错误处理提高可维护性增强扩展性五优化后的代码展示二、start.sh对焦一主要功能思考二具体代码展示三改进空间思考四优化后的代码展示三、总结思考干货分享感谢您的阅读在现代的微服务架构中如何高效地管理和启动Java服务以及如何动态配置应用程序成为关键挑战。本文将详细介绍一套基于Shell脚本的Java服务管理和配置初始化自动化方案涵盖服务的启动、停止、状态监控和配置文件的动态更新。通过这套方案可以显著简化服务管理流程提升系统的稳定性和维护效率。一、app.sh对焦Shell脚本目标用于管理一个Java服务的启动、停止、重启和状态查询。一主要功能概述启动start检查服务是否已经运行如果没有则使用Java命令启动服务并将进程ID写入PID文件。停止stop从PID文件中读取服务进程ID然后先发送普通kill信号等待一段时间后检查进程是否停止如未停止则发送强制kill信号。状态status检查服务是否在运行如果在运行则输出进程ID否则显示未运行信息。重启restart先停止服务然后再启动服务实现服务的重启操作。二代码展示#!/bin/sh ## java env export JAVA_HOME/opt/jdk1.8.0 SERVICE_NAMEservicename JAR_NAME$SERVICE_NAME-pkgvernum-RELEASE.jar #PID 代表是PID文件 PID/home/zhangyanfeng/microsvr/$SERVICE_NAME/$SERVICE_NAME.pid #使用说明用来提示输入参数 usage() { echo Usage: sh app.sh [start|stop|restart|status] exit 1 } #检查程序是否在运行 is_exist(){ pidps -ef|grep $JAR_NAME|grep -v grep|awk {print $2} #如果不存在返回1存在返回0 if [ -z ${pid} ]; then return 1 else return 0 fi } #启动方法 start(){ is_exist if [ $? -eq 0 ]; then echo ${SERVICE_NAME} is already running PID${pid} else nohup $JAVA_HOME/bin/java -Xms256m -Xmx512m -Djava.security.auth.login.config./config/kafka_zoo_server_jaas.properties -jar $JAR_NAME /dev/null 21 echo $! $PID echo start $SERVICE_NAME successed PID$! fi } #停止方法 stop(){ #is_exist pidf$(cat $PID) #echo $pidf echo ${SERVICE_NAME} PID $pidf begin kill $pidf kill $pidf rm -rf $PID sleep 2 is_exist if [ $? -eq 0 ]; then echo ${SERVICE_NAME} 2 PID $pid begin kill -9 $pid kill -9 $pid sleep 2 echo $SERVICE_NAME process stopped else echo ${SERVICE_NAME} is not running fi } #输出运行状态 status(){ is_exist if [ $? -eq 0 ]; then echo ${SERVICE_NAME} is running PID is ${pid} else echo ${SERVICE_NAME} is not running fi } #重启 restart(){ stop start } cd /home/zhangyanfeng/microsvr/$SERVICE_NAME/ #根据输入参数选择执行对应方法不输入则执行使用说明 case $1 in start) start ;; stop) stop ;; status) status ;; restart) restart ;; *) usage ;; esac exit 0优点分析功能完整覆盖了服务的启动、停止、重启和状态查询使用简单直观。使用了PID文件来跟踪和管理服务进程确保了操作的精确性。对Java环境的配置和服务启动命令使用了标准化的设置提高了可移植性和可维护性。三改进空间思考安全性在获取PID并执行kill操作时可以增加安全性检查确保只杀死属于当前用户的进程。错误处理增加更多的错误处理和日志记录以便于故障排查和调试。健壮性在启动和停止服务时可以增加超时机制或重试逻辑以应对异常情况。可维护性脚本中出现了一些硬编码路径如PID文件路径可以考虑将这些路径定义为变量提高代码的灵活性和可维护性。增加注释和文档解释脚本的各个部分功能和用法方便他人理解和修改。扩展性脚本当前只支持单个服务的管理如需支持多个服务可以考虑增加参数来动态传递服务名称和相关配置。四改进和优化可以从几个方面入手包括安全性、错误处理、可维护性和扩展性等。增强安全性限制kill权限在执行kill操作之前可以添加权限检查确保只能kill当前用户的进程防止误操作或恶意行为。# 在stop函数中添加权限检查 if [ $(id -u) -ne $(stat -c %u $PID) ]; then echo Error: You dont have permission to stop this service. return 1 fi加强PID文件安全性PID文件应当使用更安全的权限设置仅限当前用户和相关服务进程访问。PID/var/run/$SERVICE_NAME.pid改进错误处理增加错误日志在关键操作处增加错误日志记录到日志文件中便于后续故障排查和调试。# 示例在启动方法中增加错误日志 start(){ is_exist if [ $? -eq 0 ]; then echo ${SERVICE_NAME} is already running PID${pid} else nohup $JAVA_HOME/bin/java -Xms256m -Xmx512m -Djava.security.auth.login.config./config/kafka_zoo_server_jaas.properties -jar $JAR_NAME /dev/null 21 echo $! $PID if [ $? -eq 0 ]; then echo start $SERVICE_NAME successed PID$! else echo !!! Failed to start $SERVICE_NAME !!! return 1 fi fi }超时和重试机制在停止服务时可以添加超时和重试机制确保服务能够在合理的时间内停止。提高可维护性参数化路径和配置将硬编码的路径和配置参数化提高灵活性和可维护性。# 示例参数化JAR包路径和PID文件路径 JAR_NAME$SERVICE_NAME-pkgvernum-RELEASE.jar PID_DIR/var/run/${SERVICE_NAME} PID_FILE${PID_DIR}/${SERVICE_NAME}.pid增加注释和文档在关键代码处添加注释解释脚本的功能和逻辑以及每个函数的用途和参数说明。# 示例增加函数注释和使用说明 # 启动服务 start(){ is_exist if [ $? -eq 0 ]; then echo ${SERVICE_NAME} is already running PID${pid} else nohup $JAVA_HOME/bin/java -Xms256m -Xmx512m -Djava.security.auth.login.config./config/kafka_zoo_server_jaas.properties -jar $JAR_NAME /dev/null 21 echo $! $PID_FILE if [ $? -eq 0 ]; then echo start $SERVICE_NAME successed PID$! else echo !!! Failed to start $SERVICE_NAME !!! return 1 fi fi }增强扩展性支持多个服务管理允许脚本通过参数化的方式管理多个不同名称的服务增强脚本的通用性。# 示例支持通过参数管理不同的服务 case $1 in start) SERVICE_NAME$2 # 第二个参数为服务名称 start ;; stop) SERVICE_NAME$2 stop ;; ... esac支持配置文件将常用的配置参数提取到配置文件中如Java路径、服务名称等以便统一管理和修改。五优化后的代码展示#!/bin/bash # # app.sh # Description: Script to manage a Java service # # Java environment export JAVA_HOME/opt/jdk1.8.0 # Service variables SERVICE_NAMEservicename JAR_NAME$SERVICE_NAME-pkgvernum-RELEASE.jar PID_DIR/var/run/${SERVICE_NAME} PID_FILE${PID_DIR}/${SERVICE_NAME}.pid # Usage function usage() { echo Usage: $0 [start|stop|restart|status] [service_name] exit 1 } # Check if the service is running is_exist() { pid$(cat $PID_FILE 2/dev/null) if [ -z $pid ]; then return 1 # Not running else if ps -p $pid /dev/null; then return 0 # Running else return 1 # Not running fi fi } # Start the service start() { is_exist if [ $? -eq 0 ]; then echo $SERVICE_NAME is already running with PID$pid else nohup $JAVA_HOME/bin/java -Xms256m -Xmx512m -Djava.security.auth.login.config./config/kafka_zoo_server_jaas.properties -jar $JAR_NAME /dev/null 21 echo $! $PID_FILE if [ $? -eq 0 ]; then echo $SERVICE_NAME started successfully with PID$(cat $PID_FILE) else echo !!! Failed to start $SERVICE_NAME !!! return 1 fi fi } # Stop the service stop() { if [ ! -f $PID_FILE ]; then echo $SERVICE_NAME is not running return 1 fi pid$(cat $PID_FILE) echo Stopping $SERVICE_NAME PID$pid kill $pid sleep 2 if ps -p $pid /dev/null; then echo Force stopping $SERVICE_NAME PID$pid kill -9 $pid sleep 2 fi rm -f $PID_FILE echo $SERVICE_NAME stopped } # Check the status of the service status() { is_exist if [ $? -eq 0 ]; then echo $SERVICE_NAME is running with PID$(cat $PID_FILE) else echo $SERVICE_NAME is not running fi } # Restart the service restart() { stop start } # Change directory to the service location cd /home/zhangyanfeng/microsvr/$SERVICE_NAME/ # Main script execution based on input arguments case $1 in start) SERVICE_NAME${2:-$SERVICE_NAME} # Use provided or default service name start ;; stop) SERVICE_NAME${2:-$SERVICE_NAME} stop ;; status) SERVICE_NAME${2:-$SERVICE_NAME} status ;; restart) SERVICE_NAME${2:-$SERVICE_NAME} restart ;; *) usage ;; esac exit 0二、start.sh对焦Shell脚本start.sh的主要功能和目的是管理一个Java服务的运行环境。一主要功能思考管理日志和配置确保服务日志和配置文件的管理包括定时清理旧日志和初始化配置文件。动态配置更新通过confd和writeConfigValue函数实现动态更新服务的配置避免了手动修改配置文件的复杂性和错误风险。服务重启和维护使用${MICRODIR}/app.sh restart命令来管理Java服务的启动和重启保证配置变更后服务的正常运行。安全和可靠性通过权限设置和错误处理保证了脚本执行的安全性和可靠性避免了意外操作对系统的影响。这段脚本的设计旨在简化运维工作自动化配置更新和服务重启提高了系统的可维护性和稳定性。二具体代码展示#!/bin/sh cd /home/ find ./ -maxdepth 1 -name start*.log -mtime 7 -exec rm {} \; TODAYdate %Y%m%d getTimeStamp() { echo date %Y-%m-%d %H:%M:%S } log() { echo $1 echo getTimeStamp : $1 /home/start-${TODAY}.log } MICRODIR/home/zhangyanfeng/microsvr/zyf-service-user CONFIGDIR${MICRODIR}/config TEMPLDIR/etc/confd/templates NOWTIMEdate %Y%m%d%H%M%S init_confd() { log init confd ... if [ ! -f ${TEMPLDIR}/application.tmpl ] then log cp -af ${CONFIGDIR}/application.properties ${TEMPLDIR}/application.tmpl cp -af ${CONFIGDIR}/application.properties ${TEMPLDIR}/application.tmpl else log ${TEMPLDIR}/application.tmpl is exist, ignore. fi if [ ! -f ${TEMPLDIR}/bootstrap.tmpl ] then log cp -af ${CONFIGDIR}/bootstrap.properties ${TEMPLDIR}/bootstrap.tmpl cp -af ${CONFIGDIR}/bootstrap.properties ${TEMPLDIR}/bootstrap.tmpl else log ${TEMPLDIR}/bootstrap.tmpl is exist, ignore. fi chmod 755 -R ${TEMPLDIR} } common_softlink() { targetdir$1 sourcedir$2 if [ ! -d ${targetdir} ] then mkdir -p ${targetdir} log mkdir -p ${targetdir} fi if [ -L ${sourcedir} ] then log rm ${sourcedir} rm ${sourcedir} fi if [ -d ${sourcedir} ] then log mv -f ${sourcedir} ${sourcedir}_${NOWTIME} mv -f ${sourcedir} ${sourcedir}_${NOWTIME} fi ln -s ${targetdir} ${sourcedir} chmod 755 ${sourcedir} chown zxin10:root ${sourcedir} chmod -R 755 ${targetdir} chown -R zxin10:root ${targetdir} } init_softlink() { UNAMEuname -a MICRONAME1echo $UNAME | cut -d -f 2 MICRONAME2echo $MICRONAME1 | cut -d - -f 1-3 log_dir/home/log/$MICRONAME2/$MICRONAME1 common_softlink ${log_dir} /home/zhangyanfeng/microsvr/zyf-service-user/logs } start_confd() { log start confd... CONFDPID ps -ef|grep confd|grep -v grep|awk {print $2} if [ -n ${CONFDPID} ] then log stop confd kill -9 ${CONFDPID} sleep 3 fi ETCDCONFPATH/home/zhangyanfeng/confdconfig/confd-general-config.conf ETCD_SERVER_IPgrep ETCD_SERVER_HOSTIP ${ETCDCONFPATH} | awk -F {print $2} ETCD_SERVER_PORTgrep ETCD_SERVER_PORT ${ETCDCONFPATH} | awk -F {print $2} CONFD_SERVER${ETCD_SERVER_IP}:${ETCD_SERVER_PORT} log /usr/bin/confd -interval 60 -backend etcd -node http://${CONFD_SERVER} /usr/bin/confd -interval 60 -backend etcd -node http://${CONFD_SERVER} } function getConfigValue { [ $# -ne 2 ] echo function getConfigValue arguments error local fileName$1 local key$2 echo cat ${fileName}| grep ${key}| grep | awk -F {print $2} | tr -d } function writeConfigValue() { K8SCONFIGDIR/home/zhangyanfeng/confdconfig K8SCONFIGFILE${K8SCONFIGDIR}/confd-general-config.conf DBMASTURLgetConfigValue ${K8SCONFIGFILE} pgconn | awk -F , {print $1} DBSLAVEURLgetConfigValue ${K8SCONFIGFILE} pgconn | awk -F , {print $2} REDISURLgetConfigValue ${K8SCONFIGFILE} redisconn KAFKAURLgetConfigValue ${K8SCONFIGFILE} kafkaconn ZOOKEEPURLgetConfigValue ${K8SCONFIGFILE} zkconn IPSEGEMENTgetConfigValue ${K8SCONFIGFILE} innernetworksegement LIANCHUANGURLgetConfigValue ${K8SCONFIGFILE} lianchuang_url if [ -f ${CONFIGDIR}/application.properties ] then if [ -n ${IPSEGEMENT} ] then sed -i -e /^genid/s/\/[0-9]\{1,3\}.[0-9]\{1,3\}.[0-9]./\/${IPSEGEMENT}./g ${CONFIGDIR}/application.properties fi if [ -n ${ZOOKEEPURL} ] then sed -i -e /^spring.cloud.stream.kafka.binder.zk-nodes/s/spring.cloud.stream.kafka.binder.zk-nodes.*/spring.cloud.stream.kafka.binder.zk-nodes${ZOOKEEPURL}/ ${CONFIGDIR}/application.properties fi if [ -n ${KAFKAURL} ] then sed -i -e /^kafka.binder.brokers/s/kafka.binder.brokers.*/kafka.binder.brokers${KAFKAURL}/ ${CONFIGDIR}/application.properties sed -i -e /^spring.cloud.stream.kafka.binder.brokers/s/spring.cloud.stream.kafka.binder.brokers.*/spring.cloud.stream.kafka.binder.brokers${KAFKAURL}/ ${CONFIGDIR}/application.properties fi if [ -n ${LIANCHUANGURL} ] then BRFOREVALUEgetConfigValue ${CONFIGDIR}/application.properties lianchuang.url BRFORE${BRFOREVALUE#*//} sed -i s/${BRFORE}/${LIANCHUANGURL}/g ${CONFIGDIR}/application.properties fi fi if [ -f ${CONFIGDIR}/bootstrap.properties ] then if [ -n ${IPSEGEMENT} ] then sed -i -e /^eureka.client.serviceUrl.defaultZone/s/\/[0-9]\{1,3\}.[0-9]\{1,3\}.[0-9]./\/${IPSEGEMENT}./g ${CONFIGDIR}/bootstrap.properties fi fi } MAIN FUNCTION init_softlink #init_confd #start_confd writeConfigValue cd ${MICRODIR} log ${MICRODIR}/app.sh restart ${MICRODIR}/app.sh restart export TERMlinux /usr/sbin/sshd -D三改进空间思考错误处理和日志记录增强在关键操作前后增加更详细的错误处理和日志记录特别是在文件操作、进程管理和配置更新过程中确保可以追踪到每一步的执行情况和可能的异常。参数化和配置文件分离将脚本中的硬编码路径和配置参数提取为变量甚至考虑将配置信息存储在单独的配置文件中这样可以使脚本更具灵活性和可配置性便于维护和部署到不同的环境。安全性增强在涉及到重启服务和管理权限的地方确保操作的安全性。可以考虑使用更严格的权限控制或者使用特定的用户执行关键操作以防止误操作或者恶意访问。代码重构和模块化如果脚本变得足够复杂可以考虑将功能模块化每个函数尽可能独立和可测试。这样可以简化逻辑提高代码的复用性和可读性。定时任务和自动化如果需要定时执行清理日志或者其他操作可以考虑将脚本集成到系统的定时任务中或者使用专门的自动化工具来管理和调度任务避免手动执行。文档和注释在脚本中增加详细的注释和文档解释每个函数的作用、输入参数和返回值。这样可以帮助新的开发人员或者维护者快速理解脚本的功能和设计思路。四优化后的代码展示#!/bin/bash # Set Java environment export JAVA_HOME/opt/jdk1.8.0 # Service configuration SERVICE_NAMEservicename JAR_NAME${SERVICE_NAME}-pkgvernum-RELEASE.jar PID_FILE/home/zhangyanfeng/microsvr/${SERVICE_NAME}/${SERVICE_NAME}.pid # Directories MICRO_DIR/home/zhangyanfeng/microsvr/zyf-service-user CONFIG_DIR${MICRO_DIR}/config TEMPLATE_DIR/etc/confd/templates LOG_DIR/home/log # Log function to timestamp logs log() { local timestamp$(date %Y-%m-%d %H:%M:%S) echo $timestamp : $1 echo $timestamp : $1 /home/start-$(date %Y%m%d).log } # Initialize confd templates init_confd() { log Initializing confd... for tmpl_file in ${TEMPLATE_DIR}/application.tmpl ${TEMPLATE_DIR}/bootstrap.tmpl; do if [ ! -f ${tmpl_file} ]; then log Copying ${CONFIG_DIR}/$(basename ${tmpl_file}) to ${TEMPLATE_DIR} cp -af ${CONFIG_DIR}/$(basename ${tmpl_file}) ${tmpl_file} else log ${tmpl_file} already exists, skipping. fi done chmod -R 755 ${TEMPLATE_DIR} } # Function to manage symbolic links manage_symlink() { local target_dir$1 local source_dir$2 if [ ! -d ${target_dir} ]; then mkdir -p ${target_dir} log Created directory ${target_dir} fi if [ -L ${source_dir} ]; then log Removing existing symlink ${source_dir} rm ${source_dir} fi if [ -d ${source_dir} ]; then log Moving existing directory ${source_dir} to ${source_dir}_${NOWTIME} mv ${source_dir} ${source_dir}_${NOWTIME} fi ln -s ${target_dir} ${source_dir} chmod 755 ${source_dir} chown zxin10:root ${source_dir} chmod -R 755 ${target_dir} chown -R zxin10:root ${target_dir} } # Initialize symbolic links init_softlink() { local uname_info$(uname -a) local microname1$(echo ${uname_info} | cut -d -f 2) local microname2$(echo ${microname1} | cut -d - -f 1-3) local log_dir/home/log/${microname2}/${microname1} manage_symlink ${log_dir} /home/zhangyanfeng/microsvr/zyf-service-user/logs } # Start confd service start_confd() { log Starting confd... local confd_pid$(pgrep -f confd) if [ -n ${confd_pid} ]; then log Stopping existing confd process (PID ${confd_pid}) kill -9 ${confd_pid} sleep 3 fi local confd_config_file/home/zhangyanfeng/confdconfig/confd-general-config.conf local etcd_server_host$(grep ETCD_SERVER_HOSTIP ${confd_config_file} | cut -d -f 2) local etcd_server_port$(grep ETCD_SERVER_PORT ${confd_config_file} | cut -d -f 2) local confd_server${etcd_server_host}:${etcd_server_port} log Starting confd with backend etcd and node http://${confd_server} /usr/bin/confd -interval 60 -backend etcd -node http://${confd_server} } # Function to get configuration value from file getConfigValue() { if [ $# -ne 2 ]; then log getConfigValue arguments error return 1 fi local file_name$1 local key$2 grep ${key} ${file_name} | grep | awk -F {print $2} | tr -d } # Function to write configuration values to application.properties and bootstrap.properties writeConfigValue() { local k8s_config_dir/home/zhangyanfeng/confdconfig local k8s_config_file${k8s_config_dir}/confd-general-config.conf local db_master_url$(getConfigValue ${k8s_config_file} pgconn | awk -F , {print $1}) local db_slave_url$(getConfigValue ${k8s_config_file} pgconn | awk -F , {print $2}) local redis_url$(getConfigValue ${k8s_config_file} redisconn) local kafka_url$(getConfigValue ${k8s_config_file} kafkaconn) local zookeeper_url$(getConfigValue ${k8s_config_file} zkconn) local ip_segment$(getConfigValue ${k8s_config_file} innernetworksegement) local lianchuang_url$(getConfigValue ${k8s_config_file} lianchuang_url) if [ -f ${CONFIG_DIR}/application.properties ]; then if [ -n ${ip_segment} ]; then sed -i -e /^genid/s/\/[0-9]\{1,3\}.[0-9]\{1,3\}.[0-9]./\/${ip_segment}./g ${CONFIG_DIR}/application.properties fi if [ -n ${zookeeper_url} ]; then sed -i -e /^spring.cloud.stream.kafka.binder.zk-nodes/s/spring.cloud.stream.kafka.binder.zk-nodes.*/spring.cloud.stream.kafka.binder.zk-nodes${zookeeper_url}/ ${CONFIG_DIR}/application.properties fi if [ -n ${kafka_url} ]; then sed -i -e /^kafka.binder.brokers/s/kafka.binder.brokers.*/kafka.binder.brokers${kafka_url}/ ${CONFIG_DIR}/application.properties sed -i -e /^spring.cloud.stream.kafka.binder.brokers/s/spring.cloud.stream.kafka.binder.brokers.*/spring.cloud.stream.kafka.binder.brokers${kafka_url}/ ${CONFIG_DIR}/application.properties fi if [ -n ${lianchuang_url} ]; then local before_value$(getConfigValue ${CONFIG_DIR}/application.properties lianchuang.url) local before$(echo ${before_value} | sed s#^.*//##) sed -i s/${before}/${lianchuang_url}/g ${CONFIG_DIR}/application.properties fi fi if [ -f ${CONFIG_DIR}/bootstrap.properties ]; then if [ -n ${ip_segment} ]; then sed -i -e /^eureka.client.serviceUrl.defaultZone/s/\/[0-9]\{1,3\}.[0-9]\{1,3\}.[0-9]./\/${ip_segment}./g ${CONFIG_DIR}/bootstrap.properties fi fi } # Main function main() { init_softlink # init_confd # Uncomment if confd initialization is required # start_confd # Uncomment if confd start is required writeConfigValue cd ${MICRO_DIR} log Restarting ${MICRO_DIR}/app.sh ${MICRO_DIR}/app.sh restart export TERMlinux /usr/sbin/sshd -D } # Entry point main三、总结思考在这个自动化管理和配置Java服务的方案中我们通过优化和整合多个脚本实现了一套完整的管理工具。主要脚本包括app.sh和start.sh它们分别负责启动、停止、重启服务以及初始化配置、软链接管理、动态更新配置文件和日志记录等功能。通过app.sh我们可以方便地对服务进行操作包括启动、停止、重启和查询状态保证了服务的稳定运行。在start.sh中我们实现了对配置文件的动态更新通过 confd 工具实时同步配置信息确保服务配置的灵活性和实时性。同时脚本中的日志记录功能能够帮助追踪和排查问题保障服务的可靠性和可维护性。我们不仅简化了管理流程提高了运维效率还增强了服务的可配置性和自动化程度。通过这样的自动化工具链我们能够更专注于业务逻辑的开发和优化同时保持对服务稳定性和可管理性的高度把控。