nginx + lua

--nignx lua
终于要考虑在nginx下使用lua啊啦,这里首先不得不提的就是lua-nginx-module了,nginx_lua_module是由淘宝的工程师清无(王晓哲)和春来(章亦春)所开发的nginx第三方模块,它能将lua语言嵌入到nginx配置中,从而使用lua就极大增强了nginx的能力,ngx_lua_module 是一个nginx http模块,它把 lua 解析器内嵌到 nginx,用来解析并执行lua 语言编写的网页后台脚本。这个模块的特性为:特性:

    支持Windows和Linux平台。
    支持高并发高性能。
    HTML网页中内嵌LUA脚本代码,类似于PHP。
    支持非阻塞的数据库操作,目前只支持MYSQL。
    支持异步的文件IO操作。
    支持非阻塞的SOCKET IO操作。

还有一个模块是:echo-nginx-module 这个模块可以用来输出一些简单的信息,这个模块不包含在 Nginx 源码中,需要另外加载安装,该模块是国人 章亦春 开发的。

还有ngx_devel_kit(NDK)模块是一个拓展nginx服务器核心功能的模块,第三方模块开发可以基于它来快速实现。

NDK提供函数和宏处理一些基本任务,减轻第三方模块开发的代码量。

开发者如果要依赖这个模块做开发,需要将这个模块一并参与nginx编译,同时需要在自己的模块配置中声明所需要使用的特性。

PCRE(Perl Compatible Regular Expressions中文含义:perl语言兼容正则表达式)是一个用C语言编写的正则表达式函数库,由菲利普.海泽(Philip Hazel)编写。PCRE是一个轻量级的函数库,比Boost之类的正则表达式库小得多。PCRE十分易用,同时功能也很强大,性能超过了POSIX正则表达式库和一些经典的正则表达式库

zlib是提供数据压缩用的函式库,由Jean-loup Gailly与Mark Adler所开发,初版0.9版在1995年5月1日发表。zlib使用DEFLATE算法,最初是为libpng函式库所写的,后来普遍为许多软件所使用。此函式库为自由软件,使用zlib授权。

好了,现在该有的都有了,可以开工研究了:
先来配置一下配置文件增加如下:
        location /lua {                                                         
            default_type  'text/plain';
            content_by_lua  'ngx.say("hello,lua")';
        }   

        location /echo {
            default_type 'text/plain';
            echo "hello echo";
        }

然后你就可以在浏览器或者用curl来返回你的服务器记得加路径哦,然后...,你就看到了。

继续研究,ngx_lua介绍

原理

ngx_lua将Lua嵌入Nginx,可以让Nginx执行Lua脚本,并且高并发、非阻塞的处理各种请求。Lua内建协程,这样就可以很好的将异步回调转换成顺序调用的形式。ngx_lua在Lua中进行的IO操作都会委托给Nginx的事件模型,从而实现非阻塞调用。开发者可以采用串行的方式编写程序,ngx_lua会自动的在进行阻塞的IO操作时中断,保存上下文;然后将IO操作委托给Nginx事件处理机制,在IO操作完成后,ngx_lua会恢复上下文,程序继续执行,这些操作都是对用户程序透明的。

每个NginxWorker进程持有一个Lua解释器或者LuaJIT实例,被这个Worker处理的所有请求共享这个实例。每个请求的Context会被Lua轻量级的协程分割,从而保证各个请求是独立的。

ngx_lua采用“one-coroutine-per-request”的处理模型,对于每个用户请求,ngx_lua会唤醒一个协程用于执行用户代码处理请求,当请求处理完成这个协程会被销毁。每个协程都有一个独立的全局环境(变量空间),继承于全局共享的、只读的“comman data”。所以,被用户代码注入全局空间的任何变量都不会影响其他请求的处理,并且这些变量在请求处理完成后会被释放,这样就保证所有的用户代码都运行在一个“sandbox”(沙箱),这个沙箱与请求具有相同的生命周期。

得益于Lua协程的支持,ngx_lua在处理10000个并发请求时只需要很少的内存。根据测试,ngx_lua处理每个请求只需要2KB的内存,如果使用LuaJIT则会更少。所以ngx_lua非常适合用于实现可扩展的、高并发的服务。

协程

协程类似一种多线程,与多线程的区别有:

    协程并非os线程,所以创建、切换开销比线程相对要小。
    协程与线程一样有自己的栈、局部变量等,但是协程的栈是在用户进程空间模拟的,所以创建、切换开销很小。
    多线程程序是多个线程并发执行,也就是说在一瞬间有多个控制流在执行。而协程强调的是一种多个协程间协作的关系,只有当一个协程主动放弃执行权,另一个协程才能获得执行权,所以在某一瞬间,多个协程间只有一个在运行。
    由于多个协程时只有一个在运行,所以对于临界区的访问不需要加锁,而多线程的情况则必须加锁。
    多线程程序由于有多个控制流,所以程序的行为不可控,而多个协程的执行是由开发者定义的所以是可控的。


Nginx的每个Worker进程都是在epoll或kqueue这样的事件模型之上,封装成协程,每个请求都有一个协程进行处理。这正好与Lua内建协程的模型是一致的,所以即使ngx_lua需要执行Lua,相对C有一定的开销,但依然能保证高并发能力。
二、ngx_lua安装

Nginx中安装ngx_lua需要安装LuaJIT,ngx_devel_kit,ngx_lua等安装文件,我们这里用的OpenResty,内部已经集成ngx_lua,无需再安装任何模块。
三、ngx_lua用法

嵌套lua脚本

使用 content_by_lua 把lua脚本嵌入到Nginx配置里,或者使用 content_by_lua_file 把Lua脚本载入到Nginx配置里.

location /lua {
set $test "hello, world";
content_by_lua '
    ngx.header.content_type = "text/plain";
    ngx.say(ngx.var.test);
';
}

$ curl 'http://134.32.28.134:8888/lua',输出 hello, world。

include lua文件

Nginx中include lua的脚本文件方式,如:

 location /mytest {    
      content_by_lua_file conf/alcache.lua;       
 }

其中在alcache.lua中编写lua脚本即可。

确实是通过这样,便可以在alcache.lua中编写lua脚本,但有几点还是要说明一下,1.在脚本中写print语句是不会返回值的,如果你需要返回“hello world”什么的,需要用ngx.say("hello world")这个函数。我说之前怎么一直不打印hello world呢。另外,这样写了,只能在curl中访问,在浏览器中访问时它会提示你打开这个文件。至于为什么目前也不清楚,后面有时间了再研究吧。还有就是放文件的目录可以在根目录下面随便调,但是我调到家目录它就报错了。应该是只能在根目录下面放吧。

在调试的过程中还遇到了lua_package_path,查了查后才知道它是指定搜索路径的,索性将lua中指定路径的方法总结一下吧:

如果是一个 *.LUA 的文件, 里面用到了自己写的库, 或者第三方写的库, 但是你不想把它放到 lua 的安装目录里, 则在代码里面可以指定require搜索的路径。


    package.path = '/usr/local/share/lua/5.1/?.lua;/home/resty/?.lua;'    --搜索lua模块
    package.cpath = '/usr/local/lib/lua/5.1/?.so;'        --搜索so模块


如果是要在 nginx.conf 文件中引用第三方的库,则需要在 http 段中添加下面的代码

    lua_package_path '/usr/local/share/lua/5.1/?.lua;/home/resty/?.lua;';
    lua_package_cpath '/usr/local/lib/lua/5.1/?.so;';


四、实际运用中通过lua结合分布式缓存对session的处理

