nginx proxy_cache 缓存配置

前言:
由于本人工作原因,涉及到网络直播领域,其中视频的回放下载,涉及到了一些视频下载方面的技术。针对于一个完整视频的下载,目前市面上的主流做法是,先将整个视频流切片,存储到文件服务器中,在用户需要观看回放视频时。通过一个视频回源服务器,去文件服务器中逐个请求切片,返回给用户播放。
今天着重探讨的是关于回源服务器缓存的配置以及合理的缓存策略。
通过给回源服务器配置缓存的案例,详细讲解一整套缓存配置机制,并且可沿用到其他任何缓存配置场景中。

今天的讲解分为四点:
回源服务器的工作是啥
为啥需要给回源服务器加缓存
如何配置缓存
如何针对业务场景配置完备的缓存机制

回源服务器的工作:
回源服务器在下面叙述中简称:源站
如图所示,在文件下载的过程中,横跨在cdn与文件服务器之间,作为下载枢纽。

源站架构:源站是nginx+php的webserver架构,如图所示:

但如果源站只是简单的收到请求,然后下载资源,再返回,势必会存在以下几点不够优化的问题:
1、cdn可能存在多次回源现象
2、源站对同一资源的多次下载,存在网络流量带宽浪费,以及不必要的耗时。
所以为了优化这些问题,需要给源站做一层缓存。缓存策略采用nginx自带的proxy_cache模块。

proxy_cache原理:
proxy_cache模块的工作原理如图所示:
如何配置proxy_cache模块
在nginx.conf文件中添加如下代码:
http{
……
proxy_cache_path/data/nginx/tmp-test levels=1:2 keys_zone=tmp-test:100m inactive=7d max_size=1000g;
}
代码说明:
proxy_cache_path 缓存文件路径

levels 设置缓存文件目录层次;levels=1:2 表示两级目录

keys_zone 设置缓存名字和共享内存大小

inactive 在指定时间内没人访问则被删除

max_size 最大缓存空间,如果缓存空间满,默认覆盖掉缓存时间最长的资源。

当配置好之后,重启nginx,如果不报错,则配置的proxy_cache会生效

查看  proxy_cache_path /data/nginx/目录,
会发现生成了tmp-test文件夹。

如何使用proxy_cache
在你对应的nginx vhost server配置文件中添加如下代码:
location /tmp-test/ {
proxy_cache tmp-test;
proxy_cache_valid 200 206 304 301 302 10d;
proxy_cache_key $uri;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_passhttp://127.0.0.1:8081/media_store.php/tmp-test/;
}
配置项介绍:
Proxy_cache tmp-test 使用名为tmp-test的对应缓存配置
proxy_cache_valid  200 206 304 301 302 10d; 对httpcode为200…的缓存10天

proxy_cache_key $uri  定义缓存唯一key,通过唯一key来进行hash存取

proxy_set_header  自定义http header头,用于发送给后端真实服务器。

proxy_pass  指代理后转发的路径,注意是否需要最后的/

到这里,最基本的proxy_cache功能就配置成功了。当uri成功匹配到该location,则proxy_cache就会生效。

添加proxy_cache之后,请求过程的变化:
1、第一次访问:
第一次访问,proxy_cache并没有找到对应的缓存文件(未命中缓存MISS),所以当第一次请求完成的同时,proxy_cache会保持缓存:
2、保存缓存,如图所示:

3、同一个url第二次访问,当同一个文件再次到达源站,proxy_cache就会找到其对应的缓存文件(命中缓存HIT)直接返回给请求端,无需再执行php程序,如图所示:
提出疑问:
到此,就完成了最基本的proxy_cache配置和访问过程介绍,但是最基本的配置,往往无法满足我们的业务需求,我们往往会提出以下几点疑问和需求:
需要主动清理缓存文件
写入路径为一块磁盘,如果磁盘打满该怎么解决?
如何让源站支持断点续传,以及断点续传的缓存策略
如果请求端 range 请求(分片下载)一个大资源,同样的uri,如何区别请求?
还需要告诉请求端,资源的过期时间
日志统计,如何配置命中与不命中字段,如何做统计?
面对以上疑问,我们一个一个解决。

问题一:主动清理缓存
采用:nginx  proxy_cache_purge 模块 ,该模块与proxy_cache成对出现,功能正好相反。
设计方法:在nginx中,另启一个server,当需要清理响应资源的缓存时,在本机访问这个server。
例如:
访问 127.0.0.1:8083/tmp-test/TL39ef7ea6d8e8d48e87a30c43b8f75e30.txt 即可清理该资源的缓存文件。
配置方法:
location /tmp-test/ {
allow 127.0.0.1; //只允许本机访问
deny all; //禁止其他所有ip
proxy_cache_purge tmp-test $uri; //清理缓存
}
proxy_cache_purge:缓存清理模块
tmp-test:指定的key_zone
$uri:指定的生成key的参数
proxy_cache_purge缓存清理过程,如图所示:
问题二:缓存文件强磁盘打满该怎么办?
由于写入路径为一个单一目录,只能写入一块磁盘。一块磁盘很快就会被打满,解决该问题有如下两种方法:
1、将多块磁盘做磁盘阵列? 缺点是:减小了实际的存储空间。
2、巧妙得运用proxy_cache_path的目录结构,由于levels=1:2,这导致缓存文件的目录结构为两层,每层目录名,都是由hash函数生成。如图所示:

总共含有16*16*16=4096个文件目录。对该一级目录进行软连接,分别将0-f软连接到你所需要的指定磁盘目录上,如图所示:

通过软链的方法,实现:将不同盘下的目录作为真正存放数据的路径,解决了多盘利用,单盘被打满的问题。

问题三:支持range(断点续传)
添加上缓存代理之后,客户端发起的range请求将会失效,如下图所示:

导致range参数无法传递到下一级的原因如下:
当缓存代理转发http请求到后端服务器时,http header会改变,header中的部分参数,会被取消掉。其中range参数被取消,导致,后端nginx服务器没有收到range参数,最终导致这个分片下载不成功。所以需要对代理转发的header进行配置。
例如:
location /tmp-test/ {
proxy_cache tmp-test;
proxy_cache_valid 200 206 304 301 302 10d;
proxy_cache_key $uri;
<span style=”color:#ff0000;”>proxy_set_header Range $http_range;</span>
proxy_pass http://127.0.0.1:8081/media_store.php/tmp-test/;
}
红色部分的含义:将http请求中的range值($http_range)放到代理转发的http请求头中作为参数range的值。

