Skip to content

Commit dd7b10a

Browse files
vkrasnovdenji
authored andcommitted
Optimizing TCP/TLS to reduce latency for NGINX
What we do now: We use a static record size of 4K. This gives a good balance of latency and throughput. Optimize latency: By initialy sending small (1 TCP segment) sized records, we are able to avoid HoL blocking of the first byte. This means TTFB is sometime lower by a whole RTT. Optimizing throughput: By sending increasingly larger records later in the connection, when HoL is not a problem, we reduce the overhead of TLS record (29 bytes per record with GCM/CHACHA-POLY). Logic: Start each connection with small records (1369 byte default, change with ssl_dyn_rec_size_lo). After a given number of records (40, change with ssl_dyn_rec_threshold) start sending larger records (4229, ssl_dyn_rec_size_hi). Eventually after the same number of records, start sending the largest records (ssl_buffer_size). In case the connection idles for a given amount of time (1s, ssl_dyn_rec_timeout), the process repeats itself (i.e. begin sending small records again).
1 parent 74407cf commit dd7b10a

File tree

2 files changed

+503
-0
lines changed

2 files changed

+503
-0
lines changed

nginx-dyntls-1.11.5.diff

+232
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
diff --git a/src/event/ngx_event_openssl.c b/src/event/ngx_event_openssl.c
2+
index 68d02bf..2b24542 100644
3+
--- a/src/event/ngx_event_openssl.c
4+
+++ b/src/event/ngx_event_openssl.c
5+
@@ -1131,6 +1131,7 @@ ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, ngx_uint_t flags)
6+
7+
sc->buffer = ((flags & NGX_SSL_BUFFER) != 0);
8+
sc->buffer_size = ssl->buffer_size;
9+
+ sc->dyn_rec = ssl->dyn_rec;
10+
11+
sc->session_ctx = ssl->ctx;
12+
13+
@@ -1669,6 +1670,41 @@ ngx_ssl_send_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
14+
15+
for ( ;; ) {
16+
17+
+ /* Dynamic record resizing:
18+
+ We want the initial records to fit into one TCP segment
19+
+ so we don't get TCP HoL blocking due to TCP Slow Start.
20+
+ A connection always starts with small records, but after
21+
+ a given amount of records sent, we make the records larger
22+
+ to reduce header overhead.
23+
+ After a connection has idled for a given timeout, begin
24+
+ the process from the start. The actual parameters are
25+
+ configurable. If dyn_rec_timeout is 0, we assume dyn_rec is off. */
26+
+
27+
+ if (c->ssl->dyn_rec.timeout > 0 ) {
28+
+
29+
+ if (ngx_current_msec - c->ssl->dyn_rec_last_write >
30+
+ c->ssl->dyn_rec.timeout)
31+
+ {
32+
+ buf->end = buf->start + c->ssl->dyn_rec.size_lo;
33+
+ c->ssl->dyn_rec_records_sent = 0;
34+
+
35+
+ } else {
36+
+ if (c->ssl->dyn_rec_records_sent >
37+
+ c->ssl->dyn_rec.threshold * 2)
38+
+ {
39+
+ buf->end = buf->start + c->ssl->buffer_size;
40+
+
41+
+ } else if (c->ssl->dyn_rec_records_sent >
42+
+ c->ssl->dyn_rec.threshold)
43+
+ {
44+
+ buf->end = buf->start + c->ssl->dyn_rec.size_hi;
45+
+
46+
+ } else {
47+
+ buf->end = buf->start + c->ssl->dyn_rec.size_lo;
48+
+ }
49+
+ }
50+
+ }
51+
+
52+
while (in && buf->last < buf->end && send < limit) {
53+
if (in->buf->last_buf || in->buf->flush) {
54+
flush = 1;
55+
@@ -1770,6 +1806,9 @@ ngx_ssl_write(ngx_connection_t *c, u_char *data, size_t size)
56+
57+
if (n > 0) {
58+
59+
+ c->ssl->dyn_rec_records_sent++;
60+
+ c->ssl->dyn_rec_last_write = ngx_current_msec;
61+
+
62+
if (c->ssl->saved_read_handler) {
63+
64+
c->read->handler = c->ssl->saved_read_handler;
65+
diff --git a/src/event/ngx_event_openssl.h b/src/event/ngx_event_openssl.h
66+
index 24b812f..504e943 100644
67+
--- a/src/event/ngx_event_openssl.h
68+
+++ b/src/event/ngx_event_openssl.h
69+
@@ -53,11 +53,18 @@
70+
#define ngx_ssl_session_t SSL_SESSION
71+
#define ngx_ssl_conn_t SSL
72+
73+
+typedef struct {
74+
+ ngx_msec_t timeout;
75+
+ ngx_uint_t threshold;
76+
+ size_t size_lo;
77+
+ size_t size_hi;
78+
+} ngx_ssl_dyn_rec_t;
79+
80+
struct ngx_ssl_s {
81+
SSL_CTX *ctx;
82+
ngx_log_t *log;
83+
size_t buffer_size;
84+
+ ngx_ssl_dyn_rec_t dyn_rec;
85+
};
86+
87+
88+
@@ -80,6 +87,10 @@ struct ngx_ssl_connection_s {
89+
unsigned no_wait_shutdown:1;
90+
unsigned no_send_shutdown:1;
91+
unsigned handshake_buffer_set:1;
92+
+
93+
+ ngx_ssl_dyn_rec_t dyn_rec;
94+
+ ngx_msec_t dyn_rec_last_write;
95+
+ ngx_uint_t dyn_rec_records_sent;
96+
};
97+
98+
99+
@@ -89,7 +100,7 @@ struct ngx_ssl_connection_s {
100+
#define NGX_SSL_DFLT_BUILTIN_SCACHE -5
101+
102+
103+
-#define NGX_SSL_MAX_SESSION_SIZE 4096
104+
+#define NGX_SSL_MAX_SESSION_SIZE 16384
105+
106+
typedef struct ngx_ssl_sess_id_s ngx_ssl_sess_id_t;
107+
108+
diff --git a/src/http/modules/ngx_http_ssl_module.c b/src/http/modules/ngx_http_ssl_module.c
109+
index d685ae9..2362632 100644
110+
--- a/src/http/modules/ngx_http_ssl_module.c
111+
+++ b/src/http/modules/ngx_http_ssl_module.c
112+
@@ -233,6 +233,41 @@ static ngx_command_t ngx_http_ssl_commands[] = {
113+
offsetof(ngx_http_ssl_srv_conf_t, stapling_verify),
114+
NULL },
115+
116+
+ { ngx_string("ssl_dyn_rec_enable"),
117+
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
118+
+ ngx_conf_set_flag_slot,
119+
+ NGX_HTTP_SRV_CONF_OFFSET,
120+
+ offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_enable),
121+
+ NULL },
122+
+
123+
+ { ngx_string("ssl_dyn_rec_timeout"),
124+
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
125+
+ ngx_conf_set_msec_slot,
126+
+ NGX_HTTP_SRV_CONF_OFFSET,
127+
+ offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_timeout),
128+
+ NULL },
129+
+
130+
+ { ngx_string("ssl_dyn_rec_size_lo"),
131+
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
132+
+ ngx_conf_set_size_slot,
133+
+ NGX_HTTP_SRV_CONF_OFFSET,
134+
+ offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_size_lo),
135+
+ NULL },
136+
+
137+
+ { ngx_string("ssl_dyn_rec_size_hi"),
138+
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
139+
+ ngx_conf_set_size_slot,
140+
+ NGX_HTTP_SRV_CONF_OFFSET,
141+
+ offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_size_hi),
142+
+ NULL },
143+
+
144+
+ { ngx_string("ssl_dyn_rec_threshold"),
145+
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,
146+
+ ngx_conf_set_num_slot,
147+
+ NGX_HTTP_SRV_CONF_OFFSET,
148+
+ offsetof(ngx_http_ssl_srv_conf_t, dyn_rec_threshold),
149+
+ NULL },
150+
+
151+
ngx_null_command
152+
};
153+
154+
@@ -533,6 +568,11 @@ ngx_http_ssl_create_srv_conf(ngx_conf_t *cf)
155+
sscf->session_ticket_keys = NGX_CONF_UNSET_PTR;
156+
sscf->stapling = NGX_CONF_UNSET;
157+
sscf->stapling_verify = NGX_CONF_UNSET;
158+
+ sscf->dyn_rec_enable = NGX_CONF_UNSET;
159+
+ sscf->dyn_rec_timeout = NGX_CONF_UNSET_MSEC;
160+
+ sscf->dyn_rec_size_lo = NGX_CONF_UNSET_SIZE;
161+
+ sscf->dyn_rec_size_hi = NGX_CONF_UNSET_SIZE;
162+
+ sscf->dyn_rec_threshold = NGX_CONF_UNSET_UINT;
163+
164+
return sscf;
165+
}
166+
@@ -598,6 +638,20 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
167+
ngx_conf_merge_str_value(conf->stapling_responder,
168+
prev->stapling_responder, "");
169+
170+
+ ngx_conf_merge_value(conf->dyn_rec_enable, prev->dyn_rec_enable, 0);
171+
+ ngx_conf_merge_msec_value(conf->dyn_rec_timeout, prev->dyn_rec_timeout,
172+
+ 1000);
173+
+ /* Default sizes for the dynamic record sizes are defined to fit maximal
174+
+ TLS + IPv6 overhead in a single TCP segment for lo and 3 segments for hi:
175+
+ 1369 = 1500 - 40 (IP) - 20 (TCP) - 10 (Time) - 61 (Max TLS overhead) */
176+
+ ngx_conf_merge_size_value(conf->dyn_rec_size_lo, prev->dyn_rec_size_lo,
177+
+ 1369);
178+
+ /* 4229 = (1500 - 40 - 20 - 10) * 3 - 61 */
179+
+ ngx_conf_merge_size_value(conf->dyn_rec_size_hi, prev->dyn_rec_size_hi,
180+
+ 4229);
181+
+ ngx_conf_merge_uint_value(conf->dyn_rec_threshold, prev->dyn_rec_threshold,
182+
+ 40);
183+
+
184+
conf->ssl.log = cf->log;
185+
186+
if (conf->enable) {
187+
@@ -778,6 +832,28 @@ ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
188+
189+
}
190+
191+
+ if (conf->dyn_rec_enable) {
192+
+ conf->ssl.dyn_rec.timeout = conf->dyn_rec_timeout;
193+
+ conf->ssl.dyn_rec.threshold = conf->dyn_rec_threshold;
194+
+
195+
+ if (conf->buffer_size > conf->dyn_rec_size_lo) {
196+
+ conf->ssl.dyn_rec.size_lo = conf->dyn_rec_size_lo;
197+
+
198+
+ } else {
199+
+ conf->ssl.dyn_rec.size_lo = conf->buffer_size;
200+
+ }
201+
+
202+
+ if (conf->buffer_size > conf->dyn_rec_size_hi) {
203+
+ conf->ssl.dyn_rec.size_hi = conf->dyn_rec_size_hi;
204+
+
205+
+ } else {
206+
+ conf->ssl.dyn_rec.size_hi = conf->buffer_size;
207+
+ }
208+
+
209+
+ } else {
210+
+ conf->ssl.dyn_rec.timeout = 0;
211+
+ }
212+
+
213+
return NGX_CONF_OK;
214+
}
215+
216+
diff --git a/src/http/modules/ngx_http_ssl_module.h b/src/http/modules/ngx_http_ssl_module.h
217+
index 57f5941..309ac00 100644
218+
--- a/src/http/modules/ngx_http_ssl_module.h
219+
+++ b/src/http/modules/ngx_http_ssl_module.h
220+
@@ -57,6 +57,12 @@ typedef struct {
221+
222+
u_char *file;
223+
ngx_uint_t line;
224+
+
225+
+ ngx_flag_t dyn_rec_enable;
226+
+ ngx_msec_t dyn_rec_timeout;
227+
+ size_t dyn_rec_size_lo;
228+
+ size_t dyn_rec_size_hi;
229+
+ ngx_uint_t dyn_rec_threshold;
230+
} ngx_http_ssl_srv_conf_t;
231+
232+

0 commit comments

Comments
 (0)