pos机模块无应答,写一个Nginx的模块没有那么难

 新闻资讯  |   2023-04-16 10:25  |  投稿人:pos机之家

网上有很多关于pos机模块无应答,写一个Nginx的模块没有那么难的知识,也有很多人为大家解答关于pos机模块无应答的问题,今天pos机之家(www.poszjia.com)为大家整理了关于这方面的知识,让我们一起来看下吧!

本文目录一览:

1、pos机模块无应答

pos机模块无应答

Nginx作为世界第二大Web服务器(第一是Apache),越来越受到大家的青睐。受到欢迎的一个重要原因,是他的高扩展性。它是由多个不同功能、不同层次、不同类型且耦合度极低的模块组成。当我们开发自己的模块时,不仅可以使用core模块、events模块、log模块,而且我们开发的模块是嵌入到二进制文件中执行的,因此我们自己开发的模块,也能具有优秀的性能,享受Nginx的高并发特性。

本文以编写一个http_hello_module为例,介绍编写一个Nginx的http模块的步骤,以及其中涉及的数据结构。以期打破大家对编写nginx模块的恐惧。

该示例模块的功能是:每次访问http时,http进行访问计数,并把计数的值回显给客户端。

为了说明自定义的http模块,是如何读取配置文件的值,即在Nginx.conf文件中配置值,我们引入了两个配置项hello_string、hello_counter。这两个配置指令,仅可以出现在location指令的作用域中。hello_string用于展示字符串类型,它接收一个参数来设置回显的字符串,或是零个参数,则使用默认的字符串作为回显的字符串。而hello_counter,用于控制是否开启访问统计,如果设置为 on,则会在相应的字符串后面追加 Visited Times:的字样,以统计请求的次数。我们的例子中,其中一个location的配置如下。

location /test { hello_string balabala; hello_counter on;}

我们自定义模块的访问效果如图:

编写http_hello_module

一个简单的http模块,只需要定义一个结构体ngx_module_t的变量,他告诉Nginx框架,我们定义的模块,

属于什么模块(如http模块、filter模块)需要哪些配置项,在哪个领域定义(如server域、http域、location域)。我们的例子中,需要两个配置项:hello_string,hello_counter。在location作用域配置。配置项的类型,存储结构。我们的例子中,一个是string类型,一个是int类型,定义结构如下:

typedef struct{ ngx_str_t hello_string; ngx_int_t hello_counter;}ngx_http_hello_loc_conf_t;如何接收客户端请求,对请求,又应该做出怎样的应答。

下面,我们逐步了解ngx_module_t结构体是如何完成上述内容的。ngx_module_t结构体的每个成员定义如下:

