ngin Finding Everything About Nginx Here

用lua让nginx成为应用服务器

发表于 2015-06-19 阅读数 4274

相遇是件难得的事情,在一起更不简单,但是nginx和lua就是成功的结合起来。文本将以演进式方式与大家一起分享lua的威力和nginx的优秀架构。


1、从猩猩那个年代说起

从互联网进入人类生活开始,用户和服务器就扮演着你来我往的角色,从技术角度描绘就是一直存在着request/response(请求/响应)的架构。如今随着网络设施的加强,人们对网站内容的需求也更多样化,从简单的html到现在丰富的媒介内容。横空出世的开源服务器软件也承担着更多的功能。但是再怎么复杂变化,总是只有静态和动态两种类型。静态即静态资源如html、css、js等这种文件式的内容,动态即根据请求信息处理的如.php这种常见的内容。在静态方面,现在已经有足够成熟的架构方案,反倒动态的处理方案非常之多。


2、站在巨人的肩上

可以说提到web服务器,人们自然会想到apache,这个在技术发展史上不能被忽略的优秀软件,如今依然有着60%(保守估计)的使用量。nginx是个极致的东西,优势的架构让它迅速征服了很大一批工程师,这同样适合国内的情况。nginx做了两件事:处理静态请求和动态请求。完全符合我们上面说的,在静态方面,nginx的性能表现非常突出,默认使用sendfile传输内容,还可以设置缓存(针对file fd)。在动态方面,nginx可以集成很多的应用软件,其实从这角度讲它只是个代理,但是nginx能做的不仅仅是代理,这就是本文存在的价值。


3、nginx+php-fpm vs apache+mod_php

php是世界上最好的语言,这已经成为一个段子,我本身非常喜欢这个语言。这里以php为例解释下nginx的动态处理方式。首先它们都是为了处理动态请求。php是脚本语言解释器,不做处理网络请求和响应的事(虽然它可以)。nginx, apache, php-fpm 这3个都是网络服务器,处理请求和响应。mod_php是个c解释php的模块库。


client >---http-request--- apache(mod_php)

client <---http-response-- apache(mod_php)


client >---http-request--- nginx >---fastcgi-request--- php-fpm(c解释php)

client <---http-response-- nginx <---fastcgi-response-- php-fpm(c解释php)


为统一,我们把处理http的服务器称为web服务器,处理动态请求的称为应用服务器。所以apache是应用服务器,nginx是web服务器,它通过代理(fastcgi协议)转发给应用服务器php-fpm。服务器的设计原则是永远不能阻塞,但规则往往因为被打破而简化了问题。注意nginx与php-fpm的交互是异步的,mod_php, php-fpm里c解释php阻塞的。(不用纠结这两个词)


4、让nginx成为应用服务器

nginx本身有个模块ssi,全称是server side includes,它是个极小解释器,可以让响应内容有小动态的功能。ssi使用和原理test.html的文件内容,很像脚本语言吧,从这角度讲nginx已经是应用服务器。

hello: < !--# echo var="request_url" -->

脚本动态语言如此之多,php, perl, python, ruby, js, lua, blala 语法各异,粉丝从多,据说上帝为了不让巴别塔造成才搞了这么多不一样的东西,虽然他们目标是做同一件事,让事情变得更简单。实现应用服务器的方案也不在少数,都非常优秀。在上面我们清楚,现在的应用服务器干了两件,处理网络服务和执行业务脚本。网络服务本身是基于协议的,比如http,这个只要保证稳定性,高性能和健壮性即可。在执行业务脚本方面就区别大了。但大体有两种方法,网络处理和业务脚本是同个语言,比如nodejs, go等。这其实是有好处的,业务脚本可以获取网络服务的上下文。还有一种是分开的,根据语言有不同的系统,比如c+dsl系列的nginx + lua。那么问题来了,选择哪一个呢?稳定性,高性能和健壮。nginx本身在网络方面已经具备这3个要素了,这是我们选择它的原因。至于为什么选择lua,是因为这语言本身设计是为了让c写起来更轻松。所以我们选择一个东西时,它的出发点是什么很有参考价值。正如php为web而生一样。所以现在剩下的问题就是nginx如何执行lua。


5、我们不仅关注优秀的openresty,还有更多

openresty的作者可能是目前世界上在nginx的第三方模块的贡献者,除了openresty这个开源产品,还有其它优秀的模块。openresty是一个在nginx上扩展lua的应用服务器。它的官网和github上的资料非常完善,但文本的价值在于探讨如何让nginx具备应用能力。所以你不用担心是否懂openresty。我们让你明白lua是如何被加到nginx的。


6、lua与nginx的架构设计

nginx架构图(来源http://www.aosabook.org/en/nginx.html)



lua是个脚本语言,有着简洁的语法,虽然我不觉得非常易用。官方定义了语言规则和源码实现,目前稳定版是5.3。还有个luajit,这是非官方的实现,支持5.1的语法(openresty是用luajit的)。本文基于官方lua5.3版本。

---- worker process ----

nginx ---- worker process ----

---- process request 创建lua_newthread、加载请求对应文件、解释执行 ----

---- worker process ua_newstate---- ---- process request ----

---- process request ----

---- worker process ----


nginx有多个工作进程,每个worker process启动时会加载一个lua_State,然后等待请求进来,多个请求是允许同时存在的,比如有的忙着去请求远程服务,这时就异步挂着休息。当有请求过来需要处理时,nginx为每个请求创建一个lua_newthread,这个lua_newthread将加载文件、解释执行等操作。

因此你要做的就是

在init:

conf->L = lua_newstate(); // L是lua_State结构体


在handler:

L = conf->L;

NL = lua_newthread(L); // NL也是lua_State结构体

r->ctx->NL = NL;

luaL_loadfile(NL);


你可能猜到了吧,lua_newstate是个虚拟机,专门给nginx进程的,每个进程一个。lua_newthread是给请求的,每个请求也有一个,他们都具备运行lua文件的能力。但由于请求是变化的,上下文不一样,所以才需要为每个请求创建一个lua_State结构体。我们将分析lua的内部,让你更明白这个架构。


未完待续,写到这发现可以探讨的空间很大,lua的协程、虚拟机、全局变量和闭包(绝对有料,比如lua没有全局变量,那是为什么呢)、nginx调用了lua的哪些功能、为什么openresty写道lua的全局变量不能使用,c与lua的协作,如何让lua去调其它服务(memcache, redis, mongodb, mysql)。