设为首页 收藏本站
查看: 573|回复: 0

[经验分享] Nginx Modules 开发学习

[复制链接]

尚未签到

发表于 2016-12-24 11:48:46 | 显示全部楼层 |阅读模式
       nginx 模块开发,咱们先从模块分析入手,nginx的模块支持hook(钩子功能),我们可以在以下几个地方添加钩子:


  • Just before the server reads the config file
    读取配置文件时
  • For every configuration directive for the location and server for which it appears;
    读取到server或是location 配置的时候
  • When Nginx initializes the main configuration
    初始化主配置的时候
  • When Nginx initializes the server (i.e., host/port) configuration
    初始化server 配置的时候
  • When Nginx merges the server configuration with the main configuration
    使用主配置,合并server配置的时候
  • When Nginx initializes the location configuration
    初始化location配置的时候
  • When Nginx merges the location configuration with its parent server configuration
    使用server配置,合并location配置的时候
  • When Nginx's master process starts
    nginx 主进程启动的时候
  • When a new worker process starts
    nginx 启动一个新的工作进程的时候
  • When a worker process exits
    nginx 工作进程退出的时候
  • When the master exits
    nginx 主进程退出的时候
  • Handling a request
    接收的请求的时候
  • Filtering response headers
    过滤 response headers 的时候,gzip就是这个时候hook的
  • Filtering the response body
    过滤 response body 的时候,gzip就是这个时候hook的
  • Picking a backend server
    选择一个后台服务器的时候,在upstream 中选择
  • Initiating a request to a backend server
    初始化一个访问后台服务器的请求的时候

  • Re-initiating a request to a backend server
    重新初始化一个访问后台服务器的请求的时候
  • Processing the response from a backend server
    处理从后台返回的response的时候
  • Finishing an interaction with a backend server
    与后台服务器交互结束的时候
  

  下面我们分析以下nginx memcache的模块,下载nginx源码,cd src/http/modules/,在moudels 目录下,你能找到nginx所有core modules ,这里我们分析ngx_http_memcached_module.c 文件
  


#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
//配置定义
typedef struct {
ngx_http_upstream_conf_t   upstream;
ngx_int_t                  index;
} ngx_http_memcached_loc_conf_t;
//request 定义
typedef struct {
size_t                     rest;
ngx_http_request_t        *request;
ngx_str_t                  key;
} ngx_http_memcached_ctx_t;

  已下是nginx 的 hood定义

static ngx_int_t ngx_http_memcached_create_request(ngx_http_request_t *r);
static ngx_int_t ngx_http_memcached_reinit_request(ngx_http_request_t *r);
static ngx_int_t ngx_http_memcached_process_header(ngx_http_request_t *r);
static ngx_int_t ngx_http_memcached_filter_init(void *data);
static ngx_int_t ngx_http_memcached_filter(void *data, ssize_t bytes);
static void ngx_http_memcached_abort_request(ngx_http_request_t *r);
static void ngx_http_memcached_finalize_request(ngx_http_request_t *r,
ngx_int_t rc);
  使用upstream 时定义的参数

