什么是Unicorn

来源:互联网 发布:777是什么意思网络用语 编辑:程序博客网 时间:2024/05/16 18:40

The link

什么是Unicorn

Unicorn是为Ruby应用程序提供的一个HTTP服务器,类似于Mongrel or Thin。它采用Mongrel的Ragel HTTP解析器,但是采用一种截然不同的架构和理念。
在典型的配置中,你通过智能的负载均衡或者简单的轮训方式通过nginx向mongrel池发送请求。
最后,根据你负载均衡的情况,你想要更好的可视性和可靠性,所以你把haproxy也放进来了:
这里写图片描述
这样的配置可以很好的工作,但是,也存在一些问题。

  • 耗时较久的actions

当actions需要超过60s才能完成时,Mongrel将会试图杀死这些进程,对Ruby的进程来说,这已经被证明是不可靠的。Mongrels会经常陷入“卡壳”的情况,并且需要通过外部的进程(比如god或monit)来杀死。没错,这可能是我们应用程序的问题导致的,没有任何action应该耗时60s。但是我们的应用是比较复杂的,而且拥有很多移动部件,并会出错,我们的生产环境应该能够妥善处理这些错误和失败。

  • 内存增长

当mongrel占用的内存达到一定阀值后,会重启mongrel。通常,这是我们应用程序某个部分出了问题。 Engine Yard有一个关于内存膨胀,以及如何处理这种情况的帖子(Engine Yard has a great post on memory bloat and how to deal with it.)。
就像那些耗时久的action,无论如何,这些问题终究会发生。对这些不完美的事情你必须处理好,在生产环境上也是一样。我们不会经常由于内存膨胀而杀死应用程序,但是这种情况总会发生。

  • 部署缓慢

当你的服务器被挂住,需要重启9个mongrels hurts(When your server’s CPU is pegged, restarting 9 mongrels hurts. )(什么狗屎).
每一个都需要加载所有的Rails,gems,库文件和你的应用程序到内存中,然后才能提供服务。他们都在做相同的事情,但是相互之间却在竞争资源。
在这期间,你kill掉了老的mongrel,所以对你网站的访问都需要等到新的mongrel完全起来之后。如果你真的过载了,这回导致10s以上的等待。
有一些比较复杂的解决方案,通过多个haproxy的设置,重启在不同池中的mongrel来实现自动“滚动重启”。但是就像我说的,这个方案很复杂并且也不是万无一失的。

  • 重启缓慢

就上述的部署而言,任何时候由于内存增长或者超时问题导致的mongrel被杀死,都需要一段时间才能重新提供服务。在高峰期时这将会对网站的响应速度产生显著的影响。

  • 负载均衡

通过当前最流行的负载均衡方案,通过负载均衡器可以决定将请求发送给哪个mongrel处理。
这通常是为什么你从一个基于Nginx的负载均衡方案转向haproxy:haproxy在对请求的排队和将请求发送给哪些mongrel时能表现的更好。
到最后,负载均衡器继续推送请求到mongrel,此时请求并不一定会被推送给最合适的mongrel。(At the end of the day, though, the load balancer is still pushing requests to the mongrels. You run the risk of pushing a request to a mongrel who may not be the best candidate for serving a request at that time.)(这段话和上一段有关系?怎么理解起来这么困难。。。)

Unicorn

这里写图片描述
Unicorn有一个略微不同的框架,不像 nginx => haproxy => mongrel这种集群式的安装,它会是这样:
这里写图片描述
通过一个Unix域套接字(或者TCP),nginx将请求直接发送给Unicorn worker池。Unicorn的主进程管理workers,OS处理负载均衡。主进程看不到任何的请求。
这是nginx => haproxy和nginx => unicorn配置上的唯一差别:

# port 3000 is haproxyupstream github {    server 127.0.0.1:3000;}# unicorn master opens a unix domain socketupstream github {    server unix:/data/github/current/tmp/sockets/unicorn.sock;}

当Unicorn 主进程启动时,它会将应用加载到内存中,只要它准备好了提供服务,主进程就会fork出来16个workers,这些workers在socket上调用select(),当他们能够处理时,请求才会发送给他们。通过这种方式,内核可以帮助我们处理负载均衡。

  • 耗时久的actions