这里redis与memcache的支持不是调用Nginx自带redis与memcache模块,都是调用OpenResty内部集成的第三方模块

nginx.conf部分配置

location /login {    
    content_by_lua_file conf/alcache.lua;       
}

alcache.lua配置

local key = tostring(ngx.var.arg_username)
local val = tostring(ngx.var.arg_password)
local passLogin = tostring(ngx.var.arg_passLoginFlag)
local flags = tostring(ngx.var.arg_flags or 0)
local exptime = tostring(ngx.var.arg_exptime or 0)
local sessionId  = tostring(ngx.var.cookie_JSESSIONID)

ngx.say("sessionId:",sessionId)
ngx.say("key:",key)
ngx.say("val:",val)

if (key == nil and val == nil)  then return end
--if (passLogin == nil or sessionId == nil)  then return end

local memcached = require("resty.memcached")
--local redis = require("resty.redis")
local cache,err = memcached:new()
--local cache,err = redis.new()

if not cache then
        ngx.say("failed to instantiate cache: ",err)
        return
end


cache:set_timeout(1000)

local ok,err = cache:connect("134.32.28.134",11211)
--local ok,err = cache:connect("134.32.28.134",6379)
if not ok then
        ngx.say("failed to connect: ",err)
        return
end


local res,flags,err = cache:get(key)
if err then
        ngx.say("failed to get ",key," : ",err)
        return
end
if res and tostring(res) ~= sessionId then
        cache:delete(key)
        cache:set(key,sessionId,exptime,flags)
else
                cache:set(key,sessionId,exptime,flags)
end


local ok, err = cache:close()  
                if not ok then  
            ngx.say("failed to close:", err)  
        return  
end

local url = ngx.var.uri  
local res = ngx.location.capture("/proxy") 


luasocket

系统优化配置文件:/etc/sysctl.conf
/etc/sysctl.conf这个目录主要是配置一些系统信息,而且它的内容全部是对应于/proc/sys/这个目录的子目录及文件。这样或许你不理解,先看看我的系统/etc/sysctl.conf这个文件里面有什么内容:cat /etc/sysctl.conf
fs.file-max = 3145728

fs.suid_dumpable = 1
kernel.core_uses_pid = 1

kernel.printk = 4 4 1 7
kernel.shmmax = 7516192768
kernel.shmall = 1835008

net.ipv4.ip_local_port_range = 8192 65535
net.ipv4.ip_nonlocal_bind = 1
net.ipv4.netfilter.ip_conntrack_max = 1048576
net.ipv4.tcp_max_tw_buckets = 1048576
net.ipv4.tcp_syncookies = 1

net.ipv4.tcp_rmem = 4096 87380 4120576
net.ipv4.tcp_wmem = 4096 16384 4120576

#vm.nr_hugepages = 0
其实每个'.'分割的就代表一个目录,例如,fs.file-max也就代表/proc/sys/fs/file-max。那么/proc/sys/是用来做什么的呢?大家都知道/proc是每次系统启动的时候都要重新挂载的,它反映了系统内存里面的一些状态。通过/proc/可以很好的了解到当前系统的一些信息。而/proc/sys/则是这些信息的一小部分而已。你可以通过ls /proc/查看,里面还是不是有很多其他的数字命名的目录?那就是单个进程的信息了,那个数字就是进程ID。例如之前写过一篇关于two many open files解决办法的文章,那么查看某个进程打开多少FD,则可以通过ls /proc/<pid>/fd | wc -l来统计。
常用的有哪些呢?刚入门,自己用到的选项也不多。
1.  vm.nr_hugepages/vm.hugetlb_shm_group        # 第一个表示设置系统有多少大页内存,第二个表示哪些用户组的进程可以使用大页内存
2.  vm.swappiness                                                         # 如果设置为0表示禁用swap,否则值越大使用swap的可能性越高
3.  vm.overcommit_memory/vm.overcommit_ratio  # 这个设置是否可以超额分配内存,当然这是利用的virtual memory机制实现的。
前面那些参数都是在测试有什么方法防止mysqld使用swap,或者能否被避免Linux的OOM机制,详情请点击这里
还有在Linux上装oracle会要设置kernel.shmmax这个参数吧(>_<,我还没在linux下安装过~) 这个也可以在/etc/sysctl.conf里面设置。

前面说过/etc/sysctl.conf与目录/proc/sys/的关系,那么常见设置方法有三个:
1.  echo value > /proc/sys/xx/yy
2.  vi /etc/sysctl.conf #vi编辑,然后添加设置 ; sysctl -p #生效
3.  sysctl -w kernel.domainname="example.com" #利用-w生效
另外需要注意,/etc/sysctl.conf 与 sysctl命令只有root用户才有权限执行

关于/etc/sysctl.conf的介绍就差不多了,理解了这个文件的作用,更关键的是要知道里面每项的含义,以及怎么设置来调优系统。不懂的太多了,只能靠慢慢积累了~
另外几个系统缓存参数设置参见这里(感觉这篇文章写得不错~)  

这里要涉及到一个命令: sysctl是一个允许您改变正在运行中的Linux系统的接口。它包含一些 TCP/IP 堆栈和虚拟内存系统的高级选项, 这可以让有经验的管理员提高引人注目的系统性能。用sysctl可以读取设置超过五百个系统变量。基于这点,sysctl(8) 提供两个功能:读取和修改系统设置。




个人一般sysctl -p 或sysctl -a比较多使用

sysctl配置与显示在/proc/sys目录中的内核参数.可以用sysctl来设置或重新设置联网功能,如IP转发、IP碎片去除以及源路由检查等。用户只需要编辑/etc/sysctl.conf文件,即可手工或自动执行由sysctl控制的功能。

    命令格式:

    sysctl [-n] [-e] -w variable=value

    sysctl [-n] [-e] -p <filename> (default /etc/sysctl.conf)

    sysctl [-n] [-e] -a

    常用参数的意义:

    -w   临时改变某个指定参数的值,如

         sysctl -w net.ipv4.ip_forward=1

    -a   显示所有的系统参数

    -p   从指定的文件加载系统参数,如不指定即从/etc/sysctl.conf中加载

    如果仅仅是想临时改变某个系统参数的值,可以用两种方法来实现,例如想启用IP路由转发功能:

    1) #echo 1 > /proc/sys/net/ipv4/ip_forward

    2) #sysctl -w net.ipv4.ip_forward=1

    以上两种方法都可能立即开启路由功能,但如果系统重启,或执行了

    # service network restart

 命令,所设置的值即会丢失,如果想永久保留配置,可以修改/etc/sysctl.conf文件

 将 net.ipv4.ip_forward=0改为net.ipv4.ip_forward=1


关于nginx中的include
当一个nginx服务器中运行着多个虚拟主机,如果把这些虚拟主机的配置都放在主配置文件(nginx.conf)中,造成主配置文件很大,对日后的维护带来不便。其实可以把每个虚拟主机的配置信息存放在一个单独文件,然后使用include指令把该文件嵌入到主配置文件中即可。

假设有两个虚拟主机:www.netingcn.com和www.netingcn.net,分别为其创建配置文件为www_netingcn_com.conf和www_netingcn_net.conf,在nginx默认存放配置文件的目录下(/usr/local/nginx/conf/)建立一个vhost目录,把上述创建的两个文件存放在此目录中,并把虚拟主机相应的配置移到配置文件中。然后在nginx的主配置文件中添加如下配置即可:

    include /usr/local/nginx/conf/vhost/www_netingcn_com.conf;

    include /usr/local/nginx/conf/vhost/www_netingcn_net.conf;

