深入supervisor的通信机制与扩展

【背景概述】


在我们的项目中用到了supervisor作为进程守护。在业务改造过程中,需要将一些配置管理的业务迁移并放到一个独立的容器中运行,该容器和主业务容器通信完成对业务程序的配置和运维管理。

在不引入新模块的前提下,supervisor能否扩展实现相应逻辑,因此就有了本文对supervisor通信机制的调研整理。

【supervisor简介】


supervisor是一个client/server系统,允许用户在类unix操作系统上管理多个进程。

supervisor是用python开发的一套通用进程管理程序,能将一个普通的命令行程序变为后台daemon,并监控进程状态,在进程异常退出时进行自动重启。

从官方文档了解到,supervisor由4个部分组成

  • supervisord:这是supervisor的服务端程序,负责按配置启动子进程,并进行管理(监控进程异常触发重启),同时响应客户端请求执行对应的处理动作。

  • supervisorctl:supervisor的客户端程序,是一个命令行程序。supervisorctl会向supervisord发送rpc请求,获取子进程的状态,运行程序列表,主动触发启停子进程。

  • web server:supervisord提供的http服务,其功能等同于supervisorctl,只是提供了界面会更直观,操作起来也更方便。

  • xml-rpc interface:supervisord提供的类似http服务的rpc接口,http服务和supervisorctl本质上都是通过这个接口完成通信请求的。

c7156d075750d5f0e467c4c39474900d.png

【基于XML-RPC的接口扩展】


supervisord原生只提供了有限的功能集,从supervisorctl命令行的帮助信息就可以看到。

0cce50db1483b78fb409be23114aeefd.png

而如果想要进行一些功能的扩展,就可以利用xml-rpc来实现。

在官网文档的配置XML-RPC接口工厂小节中提到了,可以通过在配置文件中添加如下配置项来进行扩展

[rpcinterface:xxx]
supervisor.rpcinterface_factory = XXXModuleName.XXXInterfaceFactoryFunction

; rpcinterface为section的固定前缀
; xxx表示接口的名称
; supervisor.rpcinterface_factory 为固定配置项的key
; XXXModuleName 为接口类所在的模块名
; XXXInterfaceFactoryFunction 为接口类返回的函数

实际上,在配置文件中就有这样的默认,且必须保留的配置项。

[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

这个是supervisord默认提供的rpc接口类,像前面图中展示出来的默认命令,最终就是通过调用这些rpc接口得到的。对应的实现是在supervisor包的rpcinterface模块中,相关代码如下所示(仅列出部分)

7c3938778f673b8deb7b0ba3089b0112.png

f86f27f0a9d0fde9b556ac907b1ad054.png

有了上面的铺垫后,要进行功能接口的扩展就很方便了。

首先是扩展接口的编写与实现,简单示例代码:

import subprocess

class MyRPCInterface:
  def __init__(self, supervisord):
    self.supervisord = supervisord

  def runCommand(self, args):
    ret,val = subprocess.getstatusoutput(args)
    return val

def make_extend_rpcinterface(supervisord):
  return MyRPCInterface(supervisord)

这里,runCommand接口实现运行指定系统命令,然后将结果返回

然后在配置中添加对应的配置项,即为扩展接口增加接口工厂配置项

[rpcinterface:hncscwc]
supervisor.rpcinterface_factory = supervisor.myrpc:make_extend_rpcinterface

到这里,扩展接口就成功添加到supervisord中了(重启supervisord或者reload生效)

【与supervisorctl集成】


扩展接口可以成功添加了,那么要如何访问(调用)这个接口呢?

官方的文档里,同样给出了简单介绍,通过python的xmlrpc库来进行访问,例如:

f7a4a5236e7bd50461cfcbd7469de352.png

从上面的图可以看到,通过xmlrpclib确实可以完成rpc的调用。

但都通过直接调python脚本,看上去有点像是在"裸奔",有没有更优雅一点的方式完成调用呢?毕竟原生的使用方式(supervisorctl status/stop/start ..)显然是更优雅一些的。

本想从官网文档中找到些线索,无奈没有,网上文章也没有找到类似的介绍。那只好回到最直接的撸源码的方式了。

简单来讲:supervisorctl初始化时,有个默认的控制插件工厂类,在该类中,所有以“do_"为前缀的方法就是supervisorctl对应命令参数的调用方法。

也就是说,status/stop/start等跟在supervisorctl的这些命令,最终调用的就是这个默认插件类中的do_status/do_stop/do_start方法。

在这些方法中,最终通过xmlrpclib完成了与supervisord的rpc通信。

294aeac12c8e4951dcd340039eb13914.png

除了默认的控制插件之外,还有额外的一个步骤:从配置中加载自定义的插件模块,从而实现命令的扩展。

b8696958eac11ef0fff78657e0f101f8.png

有了这一步,我们就可以对前面的代码进行封装成简单的命令并继承到supervisorctl中使用了。

简单示例代码:

from supervisor.supervisorctl import ControllerPluginBase

class MyControllerPlugin(ControllerPluginBase):
  name="mycontroller"

  def __init__(self, controller, **config):
    self.ctl = controller

  def do_exec(self, arg):
    supervisor=self.ctl.get_server_proxy('myrpc')
    print(supervisor.runCommand(arg))

def make_extend_controllerplugin(controller, **config):
  return MyControllerPlugin(controller, **config)

同时,在配置文件中增加如下配置项

[ctlplugin:mycontroller]
supervisor.ctl_factory=supervisor.mycontroller:make_extend_controllerplugin

这样就可以通过supervisorctl直接访问我们扩展的rpc接口了

c59877dafb6645315888101720474e8b.png

【总结】


小结一下,本文主要讲述了supervisorctl与supervisorctl的通信机制,以及如果在supervisord中扩展rpc接口,以及如果在supervisorctl中扩展命令,方便集成调用我们扩展的接口,同时也可出了给运行的demo。

当然,文中的demo还比较简单,实际上可以做得更复杂,同时还可以结合配置,携带一些不同的参数设置。这个可以参考官方文档、结合对应源码自行研究。

好了,本文就介绍到这里,觉得还不错,来个三连吧(点赞,在看,分享)

6c90ef8e2e581aca73e9c999f8974f64.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值