static ngx_conf_bitmask_t  ngx_http_memcached_next_upstream_masks[] = {
{ ngx_string("error"), NGX_HTTP_UPSTREAM_FT_ERROR },
{ ngx_string("timeout"), NGX_HTTP_UPSTREAM_FT_TIMEOUT },
{ ngx_string("invalid_response"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER },
{ ngx_string("not_found"), NGX_HTTP_UPSTREAM_FT_HTTP_404 },
{ ngx_string("off"), NGX_HTTP_UPSTREAM_FT_OFF },
{ ngx_null_string, 0 }
};

  memcache nginx  自定义命令

static ngx_command_t  ngx_http_memcached_commands[] = {
{ ngx_string("memcached_pass"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
ngx_http_memcached_pass,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("memcached_bind"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_upstream_bind_set_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_memcached_loc_conf_t, upstream.local),
NULL },
{ ngx_string("memcached_connect_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_memcached_loc_conf_t, upstream.connect_timeout),
NULL },
{ ngx_string("memcached_send_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_memcached_loc_conf_t, upstream.send_timeout),
NULL },
{ ngx_string("memcached_buffer_size"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_memcached_loc_conf_t, upstream.buffer_size),
NULL },
{ ngx_string("memcached_read_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_memcached_loc_conf_t, upstream.read_timeout),
NULL },
{ ngx_string("memcached_next_upstream"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_conf_set_bitmask_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_memcached_loc_conf_t, upstream.next_upstream),
&ngx_http_memcached_next_upstream_masks },
ngx_null_command
};

   Nginx 命令结构

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;
};
  自定义命令配置说明:
  type :

      NGX_HTTP_MAIN_CONF:可出现在 http 的主作用域;
NGX_HTTP_SRV_CONF:可出现在 http 的 server 作用域;
NGX_HTTP_LOC_CONF:可出现在 http 的 location 作用域;
NGX_HTTP_UPS_CONF:可出现在 http 的 upstream 作用域;
NGX_HTTP_SIF_CONF:可出现在判断语句里;
NGX_CONF_NOARGS:指令没有参数;
NGX_CONF_TAKE1:指令读入1个参数;
NGX_CONF_TAKE2:指令读入2个参数;
......
NGX_CONF_TAKE7:指令读入7个参数;
NGX_CONF_FLAG:  指令读入1个布尔型数据(“on”或“off”);
NGX_CONF_1MORE:指令至少读入1个参数;
NGX_CONF_2MORE:指令至少读入2个参数;


   
http 上下文定义:




  • static ngx_http_module_t  ngx_http_memcached_module_ctx = {
    NULL,                                  /* preconfiguration */
    NULL,                                  /* postconfiguration */
    NULL,                                  /* create main configuration */
    NULL,                                  /* init main configuration */
    NULL,                                  /* create server configuration */
    NULL,                                  /* merge server configuration */
    ngx_http_memcached_create_loc_conf,    /* create location configuration */
    ngx_http_memcached_merge_loc_conf      /* merge location configuration */
    };

    ngx_module_t  ngx_http_memcached_module = {
    NGX_MODULE_V1,
    &ngx_http_memcached_module_ctx,        /* module context */
    ngx_http_memcached_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
    };

     
    http 上下文结构定义:

    typedef struct {
    ngx_int_t   (*preconfiguration)(ngx_conf_t *cf);
    ngx_int_t   (*postconfiguration)(ngx_conf_t *cf);
    void       *(*create_main_conf)(ngx_conf_t *cf);
    char       *(*init_main_conf)(ngx_conf_t *cf, void *conf);
    void       *(*create_srv_conf)(ngx_conf_t *cf);
    char       *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);
    void       *(*create_loc_conf)(ngx_conf_t *cf);
    char       *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
    } ngx_http_module_t;
      

    我们跟进看一下 create  and merge loc config:




    static void *
    ngx_http_memcached_create_loc_conf(ngx_conf_t *cf)
    {
    ngx_http_memcached_loc_conf_t  *conf;
    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_memcached_loc_conf_t));
    if (conf == NULL) {
    return NULL;
    }
    /*
    * set by ngx_pcalloc():
    *
    *     conf->upstream.bufs.num = 0;
    *     conf->upstream.next_upstream = 0;
    *     conf->upstream.temp_path = NULL;
    *     conf->upstream.uri = { 0, NULL };
    *     conf->upstream.location = NULL;
    */
    conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;
    conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;
    conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;
    conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;
    /* the hardcoded values */
    conf->upstream.cyclic_temp_file = 0;
    conf->upstream.buffering = 0;
    conf->upstream.ignore_client_abort = 0;
    conf->upstream.send_lowat = 0;
    conf->upstream.bufs.num = 0;
    conf->upstream.busy_buffers_size = 0;
    conf->upstream.max_temp_file_size = 0;
    conf->upstream.temp_file_write_size = 0;
    conf->upstream.intercept_errors = 1;
    conf->upstream.intercept_404 = 1;
    conf->upstream.pass_request_headers = 0;
    conf->upstream.pass_request_body = 0;
    conf->index = NGX_CONF_UNSET;
    return conf;
    }

    static char *
    ngx_http_memcached_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
    {
    ngx_http_memcached_loc_conf_t *prev = parent;
    ngx_http_memcached_loc_conf_t *conf = child;
    ngx_conf_merge_msec_value(conf->upstream.connect_timeout,
    prev->upstream.connect_timeout, 60000);
    ngx_conf_merge_msec_value(conf->upstream.send_timeout,
    prev->upstream.send_timeout, 60000);
    ngx_conf_merge_msec_value(conf->upstream.read_timeout,
    prev->upstream.read_timeout, 60000);
    ngx_conf_merge_size_value(conf->upstream.buffer_size,
    prev->upstream.buffer_size,
    (size_t) ngx_pagesize);
    ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,
    prev->upstream.next_upstream,
    (NGX_CONF_BITMASK_SET
    |NGX_HTTP_UPSTREAM_FT_ERROR
    |NGX_HTTP_UPSTREAM_FT_TIMEOUT));
    if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) {
    conf->upstream.next_upstream = NGX_CONF_BITMASK_SET
    |NGX_HTTP_UPSTREAM_FT_OFF;
    }
    if (conf->upstream.upstream == NULL) {
    conf->upstream.upstream = prev->upstream.upstream;
    }
    if (conf->index == NGX_CONF_UNSET) {
    conf->index = prev->index;
    }
    return NGX_CONF_OK;
    }








    接下来我们进入handler:

    static char *
    ngx_http_memcached_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
    {
    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
    clcf->handler = ngx_http_memcached_handler;
    return NGX_CONF_OK;
    }
      

    static ngx_int_t
    ngx_http_memcached_handler(ngx_http_request_t *r)
    {
    ngx_int_t                       rc;
    ngx_http_upstream_t            *u;
    ngx_http_memcached_ctx_t       *ctx;
    ngx_http_memcached_loc_conf_t  *mlcf;
    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
    return NGX_HTTP_NOT_ALLOWED;
    }
    rc = ngx_http_discard_request_body(r);
    if (rc != NGX_OK) {
    return rc;
    }
    if (ngx_http_set_content_type(r) != NGX_OK) {
    return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }
    if (ngx_http_upstream_create(r) != NGX_OK) {
    return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }
    u = r->upstream;
    ngx_str_set(&u->schema, "memcached://");
    u->output.tag = (ngx_buf_tag_t) &ngx_http_memcached_module;
    mlcf = ngx_http_get_module_loc_conf(r, ngx_http_memcached_module);
    u->conf = &mlcf->upstream;
    u->create_request = ngx_http_memcached_create_request;
    u->reinit_request = ngx_http_memcached_reinit_request;
    u->process_header = ngx_http_memcached_process_header;
    u->abort_request = ngx_http_memcached_abort_request;
    u->finalize_request = ngx_http_memcached_finalize_request;
    ctx = ngx_palloc(r->pool, sizeof(ngx_http_memcached_ctx_t));
    if (ctx == NULL) {
    return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }
    ctx->rest = NGX_HTTP_MEMCACHED_END;
    ctx->request = r;
    ngx_http_set_ctx(r, ctx, ngx_http_memcached_module);
    u->input_filter_init = ngx_http_memcached_filter_init;
    u->input_filter = ngx_http_memcached_filter;
    u->input_filter_ctx = ctx;
    r->main->count++;
    ngx_http_upstream_init(r);
    return NGX_DONE;
    }

     

    以上 memcache处理均在以下方法中进行:

      u->create_request = ngx_http_memcached_create_request;
    u->reinit_request = ngx_http_memcached_reinit_request;
    u->process_header = ngx_http_memcached_process_header;
    u->abort_request = ngx_http_memcached_abort_request;
    u->finalize_request = ngx_http_memcached_finalize_request;

       u->input_filter_init = ngx_http_memcached_filter_init;
    u->input_filter = ngx_http_memcached_filter;
    u->input_filter_ctx = ctx;
     







    以上是对memcached nginx模块进行的分析,小刀我也是初始,有写的不好的地方,请大家多多指教,接下来我们在看一个 not modified moudel 这个相对就容易很多。

    /*
    * Copyright (C) Igor Sysoev
    * Copyright (C) Nginx, Inc.
    */

    #include <ngx_config.h>
    #include <ngx_core.h>
    #include <ngx_http.h>

    static ngx_int_t ngx_http_test_precondition(ngx_http_request_t *r);
    static ngx_int_t ngx_http_test_not_modified(ngx_http_request_t *r);
    static ngx_int_t ngx_http_not_modified_filter_init(ngx_conf_t *cf);

    static ngx_http_module_t  ngx_http_not_modified_filter_module_ctx = {
    NULL,                                  /* preconfiguration */
    ngx_http_not_modified_filter_init,     /* 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_not_modified_filter_module = {
    NGX_MODULE_V1,
    &ngx_http_not_modified_filter_module_ctx, /* module context */
    NULL,                                  /* 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 ngx_http_output_header_filter_pt  ngx_http_next_header_filter;

    static ngx_int_t
    ngx_http_not_modified_header_filter(ngx_http_request_t *r)
    {
    if (r->headers_out.status != NGX_HTTP_OK
    || r != r->main
    || r->headers_out.last_modified_time == -1)
    {
    return ngx_http_next_header_filter(r);
    }
    if (r->headers_in.if_unmodified_since) {
    return ngx_http_test_precondition(r);
    }
    if (r->headers_in.if_modified_since) {
    return ngx_http_test_not_modified(r);
    }
    return ngx_http_next_header_filter(r);
    }

    static ngx_int_t
    ngx_http_test_precondition(ngx_http_request_t *r)
    {
    time_t  iums;
    iums = ngx_http_parse_time(r->headers_in.if_unmodified_since->value.data,
    r->headers_in.if_unmodified_since->value.len);
    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
    "http iums:%d lm:%d", iums, r->headers_out.last_modified_time);
    if (iums >= r->headers_out.last_modified_time) {
    return ngx_http_next_header_filter(r);
    }
    return ngx_http_filter_finalize_request(r, NULL,
    NGX_HTTP_PRECONDITION_FAILED);
    }

    static ngx_int_t
    ngx_http_test_not_modified(ngx_http_request_t *r)
    {
    time_t                     ims;
    ngx_http_core_loc_conf_t  *clcf;
    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
    if (clcf->if_modified_since == NGX_HTTP_IMS_OFF) {
    return ngx_http_next_header_filter(r);
    }
    ims = ngx_http_parse_time(r->headers_in.if_modified_since->value.data,
    r->headers_in.if_modified_since->value.len);
    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
    "http ims:%d lm:%d", ims, r->headers_out.last_modified_time);
    if (ims != r->headers_out.last_modified_time) {
    if (clcf->if_modified_since == NGX_HTTP_IMS_EXACT
    || ims < r->headers_out.last_modified_time)
    {
    return ngx_http_next_header_filter(r);
    }
    }
    r->headers_out.status = NGX_HTTP_NOT_MODIFIED;
    r->headers_out.status_line.len = 0;
    r->headers_out.content_type.len = 0;
    ngx_http_clear_content_length(r);
    ngx_http_clear_accept_ranges(r);
    if (r->headers_out.content_encoding) {
    r->headers_out.content_encoding->hash = 0;
    r->headers_out.content_encoding = NULL;
    }
    return ngx_http_next_header_filter(r);
    }

    static ngx_int_t
    ngx_http_not_modified_filter_init(ngx_conf_t *cf)
    {
    ngx_http_next_header_filter = ngx_http_top_header_filter;
    ngx_http_top_header_filter = ngx_http_not_modified_header_filter;
    return NGX_OK;
    }

     

    自定义返回时设置cookie:




    1. mkdir author

    2. cd author

    3. 创建一个文件叫 config 内容如下




    ngx_addon_name=nginx_add_author_filter_module
    HTTP_AUX_FILTER_MODULES="$HTTP_AUX_FILTER_MODULES nginx_add_author_filter_module"
    NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/author.c"
    4../configure --add-module=/Users/liuzheng/nginx/addOn/ 




    /*
    * Copy right (C) Liuzheng Wiyun author.c
    */
    #include <ngx_config.h>
    #include <ngx_core.h>
    #include <ngx_http.h>

    static ngx_int_t ngx_http_add_author_filter_init(ngx_conf_t *cf);
    static ngx_int_t ngx_http_add_author_header_filter(ngx_http_request_t *r);
    static ngx_http_module_t nginx_add_author_filter_module_ctx = {
    NULL,                                  /* preconfiguration */
    ngx_http_add_author_filter_init,     /* 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  nginx_add_author_filter_module = {
    NGX_MODULE_V1,
    &nginx_add_author_filter_module_ctx, /* module context */
    NULL,                                  /* 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 ngx_http_output_header_filter_pt  ngx_http_next_header_filter;

    static ngx_int_t
    ngx_http_add_author_header_filter(ngx_http_request_t *r)
    {
    ngx_table_elt_t  *set_cookie;
    set_cookie = ngx_list_push(&r->headers_out.headers);
    if (set_cookie == NULL) {
    return NGX_ERROR;
    }
    set_cookie->hash = 1;
    ngx_str_set(&set_cookie->key, "Set-Cookie");
    ngx_str_set(&set_cookie->value, "author=liuzheng");
    return ngx_http_next_header_filter(r);
    }

    static ngx_int_t
    ngx_http_add_author_filter_init(ngx_conf_t *cf)
    {
    ngx_http_next_header_filter = ngx_http_top_header_filter;
    ngx_http_top_header_filter = ngx_http_add_author_header_filter;
    return NGX_OK;
    }

     






    • Connection:

      keep-alive

    • Date:

      Mon, 13 Aug 2012 08:39:51 GMT

    • Last-Modified:

      Mon, 27 Feb 2012 09:14:41 GMT

    • Server:

      nginx/1.2.3

    • Set-Cookie:

      author=liuzheng




运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.iyunv.com/thread-318833-1-1.html 上篇帖子: nginx二级域名配置(rewrite) 下篇帖子: nginx url重写-rewrite实例
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表