Расширение существующей Twisted Service с помощью другой службы Socket / TCP / RPC для получения информации о службе - PullRequest
2 голосов
/ 16 сентября 2011

Я реализую комбинацию Twist-based Heartbeat Client / Server, основанную на этом примере .Это мой первый Twisted проект.

В основном он состоит из прослушивателя UDP (Receiver), который вызывает метод прослушивателя (DetectorService.update) при получении пакетов.DetectorService всегда содержит список активных / неактивных клиентов (я значительно расширил пример, но ядро ​​остается прежним), что позволяет реагировать на клиентов, которые кажутся отключенными в течение указанного времени ожидания.

Это источник, взятый с сайта:

UDP_PORT = 43278; CHECK_PERIOD = 20; CHECK_TIMEOUT = 15

import time
from twisted.application import internet, service
from twisted.internet import protocol
from twisted.python import log

class Receiver(protocol.DatagramProtocol):
    """Receive UDP packets and log them in the clients dictionary"""

    def datagramReceived(self, data, (ip, port)):
        if data == 'PyHB':
            self.callback(ip)

class DetectorService(internet.TimerService):
    """Detect clients not sending heartbeats for too long"""

    def __init__(self):
        internet.TimerService.__init__(self, CHECK_PERIOD, self.detect)
        self.beats = {}

    def update(self, ip):
        self.beats[ip] = time.time()

    def detect(self):
        """Log a list of clients with heartbeat older than CHECK_TIMEOUT"""
        limit = time.time() - CHECK_TIMEOUT
        silent = [ip for (ip, ipTime) in self.beats.items() if ipTime < limit]
        log.msg('Silent clients: %s' % silent)

application = service.Application('Heartbeat')
# define and link the silent clients' detector service
detectorSvc = DetectorService()
detectorSvc.setServiceParent(application)
# create an instance of the Receiver protocol, and give it the callback
receiver = Receiver()
receiver.callback = detectorSvc.update
# define and link the UDP server service, passing the receiver in
udpServer = internet.UDPServer(UDP_PORT, receiver)
udpServer.setServiceParent(application)
# each service is started automatically by Twisted at launch time
log.msg('Asynchronous heartbeat server listening on port %d\n'
    'press Ctrl-C to stop\n' % UDP_PORT)

Этот сервер пульса работает как демон в фоновом режиме.

Теперь моя проблема :

Мне нужно иметь возможность запускать скрипт «извне» для печати количества автономных / онлайн-клиентов на консоли, которое получатель собирает за время своей жизни (self.beats).Вот так:

$ pyhb showactiveclients
3 clients online 
$ pyhb showofflineclients
1 client offline 

Поэтому мне нужно добавить какой-то дополнительный сервер (Socket, Tcp, RPC - это не имеет значения. Главное, что я могу создать клиентский скриптс вышеуказанным поведением) к моему DetectorService, который позволяет подключаться к нему извне.Он должен просто дать ответ на запрос.

Этот сервер должен иметь доступ к внутренним переменным запущенного экземпляра DeteserService, поэтому я предполагаю, что мне нужно расширить DetectorService с помощью некоторого дополнительного сервиса.

После нескольких часов попыток объединить службу извещателей с несколькими другими службами, я до сих пор не представляю, как лучше всего реализовать такое поведение.Поэтому я надеюсь, что кто-нибудь может дать мне хотя бы важный совет, как начать решать эту проблему.Заранее спасибо !!!

1 Ответ

3 голосов
/ 16 сентября 2011

Я думаю, что у вас уже есть общее представление о решении, поскольку вы уже применили его к взаимодействию между Receiver и DetectorService. Идея состоит в том, чтобы ваши объекты имели ссылки на другие объекты, которые позволяют им делать то, что им нужно.

Итак, рассмотрим веб-сервис, который отвечает на запросы с результатом, основанным на данных beats:

from twisted.web.resource import Resource

class BeatsResource(Resource):
    # It has no children, let it respond to the / URL for brevity.
    isLeaf = True

    def __init__(self, detector):
        Resource.__init__(self)
        # This is the idea - BeatsResource has a reference to the detector,
        # which has the data needed to compute responses.
        self._detector = detector

    def render_GET(self, request):
        limit = time.time() - CHECK_TIMEOUT
        # Here, use that data.
        beats = self._detector.beats
        silent = [ip for (ip, ipTime) in beats.items() if ipTime < limit]
        request.setHeader('content-type', 'text/plain')
        return "%d silent clients" % (len(silent),)

# Integrate this into the existing application
application = service.Application('Heartbeat')
detectorSvc = DetectorService()
detectorSvc.setServiceParent(application)
.
.
.
from twisted.web.server import Site
from twisted.application.internet import TCPServer

# The other half of the idea - make sure to give the resource that reference 
# it needs.
root = BeatsResource(detectorSvc)
TCPServer(8080, Site(root)).setServiceParent(application)
...