Ruby Ruby Web 服务器:这十五年

laofo · 发布于 2017年03月03日 · 最后由 laofo 回复于 2017年03月03日 · 177 次阅读
4

Ruby Web服务器:这十五年

坦率的说,作为一门年轻的计算机语言,Ruby在最近二十年里的发展并不算慢。但如果与坐拥豪门的明星语言们相比,Ruby就颇显平民范儿,表现始终不温不火,批评胜于褒奖,下行多过上扬。但总有一些至少曾经自称过Rubyist的程序员们,愉快地实践了这门语言,他们没有丝毫的歧视习惯,总是努力尝试各家之长,以语言表达思想,用基准评判高下,一不小心就影响了整个技术发展的进程。本文谨以Ruby Web服务器技术的发展为线索,回顾Ruby截至目前最为人所知的Web领域中,重要性数一数二的服务器技术的发展历程,试图帮助我们了解过去,预见未来。timeline Ruby Web服务器发展时间轴

一、随波逐流

长久以来,任何Web服务器都具备的两项最重要的功能:一是根据RFC2616解析HTTP/1.1协议,二是接收、处理并响应客户端的HTTP请求。幸运的是Web技术的发展并不算太早,使得Ruby恰好能赶上这趟顺风车,但在前期也基本上受限于整个业界的进展。像Apache HTTP Server、Lighttpd和Nginx这些通用型Web服务器+合适的Web服务器接口即可完成大部分工作,而当时开发者的重心则是放在接口实现上。

cgi.rb

作为Web服务器接口的早期标准,CGI程序在调用过程中,通过环境变量(GET)或$stdin(POST)传递参数,然后将结果返回至$stdout,从而完成Web服务器和应用程序之间的通信。cgi.rb是Ruby官方的CGI协议标准库,发布于2000年的cgi.rb包含HTTP参数获取、Cookie/Session管理、以及生成HTML内容等基本功能。

Web服务器和CGI

Web服务器和CGI

当支持CGI应用的Web服务器接到HTTP请求时,需要先创建一个CGI应用进程,并传入相应的参数,当该请求被返回时再销毁该进程。因此CGI原生是单一进程/请求的,特别是每次请求时产生的进程创建/销毁操作消耗了大量系统资源,根本无法满足较高负载的HTTP请求。此外,CGI进程模型还限制了数据库连接池、内存缓存等资源的复用。

对于标准CGI应用存在的单一进程问题,各大厂商分别提出了兼容CGI协议的解决方案,包括网景的NSAPI、微软的ISAPI和后来的Apache API(ASAPI)。上述服务器API的特点是既支持在服务器进程内运行CGI程序,也支持在独立进程中运行CGI程序,但通常需要在服务器进程中嵌入一个插件以支持该API。

Webrick

作为最古老的Ruby Web服务器而不仅仅是一个接口,诞生于2000年的Webrick从Ruby 1.9.3(2011年10月正式发布)起被正式纳入标准库,成为Ruby的默认Web服务器API。Webrick支持HTTP/HTTPS、代理服务器、虚拟主机服务器,以及HTTP基础认证等RFC2617及以外的其它认证算法。同时,一个Webrick服务器还能由多个Webrick服务器或服务器小程序组合,提供类似虚拟主机或路由等功能:例如处理CGI脚本、ERb页面、Ruby块以及目录服务等。

Webrick曾被用于Rails核心团队的开发和测试中。但是,Webrick内置的HTTP Parser非常古老,文档缺失,性能低下且不易维护,功能单一且默认只支持单进程模式(但支持多线程,不过在Rails中默认关闭了对Webrick的多线程支持),根本无法满足产品环境中的并发和日常维护需求。目前一般只用于Web应用的本地开发和基准测试。

fcgi.rb

fcgi.rb是FastCGI协议的Ruby封装(latest版底层依赖libfcgi)。为了与当时的NSAPI竞争,FastCGI协议最初由Open Market提出和开发、并应用于自家Web服务器,延续了前者采用独立进程处理请求的做法:即维持一个FastCGI服务器。当Web服务器接收到HTTP请求时,请求内容和环境信息被通过Socket(本地)或TCP连接(远程)的方式传递至FastCGI服务器进行处理,再通过相反路径返回响应信息。分离进程的好处是Web服务器进程和FastCGI进程是永远保持的,只有相互之间的连接会被断开,避免了进程管理的开销。

Web服务器和FastCGI/SCGI服务器

