技术控

    今日:34| 主题:49312
收藏本版 (1)
最新软件应用技术尽在掌握

[其他] forkp多进程程序管理库的轮子

[复制链接]
空心荡 发表于 2016-11-27 23:18:11
76 1

立即注册CoLaBug.com会员,免费获得投稿人的专业资料,享用更多功能,玩转个人品牌!

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
是前两天写的一个小工具,主要目的是实现Linux平台下master进程对worker子进程的监控和管理,并且必要情况下自动重启worker进程以实现保活的功能,这应该算是运维人员最喜欢的东西了吧。
    本人在之前的文章      《浅谈多进程程序的控制和管理》中介绍了Nginx中master process和worker process的工作原理,其实这基本也可以作为多进程程序的开发范例来使用,虽然通常情况下的业务需求不用像Nginx做的那么复杂和完整,比如支持二进制程序不停服务平滑升级等,但是master进程对子进程进行监测并异常情况下自动重启服务的功能还是比较有用的。这种情况下,本人就萌生了写一个多进程程序开发的框架的想法,以后写多进程程序就不用重头做起了。   
    在Linux中进程管理的基本方式基本都是通过信号来实现的,因为子进程正常或者异常退出后,会由操作系统保证向其父进程发送SIGCHLD信号,所以父进程可以通过监听SIGCHLD的方式,高效异步的获取子进程退出的通知并对应做出相应地处理。如果只想让子进程保活,那么父进程只需要自定义处理SIGCHLD信号,并进行waitpid调用释放子进程的资源,然后重启子进程就可以了。不过既然想做一个通用点的库或者框架,那么还有一些细节的地方需要注意和完善,同时增加一些便利的功能还是很实际的。
    forkp的子进程以两种方式工作:Process库和Exec管理服务,在同一个forkp实例中,同时对这两种方式提供了支持。
    (1) Process库   
    这种模式实际就是将Nginx的子进程管理模块抽象出来的效果,在新开发的多进程项目中,子进程的服务以可调用对象的方式注册启动。Nginx的配置文件可以设置worker process的进程数量,但这也假设了产生的所有worker process都是同构的,但是实际上多进程的子进程有可能会是异构的,因此在设计的时候,forkp会严格完全按照启动时候调用spawnWorkers的种类和次数来监测子进程。
    master进程除了接收和处理SIGCHLD信号之外,还借助看门狗的模式来监测子进程,虽然实际上我不确定这有实际的意义,比如出现worker进程还没挂掉但是已经不能正常工作了的情况,因为通常程序错误或者跑飞了都会挂掉的。master进程和worker进程之间建立了匿名管道,master进程以1sec间隔向worker进程发送SIGWINCH信号,worker进程接收到该信号后通过匿名管道发回一个字节,如果master连续错过3个回应,就会发送kill杀死该子进程。这个功能是通过子进程屏蔽SIGWINCH信号来测试的。
    需要额外说明的是,这里不要期待master进程做过多业务相关的内容。通常,master进程创建了侦听套接字后,所有的worker进程也继承了这个侦听套接字,而内核可以直接将侦听套接字的连接请求自动分发到worker进程中去,如果使用forkp开发类似Nginx这类程序,就可以很容易采用这种模式来满足需求。如果要期待master进程做过多的事情,由于master进程已经集成了一个epoll异步事件框架,那么程序可能要大改,还需三思。另外一点,就是master进程如果出错,所有的子进程都会退出,让master处理过多的任务,无异于增加了单点风险。
    (2) Exec管理服务   
    这种工作模式是对于已经存在的二进制程序,此时master进程fork出worker进程后,worker进程通过execv系统调用执行新的二进制程序,这个时候我把子进程调用exec后的实体叫做      子程序。由于exec系统调用重新执行一个二进制程序,所以此时master进程和worker进程除了通常的父子进程之外没有什么其他联系了,唯一的便捷就是可以同现有的所有程序方便的集成。此时像上面看门狗的形式就不能工作了,而是通过kill不发送任何具体信号的方式,来侦测指定pid的子进程是否存在。   
    事先说明最好还是把程序和服务写稳定一些,这只是作为一个补救措施,不可过度依赖这种机制。还有就是,比如我的ss5服务器偶尔会挂掉,此时通过forkp调用,就会在退出后自动重启服务了。
    当然,工业上有现成的zabbix等更加成熟、可靠的开源方案……
    在实现中,还有一些需要注意的地方,在此标记下来:
    (1) 信号处理上下文   
    本来说,master是一个单进程,没用什么竞争条件,但是当引入了信号处理程序之后情况就比较复杂了。信号是异步的,master任何时候都可能收到子进程和用户控制台发送的信号,所以这种情况下进程上下文和信号处理上下文对数据的访问需要互斥保护,尤其对容器的迭代访问和修改会导致冲突概率非常大,而信号处理上下文默认只对正在处理的信号是BLOCK的,如果多个信号修改同一个数据,信号处理程序之间还需要同步。所以在进程中访问互斥数据的时候,除了需要使用volatile关键字防止编译器优化外,还需要对应地BLOCK住相应信号集才可以。
    如果直接用这种方式进行保护的,会发现维护工作很繁杂。其实看看Nginx的处理方式,再想想Linux信号处理下半部分,解决方式就很明朗了:在信号处理上下文中做最少的关键性工作,然后将其他任务记录并推迟,后面在进程的上下文中进行处理。所以在Nginx的信号处理中,绝大多数都是根据信号的种类设置对应的状态标识,然后master进程轮训根据状态做对应的操作,这样就可以保证处理完全串行化了。
    (2) 重定向子进程的输出   
    这个是因为当时的某个程序没有规划好日志,很多信息都是打印到标准输出和标准错误输出上的,这样默认情况下所有子进程的标准输出和错误输出都出现在了master进程的终端上,这在多个进程的情况下必然导致消息的混杂。所以这里的处理方法是,在父子进程之间创建一条匿名管道,在fork子进程之后,通过dup2将子进程的STDOUT_FILENO和STDERR_FILENO和这个管道相关联,master异步方式读取管道传输的内容后,写入进程相关的log文件中去。
      正如前面提到的,Process模式和Exec模式两种情况,实际上也是该项目两个发展维度:
    (1)前者讲求父进程和子进程之间进行一定的耦合,可以封装增强父子进程间的通信,比如创建socketpair进行全双工通信、共享内存进行数据共享等,这样master进程可以不断收集子进程的数据,甚至担任中介者的模式进行信息转发。不过进程之间的耦合也不应该过强,缺点前面说过了,而且还很容易将原本多线程的程序写成复杂的多进程程序。
    (2)后者讲求程序的控制,希望可以在不停服务的情况,动态增加、删除、控制子程序。这种情况下只用信号的方式肯定是不能满足需求的了,后续需要添加配置文件支持,以启动时候预加载某些子程序,可能还需要写一个客户端才能向forkp服务传递更加复杂的信息。
    Show you the code, just do it!
友荐云推荐




上一篇:Firefox 50 for developers
下一篇:基础篇章:关于 React Native之 ActivityIndicator 组件的讲解
酷辣虫提示酷辣虫禁止发表任何与中华人民共和国法律有抵触的内容!所有内容由用户发布,并不代表酷辣虫的观点,酷辣虫无法对用户发布内容真实性提供任何的保证,请自行验证并承担风险与后果。如您有版权、违规等问题,请通过"联系我们"或"违规举报"告知我们处理。

alen 发表于 2016-11-28 00:31:00
一脸的虚假繁荣。
回复 支持 反对

使用道具 举报

*滑动验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

我要投稿

推荐阅读

扫码访问 @iTTTTT瑞翔 的微博
回页顶回复上一篇下一篇回列表手机版
手机版/CoLaBug.com ( 粤ICP备05003221号 | 文网文[2010]257号 )|网站地图 酷辣虫

© 2001-2016 Comsenz Inc. Design: Dean. DiscuzFans.

返回顶部 返回列表