typedef struct ngx_module_s ngx_module_t;struct ngx_module_s { ngx_uint_t ctx_index; // 表示当前模块在这类模块中的序号。既用于表达优先级,又用于 Nginx 框架快速获得模块的数据 ngx_uint_t index; // 当前模块在所有模块的序号 char *name; ngx_uint_t spare0; // spare系列的保留变量,暂未使用 ngx_uint_t spare1; ngx_uint_t version; // 模块的版本号,便于将来的扩展。 const char *signature; // 用于指向一类模块的上下文结构体。ctx将会指向特定类型模块的公共接口。例如在http模块中,ctx指向ngx_http_module_t结构体 void *ctx; ngx_command_t *commands; // 将处理nginx.conf中的配置项 ngx_uint_t type; // 模块类型。官方的取值包括:NGX_HTTP_MODULE, NGX_CORE_MODULE, NGX_CONF_MODULE, NGX_EVENT_MODULE, NGX_MAIL_MODULE /* * 以下七个函数指针,表示在七个阶段,会调用这七个方法 */ ngx_int_t (*init_master)(ngx_log_t *log); // 在master进程启动时。框架暂时没用调用。所以,写了也没啥卵用 ngx_int_t (*init_module)(ngx_cycle_t *cycle); // 在初始化所有模块时被调用。在master/worker模式下,在启动worker进程前被调用 ngx_int_t (*init_process)(ngx_cycle_t *cycle); // 在每个worker进程的初始化过程会被调用 ngx_int_t (*init_thread)(ngx_cycle_t *cycle); // 不支持多线程,所以没啥卵用 void (*exit_thread)(ngx_cycle_t *cycle); // 不支持 void (*exit_process)(ngx_cycle_t *cycle); // worker 进程会在退出前调用 void (*exit_master)(ngx_cycle_t *cycle); // 在master进程退出前被调用 //没有用 uintptr_t spare_hook0; uintptr_t spare_hook1; uintptr_t spare_hook2; uintptr_t spare_hook3; uintptr_t spare_hook4; uintptr_t spare_hook5; uintptr_t spare_hook6; uintptr_t spare_hook7;};

对于ngx_module_t结构体的前7个成员,我们用Nginx提供的宏NGX_MODULE_V1进行初始化。

#define NGX_MODULE_V1 \\ NGX_MODULE_UNSET_INDEX, NGX_MODULE_UNSET_INDEX, \\ NULL, 0, 0, nginx_version, NGX_MODULE_SIGNATURE

对于ngx_module_t结构体的最后8个成员,我们用Nginx提供的宏NGX_MODULE_V1_PADDING进行初始化。

#define NGX_MODULE_V1_PADDING 0, 0, 0, 0, 0, 0, 0, 0

对于在七个阶段会调用的函数指针,我们的http_hello_module并不需要,所以统一设置为NULL。

因为我们的例子是一个http模块,因此type值,等于NGX_HTTP_MODULE。

通过以上分析可知,为了定义结构体ngx_module_s的对象,我们只需要定义以下两个结构体的对象:

结构体ngx_http_module_t的对象,赋值给ctx,用于告知Nginx框架,我们的自定义模块如何处理客户端的请求结构体ngx_command_t的数组对象,赋值给commands,用于告知Nginx框架,我们的自定义模块需要的配置项ngx_module_s的成员commands定义

对于有配置项的模块,需要定义一个结构体,用于存储配置项的值。本例中,我们需要两个配置项, hello_string和hello_counter,分别是string和int类型,因此,存储配置项的结构体定义如下。

typedef struct{ ngx_str_t hello_string; ngx_int_t hello_counter;} ngx_http_hello_loc_conf_t;

有一点需要注意的是,在模块的开发过程中,我们最好使用 Nginx 原有的命名习惯。这样跟源代码的契合度更高,看起来也更舒服。对于模块配置信息的定义,命名习惯是ngx_http_<module name>_(main|srv|loc)_conf_t。

commands数组用于定义模块的配置文件参数,每一个数组都是ngx_command_t类型,数组的结尾用ngx_null_command表示。Nginx框架在解析配置文件时,对于一个配置项,会遍历所有的模块。对每一个模块,都会遍历该模块的commmands数组,直到遇到ngx_null_command。

ngx_command_t结构体定义了自己感兴趣的一个配置项:

typedef struct ngx_command_s ngx_command_t;struct ngx_command_s { ngx_str_t name; ngx_uint_t type; char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); ngx_uint_t conf; ngx_uint_t offset; void *post; };

结构体各成员的说明如下:

name: 配置指令的名称。type: 配置项类型,该配置项可以出现的位置,如:server{}或location{},以及它可以携带的参数个数。例如:NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS|NGX_CONF_TAKE1 表示该配置项出现在location位置,接收0个或1个参数。set:是一个函数指针。出现了name中指定的配置项后,将会调用set方法处理配置项的参数conf: 该字段指定当前配置项存储的内存位置。因为 http 模块对所有 http 模块所要保存的配置信息,划分了 main, server 和 location 三个地方进行存储,每个地方都有一个内存池用来分配存储这些信息的内存。这里可能的值为 NGX_HTTP_MAIN_CONF_OFFSET、NGX_HTTP_SRV_CONF_OFFSET 或 NGX_HTTP_LOC_CONF_OFFSET。offset: 指定该配置项值的精确存放位置,一般指定为某一个结构体变量的字段偏移。post: 配置项读取后的处理方法

因为我们的例子有两个配置项,因此依次定义了两个ngx_command_t,数组用ngx_null_command结尾。