进一步,FastCGI还支持同时响应多个请求。为了尽量减少资源浪费,若干请求可以复用同一个与Web服务器之间的连接,且支持扩展至多个FastCGI服务器进程。FastCGI降低了Web服务器和应用程序之间的耦合度,进而为解决安全、性能、管理等各方面问题提供新的思路,相比一些嵌入式方案如mod_perl和mod_php更具灵活性。

由于FastCGI协议的开放性,主流Web服务器产品基本都实现了各自的FastCGI插件,从而导致FastCGI方案被广泛使用。fcgi.rb最早开发于1998年,底层包含C和Ruby两种实现方式,早期曾被广泛应用于Rails应用的产品环境。

mod_ruby

mod_ruby是专门针对Apache HTTP Server的Ruby扩展插件,支持在Web服务器中直接运行Ruby CGI代码。由于mod_ruby在多个Apache进程中只能共享同一个Ruby解释器,意味着当同时运行多个Web应用(如Rails)时会发生冲突,存在安全隐患。因此只在一些简单部署环境下被采用,实际上并没有普及。

LiteSpeed API/RubyRunner

LiteSpeed是由LiteSpeed Tech公司最初于2002年发布的商用Web服务器,特点是与被广泛采用的Apache Web服务器的配置文件兼容,但因为采用了事件驱动架构而具有更好的性能。

LiteSpeed API(LSAPI)是LiteSpeed专有的服务器API,LSAPI具备深度优化的IPC协议以提升通信性能。类似其它Web服务器,LiteSpeed支持运行CGI、FastCGI、以及后来的Mongrel。同时在LSAPI的基础上开发了Ruby接口模块,支持运行基于Ruby的Web应用。此外,LiteSpeed还提供RubyRunner插件,允许采用第三方Ruby解释器运行Ruby应用,但综合性能不如直接基于LSAPI Ruby。

由于LiteSpeed是收费产品,其普及率并不高,一般会考虑采用LiteSpeed作为Web服务器的业务场景包括虚拟主机/VPS提供商、以及相关业务的cPanel产品。同时,LiteSpeed也会被用于一些业务需求比较特殊的场合,例如对Web服务器性能要求高,且应用程序及其部署需要兼容Apache服务器。LiteSpeed于2013年发布了开源的轻量Web服务器——OpenLiteSpeed(GPL v3),移除了商业版本中偏具体业务的功能如cPanel等,更倾向于成为通用Web服务器。

scgi.rb

scgi.rb是对SCGI协议的纯Ruby实现。从原理上来看,SCGI和FastCGI类似,二者的性能并无多大差别。但比起后者复杂的协议内容来说,SCGI移除了许多非必要的功能,看起来十分简洁,且实现复杂度更低。

Web服务器和多FastCGI/SCGI服务器

与FastCGI类似,一个SCGI服务器可以动态创建服务器子进程用于处理更多请求(处理完毕将转入睡眠),直至达到配置的子进程上限。当获得Web服务器请求时,SCGI服务器进程会将其转发至子进程,并由子进程运行CGI程序处理该请求。此外,SCGI还能自动销毁退出和崩溃的子进程,具有良好的稳定性。

二、闻名天下

2005年,David Heinemeier Hansson(DHH)发布了基于Ruby的开发框架Ruby on Rails(Rails),聚光灯第一次聚焦在Ruby身上。但是业内普遍对Web服务器的方案感到棘手,本地环境Webrick/产品环境FastCGI+通用Web服务器几乎成了标配,无论是开发、部署或维护都遇到不少困难,一些吃螃蟹的人遂把此视为Rails不如J2EE、PHP方案的证据。

Mongrel

2006年,Zed Shaw发布了划时代的Mongrel。Mongrel把自己定位成一个“应用服务器”,因为其不仅可以运行Ruby Web应用,也提供标准的HTTP接口,从而使Mongrel可以被放置在Web代理、Load Balancer等任意类型的转发器后面,而非像FastCGI、SCGI一样通过调用脚本实现Web服务器和CGI程序的通信。

Mongrel采用Ragel开发HTTP/1.1协议的Ruby parser,而后者是一个高性能有限自动机编译器,支持开发协议/数据parser、词法分析器和用户输入验证,支持编译成多种主流语言(包括Ruby)。采用Regel也使parser具有更好的可移植性。但是,Mongrel本身不支持任何应用程序框架,而需要由框架自身提供这种支持。

Mongrel Web服务器

Mongrel支持多线程运行(但对于当时非线程安全的Rails来说,仍然只能采用多进程的方式提高一部分并发能力),曾被Twitter作为其第一代Web服务器,还启发了Ryan Dahl发布于2009年的Node.JS。

