From cc2242da7bfaeb57e6f57b3bc2eca948fdf43b8d Mon Sep 17 00:00:00 2001 From: Ramil Valitov Date: Mon, 21 Sep 2020 01:49:37 +0300 Subject: [PATCH] [add] info screen, parallel pending pools analysis --- zabbix/zabbix_php_fpm_discovery.sh | 133 +++++++++++++++++++++++++---- zabbix/zabbix_php_fpm_status.sh | 33 ++++--- 2 files changed, 139 insertions(+), 27 deletions(-) diff --git a/zabbix/zabbix_php_fpm_discovery.sh b/zabbix/zabbix_php_fpm_discovery.sh index 2bc1ec5..f2954ea 100644 --- a/zabbix/zabbix_php_fpm_discovery.sh +++ b/zabbix/zabbix_php_fpm_discovery.sh @@ -11,6 +11,7 @@ # After this duration is reached, the script will stop running and save its state. # So, the actual execution time will be slightly more than this parameter. # We put value equivalent to 1.5 seconds here. +# Allowed minimum value is 1 second, maximum value is 30 seconds (defined by Zabbix). MAX_EXECUTION_TIME="1500" #Status path used in calls to PHP-FPM @@ -34,6 +35,9 @@ CACHE_DIR_NAME="zabbix-php-fpm" #Full path to directory to store cache files CACHE_DIRECTORY="$CACHE_ROOT/$CACHE_DIR_NAME" +#Maximum number of tasks allowed to be run in parallel +MAX_PARALLEL_TASKS=10 + #Checking all the required executables S_PS=$(type -P ps) S_GREP=$(type -P grep) @@ -436,7 +440,10 @@ function CheckExecutionTime() { # Pass two arguments: pool name and pool socket # Function returns: # 0 if the pool is invalid -# 1 if the pool is OK +# 1 if the pool is OK and process manager is dynamic +# 2 if the pool is OK and process manager is static +# 3 if the pool is OK and process manager is ondemand +# 4 if the pool is OK and process manager is unknown function CheckPool() { local POOL_NAME=$1 local POOL_SOCKET=$2 @@ -460,8 +467,16 @@ function CheckPool() { PROCESS_MANAGER=$(echo "$STATUS_JSON" | $S_GREP -oP '"process manager":"\K([a-z]+)') if [[ -n $PROCESS_MANAGER ]]; then PrintDebug "Detected pool's process manager is $PROCESS_MANAGER" - UpdatePoolInCache "$POOL_NAME" "$POOL_SOCKET" "$PROCESS_MANAGER" - return 1 + if [[ $PROCESS_MANAGER == "dynamic" ]]; then + return 1 + elif [[ $PROCESS_MANAGER == "static" ]]; then + return 2 + elif [[ $PROCESS_MANAGER == "ondemand" ]]; then + return 3 + else + PrintDebug "Error: process manager is unknown" + return 4 + fi else PrintDebug "Error: Failed to detect process manager of the pool" fi @@ -630,7 +645,13 @@ function PrintCacheList() { done } -# Functions processes a pool by name: makes all required checks and adds it to cache, etc. +# Function processes a pool by name: makes all required checks and adds it to cache, etc. +# Function returns: +# 0 if the pool is invalid +# 1 if the pool is OK and process manager is dynamic +# 2 if the pool is OK and process manager is static +# 3 if the pool is OK and process manager is ondemand +# 4 if the pool is OK and process manager is unknown function ProcessPool() { local POOL_NAME=$1 local POOL_SOCKET=$2 @@ -647,28 +668,65 @@ function ProcessPool() { else PrintDebug "Error: socket $POOL_SOCKET didn't return valid data" fi - - DeletePoolFromPendingList "$POOL_NAME" "$POOL_SOCKET" - return 1 + return $POOL_STATUS } -for ARG in "$@"; do - if [[ ${ARG} == "debug" ]]; then +ARG_ID=1 +ARGS_COUNT=$# +while [ $ARG_ID -le $ARGS_COUNT ]; do + ARG=$1 + if [[ $ARG == "debug" ]]; then DEBUG_MODE="1" echo "Debug mode enabled" - elif [[ ${ARG} == "sleep" ]]; then + elif [[ $ARG == "sleep" ]]; then USE_SLEEP_TIMEOUT="1" echo "Debug: Sleep timeout enabled" - elif [[ ${ARG} == "nosleep" ]]; then + elif [[ $ARG == "max_tasks" ]]; then + ARG_ID=$((ARG_ID + 1)) + shift 1 + ARG=$1 + if [[ $ARG -ge 1 ]]; then + MAX_PARALLEL_TASKS=$1 + fi + echo "Debug: argument 'max_tasks' = '$ARG' detected, the resulting parameter is set to $MAX_PARALLEL_TASKS" + elif [[ $ARG == "max_time" ]]; then + ARG_ID=$((ARG_ID + 1)) + shift 1 + ARG=$1 + if [[ $ARG -ge 1 ]]; then + MAX_EXECUTION_TIME=$1 + fi + echo "Debug: argument 'max_time' = '$ARG' detected, the resulting parameter is set to $MAX_EXECUTION_TIME" + elif [[ $ARG == "nosleep" ]]; then MAX_EXECUTION_TIME="10000000" echo "Debug: Timeout checks disabled" - elif [[ ${ARG} == /* ]]; then - STATUS_PATH=${ARG} + elif [[ $ARG == "?" ]] || [[ $ARG == "help" ]] || [[ $ARG == "/?" ]]; then + $S_CAT < - sets maximum number of allowed parallel tasks to VALUE (must be >=1) + max_time - sets maximum execution time in ms of the script to VALUE (1000<=VALUE<=30000) + - sets status path used for PHP-FPM. The path should begin with slash symbol '/'. Default value is '/php-fpm-status'. + ? or help or /? - display current information +AUTHOR: Ramil Valitov ramilvalitov@gmail.com +PROJECT PAGE: https://github.com/rvalitov/zabbix-php-fpm +WIKI & DOCS: https://github.com/rvalitov/zabbix-php-fpm/wiki +EOF + exit 1 + elif [[ $ARG == /* ]]; then + STATUS_PATH=$ARG PrintDebug "Argument $ARG is interpreted as status path" else PrintDebug "Argument $ARG is unknown and skipped" fi + ARG_ID=$((ARG_ID + 1)) + shift 1 done + PrintDebug "Current user is $ACTIVE_USER" PrintDebug "Status path to be used: $STATUS_PATH" @@ -717,9 +775,8 @@ POOL_NAMES_LIST=$(${S_PRINTF} '%s\n' "${PS_LIST[@]}" | $S_AWK '{print $NF}' | $S #Update pending list with pools that are active and running while IFS= read -r POOL_NAME; do - AnalyzePool "$POOL_NAME" & + AnalyzePool "$POOL_NAME" done <<<"$POOL_NAMES_LIST" -wait if [[ -n $DEBUG_MODE ]]; then PrintDebug "Pending list generated:" @@ -729,13 +786,57 @@ fi #Process pending list PrintDebug "Processing pools" +PARALLEL_TASKS=0 +TASK_LIST=() +LAST_PENDING_ITEM=${PENDING_LIST[${#PENDING_LIST[@]} - 1]} for POOL_ITEM in "${PENDING_LIST[@]}"; do # shellcheck disable=SC2016 POOL_NAME=$(echo "$POOL_ITEM" | $S_AWK '{print $1}') # shellcheck disable=SC2016 POOL_SOCKET=$(echo "$POOL_ITEM" | $S_AWK '{print $2}') if [[ -n "$POOL_NAME" ]] && [[ -n "$POOL_SOCKET" ]]; then - ProcessPool "$POOL_NAME" "$POOL_SOCKET" + PARALLEL_TASKS=$((PARALLEL_TASKS + 1)) + if [[ $PARALLEL_TASKS -le $MAX_PARALLEL_TASKS ]]; then + PrintDebug "Starting processing task for pool $POOL_NAME $POOL_SOCKET, subprocess #$PARALLEL_TASKS..." + ProcessPool "$POOL_NAME" "$POOL_SOCKET" & + TASK_PID=$! + TASK_LIST+=("$POOL_NAME;$POOL_SOCKET;$TASK_PID") + fi + + if [[ $PARALLEL_TASKS -gt $MAX_PARALLEL_TASKS ]] || [[ $LAST_PENDING_ITEM == "$POOL_ITEM" ]]; then + #Wait till all tasks complete + if [[ $PARALLEL_TASKS -gt $MAX_PARALLEL_TASKS ]]; then + PrintDebug "Max number of parallel tasks reached ($MAX_PARALLEL_TASKS), waiting till they finish..." + else + PrintDebug "No more parallel tasks to start, waiting for running tasks to finish..." + fi + for TASK_LINE in "${TASK_LIST[@]}"; do + # shellcheck disable=SC2016 + POOL_NAME=$(echo "$TASK_LINE" | $S_AWK -F ";" '{print $1}') + # shellcheck disable=SC2016 + POOL_SOCKET=$(echo "$TASK_LINE" | $S_AWK -F ";" '{print $2}') + # shellcheck disable=SC2016 + TASK_PID=$(echo "$TASK_LINE" | $S_AWK -F ";" '{print $3}') + wait $TASK_PID + EXIT_CODE=$? + PrintDebug "Finished parallel task PID $TASK_PID for pool \"$POOL_NAME\" at $POOL_SOCKET" + DeletePoolFromPendingList "$POOL_NAME" "$POOL_SOCKET" + + if [[ $EXIT_CODE -ge 1 ]] && [[ $EXIT_CODE -le 3 ]]; then + if [[ $EXIT_CODE -eq 1 ]]; then + PROCESS_MANAGER="dynamic" + elif [[ $EXIT_CODE -eq 2 ]]; then + PROCESS_MANAGER="static" + else + PROCESS_MANAGER="ondemand" + fi + UpdatePoolInCache "$POOL_NAME" "$POOL_SOCKET" "$PROCESS_MANAGER" + fi + done + PrintDebug "All previously started parallel tasks are complete" + PARALLEL_TASKS=0 + TASK_LIST=() + fi #Confirm that we run not too much time CheckExecutionTime diff --git a/zabbix/zabbix_php_fpm_status.sh b/zabbix/zabbix_php_fpm_status.sh index 71d2ab5..dcb6018 100644 --- a/zabbix/zabbix_php_fpm_status.sh +++ b/zabbix/zabbix_php_fpm_status.sh @@ -5,24 +5,35 @@ S_FCGI=$(type -P cgi-fcgi) S_GREP=$(type -P grep) +S_CAT=$(type -P cat) +if [[ ! -x $S_CAT ]]; then + echo "Utility 'cat' not found. Please, install it first." + exit 1 +fi if [[ ! -x $S_FCGI ]]; then echo "Utility 'cgi-fcgi' not found. Please, install it first. The required package's name depends on your OS type and version and can be 'libfcgi-bin' or 'libfcgi0ldbl' or 'fcgi'." exit 1 fi - if [[ ! -x $S_GREP ]]; then echo "Utility 'grep' not found. Please, install it first." exit 1 fi if [[ -z $1 ]] || [[ -z $2 ]]; then - echo "No input data specified" - echo "Usage: $0 php-path status" - echo "where:" - echo "php-path - path to socket file, for example, /var/lib/php7.3-fpm/web1.sock" - echo "or IP and port of the PHP-FPM, for example, 127.0.0.1:9000" - echo "status - path configured in pm.status of PHP-FPM" + $S_CAT < +OPTIONS: + - either path to socket file, for example, "/var/lib/php7.3-fpm/web1.sock", or IP and port of the PHP-FPM, for example, "127.0.0.1:9000" + - path configured in "pm.status" option of the PHP-FPM pool + +AUTHOR: Ramil Valitov ramilvalitov@gmail.com +PROJECT PAGE: https://github.com/rvalitov/zabbix-php-fpm +WIKI & DOCS: https://github.com/rvalitov/zabbix-php-fpm/wiki +EOF exit 1 fi @@ -31,10 +42,10 @@ POOL_PATH=$2 #connecting to socket or address, https://easyengine.io/tutorials/php/directly-connect-php-fpm/ PHP_STATUS=$( SCRIPT_NAME=$POOL_PATH \ - SCRIPT_FILENAME=$POOL_PATH \ - QUERY_STRING=json \ - REQUEST_METHOD=GET \ - $S_FCGI -bind -connect "$POOL_URL" 2>/dev/null + SCRIPT_FILENAME=$POOL_PATH \ + QUERY_STRING=json \ + REQUEST_METHOD=GET \ + $S_FCGI -bind -connect "$POOL_URL" 2>/dev/null ) echo "$PHP_STATUS" | $S_GREP "{" exit 0