[Python网络编程]浅析守护进程后台任务的设计与实现
在做基于B/S应用中。常常有须要后台执行任务的需求,最简单比方发送邮件。在一些如防火墙,WAF等项目中,前台仅仅是为了展示内容与各种參数配置。后台守护进程才是重头戏。所以在防火墙配置页面中可能会常常看到调用cgi。但真正做事的一般并非cgi,比方说执行关机命令,他们的逻辑例如以下:
(ps:上图所说的前台界面包括通常web开发中的后端,不然也没有socket一说)
为什么要这么设计
怎样实现
class MgrService(win32serviceutil.ServiceFramework):
"""
Usage: 'python topmgr.py install|remove|start|stop|restart'
"""
#服务名
_svc_name_ = "Mgr"
#服务显示名称
_svc_display_name_ = "Daemon Mgr"
#服务描写叙述
_svc_description_ = "Daemon Mgr" def __init__(self, args):
win32serviceutil.ServiceFramework.__init__(self, args)
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None) def SvcDoRun(self):
self.ReportServiceStatus(win32service.SERVICE_START_PENDING)
INFO("mgr startting...")
self.ReportServiceStatus(win32service.SERVICE_RUNNING)
self.start()
# 等待服务被停止
INFO("mgr waitting...")
win32event.WaitForSingleObject(self.hWaitStop, win32event.INFINITE)
INFO("mgr end") def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
INFO("mgr stopping...")
self.stop()
INFO("mgr stopped")
# 设置事件
win32event.SetEvent(self.hWaitStop)
self.ReportServiceStatus(win32service.SERVICE_STOPPED) def start(self): pass def stop(self): pass
非常easy。这样就实现了windows中的服务,也就是说脱离终端。执行于后台。
INFO等函数仅仅是简单的记录作用。可直接忽略。
class Engine(MgrService):
rbufsize = -1
wbufsize = 0 def start(self):
INFO('wait connection')
self.server = StreamServer((HOST, PORT), self.msg_handle)
self.server.serve_forever() def msg_handle(self,socket,address):
try:
rfile = socket.makefile('rb', self.rbufsize)
wfile = socket.makefile('wb', self.wbufsize)
headers = Message(rfile).dict INFO('get a connection from:%s,headers:%s' % (str(address), headers)) if 'module' in headers and headers['module'] in MODULES:
MODULES[headers['module']].handle(wfile, headers)
except Exception:
ERROR('msg_handle exception,please check') def stop(self):
if hasattr(self, server):
self.server.stop()
当有新连接到来,由msg_handle处理,首先读取发送来的消息。消息格式使用了最简单的http的格式,即(键名:键值)的格式,你要问我为什么採用这个格式,哈哈,格式简单,python有现成的库解析。
上面代码的那个MODULES是个全局变量,当你加入一个模块的时候须要注冊到MODULES中,我提供了module_register方法。
MODULES = { # module: handle module class
}
def module_register(module_name, handle_class):
if module_name in MODULES:
WARN('duplicate module_name:' + module_name)
else:
MODULES[module_name] = handle_class
class Module(object):
SECRE_KEY = "YI-LUO-KEHAN"
MODULE_NAME = "BASE_MODULE"
PREFIX = "do_" # method prefix def __init__(self, wfile, headers):
self.wfile = wfile
self.headers = headers def __getattr__(self, name):
try:
return self.headers[name]
except Exception:
ERROR("%s has no attr:%s,please check" %(self.MODULE_NAME, name)) @classmethod
def handle(cls, wfile, headers):
module_obj = cls(wfile, headers)
module_obj.schedule_default() def verify(self):
if hmac.new(self.SECRE_KEY, self.MODULE_NAME).hexdigest() == self.signature:
return True
else:
WARN("client verify failed,signature:%s" % str(self.signature)) def schedule_default(self):
err_code = 0
if self.verify() and self.action:
func_name = self.PREFIX + self.action
try:
getattr(self, func_name)()
except AttributeError:
err_code = 1
ERROR("%s has no method:%s" %(self.MODULE_NAME, func_name))
except Exception:
err_code = 2
ERROR("module:%s,method:%s,exception" % (self.MODULE_NAME, func_name))
else:
err_code = 3 if err_code:
self.send_error({'err_code':err_code}) def send_success(self, msg=''):
data = {'success':True,'msg':msg}
self.wfile.write(json.dumps(data)) def send_error(self, msg=''):
data = {'success':False,'msg':msg}
self.wfile.write(json.dumps(data))
在基类模块中我们提供了默认的处理流程,即依据消息中action,调用do_action方法。并提供了一个简单但非常有效的认证方法,通过消息的signature字段,可能有些简陋。但没关系,你能够定义自己的认证方法。
TASK = {} # task_id: pid
class ScanModule(Module):
MODULE_NAME = "SCAN_MODULE"
def do_start(self):
self.send_success('start ok')
DEBUG('------------task start------------')
task_ids = [int(task_id) for task_id in self.task_ids.split(',') if int(task_id) not in TASK]
for task_id in task_ids:
try:
cmd = 'python scan.py -t %s' % task_id
DEBUG(cmd)
self.sub = Popen(cmd, shell=True, cwd=CWD)
pid = int(self.sub.pid)
TASK[task_id] = pid
INFO('%s start a new task,task_id:%s,pid:%s' %(self.MODULE_NAME, task_id, pid))
except Exception:
ERROR('%s start a new task,task_id:%s failed' % (self.MODULE_NAME, task_id))
def do_stop(self):
self.send_success('stop ok')
DEBUG('------------task stop------------')
task_ids = [int(task_id) for task_id in self.task_ids.split(',') if int(task_id) in TASK]
for task_id in task_ids:
pid = TASK.pop(task_id)
try:
INFO('%s stop a new task,task_id:%s,pid:%s' %(self.MODULE_NAME, task_id, pid))
call(['taskkill', '/F', '/T', '/PID', str(pid)])
except Exception:
ERROR('%s taskkill a task failed,task_id:%s,pid:%s' %(self.MODULE_NAME, task_id, pid))
module_register(ScanModule.MODULE_NAME, ScanModule)
所以说as soon as possible
#!/usr/bin/env python
#-*-encoding:UTF-8-*- import hmac
import gevent
from gevent import monkey
monkey.patch_socket() addr = ('localhost', 6667) def send_request(module_name,request_headers):
SECRE_KEY = "YI-LUO-KEHAN"
socket = gevent.socket.socket()
socket.connect(addr)
request_headers['module'] = module_name
request_headers['signature'] = hmac.new(SECRE_KEY, module_name).hexdigest()
h = ["%s:%s" %(k, v) for k,v in request_headers.iteritems()]
h.append('\n')
request = '\n'.join(h)
socket.send(request)
print socket.recv(8192)
socket.close() if __name__ =="__main__":
import sys
if sys.argv[1] == 'start':
send_request('SCAN_MODULE',{'action':'start','task_ids':'1'})
else:
send_request('SCAN_MODULE',{'action':'stop','task_ids':'1'})
!
!
[Python网络编程]浅析守护进程后台任务的设计与实现的更多相关文章
- python网络编程之开启进程的方式
标签(空格分隔): 开启进程的方式 multiprocessing模块介绍: python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在pyth ...
- python并发编程之守护进程、互斥锁以及生产者和消费者模型
一.守护进程 主进程创建守护进程 守护进程其实就是'子进程' 一.守护进程内无法在开启子进程,否则会报错二.进程之间代码是相互独立的,主进程代码运行完毕,守护进程也会随机结束 守护进程简单实例: fr ...
- python 并发编程 多进程 守护进程
一 守护进程 主进程创建子进程目的是:主进程有一个任务需要并发执行,那开启子进程帮我并发执行任务 主进程创建子进程,然后将该进程设置成守护自己的进程 关于守护进程需要强调两点: 其一:守护进程会在主进 ...
- python网络编程基础(线程与进程、并行与并发、同步与异步、阻塞与非阻塞、CPU密集型与IO密集型)
python网络编程基础(线程与进程.并行与并发.同步与异步.阻塞与非阻塞.CPU密集型与IO密集型) 目录 线程与进程 并行与并发 同步与异步 阻塞与非阻塞 CPU密集型与IO密集型 线程与进程 进 ...
- Python 网络编程(二)
Python 网络编程 上一篇博客介绍了socket的基本概念以及实现了简单的TCP和UDP的客户端.服务器程序,本篇博客主要对socket编程进行更深入的讲解 一.简化版ssh实现 这是一个极其简单 ...
- Python学习(22)python网络编程
Python 网络编程 Python 提供了两个级别访问的网络服务.: 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口的 ...
- Day07 - Python 网络编程 Socket
1. Python 网络编程 Python 提供了两个级别访问网络服务: 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口 ...
- python 网络编程 IO多路复用之epoll
python网络编程——IO多路复用之epoll 1.内核EPOLL模型讲解 此部分参考http://blog.csdn.net/mango_song/article/details/4264 ...
- Python 网络编程相关知识学习
Python 网络编程 Python 提供了两个级别访问的网络服务.: 低级别的网络服务支持基本的 Socket,它提供了标准的 BSD Sockets API,可以访问底层操作系统Socket接口的 ...
随机推荐
- OracleService類
using System; using System.Collections.Generic; using System.Data; using System.IO; using System.Lin ...
- CAD使用SetxDataString写数据(com接口)
主要用到函数说明: MxDrawEntity::SetxDataString 写一个字符串扩展数据,详细说明如下: 参数 说明 [in] BSTR val 字符串值 szAppName 扩展数据名称 ...
- 【东软实训】SQL函数
SQL函数 SQL是用于访问和处理数据库的标准的计算机语言,我们所使用的的是Oracle SQL 一个数据库通常包含一个或多个表,每个表有一个名字表示,下图即为一个名为“emp”的表,接下来的操作都将 ...
- 计算机中的CPU
今天写一下计算机中最核心的一部分,就是计算机的大脑---CPU.CPU也就是中央处理器(Central Processing Unit).中央处理器是一块超大规模的集成电路,是一台计算机的运算核心(C ...
- 配置Django中数据库读写分离
django在进行数据库操作的时候,读取数据与写数据(曾.删.改)可以分别从不同的数据库进行操作 修改配置文件: DATABASES = { 'default': { 'ENGINE': 'djang ...
- 启用Windows10的Linux子系统并安装图形界面
前言 目前市面上的PC电脑主要运行着四大类系统,它们分别是微软的Windows.苹果的MacOS.Linux的发行版以及Unix类系统.其中Linux和Unix都是开源的,因此市面出现的众多基于Lin ...
- Runlevel in Linux
运行级别(Runlevel)指的是Unix或者Linux等类Unix操作系统下不同的运行模式.运行级别通常分为7等,分别是从0到6,但如果必要的话也可以更多. 例如在大多数Linux操作系统下一共有如 ...
- linux上uwsgi+nginx+django发布项目
在发布项目前首先将部署环境进行搭建,尤其是依赖包一定需要提前安装. 一.虚拟环境的搭建 1.建议在linux下新建一个虚拟环境,这样有独立干净的环境. mkvirtualenv -p python3 ...
- vim 编辑器使用法则
vim 编辑器使用法则 Vi编辑器有3种使用模式:一般模式.编辑模式和命令模式. $SHELL:查看当前默认shell类型 $BASH_VERSION:查看当前shell版本 3.一般模式: 光标移 ...
- 关于必须添加对程序集“System.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a”的引用异常问题
问题描述: 下午调试代码的时候突然发现页面突然异常了,原本以为是代码哪里写错了,后来通过定位发现MVC,UI界面的Linq异常,即关于必须添加对程序集“System.Runtime, Version= ...