但是当Mongrel发布后没过多久,Shaw就与Rails社区的核心成员不和(实际上Shaw对业界的许多技术和公司都表达过不满),随后就终止了Mongrel的开发。进而在其Parser的基础上开发了其后续——语言无关的Web服务器Mongrel2(与前续毫无关系)。

尽管Mongrel迅速衰落,却成功启发了随后更多优秀Ruby应用服务器的诞生,例如后文将介绍的Thin、Unicorn和Puma。

Rack

随着Web服务器接口技术的发展,从开始时作为一个module嵌入Web服务器,到维护独立的应用服务器进程,越来越多的应用服务器产品开始涌现,同时相互之间还产生了差异化以便适应不同的应用场景。但是,由于底层协议和API的差别,基于不同的应用服务器开发Web产品时,意味着要实现各自的通信接口,从而为Web应用开发带来更多工作量。特别是对于类似Django、Rails这些被广泛使用的Web框架来说,兼容主流应用服务器几乎是必须的。

2003年,Python界权威Phillip J. Eby发表了PEP 0333(Python Web Server Gateway Interface v1.0,即WSGI),提出一种Web服务器和应用程序之间的统一接口,该接口封装了包括CGI、FastCGI、mod_python等主流方案的API,使遵循WSGI的Python Web应用能够直接部署在各类Web服务器上。与Python的发展轨迹相似,Ruby界也遇到了类似的挑战,并最终在2007年出现了与WSGI类似的Rack。

与WSGI最初只作为一份建议不同,Rack直接提供了模块化的框架实现,并由于良好的设计架构迅速统一了Ruby Web服务器和应用程序框架接口。

Rack被设计成一种中间件“框架”,接收到的HTTP请求会被rack放入不同的管线(中间件)进行处理,直到从应用程序获取响应。这种设计通过统一接口,把一般Web应用所需的底层依赖,包括Session处理、数据库操作、请求处理、渲染视图、路由/调度、以及表单处理等组件以中间件的形式“放入”rack的中间件管线中,并在HTTP请求/响应发生时依次通过上述管线传递至应用程序,从而实现Web应用程序对底层通信依赖的解绑。

Rack中间件

Rack中间件

Rack接口部分包含两类组件:Handler,用于和Web服务器通信;Adapter,用于和应用程序通信。截至Rack 1.6,Rack内置的handlers包括WEBrick、FCGI、CGI、SCGI、LiteSpeed以及Thin,上述handlers用以兼容已有的常见应用服务器。而2008年后,随着rack逐渐成为事实标准,更新的Ruby Web服务器几乎都包含Rack提供的handler。包括Rails、Sinatra、Merb等等几乎所有主流框架都引入了Rack Adapters的支持。

三、百花齐放

Mongrel和Rack的相继诞生,使Ruby Web服务器、乃至应用程序框架的发展有了一定意义上可以遵循的标准。Mongrel后相继派生出Thin、Unicorn和Puma;而Rack统一了Ruby Web服务器和应用程序框架接口,使应用开发不再需要考虑特定的部署平台。Ruby Web服务器开始依据特定需求深入发展。

Thin/Goliath

发布于2009年的Thin沿用了Mongrel的Parser,基于Rack和EventMachine开发,前者上文已有介绍,EventMachine是一个Ruby编写的、基于Reactor模式的轻量级事件驱动I/O(类似JBoss Netty、Apache MINA、Python Twisted、Node.js、libevent和libev等)和并发库,使Thin能够在面对慢客户端的同时支持高并发请求。

发表自1995年的Reactor模型的基本原理是采用一个单线程事件循环缓存所有系统事件,当事件发生时,以同步方式将该事件发送至处理模块,处理完成后返回结果。基于Reactor模型的EventMachine具备异步(非阻塞)I/O的能力,被广泛用于大部分基于Ruby的事件驱动服务器、异步客户端、网络代理以及监控工具中。

Reactor模型

Reactor模型

2011年,社交网络分析商PostRank开源了其Web服务器Goliath,与Thin相似(都采用了EventMachine)但又有很大不同,采用新的HTTP Parser,同时针对异步事件编程中的高复杂度回调函数问题,借助Ruby1.9+的纤程技术实现了线性编码,使程序具备更好的可维护性。Goliath支持MRI、JRuby和Rubinius等多平台。在附加功能方面,Goliath的目标不仅是作为Web服务器,更是一个快速构建WebServices/APIs的开发框架,但是随着之后PostRank被Google收购,Goliath项目也就不再活跃在开源界了。

Unicorn

