ITPub博客

首页 > > > Golang中的自动伸缩和自防御设计-3

Golang中的自动伸缩和自防御设计-3

作者:zhmw 时间:2019-12-24 10:09:10 0 删除 编辑

终止检测(Terminationdetection)

从技术上讲,对无响应进程的检测和解析还应包括意外终止的进程。假死进程将无法报告它们正在执行作业,因此最终它们将由于响应超时而被替换。针对这类进程,越早发现越好。

 

尝试1

当启动一个进程时,可以得到分配给它的pid。Golang os包有一个名为FindProcess的函数,它返回给定pid的一个进程对象。如果能定期检查跟踪的每个进程,那么就知道某个特定的Worker是否处于假死状态。但问题在于FindProcess函数总是会返回一些东西,即使给定的pid不存在任何进程(走进了死胡同☹)

 

尝试2

通过键入“kill – s 0 {pid}”,则不会向进程发送信号,但仍将执行错误检查。如果给定pid没有进程,则会发生错误。这可以用Golang快速实现,但不幸的是,在Golang中运行它不会产生任何错误。类似地,向不存在的进程发送0信号也不表示进程的存在。(另一个死胡同☹)

 

最终方案

幸运的是实际上已经有了一种机制来检测假死进程。还记得用来监听Worker进程的scanner程序吗?scanner监听的标准输出管道对象是名为ReadCloser的类型。顾名思义,可以关闭它,如果另一端的Worker进程以任何方式停止,就会关闭它。如果管道关闭,scanner将停止侦听并跳出循环。因此可以在循环逻辑之后,在代码中插入逻辑以捕获该Worker进程停止的信号。

 

现在需要确定的是Master进程的正常操作导致了Worker的关闭(例如杀死没有响应的Worker,或者由于负载减少而收缩),还是意外终止。在Master进程以任何原因杀死/停止一个Worker之前,它会从进程映射的Map中删除它。因此,如果scanner扫描程序发现某进程已经终止,但Worker进程的引用仍然登记在Map中,那表示它没有在Master进程的控制下关闭。如果是这样的话,需要进行替换。

if (*procs)[worker.Index] ==worker {

StartWorker(procs, worker.Index)

}

通过在终端中使用kill命令来终止Worker进程,可以很容易地测试它的功能。

 

优雅关闭(Gracefulshut down)

当第一次使用Golang构建自动伸缩行为的原型时,调用了上面列出的KillWorker函数,以便于在负载较低时终止部分进程。设想如果一个Worker正在处理作业时被KillWorker函数终止,那么该作业会发生什么?在作业完成之前,它将安全地保存在Redis的队列中。只有当Worker确认作业已完成时,它才会被移除。Master进程定期检查已假死的Redis连接,并将未响应的作业移回就绪队列。这都是由Golang Redis队列库管理的。

 

这意味着当Worker进程意外终止时,不会丢失任何作业。这也意味着手动关闭进程完全可以正常工作。然而这感觉有点low,意味着这些作业将被延迟执行。一种更好的解决方案是实现优雅关闭—即允许Worker进程完成当前正在处理的工作,然后自然退出。

 

步骤1 - Master进程通知Worker停止

To start off, we need away for the master process to tell a particular worker process to begingraceful shut down. I’ve read that a common way of doing this is to send an OSsignal such as ‘interrupt’ to the worker process, and then have the worker handlethose signals to perform graceful shut down. For now though, I preferred toleave the OS signals to their default behaviours, and instead have the masterprocess send “stop” through the standard in pipe of a worker process.

首先需要一种方法让Master进程告诉特定的Worker进程开始优雅地关闭。一种常见的方法是向Worker进程发送一个OS信号,比如“interrupt”,然后让Worker处理这些信号,执行优雅关闭。这里让Master进程通过Worker进程管道中的标准输出发送“stop”指令。

