Nginx 模块开发(学习笔记十八)

1. Nginx 介绍


     Nginx是俄罗斯人编写的十分轻量级的HTTP服务器,它的发音为“engine X”, 是一个高性能的HTTP和反向代理服务器,同时也是一个IMAP/POP3/SMTP 代理服务器.Nginx是由俄罗斯人 Igor Sysoev为俄罗斯访问量第二的 Rambler.ru站点开发的,从2004年开始它已经在该站点运行了七八年了。Igor Sysoev在建立的项目时,使用基于BSD许可。

英文主页:http://nginx.org.


     Nginx以事件驱动的方式编写,所以有非常好的性能,同时也是一个非常高效的反向代理、负载平衡。

     现在,Igor将源代码以类BSD许可证的形式发布。Nginx因为它的稳定性、丰富的模块库、灵活的配置和低系统资源的消耗而闻名.业界一致认为它是Apache+mod_proxy_balancer的轻量级代替者,不仅是因为响应静态页面的速度非常快,而且它的模块数量也非常非常丰富。对proxy 和 rewrite模块的支持很彻底,还支持mod_fcgi、ssl、vhosts ,适合用来做mongrel clusters的前端HTTP响应。

  nginx做为HTTP服务器,有以下几项基本特性:

处理静态文件,索引文件以及自动索引;打开文件描述符缓冲.

无缓存的反向代理加速,简单的负载均衡和容错.

FastCGI,简单的负载均衡和容错.

模块化的结构。包括gzipping, byte ranges, chunked responses,以及 SSI-filter等filter。如果由FastCGI或其它代理服务器处理单页中存在的多个SSI,则这项处理可以并行运行,而不需要相互等待。

支持SSL 和 TLSSNI.

  Nginx专为性能优化而开发,性能是其最重要的考量,实现上非常注重效率。它支持内核 epoll、kqueue 等高性能并发模型,能经受高负载的考验。

  Nginx具有很高的稳定性。其它HTTP服务器,当遇到访问的峰值,或者有人恶意发起慢速连接时,也很可能会导致服务器物理内存耗尽频繁交换,失去响 应,只能重启服务器。例如当前apache一旦上到200个以上进程,web响应速度就明显非常缓慢了。而Nginx采取了分阶段资源分配技术,使得它的 CPU与内存占用率非常低。nginx官方表示保持10,000个没有活动的连接,它只占2.5M内存,所以类似DOS这样的攻击对nginx来说基本上 是毫无用处的。就稳定性而言,nginx比lighthttpd更胜一筹。

  Nginx支持热部署。它的启动特别容易, 并且几乎可以做到7*24不间断运行,即使运行数个月也不需要重新启动。你还能够在不间断服务的情况下,对软件版本进行进行升级。

  Nginx采用master-slave模型,能够充分利用SMP的优势,且能够减少工作进程在磁盘I/O的阻塞延迟。当采用select()/poll()调用时,还可以限制每个进程的连接数。

  Nginx代码质量非常高,代码很规范,手法成熟, 模块扩展也很容易。特别值得一提的是强大的Upstream与Filter链。 Upstream为诸如reverse proxy,与其他服务器通信模块的编写奠定了很好的基础。而Filter链最酷的部分就是各个filter不必等待前一个filter执行完毕。它可以 把前一个filter的输出做为当前filter的输入,这有点像Unix的管线。这意味着,一个模块可以开始压缩从后端服务器发送过来的请求,且可以在 模块接收完后端服务器的整个请求之前把压缩流转向客户端。

  当然,nginx还很年轻,多多少少存在一些问题,比如:Nginx是俄罗斯人创建,目前文档方面还不是很完善.因为文档大多是俄语,所以文档方面这也是个障碍.尽管nignx的模块比较多,但它们还不够完善。对脚本的支持力度不够。

  这些问题,nginx的作者和社区都在努力解决,我们有理由相信nginx将继续以高速的增长率来分享轻量级HTTP服务器市场,会有一个更美好的未来。


2. 准备工作

去官方主页http://nginx.org/下载最新的Nginx源码包,这里给出目前最新的源码包的直接连接:http://nginx.org/download/nginx-1.0.0.tar.gz


[zieckey@freebsd7.2 ~]$ mkdir nginx

[zieckey@freebsd7.2 ~]$ cd nginx