static ngx_command_t ngx_http_hello_commands[] = { { ngx_string("hello_string"), // 配置项名字 NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS|NGX_CONF_TAKE1, // 配置项出现的位置和接收的参数个数 ngx_http_hello_string, // 在配置文件中读到该配置项后,用该函数处理配置项 NGX_HTTP_LOC_CONF_OFFSET, // 配置项的值,用LOCATION的内存池存储 offsetof(ngx_http_hello_loc_conf_t, hello_string), // 存储的精确位置 NULL }, { ngx_string("hello_counter"), NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, ngx_http_hello_counter, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_hello_loc_conf_t, hello_counter), NULL }, ngx_null_command};

对于hello_string配置项,它的处理函数实现如下,ngx_conf_set_str_slot是Nginx提供的函数,读取string类型的配置项。

static char *ngx_http_hello_string(ngx_conf_t *cf, ngx_command_t *cmd, void *conf){ ngx_http_hello_loc_conf_t* local_conf; local_conf = conf; char* rv = ngx_conf_set_str_slot(cf, cmd, conf); return rv;}

对于hello_counter配置项,他的处理函数实现如下,ngx_conf_set_flag_slot是Nginx提供的函数,读取on/off值的配置项。

static char *ngx_http_hello_counter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf){ ngx_http_hello_loc_conf_t* local_conf; local_conf = conf; char* rv = NULL; rv = ngx_conf_set_flag_slot(cf, cmd, conf); return rv;}ngx_module_s的成员ctx定义

ctx是结构体ngx_http_module_t的对象指针,主要用于定义在读取配置文件的各个阶段的处理函数。

下面展示的代码,便是ngx_http_module_t这个结构体:

typedef struct { // 解析配置文件前调用 ngx_int_t (*preconfiguration)(ngx_conf_t *cf); // 完成配置文件的解析后调用 ngx_int_t (*postconfiguration)(ngx_conf_t *cf); // 创建数据结构,用于存储main级别(直属于http{...}块的配置项)的全局配置项 void *(*create_main_conf)(ngx_conf_t *cf); // 初始化用于main级别的配置项 char *(*init_main_conf)(ngx_conf_t *cf, void *conf); //创建数据结构,用于存储srv级别(直属于虚拟主机server{...}块的配置项)的配置项 void *(*create_srv_conf)(ngx_conf_t *cf); //合并main和srv级别下的同名配置项 char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf); // 创建数据结构,用于存储loc级别(直属于location{...}块的配置项)的配置项 void *(*create_loc_conf)(ngx_conf_t *cf); // 合并srv和location级别下的同名配置项 char *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf); } ngx_http_module_t;

在我们的例子中,ngx_http_module_t的各个字段的实现如下, 我们只关心location配置项的读取以及在配置文件解析完成后的处理。location配置项读取hello_string和hello_counter的值,配置文件解析完成后,我们挂载请求的处理函数。

static ngx_http_module_t ngx_http_hello_module_ctx = { NULL, /* preconfiguration */ ngx_http_hello_init, /* postconfiguration */ NULL, /* create main configuration */ NULL, /* init main configuration */ NULL, /* create server configuration */ NULL, /* merge server configuration */ ngx_http_hello_create_loc_conf, /* create location configuration */ NULL /* merge location configuration */};

我们先来看一下ngx_http_hello_create_loc_conf是如何存储loc级别的配置项。 ngx_http_hello_create_loc_conf是告诉Ngixn模块,我们使用的loc级别的配置项的信息,并对配置项进行初始化。

static void *ngx_http_hello_create_loc_conf(ngx_conf_t *cf){ ngx_http_hello_loc_conf_t* local_conf = NULL; local_conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_hello_loc_conf_t)); if (local_conf == NULL) { return NULL; } ngx_str_null(&local_conf->hello_string); local_conf->hello_counter = NGX_CONF_UNSET; return local_conf;}

在配置文件处理完后,我们挂载模块的处理函数。即告诉Nginx框架,当符合条件的请求过来时,我们自定义的模块如何处理请求,如何响应请求。挂载是在配置文件解析完毕,由函数ngx_http_hello_init完成的。

static ngx_int_tngx_http_hello_init(ngx_conf_t *cf){ ngx_http_handler_pt *h; ngx_http_core_main_conf_t *cmcf; cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers); if (h == NULL) { return NGX_ERROR; } *h = ngx_http_hello_handler; return NGX_OK;}

从上述代码可知,我们挂载的处理函数为ngx_http_hello_handler,即由它处理客户端请求、做出具体响应。

