使用另一个 Socket/TCP/RPC 服务扩展现有的 Twisted 服务以获取服务信息

发布于 2024-12-05 03:16:51 字数 2596 浏览 1 评论 0原文

我正在实现一个基于 Twisted 的 Heartbeat 客户端/服务器组合,基于 这个例子。这是我的第一个 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 

所以我需要向我的 DetectorService 添加某种额外的服务器(Socket、Tcp、RPC - 这并不重要。要点是我能够构建具有上述行为的客户端脚本),它允许从外部连接到它。它应该只是对请求做出响应。

该服务器需要访问正在运行的 detectorservice 实例的内部变量,所以我的猜测是我必须使用某种附加服务来扩展 DetectorService。

经过几个小时的尝试将检测器服务与其他几个服务结合起来后,我仍然不知道实现该行为的最佳方法是什么。所以我希望有人至少能给我基本的提示如何开始解决这个问题。 提前致谢!!!

I'm implementing a Twisted-based Heartbeat Client/Server combo, based on this example. It is my first Twisted project.

Basically it consists of a UDP Listener (Receiver), who calls a listener method (DetectorService.update) on receiving packages. The DetectorService always holds a list of currently active/inactive clients (I extended the example a lot, but the core is still the same), making it possible to react on clients which seem disconnected for a specified timeout.

This is the source taken from the site:

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)

This heartbeat server runs as a daemon in background.

Now my Problem:

I need to be able to run a script "externally" to print the number of offline/online clients on the console, which the Receiver gathers during his lifetime (self.beats). Like this:

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

So I need to add some kind of additional server (Socket, Tcp, RPC - it doesn't matter. the main point is that i'm able to build a client-script with the above behavior) to my DetectorService, which allows to connect to it from outside. It should just give a response to a request.

This server needs to have access to the internal variables of the running detectorservice instance, so my guess is that I have to extend the DetectorService with some kind of additionalservice.

After some hours of trying to combine the detectorservice with several other services, I still don't have an idea what's the best way to realize that behavior. So I hope that somebody can give me at least the essential hint how to start to solve this problem.
Thanks in advance!!!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

酒绊 2024-12-12 03:16:51

我认为您已经对这里的解决方案有了大致的了解,因为您已经将其应用于 Receiver 和 DetectorService 之间的交互。这个想法是让你的对象引用其他对象,让它们做他们需要做的事情。

因此,考虑一个基于 beats 数据响应请求的 Web 服务:

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)

I think you already have the general idea of the solution here, since you already applied it to an interaction between Receiver and DetectorService. The idea is for your objects to have references to other objects which let them do what they need to do.

So, consider a web service that responds to requests with a result based on the beats data:

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)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文