python监听WebSocket并实现故障告警以及自动恢复告警
背景
对腾讯云云监控以及云拔测的功能补充
环境
- python
- swoole
- WebSocket服务器 A 、监听WebSocket服务器的python代码执行服务器 B
- B 服务器安装 websocket-client==0.55.0
- 网易126邮箱的第三方授权密码。
过程
- 利用Swoole在远程服务器 A 启动WebSocket服务。
Swoole 网址:https://www.swoole.com/
Swoole安装 略
<?php
$server = new swoole_websocket_server("0.0.0.0", 9502);
$server->on('open', function($server, $req) {
$server->push($req->fd, json_encode([40311, '']));
echo "connection open: {$req->fd}\n";
});
$server->on('message', function($server, $frame) {
echo "received message: {$frame->data}\n";
$server->push($frame->fd, json_encode(["hello", "world"]));
});
$server->on('close', function($server, $fd) {
echo "connection close: {$fd}\n";
});
$server->start();
将以上代码保存ws.php 并执行 php ws.php 即可启动WebSocket服务
备注:
$server->push($req->fd, json_encode([40311, '']));
此行代码是手动添加的,目的是在成功连接WebSocket服务器之后可接收的返回值。这里的返回值定义的是 [40311, ‘’]
在生产环境中,要跟研发同事对接好在成功连接WebSocket服务之后服务器返回值是多少。然后修改代码config.py内的字典对应值。
2. 在 B 服务器下载或粘贴我的源码,以下是我的代码总览
3. 在代码所在目录执行 python Main.py 即可循环监听config模块内字典配置的ws服务端
源码
#!/usr/bin/env python
#-*-coding:UTF-8-*-
__author__ = '于洪超'
import os, commands
PythonPath = commands.getstatusoutput('which python')[1]
SetSidPath = commands.getstatusoutput('which setsid')[1]
CodePath = os.path.dirname(os.path.realpath(__file__)) + '/'
MonitorWSSFileName = 'MonitorWSS.py'
def MainMonitor():
CmdMonitorWss = "%s %s %s%s >> /dev/null 2>&1 &" % (SetSidPath, PythonPath, CodePath, MonitorWSSFileName)
if os.system(CmdMonitorWss) == 0:
print("\033[32m监控WS站点状态代码 %s 启动成功!\033[0m" % MonitorWSSFileName)
else:
print("\033[31m监控WS站点状态代码 %s 启动失败!\033[0m" % MonitorWSSFileName)
if __name__ == '__main__':
MainMonitor()
#!/usr/bin/env python
#-*-coding:UTF-8-*-
# 监控字典k:监听URL v: [预期返回值, 访问超时时间, 监听频率(秒), 最大告警次数n]
MonitorDict = {
'wss://xxx.xxx.xxx/wss': ['[40311,""]', 5, 5, 2],
'ws://xx.xx.xx.xx:9502': ['[40311,""]', 5, 2, 3],
}
# 发送邮件相关配置
ForSendMess = {
'from': 'xxxxxxxx@126.com',
'password': 'xxxxxxxxxx',
'smtp_server': 'smtp.126.com',
'TOLIST': ['xxxxxxxx@126.com', 'xxxxx@qq.com', ],
}
备注:
需要注意的是 发送邮件相关配置 ForSendMess 字典里 password 这个key的value 是126网易邮箱的第三方授权密码。而不是邮箱密码。此授权密码可以在邮箱设置处设置。
MonitorDict 字典里的 预期返回值逗号后面不能有空格
MonitorWSS.py
#!/usr/bin/env python
#-*-coding:utf-8-*-
from websocket import create_connection
import time
import threading
import SendMess
from config import MonitorDict
# 监听的WSS URL个数
monitorWSScount = len(MonitorDict.keys())
def MonitorWss(i):
sended = 0
while True:
k = MonitorDict.keys()[i]
v = MonitorDict.get(k)
time.sleep(v[2])
try:
ws = create_connection(k, timeout=v[1])
if ws.connected:
if ws.recv() == v[0]:
print "%s %s" % (k, 'connected successfully !')
ws.close()
if sended != 0:
senContent = "%s 当前返回值:%s" % (k, v[0])
SendMess.sendmessage(k, senContent, 'success')
sended = 0
continue
except Exception as e:
# print(str(e))
sended += 1
# print str(sended) + '=============='
if sended < v[3]:
SendMess.sendmessage(k, str(e), 'failure')
elif sended == v[3]:
SendMess.sendmessage(k, str(e), 'failure', 'high', '1')
elif sended > v[3]:
sended = v[3] + 1
print("已经到达最大告警邮件配置,不再发送邮件!")
# 多线程实现
def main(thread_num):
threadingPool = []
for i in xrange(thread_num):
threadingPool.append(threading.Thread(target=MonitorWss, args=(i, )))
for th in threadingPool:
th.setDaemon(True)
th.start()
for th in threadingPool:
threading.Thread.join(th)
if __name__ == '__main__':
main(monitorWSScount)
#!/usr/bin/python
#-*-coding:UTF-8-*-
from __future__ import print_function
import smtplib
from email.header import Header
from email.mime.text import MIMEText
from email.utils import parseaddr, formataddr
from config import ForSendMess
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
from_addr = ForSendMess.get('from')
password = ForSendMess.get('password')
smtp_server = ForSendMess.get('smtp_server')
TOLIST = ForSendMess.get('TOLIST')
def _format_addr(s):
name, addr = parseaddr(s)
return formataddr(( \
Header(name, 'utf-8').encode(), \
addr.encode('utf-8') if isinstance(addr, unicode) else addr
)
)
def sendmessage(WSSNAME, arg2 = '', arg3 = '', arg4 = '', arg5 = ''):
SUBJECT_DOWN = "WS站点:%s 状态异常!请检查!" % WSSNAME
SUBJECT_UP = "WS站点:%s 状态恢复健康!" % WSSNAME
text = "WS站点:%s\r\n信息:%s" % (WSSNAME, arg2) # 定义邮件内容
for to_addr in TOLIST:
if arg3 == 'success':
SUBJECT = SUBJECT_UP
if arg3 == 'failure':
SUBJECT = SUBJECT_DOWN
msg = MIMEText(text, 'plain', 'utf-8')
msg['From'] = _format_addr(u'Davide <%s>' % from_addr)
msg['To'] = _format_addr(u'管理员 <%s>' % to_addr)
msg['Subject'] = Header(u'%s' % SUBJECT, 'utf-8').encode()
# 紧急
level_client = arg4
level_browser = arg5
if level_client:
msg['Importance'] = level_client
if level_browser:
msg['X-Priority'] = level_browser
server = smtplib.SMTP(smtp_server, 25)
server.set_debuglevel(1)
server.login(from_addr, password)
server.sendmail(from_addr, [to_addr], msg.as_string())
server.quit()
#!/usr/bin/env python
#-*-coding:UTF-8-*-
__author__ = '于洪超'
import commands
import sys
#Main.py开启的进程ID列表
cmd1 = "ps -ef |grep MonitorWSS |grep -v grep|awk '{print $2}'"
pList_Main_WS = commands.getstatusoutput(cmd1)[1].split('\n')
pList = pList_Main_WS
def killAll():
for p in pList:
cmd = "kill %s" % p
if commands.getstatusoutput(cmd)[0] == 0:
pass
else:
sys.exit("无法杀死 %s ID进程!" % p)
killAll()
备注:
执行python shutdown.py 关闭循环监听。