static ngx_int_tngx_http_hello_handler(ngx_http_request_t *r){ ngx_int_t rc; ngx_buf_t *b; ngx_chain_t out; ngx_http_hello_loc_conf_t* my_conf; u_char ngx_hello_string[1024] = {0}; ngx_uint_t content_length = 0; ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, "ngx_http_hello_handler is called!"); my_conf = ngx_http_get_module_loc_conf(r, ngx_http_hello_module); if (my_conf->hello_string.len == 0 ) { ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, "hello_string is empty!"); return NGX_DECLINED; } if (my_conf->hello_counter == NGX_CONF_UNSET || my_conf->hello_counter == 0) { ngx_sprintf(ngx_hello_string, "%s", my_conf->hello_string.data); } else { ngx_sprintf(ngx_hello_string, "%s Visited Times:%d", my_conf->hello_string.data, ++ngx_hello_visited_times); } ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, "hello_string:%s", ngx_hello_string); content_length = ngx_strlen(ngx_hello_string); /* we response to 'GET' and 'HEAD' requests only */ if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) { return NGX_HTTP_NOT_ALLOWED; } /* discard request body, since we don't need it here */ rc = ngx_http_discard_request_body(r); if (rc != NGX_OK) { return rc; } ngx_str_set(&r->headers_out.content_type, "text/html"); /* send the header only, if the request type is http 'HEAD' */ if (r->method == NGX_HTTP_HEAD) { r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = content_length; return ngx_http_send_header(r); } /* allocate a buffer for your response body */ b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); if (b == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } /* attach this buffer to the buffer chain */ out.buf = b; out.next = NULL; /* adjust the pointers of the buffer */ b->pos = ngx_hello_string; b->last = ngx_hello_string + content_length; b->memory = 1; /* this buffer is in memory */ b->last_buf = 1; /* this is the last buffer in the buffer chain */ /* set the status line */ r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = content_length; /* send the headers of your response */ rc = ngx_http_send_header(r); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { return rc; } /* send the buffer chain of your response */ return ngx_http_output_filter(r, &out);}如何把http_hello_module编译进Nginx

上面介绍了,编写一个http模块的基本要素。接下来,介绍如何把这个简单的模块编译进Nginx框架,从而让它发挥作用。

方案一:在执行configure命令时,添加--add-module参数

Nginx提供了一种简单的方式将第三方的模块编译进Nginx中。首先是把第三方的源代码全部放在一个目录中, 并在该目录下新建一个名为config的文件,这个config文件的目的,就是告诉Nginx如何编译我们自己的模块。在执行Nginx的configure命令时, 加入参数 --add-module=PATH,PATH就是我们存放config的目录,就可以把我们开发的http_hello_module编译进Nginx。

我们的http_hello_module是一个http模块,因此config文件中只需要定义以下三个变量:

ngx_addon_name: 仅在configure执行时使用,一般设置为模块名称。HTTP_MODULES: 添加自定义模块的名称。NGX_ADDON_SRCS:指定新增模块的源代码。如果有多个文件,则以空格隔开。

我们的http_hello_module例子,它的config文件完整内容如下。$ngx_addon_dir变量的值,等于我们在执行configure命令时,参数--add-module的值。

ngx_addon_name=ngx_http_hello_moduleHTTP_MODULES="$HTTP_MODULES ngx_http_hello_module"NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_hello_module.c"

因为我们本例是编写一个http module,因此赋值了HTTP_MODULES。如果是编写其他模块,如http 过滤模块、Nginx核心模块、事件模块、http头部过滤模块,则应该分别对变量HTTP_FILTER_MODULES、CORE_MODULES、EVENT_MODULES、HTTP_HEADER_FILTER_MODULES。

方案二:在执行configure命令后,修改Makefile文件

Nginx提供的另一种方法是直接修改Makefile文件。在执行完configure脚本后,会在objs/Makefile和objs/ngx_modules.c文件。我们可以直接修改这两个文件,从而完成http_hello_module编译进Nginx的目标。

以上就是关于pos机模块无应答,写一个Nginx的模块没有那么难的知识,后面我们会继续为大家整理关于pos机模块无应答的知识,希望能够帮助到大家!

转发请带上网址:http://www.poszjia.com/news/19308.html

你可能会喜欢:

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 babsan@163.com 举报,一经查实,本站将立刻删除。