upstream 及其相关的数据结构

2015-10-29 17:07:13   最后更新: 2015-10-30 11:45:42   访问数量:1319




nginx 作为高性能的 webserver,是不能容忍阻塞式的第三方调用的

当需要访问第三方服务时,Nginx 提供了两种全异步的第三方通信方式:upstream 和 subrequest

upstream 保证在与第三服务器交互时(包括建立连接、发送请求、接收响应、关闭连接等)不会阻塞 Nginx 的正常流程,这样保证了 nginx 在调用第三方服务时的高性能

subrequest 提供了复杂请求分解后请求的设计模式

 

upstream 的设计目标是从上游服务器获取数据,然后透传给下游,nginx 因此变成以一个代理服务器,nginx 的反向代理模块就是基于 upstream 实现的

subrequest 实现了讲一个主请求拆分为多个子请求,父请求在完全获取上游服务器的响应后再决定如何处理这次请求

 

如果希望把第三方服务内容几乎原封不动地返回给用户,则需要使用 upstream 方式,upstream 实现了非常高效的透传 HTTP

如果希望 nginx 在获取第三方服务的某些信息后再根据这些信息构造响应并发送给用户,则需要使用 subrequest 的方式

 

在 ngx_http_request_s 请求描述结构中,有一个字段 upstream,默认为空指针,可以通过调用 ngx_http_upstream_create 方法来创建 upstream:

