From 2bbd21ab07d29245e0f6a3bb46353e54421ceab8 Mon Sep 17 00:00:00 2001 From: astaxie Date: Fri, 7 Jun 2013 22:45:58 +0800 Subject: [PATCH] update docs for hotupdate --- docs/zh/HotUpdate.md | 42 ++++++++++++++++++++++++++++++++++++++++++ docs/zh/README.md | 1 + 2 files changed, 43 insertions(+) create mode 100644 docs/zh/HotUpdate.md diff --git a/docs/zh/HotUpdate.md b/docs/zh/HotUpdate.md new file mode 100644 index 00000000..066e5fcf --- /dev/null +++ b/docs/zh/HotUpdate.md @@ -0,0 +1,42 @@ +## 热升级是什么? + +热升级是什么呢?了解nginx的同学都知道,nginx是支持热升级的,可以用老进程服务先前链接的链接,使用新进程服务新的链接,即在不停止服务的情况下完成系统的升级与运行参数修改。那么热升级和热编译是不同的概念,热编译是通过监控文件的变化重新编译,然后重启进程,例如bee start就是这样的工具 + +## 热升级有必要吗? + +很多人认为HTTP的应用有必要支持热升级吗?那么我可以很负责的说非常有必要,不中断服务始终是我们所追求的目标,虽然很多人说可能服务器会坏掉等等,这个是属于高可用的设计范畴,不要搞混了,这个是可预知的问题,所以我们需要避免这样的升级带来的用户不可用。你还在为以前升级搞到凌晨升级而烦恼嘛?那么现在就赶紧拥抱热升级吧。 + +## beego如何支持热升级 +热升级的原理基本上就是:主进程fork一个进程,然后子进程exec相应的程序。那么这个过程中发生了什么呢?我们知道进程fork之后会把主进程的所有句柄、数据和堆栈、但是里面所有的句柄存在一个叫做CloseOnExec,也就是执行exec的时候,copy的所有的句柄都被关闭了,除非特别申明,而我们期望的是子进程能够复用主进程的net.Listener的句柄。一个进程一旦调用exec类函数,它本身就"死亡"了,系统把代码段替换成新的程序的代码,废弃原有的数据段和堆栈段,并为新程序分配新的数据段与堆栈段,唯一留下的,就是进程号,也就是说,对系统而言,还是同一个进程,不过已经是另一个程序了。 + +那么我们要做的第一步就是让子进程继承主进程的这个句柄,我们可以通过os.StartProcess的参数来附加Files,把需要继承的句柄写在里面。 + +第二步就是我们希望子进程能够从这个句柄启动监听,还好Go里面支持net.FileListener,直接从句柄来监听,但是我们需要子进程知道这个FD,所以在启动子进程的时候我们设置了一个环境变量设置这个FD。 + +第三步就是我们期望老的链接继续服务完,而新的链接采用新的进程,这里面有两个细节,第一就是老的链接继续服务,那么我们怎么有老链接存在?所以我们必须每次接收一个链接记录一下,这样我们就知道还存在没有服务完的链接,第二就是怎么让老进程停止接收数据,让新进程接收数据呢?大家都监听在同一个端口,理论上是随机来接收的,所以这里我们只要关闭老的链接的接收就行,这样就会使得在l.Accept的时候报错。 + +上面是我们需要解决的三个方面的问题,具体的实现大家可以看我实现的代码逻辑。 + +## 如何演示热升级 + +1. 编写代码,在beego应用的控制器中Get方法实现大概如下: + + func (this *MainController) Get() { + a, _ := this.GetInt("sleep") + time.Sleep(time.Duration(a) * time.Second) + this.Ctx.WriteString("ospid:" + strconv.Itoa(os.Getpid())) + } + +2. 打开两个终端 + + 一个终端输入:` ps -ef|grep 应用名` + + 一个终端输入请求:`curl "http://127.0.0.1:8080/?sleep=20"` + +3. 热升级 + + `kill -HUP 进程ID` + +4. 打开一个终端输入请求:`curl "http://127.0.0.1:8080/?sleep=0"` + +我们可以看到这样的结果,第一个请求等待20s,但是处理他的是老的进程,热升级之后,第一个请求还在执行,最后会输出老的进程ID,而第二次请求,输出的是新的进程ID \ No newline at end of file diff --git a/docs/zh/README.md b/docs/zh/README.md index 62b5ecd3..03d6494e 100644 --- a/docs/zh/README.md +++ b/docs/zh/README.md @@ -41,6 +41,7 @@ beego是一个类似tornado的Go应用框架,采用了RESTFul的方式来实 * [快速入门](Quickstart.md) * [一步一步开发应用](Tutorial.md) * [beego案例](Application.md) +* [热升级](HotUpdate.md) # API接口