[zieckey@freebsd7.2 ~/nginx]$ wget http://nginx.org/download/nginx-1.0.0.tar.gz

[zieckey@freebsd7.2 ~/nginx]$ tar zxf nginx-1.0.0.tar.gz

[zieckey@freebsd7.2 ~/nginx]$ mkdir module_dev_urlquery

[zieckey@freebsd7.2 ~/nginx]$ cd module_dev_urlquery/

[zieckey@freebsd7.2 module_dev_urlquery]$

这里我们下载了 nginx-1.0.0 的源码,然后准备开发一个 module_dev_urlquery 的module嵌入到nginx中

3. 准备好 module_dev_urlquery 模块的配置文件 

一个 nginx module 一般都以个文件夹的方式存在,module源文件和该module的配置文件都在这个文件下面。

这里我们的module叫 module_dev_urlquery,其路径为 /home/zieckey/nginx/module_dev_urlquery 。

其配置文件名为:config ,其路径为 /home/zieckey/nginx/module_dev_urlquery/config

[zieckey@freebsd7.2 module_dev_urlquery]$ vim config


然后输入以下内容:


[zieckey@freebsd7.2 module_dev_urlquery]$ vim config

ngx_addon_name=ngx_http_p2s_module

HTTP_MODULES="$HTTP_MODULES ngx_http_p2s_module"

NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_p2s_module.c"


这里三行内容稍稍解释下:

第一行是表示这个 nginx module 在程序中名字

第二行表示这是一个HTTP module,后面的名字与第一个行保存一致

第三是这个module的源文件路径,值得说明的是 $ngx_addon_dir 这个变量是 nginx 的内置脚本的内置变量,代表了这个 module 的文件夹的绝对路径,这里就是 /home/zieckey/nginx/module_dev_urlquery


4. 准备源代码文件 ngx_http_p2s_module.c 

这里的文件名和路径必须与上面 config 文件中的一致

[zieckey@freebsd7.2 module_dev_urlquery]$ vim ngx_http_p2s_module.c

输入以下源程序内容:


#include 

#include 

#include 

typedef struct {

unsigned long consume;

char* ini_buf;

size_t buflen;//the lenght of the ini_buf

} ngx_http_p2s_conf_t;

static char *ngx_http_p2s_urlquery_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

static void *ngx_http_p2s_create_conf(ngx_conf_t *cf);

static uint8_t* get_raw_http_body( ngx_http_request_t* r, size_t* body_len );

static ngx_command_t ngx_http_p2s_commands[] =

{/*{{{*/

{ ngx_string("p2s_urlquery"), //The command name, it MUST BE the same as nginx.conf location block's command

NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,

ngx_http_p2s_urlquery_set,

0,

0,

NULL },

ngx_null_command

};/*}}}*/

static ngx_http_module_t ngx_http_p2s_module_ctx =

{/*{{{*/

NULL, /* preconfiguration */

NULL, /* postconfiguration */

NULL, /* create main configuration */

NULL, /* init main configuration */

NULL, /* create server configuration */

NULL, /* merge server configuration */

ngx_http_p2s_create_conf, /* create location configration */

NULL /* merge location configration */

};/*}}}*/

ngx_module_t ngx_http_p2s_module =

{/*{{{*/

NGX_MODULE_V1,

&ngx_http_p2s_module_ctx, /* module context */

ngx_http_p2s_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

};/*}}}*/

/**

* @brief Get the HTTP body data from the ngx_http_request_t struct.

* @warning DONNOT release the return pointer.

* @param[in] ngx_http_request_t * r -

* The HTTP request of NGINX struct which holds the HTTP data.

* @param[out] size_t * body_len - The body data length will stored here.

* @return uint8_t* - A pointer to a memory where

* stored the HTTP body raw binary data.

* The memory is allocated from nginx memory pool,

* so the caller don't need to warry about the memory release work.

*/

static uint8_t* get_raw_http_body( ngx_http_request_t* r, size_t* body_len )

