1.热部署中间件-arthas 阿里巴巴
1)用户文档:
https://alibaba.github.io/art...
2)执行命令:
mkdir -p /home/work/local/arthas-boot
3)下载arthas.jar 至 2)新建的目录
4)执行命令
touch hot_depoy.sh
2.应用
使用arthas提供的redefine命令来替换jvm中的class,而无需重启,从而实现热部署,当然就阿尔萨斯本身是提供 mc 命令,可以直接在内存中编译java文件,但是自己试了几次失败了,所以,需要先在本地编译相关的Java文件,然后再用 redefine命令来实现热部署。
3.本地脚本编写原理
1)执行命令(简化)
mvn clean compile -DskipTests
编译所需要的模块,获取相关class文件
2)找到涉及的class文件,通过
scp -P$remote_port $local_clazz_path $remote_user_name@$remote_host:$remote_path
上传相关的class文件
3)本地通过
ssh -p$remote_port $remote_user_name@$remote_host "sh $path/hot_deploy.sh $remote_clazz_path" “$project_name”
调用远程脚本实现热部署
四、本地脚本如下:
#!/usr/bin/env bash
env_name=$1
git_manual_flag=$2
remote_host=
remote_port=
remote_pass_word=
remote_user_name=
remote_path="/home/work/local/arthas-boot/class"
remote_deploy_path="/home/work/local/arthas-boot/hot_deploy.sh"
all_java_class_path=
skip_maven=
check_parameter(){
if [ -z "$env_name" ]; then
echo "please set environment,for example:\033[31m sh hot_deploy.sh t3 git \033[0m"
exit 1
fi
}
config_env(){
case $env_name in
t1)
remote_host="11.11.11.11"
remote_port=22
remote_pass_word="123456"
remote_user_name="root"
;;
*)
echo "env_name error:$env_name"
exit 1
esac
}
process_produce_class(){
modules="$1"
if test -z "$skip_maven"; then
start_time=`date +"%F %T"`
start=`date +%s`
echo "maven compile start $start_time"
maven_modules=$(echo "$modules" | tr "\n" "," | sed 's/,$//g')
mvn clean compile -Dmaven.test.skip=true -T 8C -Dmaven.compile.fork=true -pl "$maven_modules" -am
end_time=`date +"%F %T"`
stop=`date +%s`
echo "maven compile end $end_time,total time:$[ stop - start ]s"
fi
}
scp_class(){
clazz_path="$1"
remote_clazz_path="$2"
sshpass -p "$remote_pass_word" ssh -n "$remote_user_name"@"$remote_host" -p"$remote_port" "mkdir -p $remote_clazz_path"
sshpass -p "$remote_pass_word" scp -P"$remote_port" "$clazz_path" "$remote_user_name"@"$remote_host":"$remote_clazz_path"
if [ ! "$?" -eq 0 ];then
echo "scp class failed clazz_path:$clazz_path"
return 1
fi
return 0
}
change_class(){
clazz_path="$1"
remote_clazz_path="$remote_path/$clazz_path"
project_name="$2"
if test -z "$remote_clazz_path"; then
echo "change_class,remote_clazz_path is not exits"
return
fi
if test -z "$project_name"; then
echo "change_class,project_name is not exits"
return
fi
change_res=$(sshpass -p "$remote_pass_word" ssh -n "$remote_user_name"@"$remote_host" -p"$remote_port" "sh $remote_deploy_path ${remote_clazz_path} ${project_name}")
success_count=$(echo "$change_res" | grep -c 'success')
res=
clazz_name=$(basename "$remote_clazz_path")
remote_failed_messag=
if ! test "$success_count" -eq 0; then
res=$(echo "$change_res" | tail -2 | head -1)
echo "--------------------------------------------------------------------\narthas result:\nclazz_name:$clazz_name\n$res\n-------------------------------------------------------------------"
success_clazz_path="${success_clazz_path}$clazz_path\n"
else
res="$change_res"
remote_failed_message="\nremote_clazz_path:$remote_clazz_path\nproject_name:$project_name\nerror message:"
echo "\033[31m--------------------------------------------------------------------\narthas result:\nclazz_name:$clazz_name${remote_failed_message}\n$res\n--------------------------------------------------------------------\033[0m"
fi
}
produce_class(){
batch_java_path="$1"
modules=`echo "$batch_java_path" | awk -F '/src' '{print $1}' | sort -nr | uniq`
if [ -z "$modules" ]
then
echo "modules is empty"
return 1
fi
echo "modules:$(echo $modules | tr "\n" " ")"
process_produce_class "$modules"
}
process(){
batch_java_path="$1"
batch_clazz_name=`echo "$batch_java_path" | sed 's/src\/main\/java/target\/classes/g' | sed 's/.java/.class/g' | tr "\n" " "`
echo "redefine clazz name:\n$batch_clazz_name"
for clazz in $batch_clazz_name
do
if test -f "$clazz"; then
remote_clzz_dir="$remote_path/$(dirname "$clazz")"
scp_class "$clazz" "$remote_clzz_dir"
if test "$?" -eq 0 ; then
#project_name 不同的项目截取project_name不一样,这个地方需要使用者自己去截取,适配自己的项目,因为在我现在项目里,是分多模块的,在同一台测试机器里是部署了多个项目,所以为了适配各个项目,需要获取项目名称,也就是project_name
project_name=`echo "$clazz" | awk -F '/' '{print $1}'`
if [ "$project_name" == 'web' ]; then
project_name=`echo "$clazz" | awk -F '/' '{print $2}' | awk -F '-' '{print $NF}'`
fi
change_class "$clazz" "$project_name"
fi
else
echo "clazz not exists,clazz:$clazz"
fi
done
}
count_success_failed(){
if test -n "$success_clazz_path"; then
success_class_path=$(echo "$success_clazz_path" | sed 's/\n//g'| sed 's/target\/classes/src\/main\/java/g' | sed 's/.class/.java/g')
for class in "$all_java_class_path"
do
local count=`echo "$success_class_path" | grep -cw "$class"`
if test "$count" -eq 0; then
failed_class_path="$failed_class_path$class\n"
fi
done
else
failed_class_path="$all_java_class_path"
fi
success_class_path=$(echo "$success_class_path" | sed 's/\n$//g')
failed_class_path=$(echo "$failed_class_path" | sed 's/\n$//g')
if test -n "$success_class_path"; then
echo "\033[32m********************************************************************\nsuccess class:\n$success_class_path\n********************************************************************\033[0m"
fi
if test -n "$failed_class_path"; then
echo "\033[34m********************************************************************\nfailed class:\n${failed_class_path}\n********************************************************************\033[0m"
fi
}
main_manual(){
class_names="$1"
echo "class_names:$class_names"
local batch_java_path=
for cn in $class_names
do
local class_name="$cn"
local temp_path=`find . -type f -name "$class_name.java" | sed 's/^.\///g'`
local count=`echo "$temp_path"| grep -c ".java$"`
local java_path=
if [ "$count" -eq 0 ]; then
echo "can not find this class,by name:$class_name"
elif [ "$count" -gt 1 ]; then
echo "\033[31mfile greater than 1,please choose line number: \033[0m"
temp_path_line=`echo "$temp_path" | awk '$0=NR" "$0'`
echo "\033[31m$temp_path_line \033[0m"
read num
java_path=`echo "$temp_path" | sed -n "${num}p"`
if [ "X$java_path" == "X" ]; then
echo "line number error,number:$num"
fi
else
java_path="$temp_path"
fi
if test -f "$java_path"; then
batch_java_path="$batch_java_path$java_path\n"
fi
done
if test -n "$batch_java_path"; then
batch_java_path=$(echo "$batch_java_path" | sed "s/\n$//g")
all_java_class_path="$batch_java_path"
produce_class "$batch_java_path"
process "$batch_java_path"
fi
}
main_skip_maven(){
skip_maven="true"
main_manual "$1"
}
main_git(){
local batch_java_path=`git status | grep -o "modified.*.java$" | awk -F " " '{print $2}'`
if [ -z "$batch_java_path" ]
then
echo "no modify classes by git status"
exit 1
fi
all_java_class_path="$batch_java_path"
produce_class "$batch_java_path"
process "$all_java_class_path"
}
main() {
check_parameter
config_env
if test -n "$git_manual_flag"; then
case "$git_manual_flag" in
git)
echo "use git................."
main_git
;;
skip)
echo "use skip maven................."
shift
shift
main_skip_maven "$*"
;;
*)
shift
echo "use manual.............."
main_manual "$*"
;;
esac
count_success_failed
else
echo "please use the way of git or input other class"
exit 1
fi
}
main "$@"
2.远程脚本
1)远程脚本名称:hot_deploy.sh
2)远程脚本位置:/home/work/local/arthas-boot
3)代码如下:
#!/bin/bash
class_path=$1
project_name=$2
final_port=9998
arthas_path="/home/work/local/arthas-boot"
proc_flag_name=
pid=
get_port(){
case $project_name in
project_a )
final_port=8881
proc_flag_name="org.apache.catalina.startup.Bootstrap"
;;
* )
echo "project_name error:$project_name"
exit 1
;;
esac
}
check_pid(){
pid=$(ps -ef | grep ${proc_flag_name} | grep -v 'grep' | awk '{print $2}')
if [ -z $pid ]; then
echo "pid error, pid:$pid, class_path:$class_path"
fi
}
load_class(){
echo "redefine start,class name:$class_path"
cd $arthas_path
pwd
{
echo redefine $class_path
sleep 3
echo exit
} | "${JAVA_HOME}/bin/java" -jar arthas-boot.jar ${pid} --telnet-port ${final_port} --http-port -1
}
get_port
check_pid
load_class
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。