问题四,当支持range加载后,proxy_cache_key,则需要重新配置:
如果请求端 Range请求(分片下载)一个大资源,同样的uri,proxy cache如何识别资源对应的key。
由于nginx配置为:proxy_cache_key $uri,用uri作为key
所以当请求为普通请求和range请求时,都是同样的uri作为key。proxy_cache将有可能导致错误返回。如下图所示:

解决方法如下:
修改proxy_cache_key ,配置proxy_cache_key $http_range$uri;
这样就能解决:key唯一性。可以避免不管是正常请求还是不同的range请求,第一次获取的内容和之后获取的缓存内容都不会出现异常。

问题五:如何配置-返回过期时间
需要通过返回过期时间来指定请求端,哪些资源需要缓存,哪些资源不缓存,
参数 正常请求 range请求
返回过期时间 返回 不返回
为了防止请求端将分片资源当做完整资源缓存起来,我们需要对正常请求,返回过期时间;对range请求, 不返回过期时间。
解决该问题,通过对nginx配置即可解决:
location /media_store.php {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index media_store.php;
fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
include fastcgi_params;
if ( $http_range = ”){
expires 2592000s;
}
}
在proxy_pass代理之后的location中加入对$http_range的判断,expires 表示过期时间。 2592000s指缓存过期时间。

问题七:缓存命中情况如何在http头中体现,以及在nginx日志中查看
解决方法:
利用nginx $upstream_cache_status变量:该变量代表缓存命中的状态,
如果命中,为HIT;如果未命中,为MISS
在返回nginx server配置中添加:
add_header  Nginx-Cache “$upstream_cache_status”;
在nginxlog中添加:
log_format       combinedio  …$upstream_cache_status;
http返回head截图:
nginx log日志截图:
总结:
整个一套完备的缓存策略就介绍到此,这套方案中不仅实现了基本的缓存配置,还解决了实际场景应用中会遇到的,磁盘扩展,缓存清理,断点续传,缓存过期时间,缓存命中提示等问题,只要将这套方案灵活运用,不管是再复杂的场景,基本都能满足需求。以上都是我在工作中爬过的坑,不断完善总结出的结果,希望对读者能有帮助。

基于OpenResty的web开发框架Lor

https://github.com/sumory/lor

Lor是一个运行在OpenResty上的基于Lua编写的Web框架.

  • 路由采用Sinatra风格,结构清晰,易于编码和维护.
  • API借鉴了Express的思路和设计,Node.js跨界开发者可以很快上手.
  • 支持多种路由,路由可分组,路由匹配支持正则模式.
  • 支持middleware机制,可在任意路由上挂载中间件.
  • 可作为HTTP API Server,也可用于构建传统的Web应用.

文档

http://lor.sumory.com

示例项目

快速开始

特别注意: 在使用lor之前请首先确保OpenResty已安装,并将nginx/resty命令配置到环境变量中。即在命令行直接输入nginx -vresty -v能正确执行。

一个简单示例(更复杂的示例或项目模板请使用lord命令生成):

local lor = require("lor.index")
local app = lor()

app:get("/", function(req, res, next)
    res:send("hello world!")
end)

-- 路由示例: 匹配/query/123?foo=bar
app:get("/query/:id", function(req, res, next)
    local foo = req.query.foo
    local path_id = req.params.id
    res:json({
        foo = foo,
        id = path_id
    })
end)

-- 错误处理插件,可根据需要定义多个
app:erroruse(function(err, req, res, next)
    -- err是错误对象
    ngx.log(ngx.ERR, err)
    if req:is_found() ~= true then
        return res:status(404):send("sorry, not found.")
    end
    res:status(500):send("server error")
end)

app:run()

安装

1)使用脚本安装(推荐)

使用Makefile安装lor框架:

git clone https://github.com/sumory/lor
cd lor
make install

默认lor的运行时lua文件会被安装到/usr/local/lor下, 命令行工具lord被安装在/usr/local/bin下。

如果希望自定义安装目录, 可参考如下命令自定义路径:

make install LOR_HOME=/path/to/lor LORD_BIN=/path/to/lord

执行默认安装后, lor的命令行工具lord就被安装在了/usr/local/bin下, 通过which lord查看:

$ which lord
/usr/local/bin/lord

lor的运行时包安装在了指定目录下, 可通过lord path命令查看。

2)使用opm安装

opm是OpenResty即将推出的官方包管理器,从v0.2.2开始lor支持通过opm安装:

opm install sumory/lor

注意: 目前opm不支持安装命令行工具,所以此种方式安装后不能使用lord命令。

3)使用homebrew安装

除使用以上方式安装外, Mac用户还可使用homebrew来安装lor, 该方式由@syhily提供, 更详尽的使用方法请参见这里

$ brew tap syhily/lor
$ brew install lor

至此, lor框架已经安装完毕,接下来使用lord命令行工具快速开始一个项目骨架.

使用

$ lord -h
lor ${version}, a Lua web framework based on OpenResty.

Usage: lord COMMAND [OPTIONS]

Commands:
 new [name]             Create a new application
 start                  Starts the server
 stop                   Stops the server
 restart                Restart the server
 version                Show version of lor
 help                   Show help tips

执行lord new lor_demo,则会生成一个名为lor_demo的示例项目,然后执行:

cd lor_demo
lord start

之后访问http://localhost:8888/, 即可。

更多使用方法,请参考use cases测试用例。

[转]Nginx 工作原理和优化、漏洞

1. Nginx的模块与工作原理

Nginx由内核和模块组成,其中,内核的设计非常微小和简洁,完成的工作也非常简单,仅仅通过查找配置文件将客户端请求映射到一个location block(location是Nginx配置中的一个指令,用于URL匹配),而在这个location中所配置的每个指令将会启动不同的模块去完成相应的工作。

Nginx的模块从结构上分为核心模块、基础模块和第三方模块:

核心模块:HTTP模块、EVENT模块和MAIL模块

基础模块:HTTP Access模块、HTTP FastCGI模块、HTTP Proxy模块和HTTP Rewrite模块,

第三方模块:HTTP Upstream Request Hash模块、Notice模块和HTTP Access Key模块。

用户根据自己的需要开发的模块都属于第三方模块。正是有了这么多模块的支撑,Nginx的功能才会如此强大。