{/*{{{*/

printf( "%s\n", __PRETTY_FUNCTION__ );

ngx_chain_t* bufs = r->request_body->bufs;

*body_len = 0;

ngx_buf_t* buf = NULL;

uint8_t* data_buf = NULL;

size_t content_length = 0;

if ( r->headers_in.content_length == NULL )

{

return NULL;

}

// malloc space for data_buf

content_length = atoi( (char*)(r->headers_in.content_length->value.data) );

data_buf = ( uint8_t* )ngx_palloc( r->pool , content_length + 1 );

size_t buf_length = 0;

while ( bufs )

{

buf = bufs->buf;

bufs = bufs->next;

buf_length = buf->last - buf->pos ;

if( *body_len + buf_length > content_length )

{

memcpy( data_buf + *body_len, buf->pos, content_length - *body_len );

*body_len = content_length ;

break;

}

memcpy( data_buf + *body_len, buf->pos, buf->last - buf->pos );

*body_len += buf->last - buf->pos;

}

if ( *body_len )

{

data_buf[*body_len] = '\0';

}

return data_buf;

}/*}}}*/

/**

* Process the client request.

* The client post data has stored in r

*/

static void p2s_urlquery_process_handler(ngx_http_request_t *r)

{/*{{{*/

printf( "%s\n", __PRETTY_FUNCTION__ );

ngx_int_t rc = NGX_OK;

ngx_buf_t *b = NULL;

ngx_chain_t out;

ngx_http_p2s_conf_t *conf = NULL;

conf = (ngx_http_p2s_conf_t *)ngx_http_get_module_loc_conf(r, ngx_http_p2s_module);

if (conf == NULL)

{

ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

return ;

}

struct timeval tv;

gettimeofday(&tv, NULL);

size_t bodylen = 0;

uint8_t* contents = get_raw_http_body( r, &bodylen );

printf( "time=%f http body data len=%d:\n%s\n", (tv.tv_sec + tv.tv_usec/1000000.0f ), (int)bodylen, (char*)contents );

printf("----------------------http body data end-------------------\n");

/* Prepare for output, 128 is preserved for robust */

b = ngx_create_temp_buf( r->pool, 128 + conf->buflen );

if (b == NULL)

{

ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

return ;

}

out.buf = b;

out.next = NULL;

b->last = ngx_sprintf(b->pos, "%s", conf->ini_buf);

r->headers_out.status = NGX_HTTP_OK;

r->headers_out.content_length_n = b->last - b->pos;

r->headers_out.content_type.len = sizeof("text/plain") - 1;

r->headers_out.content_type.data = (u_char *) "text/plain";

b->last_buf = 1;/* there will be no more buffers in the request */

rc = ngx_http_send_header(r);

if (rc == NGX_ERROR || rc > NGX_OK || r->header_only)

{

ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);

return ;

}

ngx_http_output_filter(r, &out);

ngx_http_finalize_request(r, 0);

}/*}}}*/

/**

* Reading data handler

* After read all the data from client we set a process handler

*/

static ngx_int_t

ngx_http_p2s_urlquery_handler(ngx_http_request_t *r)

{/*{{{*/

printf( "%s\n", __PRETTY_FUNCTION__ );

ngx_int_t rc = NGX_DONE;

rc = ngx_http_read_client_request_body( r, p2s_urlquery_process_handler );

if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {

return rc;

}

return NGX_DONE;

}/*}}}*/

/**

* set the request reading data handler

*/

static char *

ngx_http_p2s_urlquery_set( ngx_conf_t *cf, ngx_command_t *cmd, void *conf )

{/*{{{*/

printf( "%s\n", __PRETTY_FUNCTION__ );

ngx_http_core_loc_conf_t *clcf;

clcf = (ngx_http_core_loc_conf_t *)ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);

clcf->handler = ngx_http_p2s_urlquery_handler;

return NGX_CONF_OK;

}/*}}}*/

static void *

ngx_http_p2s_create_conf(ngx_conf_t *cf)

{/*{{{*/

printf( "%s\n", __PRETTY_FUNCTION__ );

ngx_http_p2s_conf_t *conf;

conf = (ngx_http_p2s_conf_t *)ngx_pcalloc(cf->pool, sizeof(ngx_http_p2s_conf_t));

if (conf == NULL) {

return NGX_CONF_ERROR;

}

conf->consume = 0;

/* we open the nginx config file and send it back to client*/

FILE *fp = fopen( "../conf/nginx.conf", "r");

if( fp == NULL )

{

return NGX_CONF_ERROR;

}

fseek(fp, 0, SEEK_END);

long len = ftell( fp );

if ( len < 0 )

{

return NGX_CONF_ERROR;

}

conf->buflen = (size_t)(len + 1);

conf->ini_buf = (char *)ngx_palloc( cf->pool, len + 1 );

fseek(fp, 0, SEEK_SET);

fread(conf->ini_buf, 1, len, fp);

conf->ini_buf[len] = 0;

fclose(fp);

return conf;

}/*}}}*/