虚拟主机是在网络服务器上分出一定的磁盘空间供用户放置站点、应用组件等,提供必要的站点功能、数据存放和传输功能。所谓虚拟主机,也叫“网站空间”,就是把一台运行在互联网上的服务器划分成多个“虚拟”的服务器,每一个虚拟主机都具有独立的域名和完整的Internet服务器(支持WWW、FTP、E-mail等)功能。虚拟主机是网络发展的福音,极大的促进了网络技术的应用和普及。同时虚拟主机的租用服务也成了网络时代新的经济形式。虚拟主机的租用类似于房屋租用。

今天看了include命令又带出了虚拟主机,又带出了如何在nginx上设置虚拟主机。虚拟主机的概念就不多说了,这个好理解。在nginx上设置虚拟主机其实很简单,用nginx官方维基的说法是:Server Blocks,什么是Server Blocks呢?就是在http模块下配置多个server模块,比如:
http {
  index index.html;
 
  server {
    server_name www.domain1.com;
    access_log logs/domain1.access.log main;
 
    root /var/www/domain1.com/htdocs;
  }
 
  server {
    server_name www.domain2.com;
    access_log  logs/domain2.access.log main;
 
    root /var/www/domain2.com/htdocs;
  }
}

但是如果有多个server模块,这样写的话就会使得配置文件非常臃肿,所以就把其中的server模块独立到另一个xxx.conf文件中,然后用include 来包含进这个文件,就比较明晰了。
设置虚拟主机可以是基于ip的和基于域名的,这两种方法我都在网上找了并收藏了在我的博客nginx中了。特别是基于域名的还要涉及到域名服务器等的一些东西,还是比较麻烦一些,现在就不花时间专门去研究了,用到的时候再说吧。

另外,include命令不但用来包含server模块,还常常用来包含location模块,如放API的服务器就常常用这种方式来搞。因此,这里的include还是和程序中的include挺一样的。

今天还是继续来研究nginx的配置:


favicon.ico中文名称是网站头像,favicon.ico图标是网站的缩略标志,可以显示在浏览器标签、地址栏左边和收藏夹,是展示网站个性的缩略logo标志,也可以说是网站头像,如果要让网站看起来更专业、更美、更有个性,favicon.ico是必不可少的。但是在我们这里nginx是作为api服务器的,所以就不需要这个东西,那就把他禁掉吧,怎么禁呢,且听下文:# 把以下配置放到 server {} 块.
关闭favicon.ico不存在时记录日志  location = /favicon.ico {log_not_found off;access_log off;}  这里还涉及到deny all指令等,如下

1.目录列表(directory listing)
nginx让目录中的文件以列表的形式展现只需要一条指令
autoindex on;
autoindex可以放在location中,只对当前location的目录起作用。你也可以将它放在server指令块则对整个站点都起作用。或者放到http指令块,则对所有站点都生效。
 下面是一个简单的例子:
 server {
 
        listen   80;
 
        server_name  domain.com www.hx95.com;
 
        access_log  /var/...........................;
 
        root   /path/to/root;
 
        location / {
 
                index  index.php index.html index.htm;
 
        }
 
        location /somedir {
 
               autoindex on;
 
        }
 
}
2.nginx禁止访问某个目录
跟Apache的Deny from all类似,nginx有deny all指令来实现。
 禁止对叫dirdeny目录的访问并返回403 Forbidden,可以使用下面的配置:
 location /dirdeny {
 
      deny all;
 
      return 403;
 
}

总结nginx的访问权限控制:
单看nginx模块名ngx_http_access_module,很多人一定很陌生,但是deny和allow相比没一个人不知道的,实际上deny和allow指令属于ngx_http_access_module.我们想控制某个uri或者一个路径不让人访问,在nginx就得靠它了。

nginx的访问控制模块语法很简单,至少比apache好理解,apache的allow和deny的顺序让很多初学者抓头.好了具体看下这个插件的使用方法吧。
1、安装模块

这个模块内置在了nginx中,除非你安装中使用了–without-http_access_module。如果你还没安装过nginx,那么请参考下ttlsa之前写的nginx安装.
2、指令

allow
语法:     allow address | CIDR | unix: | all;
默认值:     —
配置段:     http, server, location, limit_except

允许某个ip或者一个ip段访问.如果指定unix:,那将允许socket的访问.注意:unix在1.5.1中新加入的功能,如果你的版本比这个低,请不要使用这个方法。

deny
语法:     deny address | CIDR | unix: | all;
默认值:     —
配置段:     http, server, location, limit_except

禁止某个ip或者一个ip段访问.如果指定unix:,那将禁止socket的访问.注意:unix在1.5.1中新加入的功能,如果你的版本比这个低,请不要使用这个方法。
3. allow、deny实例

location / {
deny  192.168.1.1;
allow 192.168.1.0/24;
allow 10.1.1.0/16;
allow 2001:0db8::/32;
deny  all;
}

从上到下的顺序,类似iptables。匹配到了便跳出。如上的例子先禁止了192.16.1.1,接下来允许了3个网段,其中包含了一个ipv6,最后未匹配的IP全部禁止访问.  在实际生产环境中,我们也会使用nginx 的geo模块配合使用,有兴趣的请参考ttlsa相关文章nginx geo使用方法.
4. 结束语

nginx访问控制模块要数nginx里面最简单的指令,只要记住你想禁止谁访问就deny加上IP,想允许则加上allow ip,想禁止或者允许所有,那么allow all或者deny all即可.

nginx日志相关的指令总结:

nginx 日志相关指令主要有两条,一条是log_format,用来设置日志格式,另外一条是access_log,用来指定日志文件的存放路径、格式和缓存大小,通俗的理解就是先用log_format来定义自己想用的日志格式,然后在用access_log定义虚拟主机时或全局日志时在把定义的log_format 跟在后面;其实这个在默认的配置文件中就有的,打开便可以了。如下:

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
 
    access_log  logs/access.log  main;
下面说说这些日志中的变量的含义,其实看名称也能猜出个七八分了。

$remote_addr / $http_x_forwarded_for 用以记录客户端的ip地址;

$remote_user  用来记录客户端用户名称;

$time_local  用来记录访问时间与时区;

$request  用来记录请求的url与http协议;

$status  用来记录请求状态,成功是200;

$body_bytes_sent  记录发送给客户端文件主体内容大小;

$http_referer  用来记录从那个页面链接访问过来的;

$http_user_agent  记录客户端浏览器的相关信息;

通常web服务器放在反向代理的后面,这样就不能获取到客户的IP地址了,通过$remote_addr拿到的IP地址是反向代理服务器的iP地址。反向代理服务器在转发请求的http头信息中,可以增加x_forwarded_for信息,用以记录原有客户端的IP地址和原来客户端的请求的服务器地址;而事实上在新版本的nginx中,确实默认的是有这么一项的。 x_forwarded_for

用access_log指令日志文件存放路径

用了log_format 指令设置了日志格式之后,需要用access_log指令指定日志文件的存放路径。如下面的例子:

    #access_log         logs/access.log          main;
如果不想启用日志,则使用:

   access_log off ;
在定义日志目录中要注意的是,nginx进程设置的用户和组必须有对该路径创建文件的权限,假设nginx的usr指令设置的用户名和用户组都是www,而logs 目录的用户名和组是root,那么日志文件将无法被创建。

nginx的error_log类型如下(从左到右:debug最详细 crit最少):
[ debug | info | notice | warn | error | crit ]
例如:error_log logs/nginx_error.log  crit;
解释:日志文件存储在nginx安装目录下的 logs/nginx_error.log ,错误类型为 crit ,也就是记录最少错误信息;