// struct ngx_http_request_s // HTTP 请求描述结构 {{{ struct ngx_http_request_s { uint32_t signature; /* "HTTP" */ // 请求对应的连接结构 ngx_connection_t *connection; // HTTP 模块上下文 void **ctx; // main 配置 void **main_conf; // server 配置 void **srv_conf; // local 配置 void **loc_conf; // 事件读写回调函数 ngx_http_event_handler_pt read_event_handler; ngx_http_event_handler_pt write_event_handler; #if (NGX_HTTP_CACHE) ngx_http_cache_t *cache; #endif // upstream 描述结构 ngx_http_upstream_t *upstream; ngx_array_t *upstream_states; /* of ngx_http_upstream_state_t */ // 内存池 ngx_pool_t *pool; // 保存一些消息体的内容 ngx_buf_t *header_in; // 请求头部描述结构 ngx_http_headers_in_t headers_in; // 响应头部描述结构 ngx_http_headers_out_t headers_out; // 请求体描述结构 ngx_http_request_body_t *request_body; // 请求相关时间戳 time_t lingering_time; time_t start_sec; ngx_msec_t start_msec; // HTTP 请求方法 // NGX_HTTP_GET // NGX_HTTP_PUT // NGX_HTTP_POST // NGX_HTTP_COPY // NGX_HTTP_MOVE // NGX_HTTP_LOCK // NGX_HTTP_HEAD // NGX_HTTP_MKCOL // NGX_HTTP_PATCH // NGX_HTTP_TRACE // NGX_HTTP_DELETE // NGX_HTTP_UNLOCK // NGX_HTTP_OPTIONS // NGX_HTTP_PROPFIND // NGX_HTTP_PROPPATCH ngx_uint_t method; // HTTP 版本 ngx_uint_t http_version; // 请求串 ngx_str_t request_line; // URI ngx_str_t uri; // 参数 ngx_str_t args; // 额外信息 ngx_str_t exten; ngx_str_t unparsed_uri; ngx_str_t method_name; ngx_str_t http_protocol; // 上次还没发完的 buf ngx_chain_t *out; // 当前 request 链中最上面的 request ngx_http_request_t *main; // 当前 request 的父 request ngx_http_request_t *parent; // 缓存父 request 数据(也就是将要发送数据的request) ngx_http_postponed_request_t *postponed; // 保存子请求的 post request,也就是保存了需要被发送的 request ngx_http_post_subrequest_t *post_subrequest; // 所有需要处理的 request 链表,也就是说它即包含子请求也包含父请求 ngx_http_posted_request_t *posted_requests; // 请求处理阶段 ngx_int_t phase_handler; // 生成内容的处理函数 ngx_http_handler_pt content_handler; ngx_uint_t access_code; // 所有变量 ngx_http_variable_value_t *variables; #if (NGX_PCRE) ngx_uint_t ncaptures; int *captures; u_char *captures_data; #endif // 发送限制速率 size_t limit_rate; size_t limit_rate_after; /* used to learn the Apache compatible response length without a header */ // 请求头大小 size_t header_size; // 请求长度 off_t request_length; // 状态 ngx_uint_t err_status; // http 连接结构 ngx_http_connection_t *http_connection; #if (NGX_HTTP_SPDY) ngx_http_spdy_stream_t *spdy_stream; #endif // log 回调 ngx_http_log_handler_pt log_handler; ngx_http_cleanup_t *cleanup; unsigned subrequests:8; unsigned count:8; unsigned blocked:8; unsigned aio:1; unsigned http_state:4; /* URI with "/." and on Win32 with "//" */ unsigned complex_uri:1; /* URI with "%" */ unsigned quoted_uri:1; /* URI with "+" */ unsigned plus_in_uri:1; /* URI with " " */ unsigned space_in_uri:1; // 是否有效 unsigned invalid_header:1; unsigned add_uri_to_alias:1; unsigned valid_location:1; unsigned valid_unparsed_uri:1; unsigned uri_changed:1; unsigned uri_changes:4; unsigned request_body_in_single_buf:1; unsigned request_body_in_file_only:1; unsigned request_body_in_persistent_file:1; unsigned request_body_in_clean_file:1; unsigned request_body_file_group_access:1; unsigned request_body_file_log_level:3; unsigned subrequest_in_memory:1; unsigned waited:1; #if (NGX_HTTP_CACHE) unsigned cached:1; #endif #if (NGX_HTTP_GZIP) unsigned gzip_tested:1; unsigned gzip_ok:1; unsigned gzip_vary:1; #endif unsigned proxy:1; unsigned bypass_cache:1; unsigned no_cache:1; /* * instead of using the request context data in * ngx_http_limit_conn_module and ngx_http_limit_req_module * we use the single bits in the request structure */ unsigned limit_conn_set:1; unsigned limit_req_set:1; #if 0 unsigned cacheable:1; #endif unsigned pipeline:1; unsigned chunked:1; // 只返回 HEADER unsigned header_only:1; unsigned keepalive:1; unsigned lingering_close:1; unsigned discard_body:1; unsigned internal:1; unsigned error_page:1; unsigned filter_finalize:1; unsigned post_action:1; unsigned request_complete:1; unsigned request_output:1; unsigned header_sent:1; unsigned expect_tested:1; unsigned root_tested:1; unsigned done:1; unsigned logged:1; unsigned buffered:4; unsigned main_filter_need_in_memory:1; unsigned filter_need_in_memory:1; unsigned filter_need_temporary:1; unsigned allow_ranges:1; unsigned single_range:1; unsigned disable_not_modified:1; #if (NGX_STAT_STUB) unsigned stat_reading:1; unsigned stat_writing:1; #endif /* used to parse HTTP headers */ // HTTP 头部解析的过程中存储的解析状态 ngx_uint_t state; ngx_uint_t header_hash; ngx_uint_t lowcase_index; u_char lowcase_header[NGX_HTTP_LC_HEADER_LEN]; u_char *header_name_start; u_char *header_name_end; u_char *header_start; u_char *header_end; /* * a memory that can be reused after parsing a request line * via ngx_http_ephemeral_t */ u_char *uri_start; u_char *uri_end; u_char *uri_ext; u_char *args_start; u_char *request_start; u_char *request_end; u_char *method_end; u_char *schema_start; u_char *schema_end; u_char *host_start; u_char *host_end; u_char *port_start; u_char *port_end; unsigned http_minor:16; unsigned http_major:16; } // }}};

 

 

这个指针指向了一个 upstream 描述结构,如果想要使用 upstream 机制,就要设置该成员

