gfdxy3322 发表于 2018-11-7 11:15:14

通过Redis的Pub/Sub实现对服务器群的监控管理

  需求:
  为了准确监控每个服务器的内部状态,而且也不影响现有业务逻辑的情况,需要快速部署一个简单的
  服务器集中监控系统。经过考虑,可以使用Redis的Pub/Sub功能来实现一个监控系统是非常好合适的。
  当然我们也可以通这个来做批量的管理。
  Pub/Sub 从字面上理解就是发布(Publish)与订阅(Subscribe),在Redis中,你可以设定对某一个key值
  进行消息发布及消息订阅,当一个key值上进行了消息发布后,所有订阅它的客户端都会收到相应的消息。这一功能可以用作实时消息系统。

[*]  发布和订阅机制
[*]  当一个客户端通过 PUBLISH 命令向订阅者发送信息的时候,我们称这个客户端为发布者(publisher)。
[*]  而当一个客户端使用 SUBSCRIBE 或者 PSUBSCRIBE 命令接收信息的时候,我们称这个客户端为订阅者(subscriber)。
[*]  为了解耦发布者(publisher)和订阅者(subscriber)之间的关系,Redis 使用了 channel (频道)作为两者的中介 —— 发布者将信息直接发布给 channel ,而 channel 负责将信息发送给适当的订阅者,发布者和订阅者之间没有相互关系,也不知道对方的存在
  下面这个图适合批量管理,每个客户端的服务器都要有个redis-cli ,安装redis自然就有了,然后定义sub ,在管理端就可以通过pub发布一些个指令,客户端收到后,可以执行,也可以做些相应别的操作。

  下面这个适合监控,把自己的业务指标pub过去,服务端可以进行相应的处理, 可以是写到mysql里面,可以是和页面进行交互。

  其实说白了,用socket也是可以实现的,但是redis已经是较成熟的产品,内部实现采用epoll封装,有多路复用特性,非阻塞IO,而且绝大部分请求是纯粹的内存操作。    有这些个特性,咱们没必要还费劲给socket写个并发异步的重构,逼近太麻烦了。
  那咱们现在就搞吧~
  这个是订阅者

  这个是发布者

  实现不间断的收集数据,可以用php的redis模块直接进行操作~~~~
  但是直接用php对咱们来说,颇有点难度,可以先考虑用下shell~
  google到了一些个资料,但是不能用起来不爽,在这里改了接收的判断和端口的重定向
  redis-bash-cli