5. 编译运行

现在可以编译nginx和我们刚刚写好的模块了。


[zieckey@freebsd7.2 ~/nginx]$ cd

[zieckey@freebsd7.2 ~]$ cd nginx/

[zieckey@freebsd7.2 ~/nginx]$ mkdir bininstalled

[zieckey@freebsd7.2 ~/nginx]$ cd nginx-1.0.0

[zieckey@freebsd7.2 nginx-1.0.0]$ ./configure --add-module=/home/zieckey/nginx/module_dev_urlquery --prefix=/home/zieckey/nginx/bininstalled

./configure: error: the HTTP rewrite module requires the PCRE library.

You can either disable the module by using --without-http_rewrite_module

option, or install the PCRE library into the system, or build the PCRE library

statically from the source with nginx by using --with-pcre= option.

第一次错误,我们现在这个freebsd系统上没有pcre库,太悲催了,不过还好,这里给出了提示,说可以通过 --without-http_rewrite_module 来禁用使用pcre库的 HTTP rewrite 模块,我们试一试:

[zieckey@freebsd7.2 nginx-1.0.0]$ ./configure --add-module=/home/zieckey/nginx/module_dev_urlquery --prefix=/home/zieckey/nginx/bininstalled --without-http_rewrite_module

adding module in /home/zieckey/nginx/module_dev_urlquery

+ ngx_http_p2s_module was configured

重点看到上面几行信息,说明我们自己写的模块module_dev_urlquery已经被nginx接纳,生成makefile成功。

[zieckey@freebsd7.2 nginx-1.0.0]$ make

[zieckey@freebsd7.2 nginx-1.0.0]$ make install

[zieckey@freebsd7.2 nginx-1.0.0]$ cd /home/zieckey/nginx/bininstalled/

[zieckey@freebsd7.2 bininstalled]$ ls

conf html logs sbin

[zieckey@freebsd7.2 bininstalled]$ cd conf

[zieckey@freebsd7.2 conf]$ vim ngi

nginx.conf nginx.conf.default

[zieckey@freebsd7.2 conf]$ vim nginx.conf