注意error_log off并不能关闭日志记录功能,它将日志文件写入一个文件名为off的文件中,如果你想关闭错误日志记录功能,应使用以下配置:
error_log /dev/null crit;
把存储位置设置到Linux的黑洞中去

error_log file [ debug | info | notice | warn | error | crit ]  | [{  debug_core | debug_alloc | debug_mutex | debug_event | debug_http | debug_mail | debug_mysql } ]
日志级别 = 错误日志级别 | 调试日志级别; 或者
日志级别 = 错误日志级别;
错误日志的级别: emerg, alert, crit, error, warn, notic, info, debug,
调试日志的级别: debug_core, debug_alloc, debug_mutex, debug_event, debug_http, debug_mail, debug_mysql,


error_log 指令的日志级别配置分为 错误日志级别和调试日志级别
且 错误日志只能设置一个级别 且 错误日志必须书写在调试日志级别的前面 且 调试日志可以设置多个级别
其他配置方法可能达不到你的预期.



      server_names_hash_bucket_size 128;
        server_names_hash_max_size 1024;
保存服务器名字的hash表是由指令 server_names_hash_max_size 和 server_names_hash_bucket_size所控制的。参数hash bucket size总是等于hash表的大小,并且是一路处理器缓存大小的倍数。在减少了在内存中的存取次数后,使在处理器中加速查找hash表键值成为可能。如果 hash bucket size等于一路处理器缓存的大小,那么在查找键的时候,最坏的情况下在内存中查找的次数为2。第一次是确定存储单元的地址,第二次是在存储单元中查找键 值。因此,如果Nginx给出需要增大 hash max size 或 hash bucket size的提示,那么首要的是增大前一个参数的大小.

        large_client_header_buffers 4 16k;
        client_max_body_size     8m;            client_max_body_size设置所能接收的最大请求体的大小,所以有时这个参数也会限制了上传文件的大小,默认是1M,   注意:根据请求头中的Content-Length来判断请求体大小是否允许。如果大于设定值,则返回“ Request Entity Too Large”(413)错误。不过要注意的是,浏览器一般并不对这个错误进行特殊显示。
nginx在接收到客户端得请求之后,就开始解析http请求,也就是解析http header,需要分配一段buf来接收这些数据,nginx并不知道这个http header的大小,在nginx配置中client_header_buffer_size和large_client_header_buffers这两个配置项起到了作用 。 client_header_buffer_size默认是1024字节。large_client_header_buffers默认最大分配4组8192字节的buf,每次分配一个buf。nginx处理http header的过程是先处理request line(http 请求的第一行),然后在处理每一个header,那么处理request line的过程首先会分配client_header_buffer_size大小的空间,如果这个空间不够,那么再分配一个large_client_header_buffers的空间,然后把之前的client_header_buffer_size copy到大buffer的前半部分中。如果在不够,nginx就会返回给客户端400的错误。每个header也是和如上的request line一个处理步骤,所以对于request line 和每个header的大小应该不超过1个large_client_header_buffers。对于整个request line和所有header来讲,总大小不应该超过4*8192字节大小,否则也会产生400的错误。
    在解析request line的时候,回首先分配一个client_header_buffer_size来解析请求,当空间不足的时候,copy数据到第一个large_client_header_buffers中,如果这个buf仍然不能满足要求就返回400错误。函数ngx_http_process_request_line是解析request line的,在解析buf中的数据后,会判断是否解析完request line,如果buf的pos和end相等,就说明仍然未解析完成,需要再次从fd中读取数据,然后扩充buf到large buf。                                                                                                                                                           
        
      client_body_timeout 30;   设置用户请求体的超时时间  注意:只有请求体需要被1次以上读取时,该超时时间才会被设置。且如果这个时间后用户什么都没发,nginx会返回requests time out 408.

        client_header_timeout 10;  设置用户请求头的超时时间。默认1m 注意:只有请求头需要被1次以上读取时,该超时时间才会被设置。且如果这个时间后用户什么都没发,nginx会返回requests time out 408.

        client_body_buffer_size 1m;   指定用户请求体所使用的buffer的最大值 ,默认为8k或16k,注意:如果用户请求体超过了buffer的大小,则将全部内容或部分内容存储到一个临时文件中。



        client_header_buffer_size 4k;  设置用户请求头所使用的buffer大小 ,默认1k  注意:

(1)对绝大多数请求来说,1k足以满足请求头所需的buffer;

(2)对于携带有较大cookie或来自于wap用户的请求头来说,1k的buffer一般不够,这时可以使用指令large_client_header_buffers。

        sendfile on;


在apache,nginx,lighttpd等web服务器当中,都有一项sendfile相关的配置,在一些网上的资料都有谈到sendfile会提升文件传输性能,那sendfile到底是什么呢?它的原理又是如何呢?

在传统的文件传输里面(read/write方式),在实现上其实是比较复杂的,需要经过多次上下文的切换,我们看一下如下两行代码:
 

    

    read(file, tmp_buf, len);       
    write(socket, tmp_buf, len);  

 

        以上两行代码是传统的read/write方式进行文件到socket的传输。

 

当需要对一个文件进行传输的时候,其具体流程细节如下:

 

1、调用read函数,文件数据被copy到内核缓冲区

 

2、read函数返回,文件数据从内核缓冲区copy到用户缓冲区

 

3、write函数调用,将文件数据从用户缓冲区copy到内核与socket相关的缓冲区。

 

4、数据从socket缓冲区copy到相关协议引擎。

 

以上细节是传统read/write方式进行网络文件传输的方式,我们可以看到,在这个过程当中,文件数据实际上是经过了四次copy操作:

 

硬盘—>内核buf—>用户buf—>socket相关缓冲区—>协议引擎

 

而sendfile系统调用则提供了一种减少以上多次copy,提升文件传输性能的方法。Sendfile系统调用是在2.1版本内核时引进的:

 

    sendfile(socket, file, len);   

 

运行流程如下:

 

1、sendfile系统调用,文件数据被copy至内核缓冲区

 

2、再从内核缓冲区copy至内核中socket相关的缓冲区

 

3、最后再socket相关的缓冲区copy到协议引擎

 

相较传统read/write方式,2.1版本内核引进的sendfile已经减少了内核缓冲区到user缓冲区,再由user缓冲区到socket相关 缓冲区的文件copy,而在内核版本2.4之后,文件描述符结果被改变,sendfile实现了更简单的方式,系统调用方式仍然一样,细节与2.1版本的 不同之处在于,当文件数据被复制到内核缓冲区时,不再将所有数据copy到socket相关的缓冲区,而是仅仅将记录数据位置和长度相关的数据保存到 socket相关的缓存,而实际数据将由DMA模块直接发送到协议引擎,再次减少了一次copy操作。

 sendfile能有效提高web传输文件的效率。

linux kernel2.2开始支持sendfile,2.4.21支持sendfile64.只要你的kernel支持sendfile64,nginx会自动使用,无需操心,nginx的配置和使用还是挺简单的

nginx的X-Accel-Redirect就是类似x-sendfile的东西。
默认下nginx会自动启动sendfile,不管是32位还是64位的

在配置文件里用sendfile on/off开关sendfile

根据我的测试,开启sendfile比没开启静态文件rps相差1倍左右。。

如果是在程序在使用,只需输出http头 X-Accel-Redirect: 文件路径就可以了,这样下载就交给web服务器了

        tcp_nopush on;
        tcp_nodelay on;

tcp_nopush
官方:
tcp_nopush
Syntax: tcp_nopush on | off
Default: off
Context: http
server
location
Reference: tcp_nopush
 
