Skip to content

Commit 5ffeb8a

Browse files
author
yazhou.yang
committed
nginx http2 analysize
1 parent 4e2d72a commit 5ffeb8a

File tree

7 files changed

+1206
-28
lines changed

7 files changed

+1206
-28
lines changed

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,11 +153,13 @@ nginx的以下功能模块的相关代码已经阅读,并对其源码及相关
153153
HTTP2 header帧处理流程、stream建立过程、伪request结构处理过程分析
154154
HTTP2 header帧处理完成后与NGINX phase框架衔接过程分析
155155
HTTP2 报文输出过程与nginx框架结合流程分析,DATA帧输出流程分析
156-
多sream同时请求,多流交互情况下DATA帧发送过程分析
156+
多sream同时请求,多流交互情况下DATA帧发送过程分析
157+
WINDOW_UPDATE帧和流量控制原理结合分析。
157158

158159

159160
?????
160-
NGINX不支持PUSH、做反向代理情况下和后端为什么还是走HTTP1.X协议?
161+
NGINX不支持PUSH、做反向代理情况下和后端为什么还是走HTTP1.X协议?
162+
流量控制、优先级功能根本没用起来???? 为什么???/
161163

162164

163165
===================================
Binary file not shown.

nginx-1.9.2/http2相关/nghttp通过多流方式同时从nginx下载多个不同文件详细日志.txt

Lines changed: 1133 additions & 0 deletions
Large diffs are not rendered by default.

nginx-1.9.2/src/http/v2/ngx_http_v2.c

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -736,7 +736,7 @@ ngx_http_v2_state_preface_end(ngx_http_v2_connection_t *h2c, u_char *pos,
736736
return ngx_http_v2_state_head(h2c, pos + sizeof(preface) - 1, end);
737737
}
738738

739-
739+
//ngx_http_v2_read_handler读取到HTTP2客户端数据后调用该函数执行
740740
static u_char *
741741
ngx_http_v2_state_head(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end)
742742
{
@@ -780,6 +780,7 @@ ngx_http_v2_state_data(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end)
780780
ngx_http_v2_node_t *node;
781781
ngx_http_v2_stream_t *stream;
782782

783+
/* 跳过pad数据部分 */
783784
if (h2c->state.flags & NGX_HTTP_V2_PADDED_FLAG) {
784785

785786
if (h2c->state.length == 0) {
@@ -814,6 +815,7 @@ ngx_http_v2_state_data(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end)
814815
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
815816
"http2 DATA frame");
816817

818+
/* 如果对端发送过来的数据大于本端接收窗口,直接报错 */
817819
if (h2c->state.length > h2c->recv_window) {
818820
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
819821
"client violated connection flow control: "
@@ -823,10 +825,14 @@ ngx_http_v2_state_data(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end)
823825
return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_FLOW_CTRL_ERROR);
824826
}
825827

828+
/* 接收到DATA帧数据后,调整接收窗口大小 */
826829
h2c->recv_window -= h2c->state.length;
827830

828-
if (h2c->recv_window < NGX_HTTP_V2_MAX_WINDOW / 4) {
829-
831+
/* 当本端收到DATA帧的时候,如果发现该连接上的recv_window大小已经小于NGX_HTTP_V2_MAX_WINDOW / 4了,
832+
则通知本发送端把h2c->send_window调整为NGX_HTTP_V2_MAX_WINDOW,同时本端也把连接的接收recv_window恢复到该MAX值 */
833+
if (h2c->recv_window < NGX_HTTP_V2_MAX_WINDOW / 4) {
834+
//这里为什么是NGX_HTTP_V2_MAX_WINDOW?因为整个连接的recv_window(即h2c->recv_window)初始值就为NGX_HTTP_V2_MAX_WINDOW,
835+
//而且不会变化,也就是协议规定HTTP2 一个连接对应的recv_window为NGX_HTTP_V2_MAX_WINDOW
830836
if (ngx_http_v2_send_window_update(h2c, 0, NGX_HTTP_V2_MAX_WINDOW
831837
- h2c->recv_window)
832838
== NGX_ERROR)
@@ -835,7 +841,7 @@ ngx_http_v2_state_data(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end)
835841
NGX_HTTP_V2_INTERNAL_ERROR);
836842
}
837843

838-
h2c->recv_window = NGX_HTTP_V2_MAX_WINDOW;
844+
h2c->recv_window = NGX_HTTP_V2_MAX_WINDOW; //
839845
}
840846