Nginx的模块从功能上分为如下三类。

Handlers(处理器模块)。此类模块直接处理请求,并进行输出内容和修改headers信息等操作。Handlers处理器模块一般只能有一个。

Filters (过滤器模块)。此类模块主要对其他处理器模块输出的内容进行修改操作,最后由Nginx输出。

Proxies (代理类模块)。此类模块是Nginx的HTTP Upstream之类的模块,这些模块主要与后端一些服务比如FastCGI等进行交互,实现服务代理和负载均衡等功能。

图1-1展示了Nginx模块常规的HTTP请求和响应的过程。

Nginx本身做的工作实际很少,当它接到一个HTTP请求时,它仅仅是通过查找配置文件将此次请求映射到一个location block,而此location中所配置的各个指令则会启动不同的模块去完成工作,因此模块可以看做Nginx真正的劳动工作者。通常一个location中的指令会涉及一个handler模块和多个filter模块(当然,多个location可以复用同一个模块)。handler模块负责处理请求,完成响应内容的生成,而filter模块对响应内容进行处理。

Nginx的模块直接被编译进Nginx,因此属于静态编译方式。启动Nginx后,Nginx的模块被自动加载,不像Apache,首先将模块编译为一个so文件,然后在配置文件中指定是否进行加载。在解析配置文件时,Nginx的每个模块都有可能去处理某个请求,但是同一个处理请求只能由一个模块来完成。

2. Nginx的进程模型

在工作方式上,Nginx分为单工作进程和多工作进程两种模式。在单工作进程模式下,除主进程外,还有一个工作进程,工作进程是单线程的;在多工作进程模式下,每个工作进程包含多个线程。Nginx默认为单工作进程模式。

Nginx在启动后,会有一个master进程和多个worker进程。

master进程

主要用来管理worker进程,包含:接收来自外界的信号,向各worker进程发送信号,监控worker进程的运行状态,当worker进程退出后(异常情况下),会自动重新启动新的worker进程。

master进程充当整个进程组与用户的交互接口,同时对进程进行监护。它不需要处理网络事件,不负责业务的执行,只会通过管理worker进程来实现重启服务、平滑升级、更换日志文件、配置文件实时生效等功能。

我们要控制nginx,只需要通过kill向master进程发送信号就行了。比如kill -HUP pid,则是告诉nginx,从容地重启nginx,我们一般用这个信号来重启nginx,或重新加载配置,因为是从容地重启,因此服务是不中断的。master进程在接收到HUP信号后是怎么做的呢?

首先master进程在接到信号后,会先重新加载配置文件,然后再启动新的worker进程,并向所有老的worker进程发送信号,告诉他们可以光荣退休了。新的worker在启动后,就开始接收新的请求,而老的worker在收到来自master的信号后,就不再接收新的请求,并且在当前进程中的所有未处理完的请求处理完成后,再退出。

当然,直接给master进程发送信号,这是比较老的操作方式,nginx在0.8版本之后,引入了一系列命令行参数,来方便我们管理。比如,./nginx -s reload,就是来重启nginx,./nginx -s stop,就是来停止nginx的运行。

如何做到的呢?我们还是拿reload来说,我们看到,执行命令时,我们是启动一个新的nginx进程,而新的nginx进程在解析到reload参数后,就知道我们的目的是控制nginx来重新加载配置文件了,它会向master进程发送信号,然后接下来的动作,就和我们直接向master进程发送信号一样了。

worker进程:

而基本的网络事件,则是放在worker进程中来处理了。多个worker进程之间是对等的,他们同等竞争来自客户端的请求,各进程互相之间是独立的。一个请求,只可能在一个worker进程中处理,一个worker进程,不可能处理其它进程的请求。worker进程的个数是可以设置的,一般我们会设置与机器cpu核数一致,这里面的原因与nginx的进程模型以及事件处理模型是分不开的。

worker进程之间是平等的,每个进程,处理请求的机会也是一样的。当我们提供80端口的http服务时,一个连接请求过来,每个进程都有可能处理这个连接,怎么做到的呢?首先,每个worker进程都是从master进程fork过来,在master进程里面,先建立好需要listen的socket(listenfd)之后,然后再fork出多个worker进程。

所有worker进程的listenfd会在新连接到来时变得可读,为保证只有一个进程处理该连接,所有worker进程在注册listenfd读事件前抢accept_mutex,抢到互斥锁的那个进程注册listenfd读事件,在读事件里调用accept接受该连接。当一个worker进程在accept这个连接之后,就开始读取请求,解析请求,处理请求,产生数据后,再返回给客户端,最后才断开连接,这样一个完整的请求就是这样的了。

我们可以看到,一个请求,完全由worker进程来处理,而且只在一个worker进程中处理。worker进程之间是平等的,每个进程,处理请求的机会也是一样的。当我们提供80端口的http服务时,一个连接请求过来,每个进程都有可能处理这个连接,怎么做到的呢?首先,每个worker进程都是从master进程fork过来,在master进程里面,先建立好需要listen的socket(listenfd)之后,然后再fork出多个worker进程。

所有worker进程的listenfd会在新连接到来时变得可读,为保证只有一个进程处理该连接,所有worker进程在注册listenfd读事件前抢accept_mutex,抢到互斥锁的那个进程注册listenfd读事件,在读事件里调用accept接受该连接。当一个worker进程在accept这个连接之后,就开始读取请求,解析请求,处理请求,产生数据后,再返回给客户端,最后才断开连接,这样一个完整的请求就是这样的了。我们可以看到,一个请求,完全由worker进程来处理,而且只在一个worker进程中处理。

nginx的进程模型,可以由下图来表示:

3. Nginx+FastCGI运行原理

1、什么是 FastCGI

FastCGI是一个可伸缩地、高速地在HTTP server和动态脚本语言间通信的接口。多数流行的HTTP server都支持FastCGI,包括Apache、Nginx和lighttpd等。同时,FastCGI也被许多脚本语言支持,其中就有PHP。

FastCGI是从CGI发展改进而来的。传统CGI接口方式的主要缺点是性能很差,因为每次HTTP服务器遇到动态程序时都需要重新启动脚本解析器来执行解析,然后将结果返回给HTTP服务器。这在处理高并发访问时几乎是不可用的。另外传统的CGI接口方式安全性也很差,现在已经很少使用了。