2009年,Eric Wong在Mongrel 1.1.5版本的基础上开发了Unicorn。Unicorn是一个基于Unix/类Unix操作系统的、面向快客户端、低延迟和高带宽场景的Rack服务器,基于上述限制,任何情况下几乎都需要在Unicorn和客户端之间设置一个反向代理缓存请求和响应数据,这是Unicorn的设计特点所决定的,但也使得Unicorn的内部实现相对简洁、可靠。

尽管来源于Mongrel,但Unicorn只在进程级运行,且吸收和利用了一些Unix/类Unix系统内核的特性,如Prefork模型。

Unicorn由1个master进程和n个fork(2)子进程组成,子进程分别调用select(2)阻塞自己,直到出错或者超时时,才做一些写日志、处理信号以及维护与master的心跳链接等内置任务。子进程和master间通过一个共享socket实现通信,而由Unix/类Unix系统内核自身处理资源调度。

Unicorn的多进程模型

Unicorn的多进程模型

Unicorn的设计理念是“只专注一件事”:多进程阻塞I/O的方式令其无从接受慢客户端——但前置反向代理能解决这一问题;workers的负载均衡就直接交给操作系统处理。这种理念大大降低了实现复杂度,从而提高了自身可靠性。此外,类似Nginx的重加载机制,Unicorn也支持零宕机重新加载配置文件,使其允许在线部署Web应用而不用产生离线成本。

Phusion Passenger(mod_rails/mod_rack)

2008年初,一位叫赖洪礼的Ruby神童发布了mod_rails。尽管Mongrel在当时已经席卷Rails的Web服务器市场,但是面对部署共享主机或是集群的情况时还是缺少统一有效的解决方案,引起业内一些抱怨,包括DHH(也许Shaw就不认为这是个事儿)。

mod_rails最初被设计成一个Apache的module,与FastCGI的原理类似,但设置起来异常简单——只需要设置一个RailsBaseURI匹配转发至Rails服务器的URI串。mod_rails服务器会在启动时自动加载Web应用程序,然后按需创建子进程,并协调Web服务器和Rails服务器的通信,从而支持单一服务器同时部署多个应用,还允许按需自动重启应用服务器。

mod_rails遵循了Rails的设计原则,包括Convention over Configuration、Don’t Repeat Yourself,使其面向部署非常友好,很快得到了业界青睐,并在正式release时改名Passenger。

在随后的发展中,Passenger逐渐成为独立的Ruby应用服务器、支持多平台的Web服务器。截至2015年6月,Phusion Passenger的版本号已经达到5.0.10(Raptor),核心采用C++编写,同时支持Ruby、Python和Node.js应用。支持Apache、Nginx和独立HTTP模式(推荐采用独立模式),支持Unix/类Unix系统,在统计网站Builtwith上排名Ruby Web服务器使用率第一。

值得一提的是,Phusion Passenger的开源版本支持多进程模式,但是其企业版同样支持多线程运行。本文撰写时,Phusion Passenger是最近一个号称“史上最快”的Ruby Web服务器(本文最后将进一步介绍Raptor)。

Trinidad/TorqueBox

Trinidad发布于2009年,基于JRuby::Rack和Apache Tomcat,使Rails的部署和世界上最流行的Web服务器之一Tomcat结合,支持集成Java代码、支持多线程的Resque和Delayed::Job等Worker,也支持除Tomcat以外的其它Servlet容器。

与Trinidad相比,同样发布于2009年的TorqueBox不仅仅是一个Web服务器,而且被设计成一个可移植的Ruby平台。基于JRuby::Rack和WildFly(JBoss AS),支持多线程阻塞I/O,内置对消息、调度、缓存和后台进程的支持。同时具有集群、负载均衡、高可用等多种附加功能。

Puma

Puma——Mongrel最年轻的后代于2011年发布,作者是Evan Phoenix。

由于Mongrel诞生于前Rack时期,而随着Rack统一了Web服务器接口,任何基于Rack的应用再与Mongrel配合就有许多不便。Puma继承了前者的Parser,并且基于Rack重写了底层通信部分。更重要的是,Puma部分依赖Ruby的其它两个流行实现:Rubinius和JRuby,与TorqueBox类似拥有多线程阻塞I/O的能力(MRI平台不支持真正意义上的多线程,但Puma依然具有良好并发能力),支持高并发。同时Puma还包含了一个事件I/O模块以缓冲HTTP请求,以降低慢客户端的影响。但是,从获得更高吞吐量的角度来说,Puma目前仍然需要采用Rubinius和JRuby这两个平台。

Reel

Reel是最初由Tony Arcieri发布于2012年的采用事件I/O的Web服务器。采用了不同于Eventmachine的Celluloid::IO,后者基于Celluloid——Actor并发模型的Ruby实现库,解决了EM只能在单一线程中运行事件循环程序的问题,从而同时支持多线程+事件I/O,在非阻塞I/O和多线程方案间实现了良好的融合。