[*]  #!/bin/bash
[*]  source /usr/share/redis-bash/redis-bash-lib 2> /dev/null
[*]  if [ $? -ne 0 ]; then
[*]  LIBFOLDER=${0%/${0##*/}}
[*]  source ${LIBFOLDER}/redis-bash-lib 2> /dev/null
[*]  if [ $? -ne 0 ]; then
[*]  echo "can't find redis-bash-lib in /usr/share/redis-bash or ${LIBFOLDER}"
[*]  exit 127
[*]  fi
[*]  fi
[*]  REDISHOST=localhost
[*]  REDISPORT=6379
[*]  REPEAT=1
[*]  DELAY=0
[*]  while getopts ":h:n:p:r:a:i:" opt
[*]  do
[*]  case ${opt} in
[*]  h) REDISHOST=${OPTARG};;
[*]  n) REDISDB=${OPTARG};;
[*]  p) REDISPORT=${OPTARG};;
[*]  r) REPEAT=${OPTARG};;
[*]  a) AUTH=${OPTARG};;
[*]  i) DELAY=${OPTARG};;
[*]  esac
[*]  done
[*]  shift $((${OPTIND} - 1))
[*]  if [ "${REDISHOST}" != "" ] && [ "${REDISPORT}" != "" ]
[*]  then
[*]  exec6/dev/tcp/${REDISHOST}/${REDISPORT} # open fd
[*]  if [ $? -ne 0 ]; then
[*]  exit 1
[*]  fi
[*]  else
[*]  echo "Wrong arguments"
[*]  exit 255
[*]  fi
[*]  [ "${AUTH}" != "" ] && redis-client 6 AUTH ${AUTH} > /dev/null
[*]  [ "${REDISDB}" != "" ] && redis-client 6 SELECT ${REDISDB} > /dev/null
[*]  for ((z=1;z&- #close fd
  redis-bash-lib

[*]  #!/bin/bash
[*]  function redis-client() {
[*]  FD=${1}
[*]  shift;
[*]  if [ ${#} -ne 0 ]; then # always use unified protocol and let the server validate the number of parameters
[*]  local ARRAY=( "${@}" )
[*]  local CMD=("*$[${#ARRAY[@]}]")
[*]  local i=0
[*]  for ((i=0;i&${FD}
[*]  fi
[*]  local ARGV
[*]  read -r -u ${FD}
[*]  REPLY=${REPLY:0:${#REPLY}-1}
[*]  case ${REPLY} in
[*]  -*|\$-*) # error message
[*]  echo "${REPLY:1}"
[*]  return 1;;
[*]
  \$*) # message>
[*]
  [ ${BASH_VERSINFO} -eq 3 ] &&>
[*]  [ ${REPLY:1} -gt 0 ] && read -r -${SIZEDELIM:-N} $[${REPLY:1}+2] -u ${FD} # read again to get the value itself
[*]  ARGV=( "${REPLY:0:$[${#REPLY}-$[${BASH_VERSINFO}-2]]}" );;
[*]  :*) # integer message
[*]  ARGV=( "${REPLY:1}" );;
[*]  \**) # bulk reply - recursive based on number of messages
[*]  unset ARGV
[*]  for ((ARGC="${REPLY:1}";${ARGC}>0;ARGC--)); do
[*]  ARGV=("${ARGV[@]}" $(redis-client ${FD}))
[*]  done;;
[*]  +*) # standard message
[*]  ARGV=( "${REPLY:1}" );;
[*]  *) # wtf? just in case...
[*]  ARGV=( "${ARGV[@]}" "${REPLY}" );;
[*]  esac
[*]  printf "%s\n" "${ARGV[@]}"
[*]  }
  redis-publish-test

[*]  #!/bin/bash
[*]  source /usr/share/redis-bash/redis-bash-lib 2> /dev/null
[*]  if [ $? -ne 0 ]; then
[*]  LIBFOLDER=${0%/${0##*/}}
[*]  echo $LIBFOLDER
[*]  source ${LIBFOLDER}/redis-bash-lib 2> /dev/null
[*]  if [ $? -ne 0 ]; then
[*]  echo "can't find redis-bash-lib in /usr/share/redis-bash or ${LIBFOLDER}"
[*]  exit 127
[*]  fi
[*]  fi
[*]  REDISHOST=localhost
[*]  REDISPORT=6379
[*]  while getopts ":h:p:" opt
[*]  do
[*]  case ${opt} in
[*]  h) REDISHOST=${OPTARG};;
[*]  p) REDISPORT=${OPTARG};;
[*]  esac
[*]  done
[*]  shift $((${OPTIND} - 1))
[*]  while true
[*]  do
[*]  exec 5>&-
[*]  if [ "${REDISHOST}" != "" ] && [ "${REDISPORT}" != "" ]
[*]  then
[*]  exec 5/dev/tcp/${REDISHOST}/${REDISPORT} # open fd
[*]  else
[*]  echo "Wrong arguments"
[*]  exit 255
[*]  fi
[*]  redis-client 5 SUBSCRIBE ${1} > /dev/null # subscribe to the pubsub channel in fd 5
[*]  while true
[*]  do
[*]  unset ARGV
[*]  OFS=${IFS};IFS=$'\n' # split the return correctly
[*]  ARGV=($(redis-client 5))
[*]  IFS=${OFS}
[*]  if [ "${ARGV}" = "message" ] && [ "${ARGV}" = "${1}" ]
[*]  then
[*]  echo ${ARGV}
[*]  a=${ARGV}
[*]  echo $($a)
[*]  echo "Message from pubsub channel: ${ARGV}"
[*]  elif [ -z ${ARGV} ]
[*]  then
[*]  sleep 1
[*]  break
[*]  fi
[*]  done
[*]  done
  这边是订阅者,大家会发现我在订阅者那边发布的信息,这边都接收了,而且执行了linux的命令

  这边是发布者

  下面是php的例子,在这里咱们可以把php当做脚本用。
  首先是编译安装phpredis扩展,扩展的地址和api手册可见:https://github.com/nicolasff/phpredis
  发布者

[*]  $redis = new Redis();
[*]  $redis->connect('127.0.0.1',6379);
[*]  $channel = $argv;// channel
[*]  $msg = $argv; // msg
[*]  $redis->publish('channel'.$channel, $msg);
订阅者
[*]  $redis = new Redis();
[*]  $redis->connect('127.0.0.1',6379);
[*]  $channel = $argv;// channel
[*]  $redis->subscribe(array('channel'.$channel), 'callback');
[*]  function callback($instance, $channelName, $message) {
[*]  echo $channelName, "==>", $message,PHP_EOL;
[*]  }
  可以分别开三个终端窗口
  在终端1中执行:
  php redis-sub.php 1
  在终端2中执行:
  php redis-sub.php 2
  在终端3中执行:
  php redis-pub.php 1 hello
  此时在终端1中可以打印出hello;
  执行
  php redis-pub.php 2 world
  则在终端2中打印出world。


页: [1]
查看完整版本: 通过Redis的Pub/Sub实现对服务器群的监控管理