This directive permits or forbids the use of thesocket options TCP_NOPUSH on FreeBSD or TCP_CORK on Linux. This option is onlyavailable when using sendfile.
Setting this option causes nginx to attempt to sendit’s HTTP response headers in one packet on Linux and FreeBSD 4.x
You can read more about the TCP_NOPUSH and TCP_CORKsocket options here.
 
linux 下是tcp_cork,上面的意思就是说,当使用sendfile函数时,tcp_nopush才起作用,它和指令tcp_nodelay是互斥的。tcp_cork是linux下tcp/ip传输的一个标准了,这个标准的大概的意思是,一般情况下,在tcp交互的过程中,当应用程序接收到数据包后马上传送出去,不等待,而tcp_cork选项是数据包不会马上传送出去,等到数据包最大时,一次性的传输出去,这样有助于解决网络堵塞,已经是默认了。
也就是说tcp_nopush = on 会设置调用tcp_cork方法,这个也是默认的,结果就是数据包不会马上传送出去,等到数据包最大时,一次性的传输出去,这样有助于解决网络堵塞。
以快递投递举例说明一下(以下是我的理解,也许是不正确的),当快递东西时,快递员收到一个包裹,马上投递,这样保证了即时性,但是会耗费大量的人力物力,在网络上表现就是会引起网络堵塞,而当快递收到一个包裹,把包裹放到集散地,等一定数量后统一投递,这样就是tcp_cork的选项干的事情,这样的话,会最大化的利用网络资源,虽然有一点点延迟。
对于nginx配置文件中的tcp_nopush,默认就是tcp_nopush,不需要特别指定,这个选项对于www,ftp等大文件很有帮助
 
tcp_nodelay
        TCP_NODELAY和TCP_CORK基本上控制了包的“Nagle化”,Nagle化在这里的含义是采用Nagle算法把较小的包组装为更大的帧。 John Nagle是Nagle算法的发明人,后者就是用他的名字来命名的,他在1984年首次用这种方法来尝试解决福特汽车公司的网络拥塞问题(欲了解详情请参看IETF RFC 896)。他解决的问题就是所谓的silly window syndrome,中文称“愚蠢窗口症候群”,具体含义是,因为普遍终端应用程序每产生一次击键操作就会发送一个包,而典型情况下一个包会拥有一个字节的数据载荷以及40个字节长的包头,于是产生4000%的过载,很轻易地就能令网络发生拥塞,。 Nagle化后来成了一种标准并且立即在因特网上得以实现。它现在已经成为缺省配置了,但在我们看来,有些场合下把这一选项关掉也是合乎需要的。
       现在让我们假设某个应用程序发出了一个请求,希望发送小块数据。我们可以选择立即发送数据或者等待产生更多的数据然后再一次发送两种策略。如果我们马上发送数据,那么交互性的以及客户/服务器型的应用程序将极大地受益。如果请求立即发出那么响应时间也会快一些。以上操作可以通过设置套接字的TCP_NODELAY = on 选项来完成,这样就禁用了Nagle 算法。
       另外一种情况则需要我们等到数据量达到最大时才通过网络一次发送全部数据,这种数据传输方式有益于大量数据的通信性能,典型的应用就是文件服务器。应用 Nagle算法在这种情况下就会产生问题。但是,如果你正在发送大量数据,你可以设置TCP_CORK选项禁用Nagle化,其方式正好同 TCP_NODELAY相反(TCP_CORK和 TCP_NODELAY是互相排斥的)。

server_tokens  并不会让nginx执行的速度更快,但它可以关闭在错误页面中的nginx版本数字,这样对于安全性是有好处的。

sendfile 可以让sendfile()发挥作用。sendfile()可以在磁盘和TCP socket之间互相拷贝数据(或任意两个文件描述符)。Pre-sendfile是传送数据之前在用户空间申请数据缓冲区。之后用read()将数据从文件拷贝到这个缓冲区,write()将缓冲区数据写入网络。sendfile()是立即将数据从磁盘读到OS缓存。因为这种拷贝是在内核完成的,sendfile()要比组合read()和write()以及打开关闭丢弃缓冲更加有效(更多有关于sendfile)。

tcp_nopush 告诉nginx在一个数据包里发送所有头文件,而不一个接一个的发送。

tcp_nodelay 告诉nginx不要缓存数据,而是一段一段的发送--当需要及时发送数据时,就应该给应用设置这个属性,这样发送一小块数据信息时就不能立即得到返回值。

    http {
    server_tokens off;
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    ...
    }
        reset_timedout_connection on;  reset_timeout_connection 告诉nginx关闭不响应的客户端连接。这将会释放那个客户端所占有的内存空间。

        send_timeout 60;   send_timeout 指定客户端的响应超时时间。这个设置不会用于整个转发器,而是在两次客户端读取操作之间。如果在这段时间内,客户端没有读取任何数据,nginx就会关闭连接。
        open_file_cache max=655350 inactive=60s;open_file_cache 打开缓存的同时也指定了缓存最大数目,以及缓存的时间。我们可以设置一个相对高的最大时间,这样我们可以在它们不活动超过20秒后清除掉。
        open_file_cache_valid 90s;
        open_file_cache_min_uses 2;
        open_file_cache_errors on;
open_file_cache_valid 在open_file_cache中指定检测正确信息的间隔时间。

open_file_cache_min_uses 定义了open_file_cache中指令参数不活动时间期间里最小的文件数。

open_file_cache_errors 指定了当搜索一个文件时是否缓存错误信息,也包括再次给配置中添加文件。我们也包括了服务器模块,这些是在不同文件中定义的。如果你的服务器模块不在这些位置,你就得修改这一行来指定正确的位置。

        keepalive_timeout 60;keepalive_timeout  给客户端分配keep-alive链接超时时间。服务器将在这个超时时间过后关闭链接。我们将它设置低些可以让ngnix持续工作的时间更长。
        keepalive_requests 1000000;  该指令用于设置Nginx服务器能够保持活跃的连接数。默认100

        output_buffers 4 16k;

ngx_http_copy_filter_module

ngx_http_copy_filter_module是响应体过滤链(body filter)中非常重要的一个模块,这个filter模块主要是来将一些需要复制的buf(可能在文件中,也可能在内存中)重新复制一份交给后面的filter模块处理。先来看它的初始化函数:

[cpp] view plaincopy

    static ngx_int_t  
    ngx_http_copy_filter_init(ngx_conf_t *cf)  
    {  
        ngx_http_next_body_filter = ngx_http_top_body_filter;  
        ngx_http_top_body_filter = ngx_http_copy_filter;  
      
        return NGX_OK;  
    }  


可以看到,它只注册了body filter,而没有注册header filter,也就是说只有body filter链中才有这个模块。

该模块有一个命令,命令名为output_buffers,用来配置可用的buffer数和buffer大小,它的值保存在copy filter的loc conf的bufs字段,默认数量为1,大小为32768字节。

    output_buffers :这条指令虽然nginx有关的配置中出现,但是在nginx指令的索引中没有找到,而且查阅不少配置,发现更多是读取大文件时,关闭sendfile,调整此值,提高服务器的吞吐率。

        types_hash_max_size 2048;  types_hash_max_size影响散列表的冲突率。types_hash_max_size越大,就会消耗更多的内存,但散列key的冲突率会降低,检索速度就更快。types_hash_max_size越小,消耗的内存就越小,但散列key的冲突率可能上升。默认1024.
        server_tokens off;   server_tokens  并不会让nginx执行的速度更快,但它可以关闭在错误页面中的nginx版本数字,这样对于安全性是有好处的。

        fastcgi_intercept_errors on;
fastcgi_intercept_errors

