nginx,用于解决用户多线路访问的nginx cross isp module

在互联网应用中都会面对多线负载与策略的问题,nginx cross isp module提供了一种简单的解决方案。
nginx cross isp module是基于nginx(http://nginx.org/)开发的,完全用c语言编写,借助于linux系统与nginx强大的功能与性能。
nginx cross isp module的主要逻辑很简单,根据访问用户的ip,从isp数据中查找,如果找到符合条件的,则根据配置,重新定向与相对应的链接地址。isp数据存在mysql中,由于isp数据一般不会经常变化,所以是nginx启动时,一次性读入。如果数据变更,可以重新启动nginx。
在实际应用中,需要为每个isp配置不同的域名,可以是二级域名。以下是nginx cross isp module的所有源码:
#include #include #include #include #include #include #include #define HTTP_COOKIES_MAX_EXPIRES 2145916555 #define HTTP_DOMAIN_COOKIE_NAME_LEN (size_t) 6 #define HTTP_SCHEMA_LEN (size_t) 7 #define HTTPS_SCHEMA_LEN (size_t) 8 typedef struct { ngx_uint_t ip_start; ngx_uint_t ip_end; u_char isp[3]; } ngx_isp_store_t; typedef struct { u_char isp[3]; ngx_regex_t *regex; ngx_str_t pattern; ngx_str_t replace; ngx_str_t domain; } ngx_isp_domain_t; typedef struct { ngx_regex_t *regex; ngx_str_t pattern; } ngx_isp_proxy_t; typedef struct { ngx_str_t host; ngx_int_t port; ngx_str_t usr; ngx_str_t pwd; ngx_str_t db; ngx_str_t tb; ngx_str_t isp_rhost_cookie_name; ngx_str_t isp_taddr_cookie_name; time_t isp_cookies_expires; ngx_array_t isp_store; ngx_array_t isp_domains; ngx_array_t isp_proxies; } ngx_http_cross_isp_main_conf_t; static u_char ngx_http_domain_cookie_name[] = "domain"; static u_char ngx_http_cookies_expires[] = "; expires=Thu, 31-Dec-37 23:55:55 GMT"; static u_char ngx_http_schema[] = "http://"; static u_char ngx_https_schema[] = "http://"; static u_char ngx_http_302_page[] = "" CRLF "302 Redirect" CRLF "

302 Redirect

" CRLF "
" CRLF; static ngx_int_t ngx_http_cross_isp_init (ngx_conf_t * cf); static void *ngx_http_cross_isp_create_main_conf (ngx_conf_t * cf); static char *ngx_http_cross_isp_init_main_conf (ngx_conf_t * cf, void *conf); static char *ngx_conf_set_isp_cookies_expires (ngx_conf_t * cf, ngx_command_t * cmd, void *conf); static char *ngx_conf_set_isp_domain (ngx_conf_t * cf, ngx_command_t * cmd, void *conf); static char *ngx_conf_set_isp_proxy (ngx_conf_t * cf, ngx_command_t * cmd, void *conf); static ngx_int_t ngx_http_cross_isp_handler (ngx_http_request_t * r); static long ngx_isp_store_binary_search (ngx_array_t data, ngx_uint_t ip); static ngx_int_t ngx_cross_isp_redirect (ngx_http_request_t * r, ngx_str_t url); static void ngx_http_write_isp_cookie (ngx_http_request_t * r, u_char * cookie); static ngx_command_t ngx_http_cross_isp_commands[] = { { ngx_string ("isp_store_host"), NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1, ngx_conf_set_str_slot, NGX_HTTP_MAIN_CONF_OFFSET, offsetof (ngx_http_cross_isp_main_conf_t, host), NULL}, { ngx_string ("isp_store_port"), NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1, ngx_conf_set_num_slot, NGX_HTTP_MAIN_CONF_OFFSET, offsetof (ngx_http_cross_isp_main_conf_t, port), NULL}, { ngx_string ("isp_store_usr"), NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1, ngx_conf_set_str_slot, NGX_HTTP_MAIN_CONF_OFFSET, offsetof (ngx_http_cross_isp_main_conf_t, usr), NULL}, { ngx_string ("isp_store_pwd"), NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1, ngx_conf_set_str_slot, NGX_HTTP_MAIN_CONF_OFFSET, offsetof (ngx_http_cross_isp_main_conf_t, pwd), NULL}, { ngx_string ("isp_store_db"), NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1, ngx_conf_set_str_slot, NGX_HTTP_MAIN_CONF_OFFSET, offsetof (ngx_http_cross_isp_main_conf_t, db), NULL}, { ngx_string ("isp_store_tb"), NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1, ngx_conf_set_str_slot, NGX_HTTP_MAIN_CONF_OFFSET, offsetof (ngx_http_cross_isp_main_conf_t, tb), NULL}, { ngx_string ("isp_rhost_cookie_name"), NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1, ngx_conf_set_str_slot, NGX_HTTP_MAIN_CONF_OFFSET, offsetof (ngx_http_cross_isp_main_conf_t, isp_rhost_cookie_name), NULL}, { ngx_string ("isp_taddr_cookie_name"), NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1, ngx_conf_set_str_slot, NGX_HTTP_MAIN_CONF_OFFSET, offsetof (ngx_http_cross_isp_main_conf_t, isp_taddr_cookie_name), NULL}, { ngx_string ("isp_cookies_expires"), NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1, ngx_conf_set_isp_cookies_expires, NGX_HTTP_MAIN_CONF_OFFSET, offsetof (ngx_http_cross_isp_main_conf_t, isp_cookies_expires), NULL}, { ngx_string ("isp_domain"), NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE4, ngx_conf_set_isp_domain, NGX_HTTP_MAIN_CONF_OFFSET, offsetof (ngx_http_cross_isp_main_conf_t, isp_domains), NULL}, { ngx_string ("cross_isp"), NGX_HTTP_MAIN_CONF | NGX_CONF_TAKE1, ngx_conf_set_isp_proxy, NGX_HTTP_MAIN_CONF_OFFSET, offsetof (ngx_http_cross_isp_main_conf_t, isp_proxies), NULL}, ngx_null_command }; static ngx_http_module_t ngx_http_cross_isp_module_ctx = { NULL, /* preconfiguration */ ngx_http_cross_isp_init, /* postconfiguration */ ngx_http_cross_isp_create_main_conf, /* create main configuration */ ngx_http_cross_isp_init_main_conf, /* init main configuration */ NULL, /* create server configuration */ NULL, /* merge server configuration */ NULL, /* create location configuration */ NULL /* merge location configuration */ }; ngx_module_t ngx_http_cross_isp_module = { NGX_MODULE_V1, &ngx_http_cross_isp_module_ctx, /* module context */ ngx_http_cross_isp_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 ngx_int_t ngx_http_cross_isp_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_ACCESS_PHASE].handlers); if (h == NULL) { return NGX_ERROR; } *h = ngx_http_cross_isp_handler; return NGX_OK; } static void * ngx_http_cross_isp_create_main_conf (ngx_conf_t * cf) { ngx_http_cross_isp_main_conf_t *iscf; iscf = ngx_pcalloc (cf->pool, sizeof (ngx_http_cross_isp_main_conf_t)); if (iscf == NULL) { return NGX_CONF_ERROR; } if (ngx_array_init (&iscf->isp_store, cf->pool, 20000, sizeof (ngx_isp_store_t)) != NGX_OK) { return NGX_CONF_ERROR; } if (ngx_array_init (&iscf->isp_domains, cf->pool, 10, sizeof (ngx_isp_domain_t)) != NGX_OK) { return NGX_CONF_ERROR; } if (ngx_array_init (&iscf->isp_proxies, cf->pool, 100, sizeof (ngx_isp_proxy_t)) != NGX_OK) { return NGX_CONF_ERROR; } iscf->port = NGX_CONF_UNSET_UINT; iscf->isp_cookies_expires = NGX_CONF_UNSET; return iscf; } static char * ngx_http_cross_isp_init_main_conf (ngx_conf_t * cf, void *conf) { MYSQL *conn = NULL; MYSQL_RES *result = NULL; MYSQL_ROW row; char query[80]; ngx_isp_store_t *elt; ngx_http_cross_isp_main_conf_t *iscf = conf; sprintf (query, "SELECT `ip_start`,`ip_end`,`isp` FROM `%s` ORDER BY `ip_start`", (char *) iscf->tb.data); conn = mysql_init (NULL); if (conn == NULL) { ngx_conf_log_error (NGX_LOG_EMERG, cf, 0, "init mysql error(host: %V port:%d user: %V password: %V database: %V table:%V)", iscf->host, iscf->port, iscf->pwd, iscf->db, iscf->tb); } mysql_real_connect (conn, (char *) iscf->host.data, (char *) iscf->usr.data, (char *) iscf->pwd.data, (char *) iscf->db.data, (unsigned int) iscf->port, NULL, 0); mysql_real_query (conn, query, strlen (query)); result = mysql_store_result (conn); if (result == NULL) { ngx_conf_log_error (NGX_LOG_EMERG, cf, 0, "can not get isp data from mysql(host: %V port:%d user: %V password: %V database: %V table:%V)", iscf->host, iscf->port, iscf->pwd, iscf->db, iscf->tb); return NGX_CONF_ERROR; } while ((row = mysql_fetch_row (result))) { elt = (ngx_isp_store_t *) ngx_array_push (&iscf->isp_store); elt->ip_start = strtol (row[0], NULL, 10); elt->ip_end = strtol (row[1], NULL, 10); ngx_strlow (elt->isp, (u_char *) row[2], 3); } mysql_free_result (result); mysql_close (conn); return NGX_CONF_OK; } static char * ngx_conf_set_isp_cookies_expires (ngx_conf_t * cf, ngx_command_t * cmd, void *conf) { ngx_http_cross_isp_main_conf_t *iscf = conf; ngx_str_t *value; if (iscf->isp_cookies_expires != NGX_CONF_UNSET) { return "is duplicate"; } value = cf->args->elts; if (ngx_strcmp (value[1].data, "max") == 0) { iscf->isp_cookies_expires = HTTP_COOKIES_MAX_EXPIRES; return NGX_CONF_OK; } if (ngx_strcmp (value[1].data, "off") == 0) { iscf->isp_cookies_expires = 0; return NGX_CONF_OK; } iscf->isp_cookies_expires = ngx_parse_time (&value[1], 1); if (iscf->isp_cookies_expires == NGX_ERROR) { return "invalid value"; } if (iscf->isp_cookies_expires == NGX_PARSE_LARGE_TIME) { return "value must be less than 68 years"; } return NGX_CONF_OK; } static char * ngx_conf_set_isp_domain (ngx_conf_t * cf, ngx_command_t * cmd, void *conf) { ngx_regex_compile_t rc; u_char errstr[NGX_MAX_CONF_ERRSTR]; char *p = conf; ngx_isp_domain_t *s; ngx_str_t *value; ngx_array_t *a; a = (ngx_array_t *) (p + cmd->offset); s = ngx_array_push (a); if (s == NULL) { return NGX_CONF_ERROR; } value = cf->args->elts; ngx_strlow (s->isp, value[1].data, 3); s->pattern = value[2]; s->replace = value[3]; s->domain = value[4]; ngx_memzero (&rc, sizeof (ngx_regex_compile_t)); rc.pattern = s->pattern; rc.pool = cf->pool; rc.err.len = NGX_MAX_CONF_ERRSTR; rc.err.data = errstr; if (ngx_regex_compile (&rc) != NGX_OK) { ngx_conf_log_error (NGX_LOG_EMERG, cf, 0, "%V", &rc.err); return NGX_CONF_ERROR; } s->regex = rc.regex; if (s->regex == NULL) { return NGX_CONF_ERROR; } return NGX_CONF_OK; } static char * ngx_conf_set_isp_proxy (ngx_conf_t * cf, ngx_command_t * cmd, void *conf) { ngx_regex_compile_t rc; u_char errstr[NGX_MAX_CONF_ERRSTR]; char *p = conf; ngx_isp_proxy_t *s; ngx_str_t *value; ngx_array_t *a; a = (ngx_array_t *) (p + cmd->offset); s = ngx_array_push (a); if (s == NULL) { return NGX_CONF_ERROR; } value = cf->args->elts; s->pattern = value[1]; ngx_memzero (&rc, sizeof (ngx_regex_compile_t)); rc.pattern = s->pattern; rc.pool = cf->pool; rc.err.len = NGX_MAX_CONF_ERRSTR; rc.err.data = errstr; if (ngx_regex_compile (&rc) != NGX_OK) { ngx_conf_log_error (NGX_LOG_EMERG, cf, 0, "%V", &rc.err); return NGX_CONF_ERROR; } s->regex = rc.regex; if (s->regex == NULL) { return NGX_CONF_ERROR; } return NGX_CONF_OK; } static ngx_int_t ngx_http_cross_isp_handler (ngx_http_request_t * r) { ngx_http_cross_isp_main_conf_t *ipmcf; ngx_int_t pc1 = NGX_DECLINED, pc2 = NGX_DECLINED, ssl, rc; ngx_uint_t i, m; ngx_uint_t ip; ngx_str_t str_uri; ngx_str_t isp_rhost_cookie, isp_taddr_cookie; ngx_isp_proxy_t *ipt; ngx_isp_store_t *ist; ngx_isp_domain_t *idt; u_char *u_uri, *u_relative_uri, *p, *u_isp_rhost_cookie, *u_isp_taddr_cookie, *taddr; struct sockaddr_in *sin; size_t str_uri_len, str_relative_uri_len, con_schema_len, taddr_len, cookie_expires_len; char *pt; long ip_pos; int cmp_r1, cmp_r2; ipmcf = ngx_http_get_module_main_conf (r, ngx_http_cross_isp_module); ipt = ipmcf->isp_proxies.elts; for (i = 0; i < ipmcf->isp_proxies.nelts; i++) { rc = ngx_regex_exec (ipt[i].regex, &r->uri, NULL, 0); if (rc == NGX_REGEX_NO_MATCHED) { continue; } pc1 = ngx_http_parse_multi_header_lines (&r->headers_in.cookies, &ipmcf->isp_rhost_cookie_name, &isp_rhost_cookie); pc2 = ngx_http_parse_multi_header_lines (&r->headers_in.cookies, &ipmcf->isp_taddr_cookie_name, &isp_taddr_cookie); sin = (struct sockaddr_in *) (r->connection->sockaddr); ip = ntohl (sin->sin_addr.s_addr); pt = inet_ntoa (sin->sin_addr); if (pt == NULL) { ngx_log_error (NGX_LOG_ERR, r->connection->log, 0, "inet_ntoa get taddr error"); return NGX_DECLINED; } taddr_len = strlen (pt); taddr = ngx_pcalloc (r->pool, taddr_len * sizeof (u_char)); if (taddr == NULL) { ngx_log_error (NGX_LOG_ERR, r->connection->log, 0, "ngx_pcalloc no args taddr error"); return NGX_DECLINED; } p = ngx_copy (taddr, pt, taddr_len); cmp_r1 = ngx_strncmp (isp_rhost_cookie.data, r->headers_in.host->value.data, isp_rhost_cookie.len); cmp_r2 = ngx_strncmp (isp_taddr_cookie.data, taddr, isp_taddr_cookie.len); if (pc1 != NGX_DECLINED && cmp_r1 == 0 && pc2 != NGX_DECLINED && cmp_r2 == 0) { return NGX_DECLINED; } ip_pos = ngx_isp_store_binary_search (ipmcf->isp_store, ip); if (ip_pos == -1) { return NGX_DECLINED; } ist = ipmcf->isp_store.elts; idt = ipmcf->isp_domains.elts; for (m = 0; m < ipmcf->isp_domains.nelts; m++) { if (ngx_strcmp (idt[m].isp, ist[ip_pos].isp) == 0) { rc = ngx_regex_exec (idt[m].regex, &r->headers_in.host->value, NULL, 0); if (rc >= 0) { if (ipmcf->isp_cookies_expires) { cookie_expires_len = sizeof (ngx_http_cookies_expires) - 1; } if (pc1 == NGX_DECLINED || cmp_r1 != 0) { u_isp_rhost_cookie = ngx_pcalloc (r->pool, (ipmcf->isp_rhost_cookie_name.len + 1 + r->headers_in. host->value.len + cookie_expires_len + 1 + HTTP_DOMAIN_COOKIE_NAME_LEN + 1 + idt[m].domain.len + 1) * sizeof (u_char)); if (u_isp_rhost_cookie == NULL) { ngx_log_error (NGX_LOG_ERR, r->connection->log, 0, "ngx_pcalloc no args u_isp_rhost_cookie error"); return NGX_DECLINED; } p = ngx_copy (u_isp_rhost_cookie, ipmcf->isp_rhost_cookie_name.data, ipmcf->isp_rhost_cookie_name.len); *p++ = '='; p = ngx_copy (p, r->headers_in.host->value.data, r->headers_in.host->value.len); if (ipmcf->isp_cookies_expires == HTTP_COOKIES_MAX_EXPIRES) { p = ngx_copy (p, ngx_http_cookies_expires, cookie_expires_len); } else if (ipmcf->isp_cookies_expires) { p = ngx_copy (p, ngx_http_cookies_expires, sizeof ("; expires=") - 1); p = ngx_http_cookie_time (p, ngx_time () + ipmcf->isp_cookies_expires); } *p++ = ';'; p = ngx_copy (p, ngx_http_domain_cookie_name, HTTP_DOMAIN_COOKIE_NAME_LEN); *p++ = '='; p = ngx_copy (p, idt[m].domain.data, idt[m].domain.len); *p++ = '\0'; ngx_http_write_isp_cookie (r, u_isp_rhost_cookie); } if (pc2 == NGX_DECLINED || cmp_r2 != 0) { u_isp_taddr_cookie = ngx_pcalloc (r->pool, (ipmcf->isp_taddr_cookie_name.len + 1 + taddr_len + cookie_expires_len + 1 + HTTP_DOMAIN_COOKIE_NAME_LEN + 1 + idt[m].domain.len + 1) * sizeof (u_char)); if (u_isp_taddr_cookie == NULL) { ngx_log_error (NGX_LOG_ERR, r->connection->log, 0, "ngx_pcalloc no args u_isp_taddr_cookie error"); return NGX_DECLINED; } p = ngx_copy (u_isp_taddr_cookie, ipmcf->isp_taddr_cookie_name.data, ipmcf->isp_taddr_cookie_name.len); *p++ = '='; p = ngx_copy (p, taddr, taddr_len); if (ipmcf->isp_cookies_expires == HTTP_COOKIES_MAX_EXPIRES) { p = ngx_copy (p, ngx_http_cookies_expires, cookie_expires_len); } else if (ipmcf->isp_cookies_expires) { p = ngx_copy (p, ngx_http_cookies_expires, sizeof ("; expires=") - 1); p = ngx_http_cookie_time (p, ngx_time () + ipmcf->isp_cookies_expires); } *p++ = ';'; p = ngx_copy (p, ngx_http_domain_cookie_name, HTTP_DOMAIN_COOKIE_NAME_LEN); *p++ = '='; p = ngx_copy (p, idt[m].domain.data, idt[m].domain.len); *p++ = '\0'; ngx_http_write_isp_cookie (r, u_isp_taddr_cookie); } return NGX_DECLINED; } else { ssl = r->connection->ssl == NULL ? 0 : 1; con_schema_len = ssl ? HTTPS_SCHEMA_LEN : HTTP_SCHEMA_LEN; if (r->args.data == NULL) { u_relative_uri = ngx_pcalloc (r->pool, (r->uri.len + 1) * sizeof (u_char)); if (u_relative_uri == NULL) { ngx_log_error (NGX_LOG_ERR, r->connection->log, 0, "ngx_pcalloc no args u_relative_uri error"); return NGX_DECLINED; } p = ngx_copy (u_relative_uri, r->uri.data, r->uri.len); *p++ = '\0'; } else { u_relative_uri = ngx_pcalloc (r->pool, (r->uri.len + r->args.len + 2) * sizeof (u_char)); if (u_relative_uri == NULL) { ngx_log_error (NGX_LOG_ERR, r->connection->log, 0, "ngx_pcalloc with args u_relative_uri error"); return NGX_DECLINED; } p = ngx_copy (u_relative_uri, r->uri.data, r->uri.len); *p++ = '?'; p = ngx_copy (p, r->args.data, r->args.len); *p++ = '\0'; } str_relative_uri_len = ngx_strlen (u_relative_uri); str_uri_len = con_schema_len + idt[m].replace.len + str_relative_uri_len; u_uri = ngx_pcalloc (r->pool, str_uri_len * sizeof (u_char)); if (u_uri == NULL) { ngx_log_error (NGX_LOG_ERR, r->connection->log, 0, "ngx_pcalloc no args u_uri error"); return NGX_DECLINED; } p = (ssl ? ngx_copy (u_uri, ngx_https_schema, con_schema_len) : ngx_copy (u_uri, ngx_http_schema, con_schema_len)); p = ngx_copy (p, idt[m].replace.data, idt[m].replace.len); p = ngx_copy (p, u_relative_uri, str_relative_uri_len); str_uri.len = str_uri_len; str_uri.data = u_uri; return ngx_cross_isp_redirect (r, str_uri); } } } } return NGX_DECLINED; } static long ngx_isp_store_binary_search (ngx_array_t data, ngx_uint_t ip) { long low = -1; long high = data.nelts; ngx_uint_t mid; ngx_isp_store_t *ist = data.elts; while (high - low > 1) { mid = (low + high) >> 1; if (ist[mid].ip_start > ip) { high = mid; } else { low = mid; } } if ((low == -1) || !(ist[low].ip_start <= ip && ip <= ist[low].ip_end)) { return -1; } else { return low; } } static ngx_int_t ngx_cross_isp_redirect (ngx_http_request_t * r, ngx_str_t url) { ngx_buf_t *b; ngx_chain_t out; size_t ngx_http_302_page_len = ngx_strlen (ngx_http_302_page); r->headers_out.content_type.len = sizeof ("text/html") - 1; r->headers_out.content_type.data = (u_char *) "text/html"; b = ngx_pcalloc (r->pool, sizeof (ngx_buf_t)); out.buf = b; out.next = NULL; b->pos = ngx_http_302_page; b->last = ngx_http_302_page + ngx_http_302_page_len; b->memory = 1; b->last_buf = 1; r->headers_out.status = NGX_HTTP_MOVED_TEMPORARILY; r->headers_out.location = ngx_list_push (&r->headers_out.headers); r->headers_out.location->hash = 1; ngx_str_set (&r->headers_out.location->key, "Location"); r->headers_out.location->value = url; r->headers_out.content_length_n = ngx_http_302_page_len; ngx_http_send_header (r); return ngx_http_write_filter (r, &out); } static void ngx_http_write_isp_cookie (ngx_http_request_t * r, u_char * cookie) { ngx_table_elt_t *set_cookie; set_cookie = ngx_list_push (&r->headers_out.headers); if (set_cookie == NULL) { return; } set_cookie->hash = 1; set_cookie->key.len = sizeof ("Set-Cookie") - 1; set_cookie->key.data = (u_char *) "Set-Cookie"; set_cookie->value.len = ngx_strlen (cookie); set_cookie->value.data = cookie; }
nginx cross isp module将会开源在http://code.google.com/p/easy-net/,后期将会对现有功能进行改进。
Tags:  nginx安装 nginx重启 nginx配置 nginx

延伸阅读

最新评论

发表评论