与其它现代Ruby Web服务器不同的是,Reel并不是基于Rack创建,但通过Reel::Rack提供支持Rack的Adapter。尽管支持Rails,与Puma也有一定的相似性,但与Unicorn、Puma和Raptor相比,Reel在部署Rails/Rack应用方面缺少易用性。实际上基于Celluloid本身的普及程度和擅长领域,相比其它Web服务器而言,Reel更适合部署WebSocket/Stream应用。

Yahns

2013年,Eric Wong等人受Kqueue(源自FreeBSD,同时被Node.js作为基础事件I/O库)的启发启动了Yahns项目。其目标与Reel类似,同样是在非阻塞I/O设计中引入多线程。与Reel不同的是,Yahns原生支持Rack/HTTP应用。

Yahns被设计成具有良好的伸缩性和轻量化特性,当系统应用访问量较低或为零时,Yahns本身的资源消耗也会保持在较低水平。此外,yahns只支持GNU/Linux(并通过kqueue支持FreeBSD),并声称永远不会支持类似Unicorn或Passenger里的Watchdog技术,不会因为应用崩溃而自动销毁和创建进程/线程,因此对应用程序本身的可靠性有一定要求。

四、迈向未来

回顾过去,Ruby Web服务器在发展中先后解决了缺少部署方案、与Web应用程序不兼容、运维管理困难等问题,基础架构趋于成熟且稳定。而随着更多基准测试结果的出现,业界逐渐开始朝着更高性能和并发量发展,同时针对HTTP协议本身的优化和扩展引入的HTTP/2,以及HTML5的WebSocket/Stream等需求均成为未来Ruby Web服务器发展的方向。

高吞吐量

以最新的Raptor(上文提到的Phusion Passenger 5)为例,其在网络I/O模型的选择上融合了现有其它优秀产品的方案,包括Unicorn的多进程模型、内置基于多线程和事件I/O模型的反向代理缓冲(类似Nginx的功能,但对Raptor自身做了大量裁减和优化)、以及企业版具有的多线程模型(类似Puma和TorqueBox);此外,Raptor采用的Node.js HTTP Parser(基于Nginx的Parser)的性能超过了Mongrel;更进一步,Raptor甚至实现了Zero-copy和一般在大型游戏中使用的区域内存管理技术,使其对CPU和内存访问的优化达到了极致(感兴趣的话可以进一步查阅这里)。

Raptor的优化模型

另外也需要看到,当引入多线程运行方式,现有Web应用将不得不仔细检查自身及其依赖,是否是线程安全的,同时这也给构建Ruby Web应用带来更多新的挑战。这也是为什么更多人宁愿选择进程级应用服务器的方式——毕竟对大多数应用来说不需要用到太多横向扩展,引入反向代理即可解决慢客户端的问题,而采用Raptor甚至在独立模式能工作的更好(这样就不用花时间去学习Nginx)。

除非你已经开始考虑向支持大规模并发的架构迁移,并希望节省接下来的一大笔花费了。

HTTP/2

2015年5月,HTTP/2随着RFC7540正式发布。如今各主流服务器/浏览器厂商正在逐渐完成从HTTP/2测试模块到正式版本的过渡。而截至目前,主流Ruby Web服务器都还没有公开HTTP/2的开发信息。HTTP-2是在2013年由Ilya Grigorik发布的纯Ruby的HTTP/2协议实现,包括二进制帧的解析与编码、流传输的多路复用和优先级制定、连接和流传输的流量控制、报头压缩与服务器推送、连接和流传输管理等功能。随着HTTP/2的发布和普及,主流Ruby Web服务器将不可避免的引入对HTTP/2的支持。

WebSocket/流(Stream)/服务器推送事件(Server Sent Events,SSE)

2011年,RFC6455正式公布了WebSocket协议。WebSocket用于在一个TCP链接上实现全双工通信,其目的是实现客户端与服务器之间更高频次的交互,以完成实时互动业务。鉴于该特点,仅支持慢客户端的Web服务器就无法有效支撑WebSocket的并发需求,更何况后者对并发量更加严苛的要求了。而对于同样需要长连接的流服务器和服务器推送事件服务(SSE),都避免不了对长连接和高并发量的需求。尽管高性能的Ruby Web服务器都有足够的潜力完成这些任务,但是从原生设计的角度来看,更加年轻的Reel和Yahns无疑具有优势。

最近Planet ruby在Ruby邮件组发布了Awesome Webservers,该Github Repo旨在对主流Ruby Web服务器进行总结和对比,并且保持持续更新,推荐开发者关注。