FastCGI接口方式采用C/S结构,可以将HTTP服务器和脚本解析服务器分开,同时在脚本解析服务器上启动一个或者多个脚本解析守护进程。当HTTP服务器每次遇到动态程序时,可以将其直接交付给FastCGI进程来执行,然后将得到的结果返回给浏览器。这种方式可以让HTTP服务器专一地处理静态请求或者将动态脚本服务器的结果返回给客户端,这在很大程度上提高了整个应用系统的性能。

2、Nginx+FastCGI运行原理

Nginx不支持对外部程序的直接调用或者解析,所有的外部程序(包括PHP)必须通过FastCGI接口来调用。FastCGI接口在Linux下是socket(这个socket可以是文件socket,也可以是ip socket)。

wrapper:为了调用CGI程序,还需要一个FastCGI的wrapper(wrapper可以理解为用于启动另一个程序的程序),这个wrapper绑定在某个固定socket上,如端口或者文件socket。当Nginx将CGI请求发送给这个socket的时候,通过FastCGI接口,wrapper接收到请求,然后Fork(派生)出一个新的线程,这个线程调用解释器或者外部程序处理脚本并读取返回数据;接着,wrapper再将返回的数据通过FastCGI接口,沿着固定的socket传递给Nginx;最后,Nginx将返回的数据(html页面或者图片)发送给客户端。这就是Nginx+FastCGI的整个运作过程,如图1-3所示。

所以,我们首先需要一个wrapper,这个wrapper需要完成的工作:

通过调用fastcgi(库)的函数通过socket和ningx通信(读写socket是fastcgi内部实现的功能,对wrapper是非透明的)
调度thread,进行fork和kill
和application(php)进行通信
3、spawn-fcgi与PHP-FPM

FastCGI接口方式在脚本解析服务器上启动一个或者多个守护进程对动态脚本进行解析,这些进程就是FastCGI进程管理器,或者称为FastCGI引擎。 spawn-fcgi与PHP-FPM就是支持PHP的两个FastCGI进程管理器。因此HTTPServer完全解放出来,可以更好地进行响应和并发处理。

spawn-fcgi与PHP-FPM的异同:

1)spawn-fcgi是HTTP服务器lighttpd的一部分,目前已经独立成为一个项目,一般与lighttpd配合使用来支持PHP。但是ligttpd的spwan-fcgi在高并发访问的时候,会出现内存泄漏甚至自动重启FastCGI的问题。即:PHP脚本处理器当机,这个时候如果用户访问的话,可能就会出现白页(即PHP不能被解析或者出错)。

2)Nginx是个轻量级的HTTP server,必须借助第三方的FastCGI处理器才可以对PHP进行解析,因此其实这样看来nginx是非常灵活的,它可以和任何第三方提供解析的处理器实现连接从而实现对PHP的解析(在nginx.conf中很容易设置)。nginx也可以使用spwan-fcgi(需要一同安装lighttpd,但是需要为nginx避开端口,一些较早的blog有这方面安装的教程),但是由于spawn-fcgi具有上面所述的用户逐渐发现的缺陷,现在慢慢减少用nginx+spawn-fcgi组合了。

由于spawn-fcgi的缺陷,现在出现了第三方(目前已经加入到PHP core中)的PHP的FastCGI处理器PHP-FPM,它和spawn-fcgi比较起来有如下优点:

由于它是作为PHP的patch补丁来开发的,安装的时候需要和php源码一起编译,也就是说编译到php core中了,因此在性能方面要优秀一些;

同时它在处理高并发方面也优于spawn-fcgi,至少不会自动重启fastcgi处理器。因此,推荐使用Nginx+PHP/PHP-FPM这个组合对PHP进行解析。

相对Spawn-FCGI,PHP-FPM在CPU和内存方面的控制都更胜一筹,而且前者很容易崩溃,必须用crontab进行监控,而PHP-FPM则没有这种烦恼。

FastCGI 的主要优点是把动态语言和HTTP Server分离开来,所以Nginx与PHP/PHP-FPM经常被部署在不同的服务器上,以分担前端Nginx服务器的压力,使Nginx专一处理静态请求和转发动态请求,而PHP/PHP-FPM服务器专一解析PHP动态请求。

4、Nginx+PHP-FPM

PHP-FPM是管理FastCGI的一个管理器,它作为PHP的插件存在,在安装PHP要想使用PHP-FPM时在老php的老版本(php5.3.3之前)就需要把PHP-FPM以补丁的形式安装到PHP中,而且PHP要与PHP-FPM版本一致,这是必须的)

PHP-FPM其实是PHP源代码的一个补丁,旨在将FastCGI进程管理整合进PHP包中。必须将它patch到你的PHP源代码中,在编译安装PHP后才可以使用。

PHP5.3.3已经集成php-fpm了,不再是第三方的包了。PHP-FPM提供了更好的PHP进程管理方式,可以有效控制内存和进程、可以平滑重载PHP配置,比spawn-fcgi具有更多优点,所以被PHP官方收录了。在./configure的时候带 –enable-fpm参数即可开启PHP-FPM。

fastcgi已经在php5.3.5的core中了,不必在configure时添加 –enable-fastcgi了。老版本如php5.2的需要加此项。

当我们安装Nginx和PHP-FPM完后,配置信息:

PHP-FPM的默认配置php-fpm.conf:

listen_address 127.0.0.1:9000 #这个表示php的fastcgi进程监听的ip地址以及端口

start_servers

min_spare_servers

max_spare_servers

Nginx配置运行php: 编辑nginx.conf加入如下语句:

location ~ .php$ {

root html;

fastcgi_pass 127.0.0.1:9000; 指定了fastcgi进程侦听的端口,nginx就是通过这里与php交互的

fastcgi_index index.php;

include fastcgi_params;

fastcgi_param SCRIPT_FILENAME /usr/local/nginx/html$fastcgi_script_name;

}

Nginx通过location指令,将所有以php为后缀的文件都交给127.0.0.1:9000来处理,而这里的IP地址和端口就是FastCGI进程监听的IP地址和端口。

其整体工作流程:

1)、FastCGI进程管理器php-fpm自身初始化,启动主进程php-fpm和启动start_servers个CGI 子进程。

主进程php-fpm主要是管理fastcgi子进程,监听9000端口。

fastcgi子进程等待来自Web Server的连接。