841847
node = ngx_http_v2_get_node_by_id(h2c, h2c->state.sid, 0);
@@ -867,9 +873,12 @@ ngx_http_v2_state_data(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end)
867873
}
868874

869875
stream->recv_window -= h2c->state.length;
870-
876+
877+
//流的recv_window小于NGX_HTTP_V2_MAX_WINDOW / 4后,恢复本流recv_window为NGX_HTTP_V2_MAX_WINDOW,同时发送
878+
//更新帧个会对端使其也更新为NGX_HTTP_V2_MAX_WINDOW,从而保持同步
871879
if (stream->recv_window < NGX_HTTP_V2_MAX_WINDOW / 4) {
872-
880+
//为什么这里是和NGX_HTTP_V2_MAX_WINDOW比较? 因为nginx通过ngx_http_v2_send_settings发送的setting帧
881+
//把init_window设置为NGX_HTTP_V2_MAX_WINDOW对端收到后就会把自己的strem->send_window设置为NGX_HTTP_V2_MAX_WINDOW
873882
if (ngx_http_v2_send_window_update(h2c, node->id,
874883
NGX_HTTP_V2_MAX_WINDOW
875884
- stream->recv_window)
@@ -904,6 +913,7 @@ ngx_http_v2_state_data(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end)
904913
}
905914

906915

916+
//接收到数据帧后在ngx_http_v2_state_data调用该函数读取数据帧中的真实数据
907917
static u_char *
908918
ngx_http_v2_state_read_data(ngx_http_v2_connection_t *h2c, u_char *pos,
909919
u_char *end)
@@ -1936,6 +1946,7 @@ ngx_http_v2_state_priority(ngx_http_v2_connection_t *h2c, u_char *pos,
19361946
return ngx_http_v2_state_complete(h2c, pos, end);
19371947
}
19381948

1949+
//接收到RST_STREAM帧,需要关闭对应流,因此流也要处于关闭状态。 - 接收者不能够在此流上发送任何帧
19391950
//见ngx_http_v2_frame_states
19401951
static u_char *
19411952
ngx_http_v2_state_rst_stream(ngx_http_v2_connection_t *h2c, u_char *pos,
@@ -2051,7 +2062,8 @@ ngx_http_v2_state_settings(ngx_http_v2_connection_t *h2c, u_char *pos,
20512062
return ngx_http_v2_state_settings_params(h2c, pos, end);
20522063
}
20532064

2054-
2065+
//setting帧决定接收到HEAD帧后创建流的时候,确定发送窗口的大小。同时决定在通过write chain发送frame帧的时候,决定没个数据块的大小
2066+
//例如一个frame帧为10K,但是对端指定其接收数据的frame_size=5K,则该frame会被拆成两个frame_size大小的包体发送
20552067
static u_char *
20562068
ngx_http_v2_state_settings_params(ngx_http_v2_connection_t *h2c, u_char *pos,
20572069
u_char *end)
@@ -2091,6 +2103,7 @@ ngx_http_v2_state_settings_params(ngx_http_v2_connection_t *h2c, u_char *pos,
20912103
NGX_HTTP_V2_INTERNAL_ERROR);
20922104
}
20932105

2106+
//决定流的发送窗口大小
20942107
h2c->init_window = value;
20952108
break;
20962109

@@ -2106,6 +2119,7 @@ ngx_http_v2_state_settings_params(ngx_http_v2_connection_t *h2c, u_char *pos,
21062119
NGX_HTTP_V2_PROTOCOL_ERROR);
21072120
}
21082121

2122+
//决定发送FRAME帧的时候,每帧数据大小不超过该值
21092123
h2c->frame_size = value;
21102124
break;
21112125

@@ -2152,10 +2166,11 @@ ngx_http_v2_state_ping(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end)
21522166
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
21532167
"http2 PING frame, flags: %ui", h2c->state.flags);
21542168