// struct ngx_http_upstream_s // nginx upstream 机制描述结构 {{{ struct ngx_http_upstream_s { // 读事件回调函数 ngx_http_upstream_handler_pt read_event_handler; // 写事件回调函数 ngx_http_upstream_handler_pt write_event_handler; // 主动向上游服务器发起的连接 ngx_peer_connection_t peer; ngx_event_pipe_t *pipe; // 发给上游服务器的请求 ngx_chain_t *request_bufs; // 向下游发送响应的方式 ngx_output_chain_ctx_t output; ngx_chain_writer_ctx_t writer; // 配置参数描述结构 ngx_http_upstream_conf_t *conf; // 存储响应头部 ngx_http_upstream_headers_in_t headers_in; // 用于解析主机域名 ngx_http_upstream_resolved_t *resolved; ngx_buf_t from_client; // 接收上游服务器响应的缓冲区 ngx_buf_t buffer; // 上游响应包体长度 off_t length; // 当不需要转发包体,且使用默认的input_filter方法处理包体时 // out_bufs指向响应包体 // 当需要转发包体到下游时 // 指向上一次下游转发响应到现在这段时间内接收自上游的缓存响应 ngx_chain_t *out_bufs; // 当需要转发响应包体到下游时,它表示上一次向下游转发响应时没有发送完的内容 ngx_chain_t *busy_bufs; // 用于回收out_bufs中已经发送给下游的ngx_buf_t结构体 ngx_chain_t *free_bufs; // 处理包体前的初始化方法 ngx_int_t (*input_filter_init)(void *data); // 处理包体的方法 ngx_int_t (*input_filter)(void *data, ssize_t bytes); // 上述两个函数的参数 void *input_filter_ctx; #if (NGX_HTTP_CACHE) ngx_int_t (*create_key)(ngx_http_request_t *r); #endif // HTTP模块实现,用于构造发往上游服务器的请求的函数 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); // 可由HTTP模块实现的重定向函数 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_msec_t timeout; // upstream 状态描述结构,上游响应的错误码、包体长度等信息 ngx_http_upstream_state_t *state; ngx_str_t method; ngx_str_t schema; ngx_str_t uri; #if (NGX_HTTP_SSL) ngx_str_t ssl_name; #endif ngx_http_cleanup_pt *cleanup; // 是否指定缓存路径 unsigned store:1; // 是否启用文件缓存 unsigned cacheable:1; unsigned accel:1; // 是否基于SSL协议访问上游服务器 unsigned ssl:1; #if (NGX_HTTP_CACHE) unsigned cache_status:3; #endif // 向下游转发上游包体时,是否开启更大的内存及临时磁盘文件用于缓存 unsigned buffering:1; // 是否保持长连接 unsigned keepalive:1; unsigned upgrade:1; // 是否已经向上游服务器发送了请求 unsigned request_sent:1; // 是否把包头转发给客户端 unsigned header_sent:1; }; // }}}

 

 

ngx_http_upstream_conf_t 结构体中保存了配置文件中可能出现的所有配置信息