2)、当客户端请求到达Web Server Nginx是时,Nginx通过location指令,将所有以php为后缀的文件都交给127.0.0.1:9000来处理,即Nginx通过location指令,将所有以php为后缀的文件都交给127.0.0.1:9000来处理。

3)FastCGI进程管理器PHP-FPM选择并连接到一个子进程CGI解释器。Web server将CGI环境变量和标准输入发送到FastCGI子进程。

4)、FastCGI子进程完成处理后将标准输出和错误信息从同一连接返回Web Server。当FastCGI子进程关闭连接时,请求便告处理完成。

5)、FastCGI子进程接着等待并处理来自FastCGI进程管理器(运行在 WebServer中)的下一个连接。

4. Nginx+PHP正确配置

一般web都做统一入口:把PHP请求都发送到同一个文件上,然后在此文件里通过解析「REQUEST_URI」实现路由。

Nginx配置文件分为好多块,常见的从外到内依次是「http」、「server」、「location」等等,缺省的继承关系是从外到内,也就是说内层块会自动获取外层块的值作为缺省值。

例如:

server {

listen 80;
server_name foo.com;

root /path;

location / {

index index.html index.htm index.php;

if (!-e $request_filename) {

rewrite . /index.php last;

}

}

location ~ .php$ {

include fastcgi_params;

fastcgi_param SCRIPT_FILENAME /path$fastcgi_script_name;

fastcgi_pass 127.0.0.1:9000;

fastcgi_index index.php;

}

}

1) 不应该在location 模块定义index

一旦未来需要加入新的「location」,必然会出现重复定义的「index」指令,这是因为多个「location」是平级的关系,不存在继承,此时应该在「server」里定义「index」,借助继承关系,「index」指令在所有的「location」中都能生效。

2) 使用try_files

接下来看看「if」指令,说它是大家误解最深的Nginx指令毫不为过:

if (!-e $request_filename) {

rewrite . /index.php last;

}

很多人喜欢用「if」指令做一系列的检查,不过这实际上是「try_files」指令的职责:

try_files $uri $uri/ /index.php;

除此以外,初学者往往会认为「if」指令是内核级的指令,但是实际上它是rewrite模块的一部分,加上Nginx配置实际上是声明式的,而非过程式的,所以当其和非rewrite模块的指令混用时,结果可能会非你所愿。

3)fastcgi_params」配置文件:

include fastcgi_params;

Nginx有两份fastcgi配置文件,分别是「fastcgi_params」和「fastcgi.conf」,它们没有太大的差异,唯一的区别是后者比前者多了一行

「SCRIPT_FILENAME」的定义:

fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

注意:$document_root 和 $fastcgi_script_name 之间没有 /。

原本Nginx只有「fastcgi_params」,后来发现很多人在定义「SCRIPT_FILENAME」时使用了硬编码的方式,于是为了规范用法便引入了「fastcgi.conf」。

不过这样的话就产生一个疑问:为什么一定要引入一个新的配置文件,而不是修改旧的配置文件?这是因为「fastcgi_param」指令是数组型的,和普通指令相同的是:内层替换外层;和普通指令不同的是:当在同级多次使用的时候,是新增而不是替换。换句话说,如果在同级定义两次「SCRIPT_FILENAME」,那么它们都会被发送到后端,这可能会导致一些潜在的问题,为了避免此类情况,便引入了一个新的配置文件。

此外,我们还需要考虑一个安全问题:在PHP开启「cgi.fix_pathinfo」的情况下,PHP可能会把错误的文件类型当作PHP文件来解析。如果Nginx和PHP安装在同一台服务器上的话,那么最简单的解决方法是用「try_files」指令做一次过滤:

try_files $uri =404;

依照前面的分析,给出一份改良后的版本,是不是比开始的版本清爽了很多:

server {

listen 80;

server_name foo.com;

root /path;

index index.html index.htm index.php;

location / {

try_files $uri $uri/ /index.php;

}

location ~ .php$ {

try_files $uri =404;

include fastcgi.conf;

fastcgi_pass 127.0.0.1:9000;

}
}

5. Nginx为啥性能高-多进程IO模型

1、nginx采用多进程模型好处

首先,对于每个worker进程来说,独立的进程,不需要加锁,所以省掉了锁带来的开销,同时在编程以及问题查找时,也会方便很多。

其次,采用独立的进程,可以让互相之间不会影响,一个进程退出后,其它进程还在工作,服务不会中断,master进程则很快启动新的worker进程。当然,worker进程的异常退出,肯定是程序有bug了,异常退出,会导致当前worker上的所有请求失败,不过不会影响到所有请求,所以降低了风险。

2、nginx多进程事件模型:异步非阻塞

虽然nginx采用多worker的方式来处理请求,每个worker里面只有一个主线程,那能够处理的并发数很有限啊,多少个worker就能处理多少个并发,何来高并发呢?非也,这就是nginx的高明之处,nginx采用了异步非阻塞的方式来处理请求,也就是说,nginx是可以同时处理成千上万个请求的。

一个worker进程可以同时处理的请求数只受限于内存大小,而且在架构设计上,不同的worker进程之间处理并发请求时几乎没有同步锁的限制,worker进程通常不会进入睡眠状态,因此,当Nginx上的进程数与CPU核心数相等时(最好每一个worker进程都绑定特定的CPU核心),进程间切换的代价是最小的。

而apache的常用工作方式(apache也有异步非阻塞版本,但因其与自带某些模块冲突,所以不常用),每个进程在一个时刻只处理一个请求,因此,当并发数上到几千时,就同时有几千的进程在处理请求了。这对操作系统来说,是个不小的挑战,进程带来的内存占用非常大,进程的上下文切换带来的cpu开销很大,自然性能就上不去了,而这些开销完全是没有意义的。

为什么nginx可以采用异步非阻塞的方式来处理呢,或者异步非阻塞到底是怎么回事呢?

我们先回到原点,看看一个请求的完整过程:首先,请求过来,要建立连接,然后再接收数据,接收数据后,再发送数据。

具体到系统底层,就是读写事件,而当读写事件没有准备好时,必然不可操作,如果不用非阻塞的方式来调用,那就得阻塞调用了,事件没有准备好,那就只能等了,等事件准备好了,你再继续吧。阻塞调用会进入内核等待,cpu就会让出去给别人用了,对单线程的worker来说,显然不合适,当网络事件越多时,大家都在等待呢,cpu空闲下来没人用,cpu利用率自然上不去了,更别谈高并发了。

