upstream demo
目录
一 upstream的使用方式
二.upstream的配置
三 upstream的使用示例
四 实验
正文
系列文章:Nginx upstream模块与负载均衡模块分析_fdsafwagdagadg6576的专栏-CSDN博客
Subrequest实例分析_fdsafwagdagadg6576的专栏-CSDN博客
本文是nginx访问第三方服务之upstream使用_上善若水-CSDN博客 实例的详细讲解
当需要访问第三方服务时,Nginx提供了2种异步方式来与第三方服务器通信:upstream与subrequest.
本文将介绍upstream的使用方式,Nginx的HTTP反向代理模块就是基于upstream方式实现,当我们希望把第三方服务的内容几乎原封不懂地返回给用户时,一般使用upstream方式,它可以非常高效地透传HTTP。
Nginx访问上游服务器的流程大致分以下几个阶段:
启动upstream、连接上游服务器、向上游发送请求、接收上游响应(包头/包体)、结束请求。
一.upstream的配置
0 主要结构体说明
- ngx_http_upstream_t
struct ngx_http_upstream_s {//...//读写事件处理handlerngx_http_upstream_handler_pt read_event_handler;ngx_http_upstream_handler_pt write_event_handler;//该结构封装了连接获取/释放handler,不同负载均衡策略对应不同handlerngx_peer_connection_t peer;//各种缓冲区buffer//回调handler:创建请求,处理上游返回头等的回调。//proxypass与fastcgipass都实现了这些回调ngx_int_t (*create_request)(ngx_http_request_t *r);ngx_int_t (*reinit_request)(ngx_http_request_t *r);ngx_int_t (*process_header)(ngx_http_request_t *r);void (*abort_request)(ngx_http_request_t *r);void (*finalize_request)(ngx_http_request_t *r,ngx_int_t rc);ngx_int_t (*rewrite_redirect)(ngx_http_request_t *r,ngx_table_elt_t *h, size_t prefix);ngx_int_t (*rewrite_cookie)(ngx_http_request_t *r,ngx_table_elt_t *h);ngx_chain_t *request_bufs;//发什么样的请求给上游服务器,create_request中实现ngx_http_upstream_resolved_t *resolved;//可直接指定上游服务器的地址ngx_buf_t buffer;//buffer存储接收上游服务器的响应内容。复用//向客户端转发上游服务器的包体时才有用。//1-多个缓冲区以及磁盘文件转发(Nginx与上游间的网速远大于Nginx与下游客户端之间的网速)//0-只使用该结构体中的buffer缓冲区向下游转发响应包体unsigned buffering:1;//...
};
- 设置需要访问的第三方服务地址resolved成员
这部分就是熟悉套接字编程了
typedef struct{//...ngx_uint_t naddrs;struct sockaddr *sockaddr;//上游服务器地址socklen_t socklen;//...
}ngx_http_upstream_resolved_t;
二 upstream的使用方式
upstream的使用方式并不复杂,它提供了8个回调方法,用户需要实现的其中的几个回调方法,
本例中实现了3个回调方法:
struct ngx_http_upstream_s{
//...ngx_int_t (*create_request)(ngx_http_request_t *r);//构造发往上游服务器的请求
//收到上游服务器的TCP流时回调,直到不返回NGX_AGAIN,处理http响应ngx_int_t (*process_header)(ngx_http_request_t *r);void (*finalize_request)(ngx_http_request_t *r,ngx_int_t rc);//销毁upstream时回调
//...
};
0 那么upstream是如何嵌入到一个请求中的?
模块在处理任何一个请求时都有ngx_http_request_t结构对象r,该结构中有一个ngx_http_upstream_t类型的成员upstream(模块对象创建)
struct ngx_http_request_s
{//...ngx_http_upstream_t *upstream;//...
}
如果没有使用upstream机制,则需要将相应的成员置为NULL,否则需要对r->upstream进行设置.
1 HTTP模块启用upstream机制的步骤如下:
- 调用ngx_http_upstream_create方法为请求创建upstream
- 设置第三方服务器地址
- 设置upstream的回调方法
- 调用ngx_http_upstream_init方法启动upstream
2 HTTP如何运行upstream框架
使用upstream模块提供的ngx_http_upstream_init后,HTTP如何运行upstream框架,大致流程如下
- 创建发往上游服务器的请求
- 与上游服务器建立无阻塞TCP连接
- 发送请求到第三方服务
- 接收第三方服务响应并处理包头和包体
- 回调finalize_request销毁请求
整个处理过程中会调用 upstream 的回调方法进行相应的处理,其中最常用的回调方法为
ngx_http_upstream_t结构体中有8个回调方法,不过必须实现的有3个回调方法:
create_request:创建请求
process_header:处理响应
finalize_request:销毁请求
注,upstream 模块处理上游包体的方式
1.当请求结构体ngx_http_request_t中的成员subrequest_in_memory标志位为1时,upstream不转发响应包体到下游,并由HTTP模块实现的input_filter()方法处理包体;
2.当请求结构体ngx_http_request_t中的成员subrequest_in_memory标志位为0时,且ngx_http_upstream_conf_t配置结构体中的成员buffering标志位为1时,upstream将开启更多的内存和磁盘文件用于缓存上游的响应包体(此时,上游网速更快),并转发响应包体;
3.当请求结构体ngx_http_request_t中的成员subrequest_in_memory标志位为0时,且ngx_http_upstream_conf_t配置结构体中的成员buffering标志位为0时,upstream将使用固定大小的缓冲区来转发响应包体;
三 upstream的使用示例
书中给出google的URL搜索示例,由于墙的原因,我们使用baidu,其URL搜索方法是www.baidu.com/s?wd=haha在nginx.conf配置location,例如:
location /test{mytest;
}
如果访问URL是/test?lumia则可以通过upstream机制向www.baidu.com发送搜索请求。
完整代码
源码来源自深入理解Nginx,注释包含自己的理解。
0 定义结构体和声明函数
//ngx_http_mytest_module.c#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
/*上下文结构体*/
typedef struct
{ngx_http_status_t status;ngx_str_t backendServer;
}ngx_http_mytest_ctx_t;
/*uptream相关配置,例如超时等待时间等*/
typedef struct
{ngx_http_upstream_conf_t upstream;
} ngx_http_mytest_conf_t;
/*配置项处理函数*/
static char *
ngx_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
/*真正的处理函数*/
/*设置upstream的host、回调函数、启动upstream等*/
static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r);
/*创建ngx_http_mytest_conf_t结构体,硬编码参数*/
static void* ngx_http_mytest_create_loc_conf(ngx_conf_t *cf);
/*设置hide_headers_hash*/
static char *ngx_http_mytest_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child);
/*解析http头部*/
static ngx_int_t
mytest_upstream_process_header(ngx_http_request_t *r);
/*处理http响应行*/
static ngx_int_t
mytest_process_status_line(ngx_http_request_t *r);
/*安全考虑隐藏头部*/
static ngx_str_t ngx_http_proxy_hide_headers[] =
{ngx_string("Date"),ngx_string("Server"),ngx_string("X-Pad"),ngx_string("X-Accel-Expires"),ngx_string("X-Accel-Redirect"),ngx_string("X-Accel-Limit-Rate"),ngx_string("X-Accel-Buffering"),ngx_string("X-Accel-Charset"),ngx_null_string
};
1 创建模块
1.1 配置项,上下文,指令 (三段式)
/*command 结构体数组*/
static ngx_command_t ngx_http_mytest_commands[] =
{{ngx_string("mytest"),NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_NOARGS,ngx_http_mytest,NGX_HTTP_LOC_CONF_OFFSET,0,NULL},ngx_null_command
};
/*模块上下文*/
static ngx_http_module_t ngx_http_mytest_module_ctx =
{NULL, /* preconfiguration */NULL, /* postconfiguration */NULL, /* create main configuration */NULL, /* init main configuration */NULL, /* create server configuration */NULL, /* merge server configuration */ngx_http_mytest_create_loc_conf,/* create location configuration */ngx_http_mytest_merge_loc_conf /* merge location configuration */
};
/*nginx 模块*/
ngx_module_t ngx_http_mytest_module =
{NGX_MODULE_V1,&ngx_http_mytest_module_ctx, /* module context */ngx_http_mytest_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
};
1.2 执行指令
static char *
ngx_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{ngx_http_core_loc_conf_t *clcf;/*1 找到mytest配置项所属的配置块,clcf是main、srv或者loc级别配置项,也就是说在每个http{},server{},locate{}内都有一个ngx_http_core_loc_conf_t结构体*/clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);/*2 http框架在处理用户请求进行到NGX_HTTP_CONTENT_PHASE阶段时,如果请求的主机域名、URI与mytest配置项所在的配置块相匹配,就将调用我们*//*实现的ngx_http_mytest_handler方法处理这个请求*/clcf->handler = ngx_http_mytest_handler;return NGX_CONF_OK;
}
1.3 实现自定义handler
static ngx_int_t
ngx_http_mytest_handler(ngx_http_request_t *r)
{/*建立http上下文结构体ngx_http_mytest_ctx_t*/ngx_http_mytest_ctx_t* myctx = ngx_http_get_module_ctx(r, ngx_http_mytest_module);if (myctx == NULL){myctx = ngx_palloc(r->pool, sizeof(ngx_http_mytest_ctx_t));/*将新建的上下文与请求关联起来*/ngx_http_set_ctx(r, myctx, ngx_http_mytest_module);}/*对每一个要使用upstream的请求,必须调用且只能调用1次*//*ngx_http_upstream_create方法,它会初始化r->upstream成员*///1 创建upstream 模块对象ngx_http_upstream_create(r);/*得到配置upstream结构体ngx_http_mytest_conf_t*/ngx_http_mytest_conf_t *mycf = (ngx_http_mytest_conf_t*) ngx_http_get_module_loc_conf(r, ngx_http_mytest_module);/*2 创建之后,配置upstream*/ngx_http_upstream_t *u = r->upstream;/*这里用配置文件中的结构体来赋给r->upstream->conf成员*//*ngx_http_upstream_conf_t结构*/u->conf = &mycf->upstream;/*3 决定转发包体时使用的缓冲区*/u->buffering = mycf->upstream.buffering;/*4 设置上游服务器
// 以下代码开始初始化resolved结构体,用来保存上游服务器的地址u->resolved = (ngx_http_upstream_resolved_t*) ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t));/*这里的上游服务器就是www.baidu.com*/static struct sockaddr_in backendSockAddr;struct hostent *pHost = gethostbyname((char*) "www.baidu.com");/*访问上游服务器的80端口*/backendSockAddr.sin_family = AF_INET;backendSockAddr.sin_port = htons((in_port_t) 80);memcpy(&backendSockAddr.sin_addr,pHost->h_addr_list[0],sizeof(struct in_addr));char* pDmsIP = inet_ntoa(*(struct in_addr*) (pHost->h_addr_list[0]));/*inet_addr*///backendSockAddr.sin_addr.s_addr = inet_addr(pDmsIP);myctx->backendServer.data = (u_char*)pDmsIP;myctx->backendServer.len = strlen(pDmsIP);/*将地址设置到resolved成员中*/u->resolved->sockaddr = (struct sockaddr *)&backendSockAddr;u->resolved->socklen = sizeof(struct sockaddr_in);u->resolved->naddrs = 1;/*5 设置三个必须实现的回调方法:创建请求、处理头部、请求销毁*/u->create_request = mytest_upstream_create_request;u->process_header = mytest_process_status_line;u->finalize_request = mytest_upstream_finalize_request;/*这里必须将count成员加1*/r->main->count++;/*6 启动upstream*/ngx_http_upstream_init(r);/*必须返回NGX_DONE*/return NGX_DONE;
}
- 1) 创建upstream 模块对象 ngx_http_upstream_create(r);
对每一个要使用upstream的请求,必须调用且只能调用1次 - 2) 创建之后,配置upstream
ngx_http_upstream_t *u = r->upstream; u->conf = &mycf->upstream .....
- 3) 决定转发包体时使用的缓冲区
u->buffering = mycf->upstream.buffering;
- 4) 设置上游服务器
u->resolved = (ngx_http_upstream_resolved_t*) ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t));/*这里的上游服务器就是www.baidu.com*/static struct sockaddr_in backendSockAddr;struct hostent *pHost = gethostbyname((char*) "www.baidu.com");/*访问上游服务器的80端口*/backendSockAddr.sin_family = AF_INET;backendSockAddr.sin_port = htons((in_port_t) 80);.......
- 5) 设置三个必须实现的回调方法:创建请求、处理头部、请求销毁
u->create_request = mytest_upstream_create_request;u->process_header = mytest_process_status_line;u->finalize_request = mytest_upstream_finalize_request;
- 6) 启动upstream
ngx_http_upstream_init(r);
1.5 create config & merge config
static void*
ngx_http_mytest_create_loc_conf(ngx_conf_t *cf)
{ngx_http_mytest_conf_t *mycf;mycf = (ngx_http_mytest_conf_t*)ngx_pcalloc(cf->pool,sizeof(ngx_http_mytest_conf_t));/*对结构体变量中的成员进行硬编码,超时连接相关*/mycf->upstream.connect_timeout = 60000;mycf->upstream.send_timeout = 60000;mycf->upstream.read_timeout = 60000;mycf->upstream.store_access = 0600;/*处理上游服务器包体方式相关,这里以固定buffer转发包体*/mycf->upstream.buffering = 0;mycf->upstream.bufs.num = 8;mycf->upstream.bufs.size = ngx_pagesize;mycf->upstream.buffer_size = ngx_pagesize;mycf->upstream.busy_buffers_size = 2 * ngx_pagesize;mycf->upstream.temp_file_write_size = 2 * ngx_pagesize;mycf->upstream.max_temp_file_size = 1024 * 1024 * 1024;/*upstream模块要求hide_headers必须初始化*/mycf->upstream.hide_headers = NGX_CONF_UNSET_PTR;mycf->upstream.pass_headers = NGX_CONF_UNSET_PTR;return mycf;
}
static char *
ngx_http_mytest_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{ngx_http_mytest_conf_t *prev = (ngx_http_mytest_conf_t *)parent;ngx_http_mytest_conf_t *conf = (ngx_http_mytest_conf_t *)child;ngx_hash_init_t hash;hash.max_size = 100;hash.bucket_size = 1024;hash.name = "proxy_headers_hash";/*hide header*/ngx_http_upstream_hide_headers_hash(cf, &conf->upstream,&prev->upstream, ngx_http_proxy_hide_headers, &hash)return NGX_CONF_OK;
}
2 创建发给上游服务器的请求 create_request
在ngx_http_mytest_handler 中设置,在ngx_http_upstream_init中调用.
create_request 是必须实现的业务逻辑函数
static ngx_int_t
mytest_upstream_create_request(ngx_http_request_t *r)
{//=====1 构造请求string=====/*模仿baidu搜索请求 /s?wd= */static ngx_str_t backendQueryLine =ngx_string("GET /s?wd=%V HTTP/1.1\r\nHost: www.baidu.com\r\nConnection: close\r\n\r\n");/*请求长度 -2表示 %V*/ngx_int_t queryLineLen = backendQueryLine.len + r->args.len - 2;/*内存池申请内存,请求结束时内存被自动释放*/ngx_buf_t* b = ngx_create_temp_buf(r->pool, queryLineLen);/*last要指向请求的末尾*/b->last = b->pos + queryLineLen;/*作用相当于snprintf*/ngx_snprintf(b->pos, queryLineLen ,(char*)backendQueryLine.data, &r->args);/*=====2 创建发送给上游服务器的请求=====*/r->upstream->request_bufs = ngx_alloc_chain_link(r->pool);/*request_bufs这里只包含1个ngx_buf_t缓冲区*/r->upstream->request_bufs->buf = b;r->upstream->request_bufs->next = NULL;r->upstream->request_sent = 0;r->upstream->header_sent = 0;/*header_hash不可以为0*/r->header_hash = 1;return NGX_OK;
}
3 process header
框架调用.此函数表示已经从第三方获取了respond.
mytest_process_status_line is process_header.
- 解析http响应行
rc = ngx_http_parse_status_line(r, &u->buffer, &ctx->status); - 赋值操作
- 之后收到的新字符流将有新的回调函数解析
u->process_header = mytest_upstream_process_header; - 如果本次收到的字符流除了http响应行外,还有多余的字符,
将由mytest_upstream_process_header方法解析
mytest_upstream_process_header(r);
static ngx_int_t
mytest_process_status_line(ngx_http_request_t *r)
{size_t len;ngx_int_t rc;ngx_http_upstream_t *u;/*取出http请求的上下文*/ngx_http_mytest_ctx_t* ctx = ngx_http_get_module_ctx(r, ngx_http_mytest_module);u = r->upstream;/* 1 解析http响应行*/rc = ngx_http_parse_status_line(r, &u->buffer, &ctx->status);/*NGX_AGAIN表示继续解析*/if (rc == NGX_AGAIN){return rc;}/*解析到完整到响应行,将解析出来的内容赋值到headers_in结构体中*/if (u->state){u->state->status = ctx->status.code;}/* 2 赋值操作*/u->headers_in.status_n = ctx->status.code;len = ctx->status.end - ctx->status.start;u->headers_in.status_line.len = len;u->headers_in.status_line.data = ngx_pnalloc(r->pool, len);ngx_memcpy(u->headers_in.status_line.data, ctx->status.start, len); /*开始解析http响应头部*//*3 之后收到的新字符流将有新的回调函数解析*/u->process_header = mytest_upstream_process_header;/*如果本次收到的字符流除了http响应行外,还有多余的字符*//*4 将由mytest_upstream_process_header方法解析*/return mytest_upstream_process_header(r);
}
mytest_upstream_process_header
/*处理http头部*/
static ngx_int_t
mytest_upstream_process_header(ngx_http_request_t *r)
{ngx_int_t rc;/*为http头部量身定制*//*例如 key 存储"Content-Length"*//*value 存储 "1024" */ngx_table_elt_t *h;ngx_http_upstream_header_t *hh;ngx_http_upstream_main_conf_t *umcf;/*将upstream模块配置项ngx_http_upstream_main_conf_t取了*//*该结构体中存储了需要做统一处理的http头部名称和回调方法*/umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);/*1 循环的解析所有的http头部*/for ( ;; ){/*解析http头部*/rc = ngx_http_parse_header_line(r, &r->upstream->buffer, 1);/*NGX_OK解析出一行http头部*/if (rc == NGX_OK){/*向headers_in.headers这个ngx_list_t链表中添加http头部*//*ngx_list_t是先插入再赋值*/h = ngx_list_push(&r->upstream->headers_in.headers);/*构造刚刚添加到headers链表中的http头部*/h->hash = r->header_hash;/*key-头部名称 value-对应的值*/h->key.len = r->header_name_end - r->header_name_start;h->value.len = r->header_end - r->header_start;/*zx:作者这里分配这么大空间的原因在于,在结构体中有三个变量*///1.key//2.value//3.lowcase_case/*一次申请这一整段空间,+1用来'\0'来区分*//*这也在于ngx_str_t的特性在于data字段记录的只是字串地址*/h->key.data = ngx_pnalloc(r->pool,h->key.len + 1 + h->value.len + 1 + h->key.len);/*赋值操作*/h->value.data = h->key.data + h->key.len + 1;h->lowcase_key = h->key.data + h->key.len + 1 + h->value.len + 1;ngx_memcpy(h->key.data, r->header_name_start, h->key.len);h->key.data[h->key.len] = '\0';ngx_memcpy(h->value.data, r->header_start, h->value.len);h->value.data[h->value.len] = '\0';if (h->key.len == r->lowcase_index){ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);}else{ngx_strlow(h->lowcase_key, h->key.data, h->key.len);}/*upstream模块会对一些http头部做特殊处理*/hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,h->lowcase_key, h->key.len);if (hh && hh->handler(r, h, hh->offset) != NGX_OK){return NGX_ERROR;}continue;}/*返回NGX_HTTP_PARSE_HEADER_DONE表示响应中所有的http头部都解析*/if (rc == NGX_HTTP_PARSE_HEADER_DONE){/*如果之前解析http头部时没有发现server和date头部*//*据http协议添加这两个头部*/if (r->upstream->headers_in.server == NULL){h = ngx_list_push(&r->upstream->headers_in.headers);/*zx:查看ngx_hash_key_lc/ngx_hash_key源码可知,可以改写*/ngx_str_t str=ngx_string("server");h->hash=ngx_hash_key_lc(str.data,str.len);#if 0h->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash(ngx_hash('s', 'e'), 'r'), 'v'),'e'), 'r');#endifngx_str_set(&h->key, "Server");ngx_str_null(&h->value);h->lowcase_key = (u_char *) "server";}if (r->upstream->headers_in.date == NULL){h = ngx_list_push(&r->upstream->headers_in.headers);ngx_str_t str=ngx_string("date");/*zx:同上*/h->hash=ngx_hash_key_lc(str.data,str.len);#if 0h->hash = ngx_hash(ngx_hash(ngx_hash('d', 'a'), 't'), 'e');#endifngx_str_set(&h->key, "Date");ngx_str_null(&h->value);h->lowcase_key = (u_char *) "date";}return NGX_OK;}/*没有解析到完整的http头部,继续接收新的字符流*/if (rc == NGX_AGAIN){return NGX_AGAIN;}/*其他返回值都是非法的*/ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,"upstream sent invalid header");return NGX_HTTP_UPSTREAM_INVALID_HEADER;}
}
4 finalize_request
/*finalize_request回调 请求结束前会调用*/
static void
mytest_upstream_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
{ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0,"mytest_upstream_finalize_request");
}
四 实验
将模块编入nginx,启动nginx服务,curl一下,百度就出来了。
curl -v localhost:1024/test?lumia
如图:
小结:
upstream和subrequest类似,也是使用框架,直接实现callback函数,不需要自己实现内部通信.
upstream demo相关推荐
- Nginx系列--upstream模块的使用
原文网址:Nginx系列--upstream模块的使用_IT利刃出鞘的博客-CSDN博客 简介 说明 本文介绍nginx的upstream模块的使用. nginx的upstream模块 ...
- windows下nginx的简单使用
在网上是可以查得到关于nginx的一些使用,每次要用的时候都会去查一下别人的用法.既然自己用到的频率会比较高,那何不自己做个小小的笔记呢. 我使用的是windows版nginx-1.14.0,版本对命 ...
- nginx代理二级目录
在nginx中配置proxy_pass代理转发时,如果在proxy_pass后面的url加/,表示绝对根路径:如果没有/,表示相对路径,把匹配的路径部分也给代理走. upstream demo { s ...
- 浅谈网络代理的两大分类和简单实现
目录 ●代理是什么 ●正向代理 ●正向代理服务器的简单实现 ●反向代理 ●反向代理服务器的简单实现 ●小结 ●代理是什么 代理一词在不同语境有不同的含义.汉语中的解释是"暂时代人担任某单位的 ...
- Nginx--大型网站高并发处理
Nginx–>大型网站高并发处理 文章目录 **Nginx**-->**大型网站高并发处理** 一,产生背景 二,负载均衡(Load Balance) 2.1 高并发 2.2 负载均衡 2 ...
- pythonw双击没反应快捷方式是ride_RIDE安装遇到的问题及解决方法
1.按照虫师的方法,下载的wxpython3.0 ,启动robotframework-ride,无效,因为版本不一致,所以我又根据终端提示的网址:http://sourceforge.net/proj ...
- Nginx的原理、常用配置和生产案例应用
目录 Nginx的概念 Nginx的系统架构 Nginx的服务过程 Nginx.conf配置讲解 自定义日志格式 Location语法 Nginx的具体应用 一.Nginx+Lua实现动态黑名单 二. ...
- 使用NGINX Plus API动态配置upstream
使用NGINX Plus API动态配置upstream 本章将介绍如何配置上游服务器和上游服务器群动态与NGINX加REST API. 总览 先决条件 启用动态配置 使用API进行动态配置 互动 ...
- springcloud注册demo(使⽤第⼀代Spring Cloud核⼼组件完成项⽬构建、编码及测试)
一.业务描述 以注册.登录为主线,串联起验证码生成及校验.邮件发送.IP防暴刷.用户统一认证等功能. 实现需基于Spring Cloud 微服务架构,技术涉及Nginx.Eureka.Feign(Ri ...
最新文章
- 全过程实现一个最简单的FPGA项目之PWM蜂鸣器控制
- JAVA中操作符的优先级
- Python3 PyQt5 PyCharm 环境搭建
- JavaScript编程语言 基础 (1)
- Oracle数据库自动存储管理(ASM)
- 【2016年第4期】突发大数据在存储辅助光电路交换网络中的传输
- 三菱fx5u modbus tcp fb块用法_三菱PLC型号怎么选?四大方面告诉你三菱FX3U和5U的最大区别!...
- H - A Shooting Game
- Toeplitz matrix 与 Circulant matrix
- Git教程_3 IDEA管理
- struts使用拦截器注解
- 将APP变成黑白的颜色
- 写入iCloud在模拟器和真机上失败的解决办法
- 7-1 统计大写辅音字母 (15 分)
- Cadence OrCAD Capture CIS ODBC数据库文件在两台电脑上同步使用时一台电脑启动失败的问题解决图文教程
- win10官方iso镜像下载教程
- 冰火魔界服务器维修,冰火魔界手游在电脑上怎么玩 冰火魔界电脑版安装方法...
- 美女上班迟到的N个理由
- C#判断中文 日文 韩文
- win10怎么改管理员名字_新手必看,如何装最纯净的win10系统。