IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    Tengine Proc模块介绍

    yzprofile发表于 2012-09-25 00:00:00
    love 0

    在Tengine 1.3.0版本发布的时候增加了一个支持开发者通过编写Tengine模块的形式, 来让Tengine启动独立的进程做事情的一个特性.

    这个特性我自己是非常满意并充满期待的,利用Nginx的配置解析和良好的模块化,以及底层网络事件的接口封装,可以让开发者非常容易的开发出一套高性能,高可扩展,跨平台的TCP server,并且Nginx也内置了许多优秀的数据结构的实现。当然,想像总是美好的。这个年代有谁再会去裸写TCP server的?写TCP Server有谁会去用Nginx的架子呢?实事上,这个模块的诞生,也主要是为了给Tengine提供一个辅助进程,解决类似Cache Manager这样进程的问题。

    下面看下该如何开发一个proc模块:

    完整的例子可以在这里得到: ngx_proc_daytime_module

    file:config

    ngx_addon_name=ngx_proc_daytime_module
    PROCS_MODULES="$PROCS_MODULES ngx_proc_daytime_module"
    NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_proc_daytime_module.c"

    file:ngx_proc_daytime_module.c

    static ngx_command_t ngx_proc_daytime_commands[] = {
    
        { ngx_string("listen"),
          NGX_PROC_CONF|NGX_CONF_TAKE1,
          ngx_conf_set_num_slot,
          NGX_PROC_CONF_OFFSET,
          offsetof(ngx_proc_daytime_conf_t, port),
          NULL },
    
        { ngx_string("daytime"),
          NGX_PROC_CONF|NGX_CONF_FLAG,
          ngx_conf_set_flag_slot,
          NGX_PROC_CONF_OFFSET,
          offsetof(ngx_proc_daytime_conf_t, enable),
          NULL },
    
          ngx_null_command
    };
    
    
    static ngx_proc_module_t ngx_proc_daytime_module_ctx = {
        ngx_string("daytime"),            /* module name */
        NULL,                             /* create main conf */
        NULL,                             /* init main conf */
        ngx_proc_daytime_create_conf,     /* create proc conf */
        ngx_proc_daytime_merge_conf,      /* merge proc conf */
        ngx_proc_daytime_prepare,         /* before start process */
        ngx_proc_daytime_process_init,    /* init process */
        ngx_proc_daytime_loop,            /* process loop */
        ngx_proc_daytime_exit_process     /* exit process */
    };
    
    
    ngx_module_t ngx_proc_daytime_module = {
        NGX_MODULE_V1,
        &ngx_proc_daytime_module_ctx,
        ngx_proc_daytime_commands,
        NGX_PROC_MODULE,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL,
        NGX_MODULE_V1_PADDING
    };

    最主要的也就是这几个回调函数了, 其它的同普通Nginx模块都是类似的.

    static ngx_int_t ngx_proc_daytime_prepare(ngx_cycle_t *cycle);
    static ngx_int_t ngx_proc_daytime_process_init(ngx_cycle_t *cycle);
    static ngx_int_t ngx_proc_daytime_loop(ngx_cycle_t *cycle);
    static void ngx_proc_daytime_exit_process(ngx_cycle_t *cycle);

    ngx_proc_daytime_prepare,在启动进程前校验配置等信息,决定是否启动进程。或者可以利用这一步,在主进程内初始化一些需要子进程继承的数据。

    static ngx_int_t
    ngx_proc_daytime_prepare(ngx_cycle_t *cycle)
    {
        ngx_proc_daytime_conf_t  *pbcf;
    
        pbcf = ngx_proc_get_conf(cycle->conf_ctx, ngx_proc_daytime_module);
        if (!pbcf->enable) {
            return NGX_DECLINED;
        }
    
        if (pbcf->port == 0) {
            return NGX_DECLINED;
        }
    
        return NGX_OK;
    }

    例子里我们只是判断了一下是否开启daytime模块,如果开启了那么就启动进程。

    ngx_proc_daytime_process_init,在进程启动后进行进程初始化。

    static ngx_int_t
    ngx_proc_daytime_process_init(ngx_cycle_t *cycle)
    {
        int                       reuseaddr;
        ngx_event_t              *rev;
        ngx_socket_t              fd;
        ngx_connection_t         *c;
        struct sockaddr_in        sin;
        ngx_proc_daytime_conf_t  *pbcf;
    
        pbcf = ngx_proc_get_conf(cycle->conf_ctx, ngx_proc_daytime_module);
        fd = ngx_socket(AF_INET, SOCK_STREAM, 0);
    
        ...
    
        c = ngx_get_connection(fd, cycle->log);
    
        ...
        
        c->log = cycle->log;
        rev = c->read;
        rev->log = c->log;
        rev->accept = 1;
        rev->handler = ngx_proc_daytime_accept;
    
        if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
            return NGX_ERROR;
        }
    
        pbcf->fd = fd;
    
        return NGX_OK;
    }

    例子里,我们创建一个监听socket,并且挂上读事件。

    ngx_proc_daytime_loop,每次事件循环,即epoll_wait返回一次,将执行一次loop回调。因为是事件驱动,所以例子里循环里没有实现什么别的功能,只是打印一条日志。

    ngx_proc_daytime_exit_process,退出进之前的回调。例子里关闭了监听socket。

    static void
    ngx_proc_daytime_exit_process(ngx_cycle_t *cycle)
    {
        ngx_proc_daytime_conf_t *pbcf;
    
        pbcf = ngx_proc_get_conf(cycle->conf_ctx, ngx_proc_daytime_module);
    
        ngx_close_socket(pbcf->fd);
    }

    并且可以使用ngx_proc_get_conf和ngx_proc_get_main_conf来得到daytime模块的相关配置。当然,作为独立的进程,你可以使用全局变量来作为进程执行的上下文。所以proc模块只提供了get conf的接口。

    static void
    ngx_proc_daytime_accept(ngx_event_t *ev)
    {
        u_char             sa[NGX_SOCKADDRLEN], buf[256], *p;
        socklen_t          socklen;
        ngx_socket_t       s;
        ngx_connection_t  *lc;
    
        lc = ev->data;
        s = accept(lc->fd, (struct sockaddr *) sa, &socklen);
        if (s == -1) {
            return;
        }
    
        if (ngx_nonblocking(s) == -1) {
            goto finish;
        }
    
        /*
          Daytime Protocol
    
          http://tools.ietf.org/html/rfc867
    
          Weekday, Month Day, Year Time-Zone
        */
        p = ngx_sprintf(buf, "%s, %s, %d, %d, %d:%d:%d-%s",
                        week[ngx_cached_tm->tm_wday],
                        months[ngx_cached_tm->tm_mon],
                        ngx_cached_tm->tm_mday, ngx_cached_tm->tm_year,
                        ngx_cached_tm->tm_hour, ngx_cached_tm->tm_min,
                        ngx_cached_tm->tm_sec, ngx_cached_tm->tm_zone);
    
        ngx_write_fd(s, buf, p - buf);
    
    finish:
        ngx_close_socket(s);
    }

    例子中每个新建连接的accept回调,发送当前时间后关闭连接。

    测试配置

    processes {
        process daytime {
            daytime on;
            listen 8888;
        }
    }
    
    yuen@MacBook-Air ~/
    $>> telnet 127.0.0.1 8888
    Trying 127.0.0.1...
    Connected to localhost.
    Escape character is '^]'.
    Wednesday, October, 26, 2012, 17:52:55-KRATConnection closed by foreign host.

    嗯,就到这里。

    have fun. : ) EOF



沪ICP备19023445号-2号
友情链接