好吧,你说加进程数,这跟apache的线程模型有什么区别,注意,别增加无谓的上下文切换。所以,在nginx里面,最忌讳阻塞的系统调用了。不要阻塞,那就非阻塞喽。非阻塞就是,事件没有准备好,马上返回EAGAIN,告诉你,事件还没准备好呢,你慌什么,过会再来吧。

好吧,你过一会,再来检查一下事件,直到事件准备好了为止,在这期间,你就可以先去做其它事情,然后再来看看事件好了没。虽然不阻塞了,但你得不时地过来检查一下事件的状态,你可以做更多的事情了,但带来的开销也是不小的。

关于IO模型:http://blog.csdn.net/hguisu/article/details/7453390

nginx支持的事件模型如下(nginx的wiki):

Nginx支持如下处理连接的方法(I/O复用方法),这些方法可以通过use指令指定。

select– 标准方法。 如果当前平台没有更有效的方法,它是编译时默认的方法。你可以使用配置参数 –with-select_module 和 –without-select_module 来启用或禁用这个模块。
poll– 标准方法。 如果当前平台没有更有效的方法,它是编译时默认的方法。你可以使用配置参数 –with-poll_module 和 –without-poll_module 来启用或禁用这个模块。
kqueue– 高效的方法,使用于 FreeBSD 4.1+, OpenBSD 2.9+, NetBSD 2.0 和 MacOS X. 使用双处理器的MacOS X系统使用kqueue可能会造成内核崩溃。
epoll – 高效的方法,使用于Linux内核2.6版本及以后的系统。在某些发行版本中,如SuSE 8.2, 有让2.4版本的内核支持epoll的补丁。
rtsig – 可执行的实时信号,使用于Linux内核版本2.2.19以后的系统。默认情况下整个系统中不能出现大于1024个POSIX实时(排队)信号。这种情况 对于高负载的服务器来说是低效的;所以有必要通过调节内核参数 /proc/sys/kernel/rtsig-max 来增加队列的大小。可是从Linux内核版本2.6.6-mm2开始, 这个参数就不再使用了,并且对于每个进程有一个独立的信号队列,这个队列的大小可以用 RLIMIT_SIGPENDING 参数调节。当这个队列过于拥塞,nginx就放弃它并且开始使用 poll 方法来处理连接直到恢复正常。
/dev/poll – 高效的方法,使用于 Solaris 7 11/99+, HP/UX 11.22+ (eventport), IRIX 6.5.15+ 和 Tru64 UNIX 5.1A+.
eventport – 高效的方法,使用于 Solaris 10. 为了防止出现内核崩溃的问题, 有必要安装这个 安全补丁。
在linux下面,只有epoll是高效的方法

下面再来看看epoll到底是如何高效的

Epoll是Linux内核为处理大批量句柄而作了改进的poll。 要使用epoll只需要这三个系统调用:epoll_create(2), epoll_ctl(2), epoll_wait(2)。它是在2.5.44内核中被引进的(epoll(4) is a new API introduced in Linux kernel 2.5.44),在2.6内核中得到广泛应用。

epoll的优点

支持一个进程打开大数目的socket描述符(FD)

select 最不能忍受的是一个进程所打开的FD是有一定限制的,由FD_SETSIZE设置,默认值是2048。对于那些需要支持的上万连接数目的IM服务器来说显 然太少了。这时候你一是可以选择修改这个宏然后重新编译内核,不过资料也同时指出这样会带来网络效率的下降,二是可以选择多进程的解决方案(传统的 Apache方案),不过虽然linux上面创建进程的代价比较小,但仍旧是不可忽视的,加上进程间数据同步远比不上线程间同步的高效,所以也不是一种完 美的方案。不过 epoll则没有这个限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左 右,具体数目可以cat /proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大。

IO效率不随FD数目增加而线性下降

传统的select/poll另一个致命弱点就是当你拥有一个很大的socket集合,不过由于网络延时,任一时间只有部分的socket是”活跃”的,但 是select/poll每次调用都会线性扫描全部的集合,导致效率呈现线性下降。但是epoll不存在这个问题,它只会对”活跃”的socket进行操 作—这是因为在内核实现中epoll是根据每个fd上面的callback函数实现的。那么,只有”活跃”的socket才会主动的去调用 callback函数,其他idle状态socket则不会,在这点上,epoll实现了一个”伪”AIO,因为这时候推动力在os内核。

在一些 benchmark中,如果所有的socket基本上都是活跃的—比如一个高速LAN环境,epoll并不比select/poll有什么效率,相 反,如果过多使用epoll_ctl,效率相比还有稍微的下降。但是一旦使用idle connections模拟WAN环境,epoll的效率就远在select/poll之上了。

使用mmap加速内核与用户空间的消息传递。

这 点实际上涉及到epoll的具体实现了。无论是select,poll还是epoll都需要内核把FD消息通知给用户空间,如何避免不必要的内存拷贝就很 重要,在这点上,epoll是通过内核于用户空间mmap同一块内存实现的。而如果你想我一样从2.5内核就关注epoll的话,一定不会忘记手工 mmap这一步的。

内核微调

这一点其实不算epoll的优点了,而是整个linux平台的优点。也许你可以怀疑linux平台,但是你无法回避linux平台赋予你微调内核的能力。比如,内核TCP/IP协 议栈使用内存池管理sk_buff结构,那么可以在运行时期动态调整这个内存pool(skb_head_pool)的大小— 通过echo XXXX>/proc/sys/net/core/hot_list_length完成。再比如listen函数的第2个参数(TCP完成3次握手 的数据包队列长度),也可以根据你平台内存大小动态调整。更甚至在一个数据包面数目巨大但同时每个数据包本身大小却很小的特殊系统上尝试最新的NAPI网卡驱动架构。

(epoll内容,参考epoll_互动百科)

推荐设置worker的个数为cpu的核数,在这里就很容易理解了,更多的worker数,只会导致进程来竞争cpu资源了,从而带来不必要的上下文切换。而且,nginx为了更好的利用多核特性,提供了cpu亲缘性的绑定选项,我们可以将某一个进程绑定在某一个核上,这样就不会因为进程的切换带来cache的失效。

像这种小的优化在nginx中非常常见,同时也说明了nginx作者的苦心孤诣。比如,nginx在做4个字节的字符串比较时,会将4个字符转换成一个int型,再作比较,以减少cpu的指令数等等。

