1.熱部署中間件-arthas 阿里巴巴
1)用戶文檔:java
https://alibaba.github.io/art...
2)執行命令:git
mkdir -p /home/work/local/arthas-boot
3)下載arthas.jar 至 2)新建的目錄github
4)執行命令web
touch hot_depoy.sh
2.應用apache
使用arthas提供的redefine命令來替換jvm中的class,而無需重啓,從而實現熱部署,固然就阿爾薩斯自己是提供 mc 命令,能夠直接在內存中編譯java文件,可是本身試了幾回失敗了,因此,須要先在本地編譯相關的Java文件,而後再用 redefine命令來實現熱部署。bash
3.本地腳本編寫原理ssh
1)執行命令(簡化)jvm
mvn clean compile -DskipTests
編譯所須要的模塊,獲取相關class文件maven
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」
調用遠程腳本實現熱部署
4、本地腳本以下:
#!/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