重新优化了下,后续还需要继续优化日志和管道读取问题
再次改造了下,全局变量必须独享。虽然我是以字典形式做到了保证,但感觉通用性不强
最后发现还是利用GIL来控制比较有效率,用lock反而在大量并发时效率十分低下
# -*- coding:utf-8 -*-
'''
@Project: base_service
@Auth: Louishu
@File: base_service.py
@Ide: PyCharm
@Blog: https://blog.csdn.net/louishu_hu
@Git:https://github.com/neo19850910/
@QQ: 1641143982
@Email: 1641143982@qq.com
@Time: 2019-04-10 15:23:45
'''
from wsgiref.simple_server import make_server
import os
import json
from subprocess import Popen, PIPE, STDOUT
import requests
import hashlib
from configobj import ConfigObj
import signal
from multiprocessing import Process
import threading
import time
from stdlog import *
header = {'Content-Type': 'application/json'}
pidlist = {}
processes = []
old_pidset = []
#lock = threading.Lock()
def kill_child(port):
pid_set = os.popen ("lsof -i:%s|grep -v PID|awk '{print $2}'" % port).readlines ()
for pid in pid_set:
if pid != '' and pid:
old_pidset.append (pid)
def term(sig_num, addtion):
os.killpg (os.getpgid (os.getpid ()), signal.SIGKILL)
def md5_key(arg):
hash = hashlib.md5 ()
hash.update (arg)
return hash.hexdigest ()
def callback(func):
def wrapper(*args, **kwargs):
# signal.signal (signal.SIGTERM, term)
# t = Process (target=func, args=args, kwargs=kwargs)
# t.daemon = True
# t.start ()
t = threading.Thread (target=func, args=args, kwargs=kwargs)
t.start ()
return wrapper
class rtsp (object):
def __init__(self):
self.log = Logger ('all.log', level='info')
def routers(self):
urlpatterns = (
('/pyservice/start', self.start),
('/pyservice/stop', self.stop),
('/pyservice/restart', self.restart),
)
return urlpatterns
def start(self, environ, start_response):
start_response ('202 OK', [('Content-Type', 'application/json')])
try:
request_body_size = int (environ.get ('CONTENT_LENGTH', 0))
except (ValueError):
request_body_size = 0
request_body = environ['wsgi.input'].read (request_body_size)
_command = eval (request_body)['command']
_serialNumber = eval (request_body)['serialNumber']
try:
_callBackUrl = eval (request_body)['callBackUrl']
self.log.logger.info (u'回调地址是:{}'.format (_callBackUrl))
self.begin (_command, _callBackUrl, _serialNumber)
except:
self.begin (command=_command, serialNumber=_serialNumber)
return [json.dumps ({'code': 202})]
@callback
def begin(self, command, callBackUrl=None,serialNumber=None):
if callBackUrl:
if pidlist.has_key (md5_key (command)):
self.log.logger.info (u'已经进入重复回调环节')
self.log.logger.info (u'执行的命令为:{}'.format (command))
_pid = pidlist[md5_key (command)]
try:
start_time = time.time ()
res = requests.post (callBackUrl, json={'pid': _pid, 'result': 'success',
'remarks': 'Already publishing'})
self.log.logger.info (u'回调地址是:{}'.format (callBackUrl))
self.log.logger.info (u'执行请求耗时:{}'.format (time.time () - start_time))
self.log.logger.info (u'请求响应码:{}'.format (res.status_code))
except:
self.log.logger.error (u'video-transcode微服务未开启或其他原因!')
else:
_start_result = Popen ([command], shell=True, stdout=PIPE, stderr=STDOUT)
_pid = _start_result.pid
buff = ''
for i in range (20):
buff += _start_result.stdout.readline ()
if 'failed' in buff:
requests.post (callBackUrl, json={'pid': _pid, 'result': 'fail',
'remarks': 'No route to host or other reason'})
self.log.logger.error (buff)
else:
_pid = _start_result.pid
lock.acquire ()
pidlist[md5_key (command)] = _pid
lock.release ()
start_time = time.time ()
res = requests.post (callBackUrl,
json={'pid': _pid, 'result': 'success', 'remarks': 'null'})
self.log.logger.info (u'本次请求执行命令为:{}'.format (command))
self.log.logger.info (u'请求响应码:{}'.format (res.status_code))
end_time = time.time ()
self.log.logger.info (u'请求耗时:{}'.format (end_time - start_time))
else:
self.log.logger.error (u'未传入回调地址!')
def stop(self, environ, start_response):
try:
request_body_size = int (environ.get ('CONTENT_LENGTH', 0))
except (ValueError):
request_body_size = 0
request_body = environ['wsgi.input'].read (request_body_size)
_pid = eval (request_body)['pid']
_command = eval (request_body)['command']
_callBackUrl = eval (request_body)['callBackUrl']
md5_command = md5_key (_command)
if not pidlist.has_key (md5_command):
start_response ('200 OK', [('Content-Type', 'application/json')])
return [json.dumps ({'result': 'there is no such rtsp', 'code': 400})]
else:
self.end (md5_command, _pid, _callBackUrl)
start_response ('200 OK', [('Content-Type', 'application/json')])
return [json.dumps ({'code': 200})]
@callback
def end(self, command, pid, callBackUrl=None):
self.log.logger.info (u'删除pid:{}'.format (pid))
Popen (['kill -9 {}'.format (pid)], shell=True)
#lock.acquire ()
try:
pidlist.pop (command)
except:
pass
#lock.release ()
self.log.logger.info (u'本次停止的回调地址为:{}'.format (callBackUrl))
res = requests.post (callBackUrl, json={'pid': pid, 'result': 'success', 'remarks': 'stop video'})
self.log.logger.info (res.status_code)
def restart(self, environ, start_response):
start_response ('202 OK', [('Content-Type', 'application/json')])
try:
request_body_size = int (environ.get ('CONTENT_LENGTH', 0))
except (ValueError):
request_body_size = 0
request_body = environ['wsgi.input'].read (request_body_size)
_command = eval (request_body)['command']
_callBackUrl = eval (request_body)['callBackUrl']
self.log.logger.info (u'重启回调地址是:{}'.format (_callBackUrl))
self.reflush (_command, _callBackUrl)
return [json.dumps ({'code': 202})]
@callback
def reflush(self, command, callBackUrl=None):
if md5_key (command) in pidlist.keys ():
self.log.logger.info (u'进行视频重启')
_pid = pidlist[md5_key (command)]
Popen (['kill -9 {}'.format (_pid)], shell=True)
#lock.acquire ()
try:
pidlist.pop (md5_key (command))
except:
pass
#lock.release()
self.begin (command, callBackUrl)
else:
self.begin (command, callBackUrl)
def application(self, environ, start_response):
urlpatterns = self.routers ()
path = environ["PATH_INFO"]
func = None
for url, function in urlpatterns:
if url == path:
func = function
break
if func:
return func (environ, start_response)
else:
HTML_ROOT_DIR = "50x.html"
start_response ('404 not found', [('Content-Type', 'text/html')])
with open (HTML_ROOT_DIR) as f:
return f.read ()
if __name__ == '__main__':
config = ConfigObj ("lgq.ini", encoding='UTF8')
host = config['command']['host']
port = config['command']['port']
kill_child (str (port))
for pid in old_pidset:
print (u'存在僵尸进程:{},已经kill掉!'.format (pid))
os.popen ("kill -9 %s" % pid)
time.sleep (3)
httpd = make_server (host, int (port), rtsp ().application)
print 'Serving HTTP on port %s...' % port
httpd.serve_forever ()