funcStopWorker(procs*map[int]*Worker, indexint) {

worker:= (*procs)[index]

stdinPipe:=worker.StdinPipe

_, err:= (*stdinPipe).Write([]byte("stop\n"))

iferr!=nil {

// 错误及异常处理

}

}

 

步骤2 - Worker进程优雅关闭

当Worker进程接收到“stop”消息时,它使用Golang Redis队列库来停止消费,并设置一个布尔字段来指示它已经准备好优雅地关闭。另一个布尔字段用于跟踪作业当前是否在进行中。只要其中一个布尔值为真,程序就会保持活动。直到它们都为false,则意味着没有作业要处理,可以被标记为优雅关闭,此时程序自然终止。

funcscan(consumer*Consumer) {

reader:=bufio.NewReader(os.Stdin)

for {

text, _:=reader.ReadString('\n')

ifstrings.Contains(text, "stop") {

stop(consumer)

}

}

}

 

步骤3 – Worker进程反馈Master已完成

在Master流程中需要从映射Map中删除已关闭Worker。可以在给Worker发送“stop”消息后执行此操作,但是如果上一个作业碰巧导致该Worker陷入一个意外的死循环,会发生什么?为了更好地理清这个问题,当一个Worker进程完成了它的上一个任务并关闭时,它将打印一个“stop”消息。就像时间戳一样,这个消息在之前设置的scanner程序中获取。当Master看到这条消息时,可以停止跟踪该Worker并将其从Map中删除。

// Worker进程:

funcstop(consumer*Consumer) {

consumer.queue.StopConsuming()

consumer.running = false

if !consumer.doingJob {

fmt.Println("stop")

}

}

// Master进程的扫描流程:

ifstrings.Contains(line, "stop") {

delete(*procs, worker.Index)

break

}

 

谁来监视监控者?

此时dSYM-worker进程可以自动伸缩以处理突增的负载,并具有针对意外终止和无响应性的自卫机制。但是Master进程本身是否存在单点故障的可能性呢?它比Worker进程简单得多,但是仍然有崩溃的危险。如果它发生故障,一切就又回到了老路上了。此时也可以设计一个自动重启Master进程的机制。

 

有几种方法可以确保Master进程在失败时重新启动。一种方法是在启动守护进程配置中使用“KeepAlive”选项。另一种选择是编写一个脚本来检查进程的健康状况,如果发生故障就启动它。类似的脚本可以通过Cron作业来实现,每5分钟左右运行一次。

 

最后创建了另一个Golang程序,它首先启动Master进程,然后在检测到终止时重新启动它。这是采用与Master进程类似方式实现的。总的来说,它小而美且运行至今情况良好。

 

孤立的Worker

如果Master进程通过中断信号被关闭,它关联的所有Worker进程也将自动被妥善关闭。这对于部署新版本非常方便,因为只需要通知顶层Master进程关闭即可。但是如果Master进程崩溃了,那就另当别论了。所有的Worker进程都在不停地进行作业,但却没有人监督它们。当一个新的Master进程启动时,会产生更多的Worker,如果这种情况持续发生而不进行任何清理,这时候就悲剧了。

 

这是一个需要处理的重要场景,有一个简单的解决方案。Golang中的os包提供了一个Getppid()的方法。它不接受任何参数,因此可以随时调用它并获得父进程的pid。如果父进程死亡,子进程将成为孤儿,函数将返回1(初始值)。因此,在Worker进程中,可以很容易地检测它是否是孤立的。当一个Worker第一次启动时,可以获取并保存其初始父进程的pid。然后,定期调用Getppid函数并将结果与初始父pid进行比较。如果父pid发生变化,则表示该Worker已成为孤儿,此时可以优雅关闭它。

ppid:=os.Getppid()

ifppid!=initialParentId {

stop(consumer) // 开始优雅关闭

}

 

结束语

本文介绍了如何在Golang中实现自动伸缩和自防御服务,到目前为止,它运行良好。

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69901620/viewspace-3148181/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2018-12-04

  • 博文量
    295
  • 访问量
    9537