默认的配置文件是下面(将‘#’开头的注释行删除之后)

worker_processes 1;

events {

worker_connections 1024;

}

http {

include mime.types;

default_type application/octet-stream;

sendfile on;

keepalive_timeout 65;

server {

listen 80;

server_name localhost;

location / {

root html;

index index.html index.htm;

}

}

}

我们在最上面添加如下选项:daemon off;  可以让程序不以daemon的方式运行,这样我们可以看到一些调试的打印信息。

另外,在 server 里面添加一个 URI :

location /urlquery {

p2s_urlquery;

}

最后,我们修改下http监听的端口号,从默认的80改为8088,因为有些时候,我们并没有权限在80端口上监听连接。

修改之后的配置文件全文如下:

daemon off;

worker_processes 1;

events {

worker_connections 1024;

}

http {

include mime.types;

default_type application/octet-stream;

sendfile on;

keepalive_timeout 65;

server {

listen 8088;

server_name localhost;

location / {

root html;

index index.html index.htm;

}

location /urlquery {

p2s_urlquery;

}

}

}

好了,到此为止,我们的所有工作就都准备好了,可以启动nginx

[zieckey@freebsd7.2 conf]$ cd /home/zieckey/nginx/bininstalled/sbin/

[zieckey@freebsd7.2 sbin]$ ./nginx

ngx_http_p2s_create_conf

ngx_http_p2s_create_conf

ngx_http_p2s_create_conf

ngx_http_p2s_create_conf

ngx_http_p2s_urlquery_set

新开一个终端,用curl来发起一个http post请求,post数据由-d参数指定:

[zieckey@freebsd7.2 ~]$ curl -d "user=zieckey&verifykey=123456" http://localhost:8088/urlquery

daemon off;

worker_processes 1;

events {

worker_connections 1024;

}

http {

include mime.types;

default_type application/octet-stream;

sendfile on;

keepalive_timeout 65;

server {

listen 8088;

server_name localhost;

location / {

root html;

index index.html index.htm;

}

location /urlquery {

p2s_urlquery;

}

}

}

[zieckey@freebsd7.2 ~]$

然后可以看看nginx服务器的一些输出信息:

[zieckey@freebsd7.2 sbin]$ ./nginx

ngx_http_p2s_create_conf

ngx_http_p2s_create_conf

ngx_http_p2s_create_conf

ngx_http_p2s_create_conf

ngx_http_p2s_urlquery_set

ngx_http_p2s_urlquery_handler

p2s_urlquery_process_handler

get_raw_http_body

time=1303552000.000000 http body data len=29:

user=zieckey&verifykey=123456

----------------------http body data end-------------------


6. 其他说明

6.1 配置文件和ngx_command_t的对应关系:

static ngx_command_t ngx_http_p2s_commands[] =

{/*{{{*/

{ ngx_string("p2s_urlquery"), // 命令名,请对照 nginx.conf 看这个

NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,

ngx_http_p2s_urlquery_set,

0,

0,

NULL },

ngx_null_command

};/*}}}*/

nginx.conf 中的 location 字段的配置是这样的:

location /urlquery {

p2s_urlquer;

}


这里的 /urlquery 是 URL 请求里的 URI 部分,

例如http://localhost/urlquery这条URL的请求会被这条配置项处理,

这条配置项,会去找命令名为“p2s_urlquery”的 ngx_command 去处理。


6.2 模块名

代码中的模块变量名必须与config配置文件的名字一致

config 配置文件的内容:ngx_addon_name=ngx_http_p2s_module

代码中的内容:


ngx_module_t ngx_http_p2s_module =

{/*{{{*/

NGX_MODULE_V1,

&ngx_http_p2s_module_ctx, /* module context */

ngx_http_p2s_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

};/*}}}*/


模块的config配置文件会帮助 configure 生成一个ngx_modules.c文件,该文件里会引用 ngx_module_t  ngx_http_p2s_module 这个外部定义的变量,所以必须保持一致。在执行 ./configure 完后,在nginx-1.0.0源码的根目录会多出来一个文件夹objs,在这里可以找到ngx_modules.c文件。


6.3 代码调用关系

nginx程序启动的时候,会去读配置文件 conf/nginx.conf ,每读到一个配置项,就会调用 module 注册的回调函数,所以这里可以看到nginx启动的时候调用 ngx_http_p2s_create_conf 函数好几次。 这个函数是由 


static ngx_http_module_t ngx_http_p2s_module_ctx =

{/*{{{*/

NULL, /* preconfiguration */

NULL, /* postconfiguration */

NULL, /* create main configuration */

NULL, /* init main configuration */

NULL, /* create server configuration */

NULL, /* merge server configuration */

ngx_http_p2s_create_conf, /* create location configration */

NULL /* merge location configration */

};/*}}}*/

指定的。


然后,在一个客户端发起http请求的时候,例如我们这里的 “curl http://localhost:8088/urlquery” 命令实际上是发起一个 http 请求,URI 是 urlquery,所以,nginx 根据配置文件的配置项,找到  urlquery 对应的处理模块名 p2s_urlquery, 然后调用 p2s_urlquery 这个名字关联的 ngx_http_p2s_urlquery_set 回调函数。

ngx_http_p2s_urlquery_set 回调函数又会设置一个 ngx_http_p2s_urlquery_handler 回调函数,ngx_http_p2s_urlquery_handler 回调函数会在 nginx 读取客户端发过来的http数据的时候被调用。

ngx_http_p2s_urlquery_handler 又会设置一个 p2s_urlquery_process_handler 回调函数,

p2s_urlquery_process_handler 会在 nginx 读取完客户端发过来的http数据之后的时候被调用,

不难发现,p2s_urlquery_process_handler函数是我们真正的处理逻辑,因为这个时候已经收完了客户端的http数据。


p2s_urlquery_process_handler 函数先调用 get_raw_http_body 这个我们自己封装的函数将客户端发送过来的 http data 读取到一个缓冲区中,然后处理(这里我们只是简单的printf)。然后,我们准备好数据,发送会客户端。


到此,我们完成了一个很简单的模块的开发和调试工作,也不是那么困难。

同时对一些函数调用,配置文件关系等等比较含糊的地方做了详细说明。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270