转自:http://insights.thoughtworkers.org/ruby-web-server/

共收到 2 条回复
4
laofo · #1 · 2017年03月03日

Ruby 服务器对比

之前一直对 Ruby 的服务器不是很了解,在stackoverflow看到一篇文章感觉不错,顺便翻译了一下,英语不好,如果哪里错了请大家指出,避免误导他人。 原文地址:http://stackoverflow.com/questions/4113299/ruby-on-rails-server-options

The word "deployment" can have two meanings depending on the context. You are also confusing the roles of Apache/Nginx with the roles of other components.

Historic note: This article was originally written on November 6, 2010, when the Ruby app server ecosystem was limited. I've updated this article on March 15 2013 with all the latest updates in the ecosystem.

Disclaimer: I am one of the authors of Phusion Passenger, one of the app servers.

一,Apache vs Nginx

他们都是web服务器,都能伺服静态文件,利用恰当的模块也能伺服动态的web应用。Apache更加流行,拥有更多的功能;Nginx则相对小巧、快速、功能少。

让Apache 和 Nginx 来伺服 Ruby 服务器都不是开箱即用(out-of-the-box)的,为此你需要使用另外的插件来组合他们。

Apache 和 Nginx都能作为反向代理,就是说他们能够把进来的 HTTP 请求发给其他服务器,接着把该服务器的 HTTP 响应转给客户端,后面会看到为什么和这个有关系。

二,Mongrel以及其他production环境的服务器 vs WEBrick

Mongrel是Ruby实现的应用服务器,具体来说:

1,在自己的进程空间中加载 Ruby 应用。

2, 创建一个 TCP socket,允许它可以和外部世界通信(例如Internet)。Mongrel 在这个socket上监听 HTTP 请求,并把请求数据转发给 Ruby 应用。

3,Ruby 应用返回一个描述 HTTP 响应的对象,Mongrel 将其转换为真正的 HTTP 响应字节,并发回到 socket 中。

然而 Mongrel 已经不再维护了,其他替代的服务器是:

Phusion Passenger Unicorn Thin Puma Trinidad (JRuby only) TorqueBox (JRuby only) 接下来我会讲一讲他们和 Mongrel 的区别

WEBrick 和 Mongrel 很像,区别如下:

WEBrick 不适合用于 production。WEBrick 完全是用 Ruby 写的;Mongrel( 以及其他 Ruby 应用服务器)是部分 Ruby 部分C,主要是 Ruby,但它的 HTTP 解析器为了性能是用 C 写的。 WEBrick 速度比较慢而且不够强壮,有普遍知道的内存泄漏问题,以及 HTTP 解析问题。 因为 WEBrick 是 Ruby 默认自带的,所以 WEBrick 经常用于 development 环境下作为默认服务器,而其他的服务器则需要另外安装。不建议在production 环境下使用 WEBrick 服务器,虽然因为某些原因,Heroku 选择了WEBrick 作为默认服务器,他们以前使用的是 Thin,但我不知道他们为什么换到了 WEBrick。 三,应用服务器世界

当前所有的Ruby 应用服务器都是 HTTP 类型的,一些服务器直接将80端口暴露到 Internet 中,另一些则没有。

暴露80端口的:Phusion Passenger, Rainbows。 没有直接暴露的:Mongrel, Unicorn, Thin, Puma。这些服务器必须必须置于反向代理服务器之后,比如Apache 和 Nginx。 我不了解 Trinidad 和 TorqueBox,所以就忽略了。 为什么有些服务器需要置于反向代理之后?

