GitLab系列3 Unicorn

GitLab 为何须要 Unicorn

上一回咱们主要讲解了 GitLab-workhorse 组件的智能代理功能,从这一回起将开始介绍最核心也最复杂的组件:Unicorn(GitLab Rails),上次也说了这个组件主要处理动态网页和 api 接口html


此篇文章先介绍 Unicorn 的做用,光是这个就能讲不少内容了java


Unicorn 是使用多进程模型的 Ruby web 服务器,遵循 Rack 协议。若是要类比 java web 开发技术栈的话,Rails 应用至关于 Spring MVC 框架应用,Unicorn web 服务器至关于 tomcatnginx

GitLab 的 Rails 应用程序(即 gitlab-ce)是在 Unicorn 服务器内运行的,使用 Unicorn 的缘由是: Unicorn 能为 Rails 应用提供并发处理客户端请求的能力,而且提供了更强的容错处理git

Unicorn 的多进程模型能很好地利用服务器多核 CPU 的资源,以提供更好的并发能力。当 Unicorn 启动时,Unicorn 的主进程,即 master 进程,会以 fork 的方式建立一系列 worker 进程。Unicorn 经过一个 master 进程来管理多个 worker 进程,其中 master 进程不负责处理客户端的 HTTP 请求,多个 worker 进程监听同一组套接字以处理客户端请求github


实际上,worker 进程可能会挂掉或者超时(超时是指,若是 master 进程发现某个 worker 进程耗费太长时间在处理一个请求,master 进程将以发送信号(SIGKILL, kill -9)终止 worker 进程)web

# unicorn_stderr.log 日志,如下表示id 为 10 的 worker 的进程超时,master 进程杀掉后又重启了新进程,重启先后 pid 是不一致的
[2015-06-05T10:58:08.660325 #56227] ERROR -- : worker=10 PID:53009 timeout (61s > 60s), killing
[2015-06-05T10:58:08.699360 #56227] ERROR -- : reaped #<Process::Status: pid 53009 SIGKILL (signal 9)> worker=10
[2015-06-05T10:58:08.708141 #62538] INFO -- : worker=10 spawned pid=62538
[2015-06-05T10:58:08.708824 #62538] INFO -- : worker=10 ready

复制代码

无论 worker 进程会以哪一种方式终止,master 进程总会建立全新的 worker 进程替代原 worker 进程,整个过程不会丢弃用户的请求编程

gitlab-ce 自己是内存泄漏的应用。因为 Unicorn 在运行过程会 fork 大量的 worker 进程,内存泄漏会显如今长时间运行的进程中,如 worker 进程(master 进程由于不频繁处理用户请求而几乎没有内存泄漏的现象)。而 Unicorn 自己并无提供自动重启 worker 进程的功能,为解决这个问题就出现了 unicorn-worker-killer ,具体查看 github.com/kzk/unicorn…api

GitLab 使用了 unicorn-worker-killer 以让这些进程的内存泄漏得以管控:Unicorn worker 进程在每处理 16 个请求后将进行内存自检,若是 worker 进程占用的内存大小超过了预设定的值,Unicorn master 进程将会自动将此 worker 进程替换掉,整个过程不会影响任何请求的处理tomcat

# unicorn_stderr.log 日志,如下表示 worker 进程的当前占有内存超过约 250M,因而对此 worker 进程执行重启替换的操做
[2015-06-05T12:07:41.828374 #125918] WARN -- : #<Unicorn::HttpServer:0x00000002734770>: worker (pid: 125918) exceeds memory limit (256413696 bytes > 254802235 bytes)
[2015-06-05T12:07:41.828472 #125918] WARN -- : Unicorn::WorkerKiller send SIGQUIT (pid: 125918) alive: 23 sec (trial 1)
[2015-06-05T12:07:42.025916 #117565] INFO -- : reaped #<Process::Status: pid 125918 exit 0> worker=4
[2015-06-05T12:07:42.034527 #127549] INFO -- : worker=4 spawned pid=127549
[2015-06-05T12:07:42.035217 #127549] INFO -- : worker=4 ready

复制代码

综上所述,GitLab 使用 Unicorn 的目的:bash

  1. 充分利用服务器多核 CPU 以并发处理客户端请求
  2. 可用性:一个进程的异常不会导致整个 GitLab 应用瘫痪;
  3. 管理 Rails 应用的内存泄漏

固然它也带来了一些问题:

  1. 使用多进程就不得不面临吃内存的难题,同时使得 unicorn 的 worker 进程数量较受限制
  2. 多进程阻塞式 IO 难以接受慢客户端形成的性能损失(想象全部的 worker 进程都正在处理慢客户端,若是客户端还在慢慢地读取 worker 进程准备好的响应信息,那 worker 进程就没办法处理下一个请求),所以通常状况下需经过反向代理服务器(如 nginx 服务器,或 gitlab-workhorse 等)才能解决慢客户端的问题(worker 进程把处理好的响应消息交给反向代理服务器缓冲区,由反向代理服务器继续和客户端慢慢纠缠,本身继续处理下一个请求,这样 Unicorn 的吞吐量天然就不会受影响了)


  3. Unicorn 的设计与 GitLab 的 git-over-http/https 业务不兼容,即经过 HTTP/HTTPS 对 Git 存储仓库进行访问(git clone/push 等)。git-over-http/https 自己是相对比较耗时的操做,而 unicorn 服务器若是为了知足此业务而调大请求的超时参数显然是不合适的

附录

参考连接

相关文章
相关标签/搜索