Unicorn 主进程明确的知道每个worker对当前的请求已经处理了多久,如果一个worker超过30s才响应,那么主进程就会立即杀死worker并fork出来一个新的。新的worker能立即处理新的请求。当这种情况发生时,会向客户端发送一个502的错误页面,这意味着你的请求在完成前就被Kill掉了。

  • 内存增长
    当一个worker占用过多内存时,god或者monit会向它发送一个QUIT信号,通知worker在处理完当前的请求之后就可以去死了。。。一旦这个worker挂掉之后,主进程会立即fork出来一个新的work,并且可以马上开始处理请求。

  • 部署慢
    现在的部署很简单,加上我们自定义的Capistrano配置,部署会非常快。下面是我们要做的。
    首先,我们向已经启动的Unicorn主进程发送一个USR2信号,告诉它启动一个新的主进程,重新加载应用代码,当新的主进程加载完后,它会fork出来需要的workers。fork出来的第一个worker发现还有一个老的主进程,会想起发送一个QUIT信号。
    当老的主进程收到QUIT信号后,它开始优雅的杀死他的workers,当所有的workers处理完请求后,就会死亡。我们现在就有了一个新版本的应用,已经加载完了并准备好了处理请求。中间没有任何的宕机时间,老的和新的workers共享UNIX域套接字,所以nginx甚至不需要关系中间的过度。
    也可以通过这个过程来升级Unicorn.
    那么迁移呢?很简单:只需要挂出“该网站暂时停机维护”的页面,运行迁移,重启Unicorn,然后删除“该网站暂时停机维护”的页面。

  • 重启缓慢
    就像上面提到的,只有在master需要启动时重启才会慢,workers能被杀死并快速的re-fork。在一个完整的重启过程中,只有主进程才会加载应用程序,无需等待。

  • 负载均衡
    现在不是推送请求给处理者了,而是workers来获取请求。Ryan Tomayko有一篇文章关于这个过程的本质,叫做 I like Unicorn because it’s Unix.
    基本上是,一个worker准备好后会主动去获取一个请求,然后进行处理。
    所以,也就没有什么负载均衡而言了。

迁移策略

如果你想从thin或者mongrel集群迁移到Unicorn,如果你当前使用的是nginx => haproxy => cluster设置,这就非常简单了。你可以不需要改动配置,只需要简单的告诉Unicorn workers监听一个TCP端口,这些端口和你当前mongrels的端口是对应的。
戳这里有个例子

after_fork do |server, worker|  # per-process listener ports for debugging/admin/migrations  addr = "127.0.0.1:#{9293 + worker.nr}"  server.listen(addr, :tries => -1, :delay => 5, :tcp_nopush => true)end

上面是告诉每个worker监听#{9293 + worker.nr}这个端口,worker会一直尝试绑定到该端口。
通过这种方式你可以启动Unicorn workers池,当Unicorn启动ok之后,你就可以干掉你的mongrel或者thin应用服务了。workers会立即绑定到这些端口并开始处理请求。
这是一种熟悉Unicorn的很好的方式,通过这种方式你不需要修改你的haproxy或者nginx配置。
(For fun, try running “kill -9” on a worker then doing a “ps aux”. You probably won’t even notice it was gone.)
一旦你熟悉了Unicorn并且部署脚本也都写好了,你就可以修改nginx的上游了,可以使用Unix域套接字,并且停掉workers对这些端口的监听。这样,你也就不需要haproxy了。

GitHub的配置

这是我们Unicorn的配置(但是貌似访问不了了。。。)
I recommend making the SIGNALS documentation your new home page and reading all the other pages available at the Unicorn site. It’s very well documented and Eric is focusing on improving it every day.

速度

说实话,我并不关心速度。我想要一个能够从容处理混乱而不是仅仅速度快的生产环境。相比速度,我更青睐稳定性和可靠性。
幸运的是,Unicorn看起来这些方便都不错。
Here are Tom’s benchmarks on our Rackspace bare metal hardware. We ran GitHub on one machine and the benchmarks on a separate machine. The servers are 8 core 16GB boxes connected via gigabit ethernet.

What we’re testing is a single Rails action rendering a simple string. This means each requeust goes through the entire Rails routing process and all that jazz.

Mongrel has haproxy in front of it. unicorn-tcp is using a port opened by the master, unicorn-unix with a 1024 backlog is the master opening a unix domain socket with the default “listen” backlog, and the 2048 backlog is the same setup with an increased “listen” backlog.

These benchmarks examine as many requests as we were able to push through before getting any 502 or 500 errors. Each test uses 8 workers.

mongrel 8: Reply rate [replies/s]:          min 1270.4 avg 1301.7 max 1359.7 stddev 50.3 (3 samples)unicorn-tcp 8: Reply rate [replies/s]:          min 1341.7 avg 1351.0 max 1360.7 stddev 7.8 (4 samples)unicorn-unix (1024 backlog) 8: Reply rate [replies/s]:          min 1148.2 avg 1149.7 max 1152.1 stddev 1.8 (4 samples)unicorn-unix (2048 backlog) 8: Reply rate [replies/s]:          min 1462.2 avg 1502.3 max 1538.7 stddev 39.6 (4 samples)

结论

Passenger 很棒. Mongrel 很棒. Thin 很棒.
使用最适合你的。看看你需要什么,然后评估有哪些可用选项。不要仅仅因为GitHub使用了你就使用,选择一个能解决你问题的工具。

We use Thin to serve the GitHub Services and I use Passenger for many of my side projects. Unicorn isn’t for every app.

But it’s working great for us.

Edit: Tweaked a diagram and clarified the Unicorn master’s role based on feedback from Eric.

0 0
原创粉丝点击