代码来总结一下nginx的事件处理模型:

while (true) {

for t in run_tasks:

t.handler();

update_time(&now);

timeout = ETERNITY;

for t in wait_tasks: /* sorted already */

if (t.time <= now) { t.timeout_handler(); } else { timeout = t.time – now; break; } nevents = poll_function(events, timeout); for i in nevents: task t; if (events[i].type == READ) { t.handler = read_handler; } else { /* events[i].type == WRITE */ t.handler = write_handler; } run_tasks_add(t); }

[转] Nginx 服务器 select 和epoll的区别

epoll为什么这么快

epoll是多路复用IO(I/O Multiplexing)中的一种方式,但是仅用于linux2.6以上内核,在开始讨论这个问题之前,先来解释一下为什么需要多路复用IO.

以一个生活中的例子来解释.

假设你在大学中读书,要等待一个朋友来访,而这个朋友只知道你在A号楼,但是不知道你具体住在哪里,于是你们约好了在A号楼门口见面.

如果你使用的阻塞IO模型来处理这个问题,那么你就只能一直守候在A号楼门口等待朋友的到来,在这段时间里你不能做别的事情,不难知道,这种方式的效率是低下的.

现在时代变化了,开始使用多路复用IO模型来处理这个问题.你告诉你的朋友来了A号楼找楼管大妈,让她告诉你该怎么走.这里的楼管大妈扮演的就是多路复用IO的角色.

进一步解释select和epoll模型的差异.

select版大妈做的是如下的事情:比如同学甲的朋友来了,select版大妈比较笨,她带着朋友挨个房间进行查询谁是同学甲,你等的朋友来了,于是在实际的代码中,select版大妈做的是以下的事情:

int n = select(&readset,NULL,NULL,100);

for (int i = 0; n > 0; ++i)
{
if (FD_ISSET(fdarray[i], &readset))
{
do_something(fdarray[i]);
–n;
}
}

epoll版大妈就比较先进了,她记下了同学甲的信息,比如说他的房间号,那么等同学甲的朋友到来时,只需要告诉该朋友同学甲在哪个房间即可,不用自己亲自带着人满大楼的找人了.于是epoll版大妈做的事情可以用如下的代码表示:

n=epoll_wait(epfd,events,20,500);

for(i=0;i<n;++i)
{
do_something(events[n]);
}

在epoll中,关键的数据结构epoll_event定义如下:

