本地實現Java遠程熱部署

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
相關文章
相關標籤/搜索