mirror of
https://github.com/django-q2/django-q2.git
synced 2026-10-08 05:28:12 +08:00
Reduced code for stats. Stat now uses Redis pubsub
This commit is contained in:
+26
-45
@@ -3,6 +3,7 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import signal
|
import signal
|
||||||
from multiprocessing import Queue, Event, Process, current_process
|
from multiprocessing import Queue, Event, Process, current_process
|
||||||
|
import socket
|
||||||
import sys
|
import sys
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
@@ -62,9 +63,6 @@ class Cluster(object):
|
|||||||
self.sentinel.join()
|
self.sentinel.join()
|
||||||
logger.info('Q cluster has stopped.')
|
logger.info('Q cluster has stopped.')
|
||||||
|
|
||||||
def stats(self):
|
|
||||||
return Stat.load(self.pid)
|
|
||||||
|
|
||||||
def sig_handler(self, signum, frame):
|
def sig_handler(self, signum, frame):
|
||||||
logger.debug('{} got signal {}'.format(current_process().name, SIGNAL_NAMES.get(signum, 'UNKNOWN')))
|
logger.debug('{} got signal {}'.format(current_process().name, SIGNAL_NAMES.get(signum, 'UNKNOWN')))
|
||||||
self.stop()
|
self.stop()
|
||||||
@@ -77,7 +75,8 @@ class Sentinel(object):
|
|||||||
self.pid = current_process().pid
|
self.pid = current_process().pid
|
||||||
self.parent_pid = os.getppid()
|
self.parent_pid = os.getppid()
|
||||||
self.name = current_process().name
|
self.name = current_process().name
|
||||||
self.status = 'Initializing'
|
self.status = None
|
||||||
|
self.reincarnations = 0
|
||||||
self.tob = timezone.now()
|
self.tob = timezone.now()
|
||||||
self.stop_event = event
|
self.stop_event = event
|
||||||
self.pool_size = WORKERS
|
self.pool_size = WORKERS
|
||||||
@@ -122,8 +121,10 @@ class Sentinel(object):
|
|||||||
else:
|
else:
|
||||||
self.spawn_worker()
|
self.spawn_worker()
|
||||||
logger.warn("reincarnated work worker after death of {}".format(pid))
|
logger.warn("reincarnated work worker after death of {}".format(pid))
|
||||||
|
self.reincarnations += 1
|
||||||
|
|
||||||
def spawn_cluster(self):
|
def spawn_cluster(self):
|
||||||
|
self.set_status('Starting')
|
||||||
for i in range(self.pool_size):
|
for i in range(self.pool_size):
|
||||||
self.spawn_worker()
|
self.spawn_worker()
|
||||||
self.monitor_pid = self.spawn_monitor()
|
self.monitor_pid = self.spawn_monitor()
|
||||||
@@ -131,7 +132,7 @@ class Sentinel(object):
|
|||||||
|
|
||||||
def guard(self):
|
def guard(self):
|
||||||
logger.info('{} guarding cluster at {}'.format(current_process().name, self.pid))
|
logger.info('{} guarding cluster at {}'.format(current_process().name, self.pid))
|
||||||
self.status = 'Running'
|
self.set_status('Running')
|
||||||
while not self.stop_event.is_set():
|
while not self.stop_event.is_set():
|
||||||
for p in list(self.pool):
|
for p in list(self.pool):
|
||||||
if not p.is_alive():
|
if not p.is_alive():
|
||||||
@@ -140,16 +141,12 @@ class Sentinel(object):
|
|||||||
self.pool.remove(p)
|
self.pool.remove(p)
|
||||||
# Replace it with a fresh one
|
# Replace it with a fresh one
|
||||||
self.reincarnate(p.pid)
|
self.reincarnate(p.pid)
|
||||||
self.stats()
|
Stat(self).publish()
|
||||||
sleep(1)
|
sleep(2)
|
||||||
self.stop()
|
self.stop()
|
||||||
|
|
||||||
def stats(self):
|
|
||||||
Stat(self).save()
|
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self.status = 'Stopping'
|
self.set_status('Stopping')
|
||||||
# Send the STOP signal to the pool
|
|
||||||
name = current_process().name
|
name = current_process().name
|
||||||
logger.info('{} stopping pool processes'.format(name))
|
logger.info('{} stopping pool processes'.format(name))
|
||||||
# Stopping pusher
|
# Stopping pusher
|
||||||
@@ -166,7 +163,10 @@ class Sentinel(object):
|
|||||||
# Finally stop the monitor
|
# Finally stop the monitor
|
||||||
self.done_queue.put('STOP')
|
self.done_queue.put('STOP')
|
||||||
self.pool = []
|
self.pool = []
|
||||||
self.status = 'Stopped'
|
self.set_status('Stopped')
|
||||||
|
|
||||||
|
def set_status(self, message=None):
|
||||||
|
Stat(self, message).publish()
|
||||||
|
|
||||||
|
|
||||||
def pusher(task_queue, e):
|
def pusher(task_queue, e):
|
||||||
@@ -185,9 +185,9 @@ def monitor(done_queue):
|
|||||||
logger.info("{} monitoring results at {}".format(name, current_process().pid))
|
logger.info("{} monitoring results at {}".format(name, current_process().pid))
|
||||||
for task in iter(done_queue.get, 'STOP'):
|
for task in iter(done_queue.get, 'STOP'):
|
||||||
if task['success']:
|
if task['success']:
|
||||||
logger.info("Finished [{}:{}]".format(task['func'], task['name']))
|
logger.info("Finished [{}]".format(task['name']))
|
||||||
else:
|
else:
|
||||||
logger.error("Failed [{}:{}] - {}".format(task['func'], task['name'], task['result']))
|
logger.error("Failed [{}] - {}".format(task['name'], task['result']))
|
||||||
save_task(task)
|
save_task(task)
|
||||||
logger.info("{} stopped monitoring results".format(name))
|
logger.info("{} stopped monitoring results".format(name))
|
||||||
|
|
||||||
@@ -210,7 +210,7 @@ def worker(task_queue, done_queue):
|
|||||||
done_queue.put(task)
|
done_queue.put(task)
|
||||||
continue
|
continue
|
||||||
module, func = task['func'].rsplit('.', 1)
|
module, func = task['func'].rsplit('.', 1)
|
||||||
logger.info('{} processing [{}:{}]'.format(name, task['func'], task['name']))
|
logger.info('{} processing [{}]'.format(name, task['name']))
|
||||||
try:
|
try:
|
||||||
m = importlib.import_module(module)
|
m = importlib.import_module(module)
|
||||||
f = getattr(m, func)
|
f = getattr(m, func)
|
||||||
@@ -288,8 +288,11 @@ class JSONPickleSerializer(object):
|
|||||||
|
|
||||||
|
|
||||||
class Stat(object):
|
class Stat(object):
|
||||||
def __init__(self, sentinel):
|
def __init__(self, sentinel, message=None):
|
||||||
self.cluster_id = sentinel.parent_pid
|
self.cluster_id = sentinel.parent_pid
|
||||||
|
self.host = socket.gethostname()
|
||||||
|
if message:
|
||||||
|
sentinel.status = message
|
||||||
self.status = sentinel.status
|
self.status = sentinel.status
|
||||||
self.sentinel = sentinel.pid
|
self.sentinel = sentinel.pid
|
||||||
self.monitor = sentinel.monitor_pid
|
self.monitor = sentinel.monitor_pid
|
||||||
@@ -297,6 +300,7 @@ class Stat(object):
|
|||||||
self.workers = []
|
self.workers = []
|
||||||
for w in sentinel.pool:
|
for w in sentinel.pool:
|
||||||
self.workers.append(w.pid)
|
self.workers.append(w.pid)
|
||||||
|
self.reincarnations = sentinel.reincarnations
|
||||||
self.task_q_size = sentinel.task_queue.qsize()
|
self.task_q_size = sentinel.task_queue.qsize()
|
||||||
self.done_q_size = sentinel.done_queue.qsize()
|
self.done_q_size = sentinel.done_queue.qsize()
|
||||||
self.tob = sentinel.tob
|
self.tob = sentinel.tob
|
||||||
@@ -304,34 +308,11 @@ class Stat(object):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def key(self):
|
def key(self):
|
||||||
return self.get_key(self.cluster_id)
|
return self.get_key()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_key(cluster_id):
|
def get_key():
|
||||||
return '{}:cluster:{}'.format(prefix, cluster_id)
|
return '{}:cluster'.format(prefix)
|
||||||
|
|
||||||
def save(self):
|
def publish(self):
|
||||||
r.set(self.key, SignedPackage.dumps(self, True), 5)
|
r.publish(self.key, SignedPackage.dumps(self, True))
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def load(cluster_id=None):
|
|
||||||
if not cluster_id:
|
|
||||||
stats = []
|
|
||||||
keys = r.keys(pattern='{}:cluster:*'.format(prefix))
|
|
||||||
if not keys:
|
|
||||||
return None
|
|
||||||
packs = r.mget(keys)
|
|
||||||
for pack in packs:
|
|
||||||
try:
|
|
||||||
stats.append(SignedPackage.loads(pack))
|
|
||||||
except signing.BadSignature:
|
|
||||||
continue
|
|
||||||
return stats
|
|
||||||
else:
|
|
||||||
pack = r.get(Stat.get_key(cluster_id))
|
|
||||||
if pack:
|
|
||||||
try:
|
|
||||||
return SignedPackage.loads(pack)
|
|
||||||
except signing.BadSignature:
|
|
||||||
return None
|
|
||||||
return None
|
|
||||||
|
|||||||
Reference in New Issue
Block a user