语法:fastcgi_intercept_errors on|off
默认值:fastcgi_intercept_errors off
使用字段:http, server, location
这个指令指定是否传递4xx和5xx错误信息到客户端,或者允许nginx使用error_page处理错误信息。
你必须明确的在error_page中指定处理方法使这个参数有效,正如Igor所说“如果没有适当的处理方法,nginx不会拦截一个错误,这个错误不会显示自己的默认页面,这里允许通过某些方法拦截错误。
#FastCGI相关参数是为了改善网站的性能:减少资源占用,提高访问速度。下面参数看字面意思都能理解。
        fastcgi_connect_timeout 300;   指定连接到后端FastCGI的超时时间。
        fastcgi_send_timeout 300;
        fastcgi_read_timeout 300;
fastcgi_read_timeout是指fastcgi进程向nginx进程发送response的整个过程的超时时间
fastcgi_send_timeout是指nginx进程向fastcgi进程发送request的整个过程的超时时间
 
这两个选项默认都是秒(s),可以手动指定为分钟(m),小时(h)等
        fastcgi_buffer_size 64k;
        fastcgi_buffers 4 64k;

fastcgi_buffer_size 16k;

  指定读取FastCGI应答第一部分需要用多大的缓冲区,这里可以设置为fastcgi_buffers指令指定的缓冲区大小,上面的指令指定它将使用1个16k的缓冲区去读取应答的第一部分,即应答头,其实这个应答头一般情况下都很小(不会超过1k),但是你如果在fastcgi_buffers指令中指定了缓冲区的大小,那么它也会分配一个fastcgi_buffers指定的缓冲区大小去缓存。

fastcgi_buffers 16 16k;

  指定本地需要用多少和多大的缓冲区来缓冲FastCGI的应答,如上所示,如果一个php脚本所产生的页面大小为256k,则会为其分配16个16k的缓冲区来缓存,如果大于256k,增大于256k的部分会缓存到fastcgi_temp指定的路径中,当然这对服务器负载来说是不明智的方案,因为内存中处理数据速度要快于硬盘,通常这个值的设置应该选择一个你的站点中的php脚本所产生的页面大小的中间值,比如你的站点大部分脚本所产生的页面大小为256k就可以把这个值设置为16 16k,或者4 64k 或者64 4k,但很显然,后两种并不是好的设置方法,因为如果产生的页面只有32k,如果用4 64k它会分配1个64k的缓冲区去缓存,而如果使用64 4k它会分配8个4k的缓冲区去缓存,而如果使用16 16k则它会分配2个16k去缓存页面,这样看起来似乎更加合理。
        fastcgi_busy_buffers_size 128k;  fastcgi_busy_buffers_size的默认值是fastcgi_buffers的两倍。它是在fastcgi_buffers不够用的时候来扩充他的。
fastcgi_temp_file_write_size 128k;
fastcgi_busy_buffers_size 32k;

  这个指令我也不知道是做什么用,只知道默认值是fastcgi_buffers的两倍。

fastcgi_temp_file_write_size 32k;

  在写入fastcgi_temp_path时将用多大的数据块,默认值是fastcgi_buffers的两倍。

fastcgi_cache TEST

  开启FastCGI缓存并且为其制定一个名称。个人感觉开启缓存非常有用,可以有效降低CPU负载,并且防止502错误。但是这个缓存会引起很多问题,因为它缓存的是动态页面。具体使用还需根据自己的需求。

fastcgi_cache_valid 200 302 1h;
fastcgi_cache_valid 301 1d;
fastcgi_cache_valid any 1m;

  为指定的应答代码指定缓存时间,如上例中将200,302应答缓存一小时,301应答缓存1天,其他为1分钟。

fastcgi_cache_min_uses 1;

  缓存在fastcgi_cache_path指令inactive参数值时间内的最少使用次数,如上例,如果在5分钟内某文件1次也没有被使用,那么这个文件将被移除。

fastcgi_cache_use_stale error timeout invalid_header http_500;

  不知道这个参数的作用,猜想应该是让nginx知道哪些类型的缓存是没用的。 以上为nginx中FastCGI相关参数,另外,FastCGI自身也有一些配置需要进行优化,如果你使用php-fpm来管理FastCGI,可以修改配置文件中的以下值:

<value name="max_children">60</value>

  同时处理的并发请求数,即它将开启最多60个子线程来处理并发连接。

<value name="rlimit_files">102400</value>

  最多打开文件数。

<value name="max_requests">204800</value>

  每个进程在重置之前能够执行的最多请求数。

#gzip模块设置
gzip on; #开启gzip压缩输出
gzip_min_length 1k; #最小压缩文件大小
gzip_buffers 4 16k; #压缩缓冲区
gzip_http_version 1.0; #压缩版本(默认1.1,前端如果是squid2.5请使用1.0)
gzip_comp_level 2; #压缩等级
gzip_types text/plain application/x-javascript text/css application/xml;
#压缩类型,默认就已经包含text/html,所以下面就不用再写了,写上去也不会有问题,但是会有一个warn。
gzip_vary on;
#limit_zone crawler $binary_remote_addr 10m; #开启限制IP连接数的时候需要使用

upstream blog.ha97.com {
#upstream的负载均衡,weight是权重,可以根据机器配置定义权重。weigth参数表示权值,权值越高被分配到的几率越大。
server 192.168.80.121:80 weight=3;
server 192.168.80.122:80 weight=2;
server 192.168.80.123:80 weight=3;
}

一、什么是 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服务器专一地处 理静态请求或者将动态脚本服务器的结果返回给客户端,这在很大程度上提高了整个应用系统的性能。
二、Nginx+FastCGI运行原理
Nginx不支持对外部程序的直接调用或者解析,所有的外部程序(包括PHP)必须通过FastCGI接口来调用。FastCGI接口 在Linux下是socket,(这个socket可以是文件socket,也可以是ip socket)。为了调用CGI程序,还需要一个FastCGI的wrapper(wrapper可以理解为用于启动另一个程序的程序),这个 wrapper绑定在某个固定socket上,如端口或者文件socket。当Nginx将CGI请求发送给这个socket的时候,通过FastCGI 接口,wrapper接纳到请求,然后派生出一个新的线程,这个线程调用解释器或者外部程序处理脚本并读取返回数据;接着,wrapper再将返回的数据 通过FastCGI接口,沿着固定的socket传递给Nginx;最后,Nginx将返回的数据发送给客户端,这就是Nginx+FastCGI的整个 运作过程。详细的过程,如图1所示。
 
 
 
图1 Nginx+FastCGI运行原理


