1.为什么选择Nginx
Nginx是当前最流行的Web服务器之一,与Apache相比,Nginx在高并发情况下具有巨大的性能优势。
Nginx具有高扩展性,高可靠性,低内存消耗等特点,并且Nginx拥有无数官方功能模块,第三方功能模块,这些功能模块可以叠加以实现更加强大/复杂的功能。
选择Nginx的核心理由是它能在支持高并发请求的同时保持高效的服务。
2.Nginx的安装与运行Nginx官方下载地址:http://nginx.org/en/download.html
解压:$ tar -zxvf nginx-1.4.4.tar.gz
进入nginx目录,执行如下命令:
$ ./configure
$ make
$ make install
启动Nginx:$ /usr/local/nginx/sbin/nginx
检测Nginx是否成功运行:打开http://localhost/,看是否有Nginx欢迎界面。
停止Nginx服务:/usr/local/nginx/sbin/nginx -s stop
3.开发一个简单的HTTP模块-Hello World
Nginx提供了一种简单的方式将第三方模块编译到Nginx中。
首先把源代码文件全部放到一个目录中:
my_test_module/
├── config
└── ngx_http_mytest_module.c
configure用于通知Nginx如何编译本模块,而且必须命名为configure。configure文件其实是一个可执行shell脚本,需要定义以下3个变量:
ngx_addon_name:仅在configure执行时使用,一般设置为模块名称。
HTTP_MODULES:保存所有的HTTP模块名称,每个HTTP模块间由空格相连。
NGX_ADDON_SRCS:用于制定新增模块的源代码,多个待编译的源代码间以空格相连。
这里configure内容如下:
ngx_addon_name=ngx_http_mytest_module HTTP_MODULES="$HTTP_MODULES ngx_http_mytest_module" NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_mytest_module.c"
这里ngx_http_mytest_module.c内容如下:
#include <ngx_config.h> #include <ngx_core.h> #include <ngx_http.h> static char * ngx_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r); static ngx_command_t ngx_http_mytest_commands[] = { { ngx_string("mytest"), NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_NOARGS, ngx_http_mytest, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }, ngx_null_command }; static ngx_http_module_t ngx_http_mytest_module_ctx = { NULL, /* preconfiguration */ NULL, /* postconfiguration */ NULL, /* create main configuration */ NULL, /* init main configuration */ NULL, /* create server configuration */ NULL, /* merge server configuration */ NULL, /* create location configuration */ NULL /* merge location configuration */ }; ngx_module_t ngx_http_mytest_module = { NGX_MODULE_V1, &ngx_http_mytest_module_ctx, /* module context */ ngx_http_mytest_commands, /* module directives */ NGX_HTTP_MODULE, /* module type */ NULL, /* init master */ NULL, /* init module */ NULL, /* init process */ NULL, /* init thread */ NULL, /* exit thread */ NULL, /* exit process */ NULL, /* exit master */ NGX_MODULE_V1_PADDING }; static char * ngx_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_core_loc_conf_t *clcf; //首先找到mytest配置项所属的配置块,clcf貌似是location块内的数据 //结构,其实不然,它可以是main、srv或者loc级别配置项,也就是说在每个 //http{}和server{}内也都有一个ngx_http_core_loc_conf_t结构体 clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); //http框架在处理用户请求进行到NGX_HTTP_CONTENT_PHASE阶段时,如果 //请求的主机域名、URI与mytest配置项所在的配置块相匹配,就将调用我们 //实现的ngx_http_mytest_handler方法处理这个请求 clcf->handler = ngx_http_mytest_handler; return NGX_CONF_OK; } static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r) { //必须是GET或者HEAD方法,否则返回405 Not Allowed if (!(r->method & (NGX_HTTP_GET | NGX_HTTP_HEAD))) { return NGX_HTTP_NOT_ALLOWED; } //丢弃请求中的包体 ngx_int_t rc = ngx_http_discard_request_body(r); if (rc != NGX_OK) { return rc; } //设置返回的Content-Type。注意,ngx_str_t有一个很方便的初始化宏 //ngx_string,它可以把ngx_str_t的data和len成员都设置好 ngx_str_t type = ngx_string("text/plain"); //返回的包体内容 ngx_str_t response = ngx_string("Hello World!"); //设置返回状态码 r->headers_out.status = NGX_HTTP_OK; //响应包是有包体内容的,所以需要设置Content-Length长度 r->headers_out.content_length_n = response.len; //设置Content-Type r->headers_out.content_type = type; //发送http头部 rc = ngx_http_send_header(r); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { return rc; } //构造ngx_buf_t结构准备发送包体 ngx_buf_t *b; b = ngx_create_temp_buf(r->pool, response.len); if (b == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } //将Hello World拷贝到ngx_buf_t指向的内存中 ngx_memcpy(b->pos, response.data, response.len); //注意,一定要设置好last指针 b->last = b->pos + response.len; //声明这是最后一块缓冲区 b->last_buf = 1; //构造发送时的ngx_chain_t结构体 ngx_chain_t out; //赋值ngx_buf_t out.buf = b; //设置next为NULL out.next = NULL; //最后一步发送包体,http框架会调用ngx_http_finalize_request方法 //结束请求 return ngx_http_output_filter(r, &out); }
修改配置文件:$ vim //usr/local/nginx/conf/nginx.conf,增加下面的配置:
http { include mime.types; default_type application/octet-stream; server { listen 80; server_name localhost; location / { root html; index index.php index.html index.htm; } location /test { mytest; } } }
编译安装模块:
./configure --prefix=/usr/local/nginx(指定安装部署后的根目录) --add-module=/home/luhaiyang/ngxdev/ngx_http_mytest(新模块存放目录) make sudo make install
测试模块:$ curl -i http:
//localhost/test
HTTP/1.1 200 OK Server: nginx/1.4.4 Date: Mon, 02 Dec 2013 09:35:50 GMT Content-Type: text/plain Content-Length: 12 Connection: keep-alive Hello World!
阅读《深入理解Nginx》,简单知道了Nginx的基本概念,并参照书籍配置了一个静态Web服务器。在我的学习习惯里,我喜欢先赶紧实现一个简单的例子,这样对于新事物有一个大概的总体的了解,知道它到底是什么,能干什么,然后再继续深入挖掘理解。
最近与基友ZK讨论学习习惯的问题,他这段之间在接受java培训,与我讲述了他的烦恼。他认为只有学习掌握好了当前内容才能继续下一阶段的学习,所以不能理解他的项目经理提前讲解后面的知识章节,然后我跟他分享了我的学习方法。我认为这是两种不同的学习习惯,只有适合自己的才是最好的。
参考资料:《深入理解Nginx》