wrapper 如何获取字段_技术分享 | wrapper 和 dble 之间的那些事

作者:鲍凤其

背景

在刚刚发布的 dble 2.19.09.0 版本中,我们升级了 dble 中 JSW 的版本,将 JSW 版本从 3.2.3 版本升级到 3.5.41 版本。升级的原因在于我们在使用过程中发现了几个比较严重的 bug,这几个 bug 会导致 dble 的守护进程异常退出和 hang 死。hang 死的案例可参考 issue:https://github.com/actiontech/dble/issues/1402。

看到这里,可能有些同学对 JSW 还不太了解,我在这里先简单介绍一下。

JSW 介绍

JSW 是 Java Service Wrapper 的缩写,也就是 dble 中通常所说的 wrapper。JSW 可用于将 Java 程序包装成一个后台服务运行。除此之外,JSW 还可以在你的 Java 程序宕掉以后,自动把服务拉起,相当于提供了一个守护进程的功能。它的一个主要目标就是,单点服务做到尽可能高可靠,宕掉后第一时间帮你把它拉起来!这样,能够最大化降低运维成本。

JSW 除了 Windows 和 Linux 还支持其他平台,社区中经常听到有同学问 dble 有 Windows 版本此类的问题。在这里我想说可以有,但是 dble 官方只提供了 Linux 版,Windows 版本就需要各位同学自己动手编译打包了。Java Service Wrapper 分为社区版和企业版,企业版的功能更加强大,但是要收费。目前一般使用的都是社区版,免费并且开源。

JSW 如何守护 dble

概述

要了解上面的 bug,我们需要先了解一下 JSW 的整体流程。下面我们先从整体来看一下 dble 和 JSW 守护进程的关系,如下图:

19d2e439058a381b59c0d8304b08d852.png

dble 在启动后,如果通过系统命令查看后台进程,会发现其实后台运行了两个进程。

  • 其中之一是 JSW 守护进程,下面直接叫做守护进程。它会守护我们的 dble 程序,挂掉后立马拉起。守护进程会开启一个 ServerSocket 端口,通过这个端口守护进程可以对 wrapper 下发指令,比如 ping,重启 dble 等等。
  • 另一个就是 Java 程序的进程了。JSW 会在 dble 程序之外包装一层 wrapper,这个 wrapper 的主要作用有两个:一是监听端口,处理守护进程发送来的指令;二是在合适的时机加载 dble。
  • 这两个进程是父子进程的关系,守护进程是父进程,Java 程序是其子进程。在上面的图中,其实漏掉了其中最重要的部分,那就是守护进程对 JVM 状态的处理和变更,这个会在下面详细介绍。

守护进程启动过程

守护进程的启动,我大致分为两个阶段:1. 初始化阶段,2. 状态和事件处理阶段。

初始化阶段

1. 守护进程内部会初始化 Java 程序状态的变量,比如记录当前 Java 程序是停止,正在启动还是运行等。初始时状态即为停止状态即下文中的 down。

2. 注册信号事件处理函数,其中特别需要注意的是注册了 SIGCHLD 这个信号的处理函数。这个信号是当子进程退出时,它会向父进程发送 SIGCHLD 信号。

3. 开启一个 ServerSocket 监听端口用于 wrapper 和守护进程通信。

状态和事件处理阶段

在此阶段,守护进程不停的轮询监听端口是否有事件达到,根据 Java 程序的状态执行不同的操作,以此反复。下面详细描述下过程,可对照下面的图来看。

  • 在守护进程启动之初,内部 Java 程序的状态为 down,若是初次启动,此时守护进程会直接将状态置为 lauch。
  • 在 lauch 状态下,守护进程调用 Linux fork 系统调用创建一个 Java 的子进程后将状态置为 lauching。
  • 在 lauching 状态下,守护进程会一直等待 Java 程序启动成功的事件达到,若在超时时间内没有等到,则会将子进程杀死并重启。一旦等到启动成功事件,状态就会被守护进程置为 lauched。
  • 在 lauched 状态下,守护进程对 wrapper 下发 start 命令,让 wrapper 加载 dble 的启动类并运行。命令被下发之后,状态被置为 starting,守护进程等待 dble 启动完成。与启动 Java 程序一样,这里也有个超时时间。
  • 一旦dble加载成功,状态即变为 started,此时 dble 在正常的运行状态。
2954fb05214cde33964c43ccebf55c29.png
  • 在运行状态期间,守护进程会定期向 wrapper 发送 ping 包,若 wrapper 按时返回 ping 的 response 包,则守护进程则认为它正常。

异常处理

在这里我们假设 JVM 由于某种原因 hang 了一段时间。我们来看下守护进程是如何处理的。一旦 JVM hang 住了,则守护进程的 ping 命令不能及时返回,此时守护进程将状态置为 killing 并准备杀死 Java 程序。守护进程调用系统调用 kill 向子进程发送 SIGKILL 信号,发送信号之后,守护进程会等待 0.5s 以确保这期间子进程被回收和状态的正确性。

219771526d5ecb3ac597ed3c8fd77c10.png

问题分析

到这里我们已经介绍完 JWS 的启动流程和当 Java 程序异常时的处理。现在我们来看下之前我们遇到的问题:

1. issue:https://github.com/actiontech/dble/issues/1402

在最后的异常处理那节,从 killing 到 down 状态的转换过程中,主线程内会打印 log 来提示用户。打印日志前需要获取日志锁,但是此时主线程接收到 SIGCHLD 信号并回调了该信号的处理函数,在处理函数中也需要打印日志,此时也去获取日志锁,但是此锁是不可重入锁,因此发生死锁,导致守护进程主线程 hang 死。在 JWS 新的版本中,打印日志的操作会放在单独的线程中处理来解决这个问题。

2. 守护进程异常退出

这个问题是一系列问题的总成,但是根本原因是相同的,还是在于 SIGCHLD 信号。在最后一个异常处理那个图中 SIGCHLD 信号的返回我画了两个。正常情况下是在守护进程等待 Java 子进程被回收的过程中收到该信号。但是如果 Java 子进程资源回收超过 0.5s 时,守护进程已经往下执行准备重启 Java 程序了,此时再回调到 SIGCHLD 信号处理函数,会导致状态异常而退出。新版本在这方面做出了一些改善。

总结

上面我对 dble 中使用的 JWS 从启动流程和错误处理两个方面做了简单的介绍,在最后对平常工作中遇到的 bug 进行了探究,希望对你们有帮助。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值