2155-
if (h2c->state.flags & NGX_HTTP_V2_ACK_FLAG) {
2169+
if (h2c->state.flags & NGX_HTTP_V2_ACK_FLAG) { //ping帧的ACK信息
21562170
return ngx_http_v2_state_skip(h2c, pos, end);
21572171
}
21582172

2173+
//发送PING帧的ACK帧
21592174
frame = ngx_http_v2_get_frame(h2c, NGX_HTTP_V2_PING_SIZE,
21602175
NGX_HTTP_V2_PING_FRAME,
21612176
NGX_HTTP_V2_ACK_FLAG, 0);
@@ -2165,14 +2180,16 @@ ngx_http_v2_state_ping(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end)
21652180

21662181
buf = frame->first->buf;
21672182

2183+
//PING帧带有8字节负载,8个字节负载,值随意填写。
21682184
buf->last = ngx_cpymem(buf->last, pos, NGX_HTTP_V2_PING_SIZE);
21692185

21702186
ngx_http_v2_queue_blocked_frame(h2c, frame);
21712187

21722188
return ngx_http_v2_state_complete(h2c, pos + NGX_HTTP_V2_PING_SIZE, end);
21732189
}
21742190

2175-
//见ngx_http_v2_frame_states
2191+
//见ngx_http_v2_frame_states
2192+
//GOAWAY帧直接跳过的,啥也没处理
21762193
static u_char *
21772194
ngx_http_v2_state_goaway(ngx_http_v2_connection_t *h2c, u_char *pos,
21782195
u_char *end)
@@ -2233,15 +2250,15 @@ ngx_http_v2_state_window_update(ngx_http_v2_connection_t *h2c, u_char *pos,
22332250
ngx_http_v2_state_window_update);
22342251
}
22352252

2236-
window = ngx_http_v2_parse_window(pos);
2253+
window = ngx_http_v2_parse_window(pos); //获取对方发送过来的更新窗口大小
22372254

22382255
pos += NGX_HTTP_V2_WINDOW_UPDATE_SIZE;
22392256

22402257
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
22412258
"http2 WINDOW_UPDATE frame sid:%ui window:%uz",
22422259
h2c->state.sid, window);
22432260