// struct ngx_http_upstream_conf_t // 配置参数描述结构 {{{ typedef struct { // 上游服务器配置 // 当ngx_http_upstream_t中没有实现resolved成员时,这个变量才会生效 ngx_http_upstream_srv_conf_t *upstream; // 建立 TCP 连接超时 ngx_msec_t connect_timeout; // 发送请求超时 ngx_msec_t send_timeout; // 读取请求超时 ngx_msec_t read_timeout; ngx_msec_t timeout; ngx_msec_t next_upstream_timeout; // TCP的SO_SNOLOWAT选项,表示发送缓冲区的下限 size_t send_lowat; // 接收头部的缓冲区大小(ngx_http_upstream_t中的buffer缓冲区) size_t buffer_size; size_t limit_rate; // 当buffering=1,并且向下游转发响应时生效 size_t busy_buffers_size; // 临时文件大小 // buffering=1,若上游速度快于下游,则可能把上游的响应存在临时文件中 size_t max_temp_file_size; // 一次写入临时文件的字符流最大长度 size_t temp_file_write_size; size_t busy_buffers_size_conf; size_t max_temp_file_size_conf; size_t temp_file_write_size_conf; // 以缓存响应的方式转发上游服务器的包体时所用的内存大小 ngx_bufs_t bufs; // 针对ngx_http_upstream_t中的header_in成员 // ignore_headers可根据位操作跳过一些头部 ngx_uint_t ignore_headers; // 出错选择的下一个上游服务器 ngx_uint_t next_upstream; // 创建的目录、文件的权限 ngx_uint_t store_access; ngx_uint_t next_upstream_tries; // 决定转发响应方式的标志位 // 1:认为上游快于下游,会尽量地在内存或者磁盘中缓存来自上游的响应 // 0:仅开辟一块固定大小的内存块作为缓存来转发响应 ngx_flag_t buffering; ngx_flag_t pass_request_headers; ngx_flag_t pass_request_body; // 如果为 1,上游服务器交互时不检查是否与下游客户端断开连接,继续执行交互内容 ngx_flag_t ignore_client_abort; // 截取错误码,查看是否有对应可以返回的语义 ngx_flag_t intercept_errors; // 是否复用临时文件中已经使用过的空间 ngx_flag_t cyclic_temp_file; ngx_flag_t force_ranges; // 存放临时文件的路径 ngx_path_t *temp_path; // 根据ngx_http_upstream_hide_headers_hash函数构造出的需要隐藏的HTTP头部散列表 ngx_hash_t hide_headers_hash; // 不希望转发的头部 ngx_array_t *hide_headers; // 明确希望转发的头部 ngx_array_t *pass_headers; // 连接上游服务器时的本机地址 ngx_http_upstream_local_t *local; #if (NGX_HTTP_CACHE) ngx_shm_zone_t *cache; ngx_uint_t cache_min_uses; ngx_uint_t cache_use_stale; ngx_uint_t cache_methods; ngx_flag_t cache_lock; ngx_msec_t cache_lock_timeout; ngx_flag_t cache_revalidate; ngx_array_t *cache_valid; ngx_array_t *cache_bypass; ngx_array_t *no_cache; #endif // 存放路径的长度 ngx_array_t *store_lengths; // 存储路径 ngx_array_t *store_values; signed store:2; // 是否捕获到404直接转发 unsigned intercept_404:1; // 是否动态决定buffering标志位 // 根据 ngx_http_upstream_t 的 headers_in 中的 // X-Accel-Buffering(yes/no)的值来确定buffering unsigned change_buffering:1; #if (NGX_HTTP_SSL) ngx_ssl_t *ssl; ngx_flag_t ssl_session_reuse; ngx_http_complex_value_t *ssl_name; ngx_flag_t ssl_server_name; ngx_flag_t ssl_verify; #endif // upstream 模块名称,仅用于日志记录 ngx_str_t module; } ngx_http_upstream_conf_t; // }}}

 

 

上游主机信息就是在 upstream 的 resolved 成员中指定的,在 ngx_http_upstream_module 中,这个成员只能通过配置文件中指定,如果用户自己实现模块,就可以实现灵活指定了

这个成员是 ngx_http_upstream_resolved_t 类型的:

// struct ngx_http_upstream_resolved_t // 上游主机描述结构 {{{ typedef struct { ngx_str_t host; in_port_t port; ngx_uint_t no_port; /* unsigned no_port:1 */ ngx_uint_t naddrs; ngx_addr_t *addrs; struct sockaddr *sockaddr; socklen_t socklen; ngx_resolver_ctx_t *ctx; } ngx_http_upstream_resolved_t; // }}}

 

 

ngx_http_upstream_t 结构中的 subrequest_in_memory 域指定了如何转发 upstream 请求

upstream 转发请求的方式
subrequest_in_memory 取值转发方式
1不转发包体到下游,由 http 模块实现的 input_filter 方法处理请求包体
0直接转发响应包体

 

同时,如果 ngx_http_upstream_conf_t 结构中的 buffering 字段为 1,则会开启更多的缓存用来存储响应,否则只会使用固定大小的缓冲区

  1. 这是为了适应不同的应用场景的,如果上游网速很慢,那么就需要更多的并发请求,每个请求所消耗的内存要限制在尽量小的范围内,因此,将 buffering 设为 0,可以节省开销,这块内存缓存既用来接收上游响应又用来保存转发给下游的响应内容
  2. 而当上游网速远快于下游网速时,就必须开辟足够的内存缓冲区来缓存上游的响应,当内存使用达到上限时,还会把响应缓存到 max_temp_file_size 指定的磁盘文件大小的磁盘缓冲区中

 






技术帖      龙潭书斋      服务器      nginx      server      源码      sourcecode      http      source      code      upstream      ngx_http_upstream_module      反向代理      代理      proxy      websever     


京ICP备15018585号