nginx lua使用注意事项(开发过程中会不断更新) 
2013-10-23 11:18:15|  分类: Lua |  标签:nginx_lua  lua   |举报 |字号 订阅
使用nginx lua已经两三个月了,项目接开发完毕了,这几天准备上线并且跟高德地图对接。回顾下来lua在项目中占得必中还是比较大的,跟PHP的占比差不多持平了,因此在开发中遇到一些问题备忘一下
1:content_by_lua中代码容量有限制,一般不要写太多代码,正常编写代码一般在100行左右(具体容量没有细心测哈哈,在4kb左右),如果超出了则重启nginx的时候会报too long parameters
2:如果引入lua脚本文件也得控制一下lua脚本中函数里面代码的容量,不要太多呵呵
3:编写lua代码时一定得健壮,不然nginx虽然可以重启但是经常会导致500错误,比如参数的判断,使用一些未定义的变量(当然lua中是可以的,但是现在是在nginx环境中,情况有些不一样)
4:nginx_lua中不支持使用"..."的不限制参数模式的函数参数
5:content_by_lua中的代码一定要注意单引号或者双引号,引号和content_by_lua之间要有空格
6:在content_by_lua中如果使用正则(string.match,string.gmatch)的时候如果content_by_lua后面用如果单引号引起来lua代码的话,正则里面单引号要用"\"进行转移而不是"%"转义符
以上描述可能绕口,直接贴代码
  content_by_lua '
 local res = ngx.location.capture("/pass_get",{
...
local ids = string.gmatch( h,"show_surveil_detail%S\'(%d+)\',\'(%d+)\',\'0\'%S" );
';
7:在nginx_lua中nil的变量跟数字相加是不允许的,nginx会报500错误的.
8:经常在写lua脚本的时候有时nginx的reload不起作用,导致新写的nginx配置不生效,可以在reload之前nginx -t检查一下看那里报错
9:在使用共享内存api的时候一定要注意如:使用lua_shared_dict、ngx.shared.DICT的时候最好不要使用get_keys,否则指不定那次获取比较多的数据的时候共享内存被锁定,严重时可能导致nginx阻塞
10:在ngx_lua中数字类型跟字符类型的数字进行运算时会报错的,必须将两者都统一成数字类型的,如
local a=123;local b="333";
a+b(错误)
a+(b+0)这样就可以了
字符类型的数字加上0可以转换成数字类型的


default_type 设置文件使用的默认的MIME-type

什么是 MIME Type?
一、
首先,我们要了解浏览器是如何处理内容的。在浏览器中显示的内容有 HTML、有 XML、有 GIF、还有 Flash ……那么,浏览器是如何区分它们,决定什么内容用什么形式来显示呢?答案是 MIME Type,也就是该资源的媒体类型。

媒体类型通常是通过 HTTP 协议,由 Web 服务器告知浏览器的,更准确地说,是通过 Content-Type 来表示的,例如:

Content-Type: text/HTML


表 示内容是 text/HTML 类型,也就是超文本文件。为什么是“text/HTML”而不是“HTML/text”或者别的什么?MIME Type 不是个人指定的,是经过 ietf 组织协商,以 RFC 的形式作为建议的标准发布在网上的,大多数的 Web 服务器和用户代理都会支持这个规范 (顺便说一句,Email 附件的类型也是通过 MIME Type 指定的)。

通常只有一些在互联网上获得广泛应用的格式才会获得一个 MIME Type,如果是某个客户端自己定义的格式,一般只能以 application/x- 开头。

XHTML 正是一个获得广泛应用的格式,因此,在 RFC 3236 中,说明了 XHTML 格式文件的 MIME Type 应该是 application/xHTML+XML。

当 然,处理本地的文件,在没有人告诉浏览器某个文件的 MIME Type 的情况下,浏览器也会做一些默认的处理,这可能和你在操作系统中给文件配置的 MIME Type 有关。比如在 Windows 下,打开注册表的“HKEY_LOCAL_MACHINESOFTWAREClassesMIMEDatabaseContent Type”主键,你可以看到所有 MIME Type 的配置信息。
 
二、
在把输出结果传送到浏览器上的时候,浏览器必须启动适当的应用程序来处理这个输出文档。这可以通过多种类型MIME(多功能网际邮件扩充协议)来完成。在HTTP中,MIME类型被定义在Content-Type header中。

例 如,架设你要传送一个Microsoft Excel文件到客户端。那么这时的MIME类型就是“application/vnd.ms-excel”。在大多数实际情况中,这个文件然后将传送给 Execl来处理(假设我们设定Execl为处理特殊MIME类型的应用程序)。在ASP中,设定MIME类型的方法是通过Response对象的 ContentType属性。


多媒体文件格式MIME

最早的HTTP协议中,并没有附加的数据类型信息,所有传送的数据都被客户程序解释为超文本标记语言HTML 文档,而为了支持多媒体数据类型,HTTP协议中就使用了附加在文档之前的MIME数据类型信息来标识数据类型。

MIME意为多目Internet邮件扩展,它设计的最初目的是为了在发送电子邮件时附加多媒体数据,让邮件客户程序能根据其类型进行处理。然而当它被HTTP协议支持之后,它的意义就更为显著了。它使得HTTP传输的不仅是普通的文本,而变得丰富多彩。

每个MIME类型由两部分组成,前面是数据的大类别,例如声音audio、图象image等,后面定义具体的种类。

常见的MIME类型

超文本标记语言文本 .html,.html text/html
普通文本 .txt text/plain
RTF文本 .rtf application/rtf
GIF图形 .gif image/gif
JPEG图形 .ipeg,.jpg image/jpeg
au声音文件 .au audio/basic
MIDI音乐文件 mid,.midi audio/midi,audio/x-midi
RealAudio音乐文件 .ra, .ram audio/x-pn-realaudio
MPEG文件 .mpg,.mpeg video/mpeg
AVI文件 .avi video/x-msvideo
GZIP文件 .gz application/x-gzip
TAR文件 .tar application/x-tar

Internet 中有一个专门组织IANA来确认标准的MIME类型,但Internet发展的太快,很多应用程序等不及IANA来确认他们使用的MIME类型为标准类 型。因此他们使用在类别中以x-开头的方法标识这个类别还没有成为标准,例如:x-gzip,x-tar等。事实上这些类型运用的很广泛,已经成为了事实 标准。只要客户机和服务器共同承认这个MIME类型,即使它是不标准的类型也没有关系,客户程序就能根据MIME类型,采用具体的处理手段来处理数据。而 Web服务器和浏览器(包括操作系统)中,缺省都设置了标准的和常见的MIME类型,只有对于不常见的 MIME类型,才需要同时设置服务器和客户浏览器,以进行识别。

由于MIME类型与文档的后缀相关,因此服务器使用文档的后缀来区分不同 文件的MIME类型,服务器中必须定义文档后缀和MIME类型之间的对应关系。而客户程序从服务器上接收数据的时候,它只是从服务器接受数据流,并不了解 文档的名字,因此服务器必须使用附加信息来告诉客户程序数据的MIME类型。服务器在发送真正的数据之前,就要先发送标志数据的MIME类型的信息,这个 信息使用Content-type关键字进行定义,例如对于HTML文档,服务器将首先发送以下两行MIME标识信息,这个标识并不是真正的数据文件的一 部分。

Content-type: text/html

注意,第二行为一个空行,这是必须的,使用这个空行的目的是将MIME信息与真正的数据内容分隔开。

MIME (Multipurpose Internet Mail Extensions) 是描述消息内容类型的因特网标准。
MIME 消息能包含文本、图像、音频、视频以及其他应用程序专用的数据。
官方的 MIME 信息是由 Internet Engineering Task Force (IETF) 在下面的文档中提供的:
RFC-822 Standard for ARPA Internet text messages
RFC-2045 MIME Part 1: Format of Internet Message Bodies
RFC-2046 MIME Part 2: Media Types
RFC-2047 MIME Part 3: Header Extensions for Non-ASCII Text
RFC-2048 MIME Part 4: Registration Procedures
RFC-2049 MIME Part 5: Conformance Criteria and Examples
不同的应用程序支持不同的 MIME 类型。

最近在看nginx lua,涉及到一些配置,这里总结一下:

init_worker_by_lua
syntax: init_worker_by_lua <lua-script-str>
context: http
phase: starting-worker
Runs the specified Lua code upon every Nginx worker process's startup when the master process is enabled. When the master process is disabled, this hook will just run after init_by_lua*.
This hook is often used to create per-worker reoccurring timers (via the ngx.timer.at Lua API), either for backend healthcheck or other timed routine work. Below is an example,
    init_worker_by_lua '
        local delay = 3  -- in seconds
        local new_timer = ngx.timer.at
        local log = ngx.log
        local ERR = ngx.ERR
        local check
 
        check = function(premature)
            if not premature then
                -- do the health check or other routine work
                local ok, err = new_timer(delay, check)
                if not ok then
                    log(ERR, "failed to create timer: ", err)
                    return
                end
            end
        end
 
        local ok, err = new_timer(delay, check)
        if not ok then
            log(ERR, "failed to create timer: ", err)
            return
        end
    ';
This directive was first introduced in the v0.9.5 release.
init_worker_by_lua_file
syntax: init_worker_by_lua_file <lua-file-path>
context: http
phase: starting-worker
Similar to init_worker_by_lua, but accepts the file path to a Lua source file or Lua bytecode file.
This directive was first introduced in the v0.9.5 release.
lua_need_request_body
syntax: lua_need_request_body <on|off>
default: off
context: main | server | location
phase: depends on usage
Determines whether to force the request body data to be read before running rewrite/access/access_by_lua* or not. The Nginx core does not read the client request body by default and if request body data is required, then this directive should be turned on or the ngx.req.read_body function should be called within the Lua code.
To read the request body data within the $request_body variable, client_body_buffer_size must have the same value as client_max_body_size. Because when the content length exceeds client_body_buffer_size but less than client_max_body_size, Nginx will buffer the data into a temporary file on the disk, which will lead to empty value in the $request_body variable.
If the current location includes rewrite_by_lua or rewrite_by_lua_file directives, then the request body will be read just before the rewrite_by_lua or rewrite_by_lua_file code is run (and also at the rewrite phase). Similarly, if only content_by_lua is specified, the request body will not be read until the content handler's Lua code is about to run (i.e., the request body will be read during the content phase).
It is recommended however, to use the ngx.req.read_body and ngx.req.discard_body functions for finer control over the request body reading process instead.
This also applies to access_by_lua and access_by_lua_file.
rewrite_by_lua_file
syntax: rewrite_by_lua_file <path-to-lua-script-file>
context: http, server, location, location if
phase: rewrite tail
Equivalent to rewrite_by_lua, except that the file specified by <path-to-lua-script-file> contains the Lua code, or, as from the v0.5.0rc32 release, the Lua/LuaJIT bytecode to be executed.
Nginx variables can be used in the <path-to-lua-script-file> string to provide flexibility. This however carries some risks and is not ordinarily recommended.
When a relative path like foo/bar.lua is given, they will be turned into the absolute path relative to the server prefix path determined by the -p PATH command-line option while starting the Nginx server.
When the Lua code cache is turned on (by default), the user code is loaded once at the first request and cached and the Nginx config must be reloaded each time the Lua source file is modified. The Lua code cache can be temporarily disabled during development by switching lua_code_cache off in nginx.conf to avoid reloading Nginx.
The rewrite_by_lua_file code will always run at the end of the rewrite request-processing phase unless rewrite_by_lua_no_postpone is turned on.

log_by_lua_file
syntax: log_by_lua_file <path-to-lua-script-file>
context: http, server, location, location if
phase: log
Equivalent to log_by_lua, except that the file specified by <path-to-lua-script-file> contains the Lua code, or, as from the v0.5.0rc32 release, the Lua/LuaJIT bytecode to be executed.
When a relative path like foo/bar.lua is given, they will be turned into the absolute path relative to the server prefix path determined by the -p PATH command-line option while starting the Nginx server.
This directive was first introduced in the v0.5.0rc31 release.





研究在lua程序中读不到body的问题:
 lua_need_request_body

syntax: lua_need_request_body <on|off>

default: off

context: main | server | location

phase: depends on usage

Determines whether to force the request body data to be read before running rewrite/access/access_by_lua* or not. The Nginx core does not read the client request body by default and if request body data is required, then this directive should be turned on or the ngx.req.read_body function should be called within the Lua code.

To read the request body data within the $request_body variable, client_body_buffer_size must have the same value as client_max_body_size. Because when the content length exceeds client_body_buffer_size but less than client_max_body_size, Nginx will buffer the data into a temporary file on the disk, which will lead to empty value in the $request_body variable.

If the current location includes rewrite_by_lua or rewrite_by_lua_file directives, then the request body will be read just before the rewrite_by_lua or rewrite_by_lua_file code is run (and also at the rewrite phase). Similarly, if only content_by_lua is specified, the request body will not be read until the content handler's Lua code is about to run (i.e., the request body will be read during the content phase).

It is recommended however, to use the ngx.req.read_body and ngx.req.discard_body functions for finer control over the request body reading process instead.

This also applies to access_by_lua and access_by_lua_file.

 ngx.req.get_body_data

syntax: data = ngx.req.get_body_data()

context: rewrite_by_lua*, access_by_lua*, content_by_lua*

Retrieves in-memory request body data. It returns a Lua string rather than a Lua table holding all the parsed query arguments. Use the ngx.req.get_post_args function instead if a Lua table is required.

This function returns nil if

    the request body has not been read,
    the request body has been read into disk temporary files,
    or the request body has zero size.

If the request body has not been read yet, call ngx.req.read_body first (or turned on lua_need_request_body to force this module to read the request body. This is not recommended however).

If the request body has been read into disk files, try calling the ngx.req.get_body_file function instead.

To force in-memory request bodies, try setting client_body_buffer_size to the same size value in client_max_body_size.

Note that calling this function instead of using ngx.var.request_body or ngx.var.echo_request_body is more efficient because it can save one dynamic memory allocation and one data copy.

This function was first introduced in the v0.3.1rc17 release.

See also ngx.req.get_body_file.

 ngx.req.read_body

syntax: ngx.req.read_body()

context: rewrite_by_lua*, access_by_lua*, content_by_lua*

Reads the client request body synchronously without blocking the Nginx event loop.

    ngx.req.read_body()
    local args = ngx.req.get_post_args()

If the request body is already read previously by turning on lua_need_request_body or by using other modules, then this function does not run and returns immediately.

If the request body has already been explicitly discarded, either by the ngx.req.discard_body function or other modules, this function does not run and returns immediately.

In case of errors, such as connection errors while reading the data, this method will throw out a Lua exception or terminate the current request with a 500 status code immediately.

The request body data read using this function can be retrieved later via ngx.req.get_body_data or, alternatively, the temporary file name for the body data cached to disk using ngx.req.get_body_file. This depends on

    whether the current request body is already larger than the client_body_buffer_size,
    and whether client_body_in_file_only has been switched on.

 ngx.req.get_post_args

syntax: args, err = ngx.req.get_post_args(max_args?)

context: rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua, log_by_lua*

Returns a Lua table holding all the current request POST query arguments (of the MIME type application/x-www-form-urlencoded). Call ngx.req.read_body to read the request body first or turn on the lua_need_request_body directive to avoid errors.

    location = /test {
        content_by_lua '
            ngx.req.read_body()
            local args, err = ngx.req.get_post_args()
            if not args then
                ngx.say("failed to get post args: ", err)
                return
            end
            for key, val in pairs(args) do
                if type(val) == "table" then
                    ngx.say(key, ": ", table.concat(val, ", "))
                else
                    ngx.say(key, ": ", val)
                end
            end
        ';
    }



根据nginx lua模块的官方文档,在lua中获取http的body部分有以下三种方案:
1.ngx.req.get_body_data 函数 +   lua_need_request_body 设置为on
2.ngx.req.read_body()函数 + ngx.req.get_post_args()函数
3.ngx.req.get_body_file 函数 +  client_body_in_file_only设置为on : 读到文件中
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值