2244-
if (h2c->state.sid) {
2261+
if (h2c->state.sid) { //是针对某个流进行窗口更新,则把该流的发送窗口增加window大小
22452262
node = ngx_http_v2_get_node_by_id(h2c, h2c->state.sid, 0);
22462263

22472264
if (node == NULL || node->stream == NULL) {
@@ -2253,6 +2270,7 @@ ngx_http_v2_state_window_update(ngx_http_v2_connection_t *h2c, u_char *pos,
22532270

22542271
stream = node->stream;
22552272

2273+
/* 对端告诉本端我们可以把发送窗口调大window大小,但是本端发送窗口调大window后会超过NGX_HTTP_V2_MAX_WINDOW限制,这是不规范的 */
22562274
if (window > (size_t) (NGX_HTTP_V2_MAX_WINDOW - stream->send_window)) {
22572275

22582276
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
@@ -2289,6 +2307,8 @@ ngx_http_v2_state_window_update(ngx_http_v2_connection_t *h2c, u_char *pos,
22892307
return ngx_http_v2_state_complete(h2c, pos, end);
22902308
}
22912309

2310+
/* 说明是针对整个连接的发送窗口更新,对整个连接的发送窗口增加window大小 */
2311+
22922312
if (window > NGX_HTTP_V2_MAX_WINDOW - h2c->send_window) {
22932313
ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,
22942314
"client violated connection flow control: "
@@ -2704,7 +2724,7 @@ ngx_http_v2_send_rst_stream(ngx_http_v2_connection_t *h2c, ngx_uint_t sid,
27042724
return NGX_OK;
27052725
}
27062726

2707-
2727+
//http2处理完成后,在ngx_http_v2_finalize_connection中调用该函数和对端说拜拜
27082728
static ngx_int_t
27092729
ngx_http_v2_send_goaway(ngx_http_v2_connection_t *h2c, ngx_uint_t status)
27102730
{
@@ -4014,9 +4034,9 @@ ngx_http_v2_finalize_connection(ngx_http_v2_connection_t *h2c,
40144034
ngx_http_close_connection(c);
40154035
}
40164036

4017-
4037+
//调整连接h2c上的所有流的发送窗口大小,增加delta,如果加上delta后超过了阈值,则直接发送RST复位帧
40184038
static ngx_int_t
4019-
ngx_http_v2_adjust_windows(ngx_http_v2_connection_t *h2c, ssize_t delta)
4039+
ngx_http_v2_adjust_windows(ngx_http_v2_connection_t *h2c, ssize_t delta) //delta为流的发送窗口增加量
40204040
{
40214041
ngx_uint_t i, size;
40224042
ngx_event_t *wev;
@@ -4038,6 +4058,7 @@ ngx_http_v2_adjust_windows(ngx_http_v2_connection_t *h2c, ssize_t delta)
40384058
continue;
40394059
}
40404060

4061+
/* 如果流的发送窗口加上delta后超过了NGX_HTTP_V2_MAX_WINDOW最大限制,则发送RST帧 */
40414062
if (delta > 0
40424063
&& stream->send_window
40434064
> (ssize_t) (NGX_HTTP_V2_MAX_WINDOW - delta))

nginx-1.9.2/src/http/v2/ngx_http_v2.h

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -473,11 +473,16 @@ Frame Types vs Flags and Stream ID
473473
#define NGX_HTTP_V2_DATA_FRAME 0x0
474474
#define NGX_HTTP_V2_HEADERS_FRAME 0x1
475475
#define NGX_HTTP_V2_PRIORITY_FRAME 0x2
476-
#define NGX_HTTP_V2_RST_STREAM_FRAME 0x3
476+
//接收到RST_STREAM帧,需要关闭对应流,因此流也要处于关闭状态。 - 接收者不能够在此流上发送任何帧
477+
//例如更新窗口设置的发送窗口增加量加上已有的send_buf值超过了最大限度则会发送RST帧
478+
#define NGX_HTTP_V2_RST_STREAM_FRAME 0x3
479+
//setting帧决定接收到HEAD帧后创建流的时候,确定发送窗口的大小和流窗口大小。同时决定在通过write chain发送frame帧的时候,决定没个数据块的大小
480+
//例如一个frame帧为10K,但是对端指定其接收数据的frame_size=5K,则该frame会被拆成两个frame_size大小的包体发送
477481
#define NGX_HTTP_V2_SETTINGS_FRAME 0x4
478482
#define NGX_HTTP_V2_PUSH_PROMISE_FRAME 0x5
479483
#define NGX_HTTP_V2_PING_FRAME 0x6
480484
#define NGX_HTTP_V2_GOAWAY_FRAME 0x7
485+
//HTTP/2定义的帧类型的一种,用途是通知对端增加窗口值,WINDOW_UPDATE会指定增加的大小
481486
#define NGX_HTTP_V2_WINDOW_UPDATE_FRAME 0x8
482487
#define NGX_HTTP_V2_CONTINUATION_FRAME 0x9
483488

@@ -597,12 +602,22 @@ struct ngx_http_v2_connection_s {
597602
ngx_uint_t processing;
598603

599604
/* 注意ngx_http_v2_connection_s的send_window、recv_window、init_window与ngx_http_v2_stream_s的send_window、recv_window的区别 */
600-
size_t send_window;//默认NGX_HTTP_V2_DEFAULT_WINDOW
601-
size_t recv_window;//默认NGX_HTTP_V2_MAX_WINDOW
602-
// 接收到对端的setting帧后,会做调整,见ngx_http_v2_state_settings_params
603-
size_t init_window;//默认NGX_HTTP_V2_DEFAULT_WINDOW
604-
605-
//接收到对端的setting帧后,会做调整,见ngx_http_v2_state_settings_params
605+
/* ngx_http_v2_send_chain发送多少DATA帧,h2c->send_window就会减少多少,对端接收到后其h2c->recv_window也会减少多少,
606+
当对端收到DATA帧的时候,如果发现该连接上的recv_window大小已经小于NGX_HTTP_V2_MAX_WINDOW / 4了,则通知本发送端
607+
把h2c->send_window调整为NGX_HTTP_V2_MAX_WINDOW,同时对端也把连接的接收recv_window调整到该MAX值当对端收到DATA帧
608+
的时候,如果发现该连接上的recv_window大小已经小于NGX_HTTP_V2_MAX_WINDOW / 4了,则通知本发送端把h2c->send_window
609+
调整为NGX_HTTP_V2_MAX_WINDOW,同时对端也把连接的接收recv_window调整到该MAX值
610+
*/ //最终该连接本地的send_window始终和对端的recv_window保持一致的
611+
//流的发送窗口可以通过setting帧调整,但是接受窗口都是默认值NGX_HTTP_V2_MAX_WINDOW
612+
size_t send_window;//默认NGX_HTTP_V2_DEFAULT_WINDOW 65535
613+
/* 在接收对端发送过来的DATA帧后,会调整该值,例如接收到对端发送过来的len长度数据,则本端接收窗口减少这么多,
614+
生效见ngx_http_v2_state_data */
615+
size_t recv_window;//默认NGX_HTTP_V2_MAX_WINDOW 2^32 -1
616+
// 接收到对端的setting帧后,会做调整,见ngx_http_v2_state_settings_params stream->send_window = h2c->init_window;
617+
//流的发送窗口可以通过setting帧调整,但是接受窗口都是默认值NGX_HTTP_V2_MAX_WINDOW
618+
size_t init_window;//默认NGX_HTTP_V2_DEFAULT_WINDOW 决定流的发送窗口大小 一般对端会发送SETTING帧进行调整
619+
620+
//接收到对端的setting帧后,会做调整,见ngx_http_v2_state_settings_params 决定发送FRAME帧的时候,每帧数据大小不超过该值
606621
size_t frame_size;//默认NGX_HTTP_V2_DEFAULT_FRAME_SIZE 接收到对端的setting帧后,会做调整,见ngx_http_v2_state_settings_params
607622

608623
ngx_queue_t waiting;
@@ -675,8 +690,13 @@ struct ngx_http_v2_stream_s {
675690
* send_window to become negative, hence it's signed.
676691
*/
677692
/* 注意ngx_http_v2_connection_s的send_window、recv_window、init_window与ngx_http_v2_stream_s的send_window、recv_window的区别 */
678-
ssize_t send_window; //默认值NGX_HTTP_V2_DEFAULT_WINDOW
679-
size_t recv_window; //默认值NGX_HTTP_V2_MAX_WINDOW
693+
/* ngx_http_v2_state_data没在该流上每发送多少DATA帧数据,stream->send_window就减少多少,对方收到该数据后,其
694+
stream->recv_window也相应的减少多少,当对方发现其recv_window少于NGX_HTTP_V2_MAX_WINDOW / 4则又把其recv_window
695+
还原为NGX_HTTP_V2_MAX_WINDOW,同时通知本端增加相应的量来还原为NGX_HTTP_V2_MAX_WINDOW
696+
最终,本端stream的send_window始终和对端strean的recv_window保持一致 */
697+
//默认值NGX_HTTP_V2_DEFAULT_WINDOW 但是在收到对方的Setting帧后会对send_window进行调整
698+
ssize_t send_window; //默认等于h2c->init_window,也就是65535
699+
size_t recv_window; //默认值NGX_HTTP_V2_MAX_WINDOW 2^32 - 1
680700

681701
ngx_http_v2_out_frame_t *free_frames;
682702
ngx_chain_t *free_data_headers;

nginx-1.9.2/src/http/v2/ngx_http_v2_filter_module.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1161,7 +1161,7 @@ ngx_http_v2_data_frame_handler(ngx_http_v2_connection_t *h2c,
11611161

11621162
if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_data_frame) {
11631163

1164-
if (cl->buf->pos != cl->buf->last) {
1164+
if (cl->buf->pos != cl->buf->last) { /* 说明该frame只有部分数据发送了 */
11651165
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,
11661166
"http2:%ui DATA frame %p was sent partially",
11671167
stream->node->id, frame);

阅读说明.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,11 +153,13 @@ nginx的以下功能模块的相关代码已经阅读,并对其源码及相关
153153
HTTP2 header帧处理流程stream建立过程伪request结构处理过程分析
154154
HTTP2 header帧处理完成后与NGINX phase框架衔接过程分析
155155
HTTP2 报文输出过程与nginx框架结合流程分析,DATA帧输出流程分析
156-
多sream同时请求多流交互情况下DATA帧发送过程分析
156+
多sream同时请求多流交互情况下DATA帧发送过程分析
157+
WINDOW_UPDATE帧和流量控制原理结合分析
157158

158159

159160
?????
160-
NGINX不支持PUSH做反向代理情况下和后端为什么还是走HTTP1.X协议
161+
NGINX不支持PUSH做反向代理情况下和后端为什么还是走HTTP1.X协议
162+
流量控制优先级功能根本没用起来???? 为什么???/
161163

162164

163165
===================================

0 commit comments

Comments
 (0)