某些服务器的每个进程在同一时间只能处理一个请求,如果想同时处理两个请求,你就需要启动多个服务器实例,都伺服同一个 Ruby 应用。这种应用服务器进程的集合称为应用服务器集群(比如Mongrel Cluster, Thin Cluster)。然后你必须启动 Apache 或者 Nginx,给集群做反向代理,Apache/Nginx 会处理好集群中不同应用实例间的请求分发。(更多内容参见章节 "I/O并发模型”) Web 服务器可以缓存请求和响应。有些客户端发送数据、接收数据的速度缓慢,你当然不希望应用服务器在等待客户端收发数据时什么也不干,Web 服务器可以隔离应用服务器和慢客户端。Apache 和 Nginx 擅长同时很多事情,因为他们是多线程或者基于事件的。 大多数的应用服务器可以伺服静态文件,但不擅长,而Apache 和 Nginx可以更快速度处理这件事情。 人们经常直接使用Apache 或者 Nginx伺服静态文件,而不会处理需要转发的请求(forward requests),这是比较安全的策略。Apache 和Nginx足够聪明,可以保护应用服务器远离恶意请求。 为什么有些服务器可以直接暴露在Internet中?

Phusion Passenger 和其他应用服务器不一样,其中一个特点是可以融入其他服务器。 Rainbows 的作者公开指出,Rainbows 可以直接暴露在 Internet 中。他非常确认在解析 HTTP 过程中不会轻易遭受攻击。当然,作者也并不做任何担保,并表示不同应用环境下有其相应的风险。 四,应用服务器对比

在这一章中,我会比较我提过的大多数服务器,但不包括 Phusion Passenger。Phusion Passenger 和其他的不一样,我会单独开出一章。我还会忽略 Trinidad 和 TorqueBox,因为我对他们不是很了解,只有你用到 JRuby 的时候才会涉及到他们。

Mongrel 只有最基础的功能。像之前提到的,Mongrel 仅仅是单线程、多进程,所以它只用于集群(cluster)中。没有进程监控,意味着如果集群中一个进程崩溃了,则需要手动重启。人们需要使用额外的进程来照看 Mongrel,比如 Monit 和 God。
Unicorn 是从 Mongrel 中fork出来的。支持监控一定数量的的进程:如果一个进程崩溃了,则会被主进程自动重启。它能让所有进程都监听同一个共享的socket,而不是每个进程使用单独的socket,这会简化反向代理的配置。像Mongrel一样,Unicorn 是单线程、多进程。 Thin 利用 EventMachine 库,实现基于事件的 I/O 模型。它并不是使用 Mongrel 的 HTTP 解析器,没有基于 Mongrel。它的集群节点没有进程监控,所以你需要去监控进程是否崩溃。每个进程监听各自的socket,不像 Unicorn 一样共享socket。理论上来说,Thin 的I/O 模型允许高并发,这也是 Thin 被应用的大部分场合。一个 Thin 的进程只能处理一个并发请求,所以你还需要集群。关于这个古怪的性质,更多内容参见“I/O并发模型”。 Puma 也是从 Mongrel 中fork出来的,但和 Unicorn 不一样的是,Puma 是基于多线程,使用 Thread Pool 实现。因为 GIL 的存在,所以 MRI 不能利用多核实现CPU并行,所以你需要特别确认的是你能利用多核。更多内容参见“I/O并发模型”。 Rainbows 通过给不同的库实现多种并发模型 。 五,Phusion Passenger

Phusion Passenger 和其他的不一样。他直接融入 Apache 或者 Nginx,类似于 Apache 的 mod_php。就像 mod_php 使 Apache伺服 PHP 应用一样, Phusion Passenger 也可以使 Apache 或者 Nginx 伺服 Ruby 应用。Phusion Passenger 的目标是所有的事情做起来尽可能地减少麻烦。

如果使用 Phusion Passenger的话,你不需要为你的应用启动一个进程或者集群,为 Apache/Nginx 配置静态目录,或者设置反向代理。

你只需要:

编辑web服务器的配置文件,写入 Ruby 应用下public文件夹的路径 没有第二步。 所有的配置工作都在Web 服务器配置文件的指导下做完了,Phusion Passenger几乎自动化了所有事情,不需要启动集群以及管理进程。启动或者停止进程,他们崩溃时重启,这些都被自动化了。和其他应用服务器相比,Phusion Passenger 所需要做的改动工作非常少,这是人们使用 Phusion Passenger 的主要原因。

和其他应用服务器不同的是,Phusion Passenger 主要是用 C++ 写的,速度很快。

Phusion Passenger 的企业版有更多的特性,比如自动回滚重启,支持多线程,容错部署。(such as automated rolling restarts, multithreading support, deployment error resistance, etc.)

基于以上原因,Phusion Passenger是当前最流行的 Ruby 应用服务器,服务于超过50,000 站点,包括大型站点:New York Times, Pixar, Airbnb。

六,Phusion Passenger vs 其他服务器

相对其他的服务器 ,Phusion Passenger 提供了更多的特性功能:

根据访问量自动调整进程的数量。在资源有限的服务器上,我们运行多个 Ruby 应用,而我们的应用不对外公开,而且组织内的访客每天的访问量也很低,例如 Gitlab, Redmine等。Phusion Passenger 能够在进程不使用的时候挂起他们,需要的时候恢复进程,为更重要的应用腾出资源。相比之下,其他的服务器的所有进程会一直运行着。 有些服务器不适合某些特定的负荷。例如 Unicorn 为轻量快速的请求而设计,See the Unicorn website section "Just Worse in Some Cases". Unicorn 不擅长的负荷:

> 流(e.g. Rails 4 live streaming or Rails 4 template streaming)

> 调用 HTTP API

Phusion Passenger Enterprise 4及后续版本中多种I/O模型,使它非常适合这种负荷。

其他的应用服务器需要用户为每个应用至少启动一个实例,相比之下,Phusion Passenger 支持一个实例多个应用。这大大减少管理员的开支。 Phusion Passenger 支持多个 MRI Ruby, JRuby 和 Rubinius。Mongrel, Unicorn 和 Thin 只支持 MRI,Puma也支持三个。 Phusion Passenger 不仅支持 Ruby,它还支持 Python WSGI,所以同样能运行 Django 和 Flask 应用。实际上,Phusion Passenger正在向多语言服务器发展的道路上前进,Node.js的支持已经列在计划中。 Out-of-band garbage collection。Phusion Passenger 可以在请求的循坏外执行 Ruby 的垃圾回收,可以潜在地减少几百毫秒的请求时间(reducing request times by hundreds of milliseconds)。Unicorn也有类似的功能,但是Phusion Passenger 的版本更加灵活,因为 1,it's not limited to GC and can be used for arbitrary work。2,Phusion Passenger的版本适应多进程应用,而 Unicorn 则不行。 自动回滚重启(Automated rolling restarts)。Unicorn 以及其他应用服务器的回滚重启需要脚本来执行,Phusion Passenger 企业版自动帮你完成了。 还有更多的特性和优势,暂不一一列举。你可以参考全面的 Phusion Passenger 手册( Apache version, Nginx version),或者到官网得到更多信息。

七,I/O并发模型

单线程,多进程。 Ruby 应用服务器中比较常见、流行的 I/O 模型,主要是因为 Ruby 生态系统中的多线程支持比较差。一个进程同时仅且只能处理一个请求,web 服务器通过多进程来进行均衡负载。这种模型比较稳定,开发者不会轻易造成并发bug。这种模型适合执行快速的短请求,不适合速度慢、长请求阻塞I/O的运算,例如 调用HTTP API。 纯多线程 。现在Ruby生态系统已经很支持多线程了,所以这种I/O模型变得切实可行。多线程支持高I/O并发,既适合短请求也适合长请求。开发者也很容易引入并发bug,幸运的是大多数框架按照这种方式设计,所以也不太可能发生。有一个需要注意的事情是,因为使用了全局解释器锁(GIL),MRI Ruby 解释器不能均衡使用多个CPU内核,即使有多个线程。为此,你可以使用多个进程,每个进程使用一个CPU内核。JRuby 和 Rubinius没有GIL,所以他们的一个进程可以均衡负载多个CPU内核 。 结合多线程、多进程 。Phusion Passenger Enterprise 4以后版本实现了。你可以轻易在以下模型切换:单进程多线程,纯多线程,多进程多线程。这种模型给出了最好的选择方式。 事件。这种模型和之前提到的模型不一样。它允许极高的I/O并发,以及非常适合长请求。为实现此功能,需要从应用到框架的详细支持。然而主要的框架(Rails 和 Sinatra)并不支持事件模型。这也是实际上一个Thin进程同时不能处理多个请求的原因,就像单线程多进程模型一样。只有专门的框架才充分利用事件I/O模型,例如Cramp。 八,Capistrano

Capistrano和其他的不一样。在之前的所有章节中,“部署”指的是在服务器上启动 Ruby 应用,然后对访客开放,但是在这之前需要一些准备工作,例如:

将 Ruby 应用的代码和文件上传到服务器机器上。 安装应用所依赖的库。 创建或者迁移数据库。 启动或者停止应用依赖的守护进程,例如 Sidekiq/Resque 或者 whatever。 启动应用时需要做的所有其他事情。 在 Capistrano 的语义中,“部署”意味着去做所有的准备工作。Capistrano 不是一个服务器,反而是一个自动做那些准备工作的工具。你告诉 Capistrano 你的服务器在哪里,以及每当你部署新版本应用的时候需要执行的命令,Capistrano 就能帮你上传 Ruby 应用到服务器上并且运行你指定的命令。

Capistrano 总是与应用服务器组合使用,并不会替代应用服务器。反之亦然,应用服务器也不会替代 Capistrano。

当然 Capistrano 也不是必须使用的。如果你可以使用 FTP 或者 手动上传 Ruby 应用,每一次都执行相同的动作。其他人厌倦这种重复,所以他们在 Capistrano 中自动执行这些步骤。

转自:https://ruby-china.org/topics/25276

4
laofo · #2 · 2017年03月03日

Ruby Web服务器发展时间轴

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册