typedef union epoll_data {
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;

struct epoll_event {
__uint32_t events;      /* Epoll events */
epoll_data_t data;      /* User data variable */
};

可以看到,epoll_data是一个union结构体,它就是epoll版大妈用于保存同学信息的结构体,它可以保存很多类型的信息:fd,指针,等等.有了这个结构体,epoll大妈可以不用吹灰之力就可以定位到同学甲.

别小看了这些效率的提高,在一个大规模并发的服务器中,轮询IO是最耗时间的操作之一.再回到那个例子中,如果每到来一个朋友楼管大妈都要全楼的查询同学,那么处理的效率必然就低下了,过不久楼底就有不少的人了.

对比最早给出的阻塞IO的处理模型, 可以看到采用了多路复用IO之后, 程序可以自由的进行自己除了IO操作之外的工作, 只有到IO状态发生变化的时候由多路复用IO进行通知, 然后再采取相应的操作, 而不用一直阻塞等待IO状态发生变化了.

从上面的分析也可以看出,epoll比select的提高实际上是一个用空间换时间思想的具体应用.

原文链接:http://blog.csdn.net/caoshuming_500/article/details/7372993

[大大爱二二网]全面支持https

无意中看到一篇文章,讲免费申请https证书。之前以为https需要年费一直没有做,现在有时间就把自己的网站支持https,另外微信小程序的接口都必须是https的,可以在小程序中调用我的https api.

1申请证书

网址 startssl

1,首先注册网站,注册网站后需要下载证书登录或者邮件登录,选择简单的邮件登录即可。

2,认证指引

b8ebc793a687f1688c4cf7960ac84f7e

选择DV SSL Certificate, 

会要求你先输入域名并验证邮箱密码,我相信自己买的域名都留了自己的邮箱,填入邮箱中的密码即可。

3,生成key

dcfe160922fd08e023f0a3e960e25088

同时在机器上生成私钥

openssl req -newkey rsa:2048 -keyout dadaaierer.key -out dadaaierer.csr

这步可能会需要输入密码,记住密码即可!!

将 dadaaierer.csr粘贴到表单里就生成了证书.zip,会看到各种服务器的crt,这里我用的nginx,所以选择1_dadaaierer.com_bundle.crt 即可。

4,配置nginx服务器

这里直接使用dadaaierer.key,并且重启nginx需要输入密码,并且会报错

SSL_CTX_use_PrivateKey_file(“/home/work/ssl/www.dadaaierer.com.key”) failed (SSL: error:0906406D:PEM routines:PEM_def_callback:problems getting password error:0907B068:PEM routines:PEM_READ_BIO_PRIVATEKEY:bad password read error:140B0009:SSL routines:SSL_CTX_use_PrivateKey_file:PEM lib)

很是郁闷!查了一下午,应该去掉PEM的密码

openssl rsa -in dadaaierer.key -out www.nopass.key

server {
listen 443 ;

ssl on;
ssl_certificate /home/work/ssl/3_dadaaierer.com_bundle.crt ;
ssl_certificate_key /home/work/ssl/www.nopass.key;

重启nginx,访问网址https://大大爱二二网 可以啦!!另外全站都已经支持了https.

5 后记

因为前端https链接是不允许降级访问http的,所以如果引用了一些https的资源会加载失败,需要把之前加载http的资源改为https.

startssl网站的证书免费三年,感觉整体还是不错的!

Nginx 禁止IP访问

网上的方法!

我们在使用的时候会遇到很多的恶意IP攻击,这个时候就要用到Nginx 禁止IP访问了。下面我们就先看看Nginx的默认虚拟主机在用户通过IP访问,或者通过未设置的域名访问(比如有人把他自己的域名指向了你的ip)的时 候生效最关键的一点是,在server的设置里面添加这一行:

listen 80 default;

后面的default参数表示这个是默认虚拟主机。

Nginx 禁止IP访问这个设置非常有用。

比如别人通过ip或者未知域名访问你的网站的时候,你希望禁止显示任何有效内容,可以给他返回500.目前国内很多机房都要求网站主关闭空主机头,防止未备案的域名指向过来造成麻烦。就可以这样设置:

server {  
      listen 80 default;  
      return 500;  
   }

也可以把这些流量收集起来,导入到自己的网站,只要做以下跳转设置就可以:

server {  
       listen 80 default;  
       rewrite ^(.*) http://www.example.com permanent;  
   }

按照如上设置后,确实不能通过IP访问服务器了,但是在应该用中出现当server_name后跟多个域名时,其中一个域名怎么都无法访问,设置如下:

server  {  
        listen 80;  
        server_name www.example.com example.com    
   }

没更改之前,通过server_name 中的www.example.com example.com均可访问服务器,加入Nginx 禁止IP访问的设置后,通过example.com无法访问服务器了,www.example.com可以访问,用 Nginx -t 检测配置文件会提示warning:

   [warn]: conflicting server name “example.com” on 0.0.0.0:80, 
      ignored  
   the configuration file /usr/local/Nginx/conf/
      Nginx.conf syntax is ok  
   configuration file /usr/local/Nginx/conf/Nginx.
      conf test is successful

最后通过在listen 80 default;后再加server_name _;解决,形式如下:

 
  #禁止IP访问  
   server  {  
       listen 80 default;  
       server_name _;  
       server_name www.example.com example.com 
       return 500;  
   }

这样,通过example.com就能访问服务器了.

但是 网上的方法不管用,最后通过一步判断


if ($host ~ "xx.xx.xx.xx";) {

return 500;
}

done

[openresty]第三节:操作redis数据

同样的看redis组件

https://github.com/openresty/lua-resty-redis

按照实例添加redis.conf


worker_processes 1;
error_log logs/error.log;
events {
worker_connections 1024;
}
http {
server {
listen 8091;
location / {
default_type text/html;
content_by_lua_block {
local redis = require "resty.redis"
local red = redis:new()

red:set_timeout(1000)
local ok,err = red:connect("127.0.0.1", 6379)
if not ok then
ngx.say("fail to connent", err)
return
end

local res,err = red:get("lua")
if not res then
ngx.say("failed to get lua", err)
return
end
ngx.say(res)
}
}
}
}

启动redis.conf配置

../nginx/sbin/nginx -p `pwd`/ -c conf/redis.conf

访问localhost:8091 获取了key为lua的数据,使用起来非常简单。

[openresty]第二节:操作mysql数据库

openresty操作数据库本质上调用了lua-resty-mysql 组件

具体参照git:https://github.com/openresty/lua-resty-mysql#table-of-contents

同样的在/usr/local/openresty/work/conf/ 文件夹下添加新的文件mysql.conf

内容如下:


worker_processes 1;
error_log logs/error.log;
events {
worker_connections 1024;
}
http {
server {
listen 8089;
server_name localhost;
location / {
content_by_lua '
local arg = ngx.req.get_uri_args()
local mysql = require "resty.mysql"
local db, err = mysql:new()
if not db then
ngx.say("failed to instantiate mysql: ", err)
return
end

db:set_timeout(1000) -- 1 sec
local ok, err, errcode, sqlstate = db:connect{
host = "127.0.0.1",
port = 3306,
database = "test",
user = "xxxx",
password = "xxxxxxx",
max_packet_size = 1024 * 1024 }

if not ok then
ngx.say("failed to connect: ", err, ": ", errcode, " ", sqlstate)
return
end

--ngx.say("connected to mysql.")
local id = tonumber(arg.id)
queryStr = "select goods_id,goods_name from goods_test where goods_id ="..id
--ngx.say(queryStr)
res, err, errcode, sqlstate =
--db:query("select * from goods_test order by goods_id asc", 10)
db:query(queryStr)
if not res then
ngx.say("bad result: ", err, ": ", errcode, ": ", sqlstate, ".")
return
end
local cjson = require "cjson"
ngx.say("result: ", cjson.encode(res))
';
}

}
}

是不是很简单,然后../nginx/sbin/nginx -p `pwd`/ -s reload -c conf/mysql.conf

重新加载配置文件,这样访问localhost:8089?id=1 就可以筛选数据库中主键ID=1的数据了。应该有mysql注入问题,所以参数全部转化为int类型,基本的网络安全意识不能没有。

 

[openresty]安装和入门(一)

首先推荐官网:http://openresty.org/en/

一,安装openresty包

1,安装依赖

yum install perl dos2unix

yum install pcre-devel openssl-devel gc-c++

wget https://openresty.org/download/openresty-1.11.2.1.tar.gz

解压tar zxvf openresty-1.11.2.1

./configure

gmake

gmake install

安装完成

默认的路径是/usr/local/openresty/

二 hello world 程序

参考http://openresty.org/cn/getting-started.html

Prepare directory layout

We first create a separate directory for our experiments. You can use an arbitrary directory. Here for simplicity, we just use~/work:

mkdir ~/work
cd ~/work
mkdir logs/ conf/

Note that we’ve also created the logs/ directory for logging files and conf/ for our config files.

Prepare the nginx.conf config file

Create a simple plain text file named conf/nginx.conf with the following contents in it:

worker_processes  1;
error_log logs/error.log;
events {
    worker_connections 1024;
}
http {
    server {
        listen 8088;
        location / {
            default_type text/html;
            content_by_lua '
                ngx.say("<p>hello, world</p>")
            ';
        }
    }
}

If you’re familiar with Nginx configuration, it should look very familiar to you. OpenResty is just an enhanced version of Nginxby means of addon modules anyway. You can take advantage of all the exisitng goodies in the Nginx world.

Start the Nginx server

Assuming you have installed OpenResty into/usr/local/openresty (this is the default)

Then we start the nginx server with our config file this way:
/usr/local/openresty/sbin/nginx -p `pwd`/ -c conf/nginx.conf

-p 标识的是

-p prefix : set prefix path (default: /usr/local/openresty/nginx/) 路径
-c filename : set configuration file (default: conf/nginx.conf)     配置

Error messages will go to the stderr device or the default error log files logs/error.log in the current working directory.

Access our HelloWorld web service

We can use curl to access our new web service that says HelloWorld:

curl http://localhost:8088/

If everything is okay, we should get the output

<p>hello, world</p>

ab 压测一下

ab -c 10 -n 100 http://localhost:8088/

在单核阿里云的配置下:

Requests per second:    240.93 [#/sec] (mean) 还算比较高的

未完待续。。。。