Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
N
nghttp2
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Libraries
nghttp2
Commits
27b250ac
Commit
27b250ac
authored
Sep 08, 2016
by
Tatsuhiro Tsujikawa
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
nghttpx: Add experimental TCP optimization for h2 frontend
parent
b14375ec
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
230 additions
and
18 deletions
+230
-18
gennghttpxfun.py
gennghttpxfun.py
+2
-0
src/shrpx.cc
src/shrpx.cc
+36
-0
src/shrpx_client_handler.cc
src/shrpx_client_handler.cc
+20
-14
src/shrpx_client_handler.h
src/shrpx_client_handler.h
+2
-0
src/shrpx_config.cc
src/shrpx_config.cc
+18
-0
src/shrpx_config.h
src/shrpx_config.h
+8
-0
src/shrpx_connection.cc
src/shrpx_connection.cc
+52
-0
src/shrpx_connection.h
src/shrpx_connection.h
+7
-0
src/shrpx_http2_upstream.cc
src/shrpx_http2_upstream.cc
+82
-4
src/shrpx_http2_upstream.h
src/shrpx_http2_upstream.h
+3
-0
No files found.
gennghttpxfun.py
View file @
27b250ac
...
...
@@ -136,6 +136,8 @@ OPTIONS = [
"backend-max-backoff"
,
"server-name"
,
"no-server-rewrite"
,
"frontend-http2-optimize-write-buffer-size"
,
"frontend-http2-optimize-window-size"
,
]
LOGVARS
=
[
...
...
src/shrpx.cc
View file @
27b250ac
...
...
@@ -2032,6 +2032,27 @@ HTTP/2 and SPDY:
backend session is relayed to frontend, and server push
via Link header field is also supported. SPDY frontend
does not support server push.
--frontend-http2-optimize-write-buffer-size
(Experimental) Enable write buffer size optimization in
frontend HTTP/2 TLS connection. This optimization aims
to reduce write buffer size so that it only contains
bytes which can send immediately. This makes server
more responsive to prioritized HTTP/2 stream because the
buffering of lower priority stream is reduced. This
option is only effective on recent Linux platform.
--frontend-http2-optimize-window-size
(Experimental) Automatically tune connection level
window size of frontend HTTP/2 TLS connection. If this
feature is enabled, connection window size starts with
the default window size, 65535 bytes. nghttpx
automatically adjusts connection window size based on
TCP receiving window size. The maximum window size is
capped by the value specified by
--frontend-http2-connection-window-bits. Since the
stream is subject to stream level window size, it should
be adjusted using --frontend-http2-window-bits option as
well. This option is only effective on recent Linux
platform.
Mode:
(default mode)
...
...
@@ -2842,6 +2863,10 @@ int main(int argc, char **argv) {
{
SHRPX_OPT_BACKEND_MAX_BACKOFF
.
c_str
(),
required_argument
,
&
flag
,
127
},
{
SHRPX_OPT_SERVER_NAME
.
c_str
(),
required_argument
,
&
flag
,
128
},
{
SHRPX_OPT_NO_SERVER_REWRITE
.
c_str
(),
no_argument
,
&
flag
,
129
},
{
SHRPX_OPT_FRONTEND_HTTP2_OPTIMIZE_WRITE_BUFFER_SIZE
.
c_str
(),
no_argument
,
&
flag
,
130
},
{
SHRPX_OPT_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE
.
c_str
(),
no_argument
,
&
flag
,
131
},
{
nullptr
,
0
,
nullptr
,
0
}};
int
option_index
=
0
;
...
...
@@ -3448,6 +3473,17 @@ int main(int argc, char **argv) {
cmdcfgs
.
emplace_back
(
SHRPX_OPT_NO_SERVER_REWRITE
,
StringRef
::
from_lit
(
"yes"
));
break
;
case
130
:
// --frontend-http2-optimize-write-buffer-size
cmdcfgs
.
emplace_back
(
SHRPX_OPT_FRONTEND_HTTP2_OPTIMIZE_WRITE_BUFFER_SIZE
,
StringRef
::
from_lit
(
"yes"
));
break
;
case
131
:
// --frontend-http2-optimize-window-size
cmdcfgs
.
emplace_back
(
SHRPX_OPT_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE
,
StringRef
::
from_lit
(
"yes"
));
break
;
default:
break
;
}
...
...
src/shrpx_client_handler.cc
View file @
27b250ac
...
...
@@ -244,17 +244,21 @@ int ClientHandler::write_tls() {
ERR_clear_error
();
for
(;;)
{
if
(
on_write
()
!=
0
)
{
return
-
1
;
}
if
(
on_write
()
!=
0
)
{
return
-
1
;
}
auto
iovcnt
=
upstream_
->
response_riovec
(
&
iov
,
1
);
if
(
iovcnt
==
0
)
{
conn_
.
start_tls_write_idle
();
break
;
}
auto
iovcnt
=
upstream_
->
response_riovec
(
&
iov
,
1
);
if
(
iovcnt
==
0
)
{
conn_
.
start_tls_write_idle
();
conn_
.
wlimit
.
stopw
();
ev_timer_stop
(
conn_
.
loop
,
&
conn_
.
wt
);
return
0
;
}
for
(;;)
{
auto
nwrite
=
conn_
.
write_tls
(
iov
.
iov_base
,
iov
.
iov_len
);
if
(
nwrite
<
0
)
{
return
-
1
;
...
...
@@ -265,12 +269,12 @@ int ClientHandler::write_tls() {
}
upstream_
->
response_drain
(
nwrite
);
}
conn_
.
wlimit
.
stopw
();
ev_timer_stop
(
conn_
.
loop
,
&
conn_
.
wt
);
return
0
;
iovcnt
=
upstream_
->
response_riovec
(
&
iov
,
1
);
if
(
iovcnt
==
0
)
{
return
0
;
}
}
}
int
ClientHandler
::
upstream_noop
()
{
return
0
;
}
...
...
@@ -1445,4 +1449,6 @@ StringRef ClientHandler::get_forwarded_for() const {
const
UpstreamAddr
*
ClientHandler
::
get_upstream_addr
()
const
{
return
faddr_
;
}
Connection
*
ClientHandler
::
get_connection
()
{
return
&
conn_
;
};
}
// namespace shrpx
src/shrpx_client_handler.h
View file @
27b250ac
...
...
@@ -154,6 +154,8 @@ public:
void
repeat_read_timer
();
void
stop_read_timer
();
Connection
*
get_connection
();
private:
Connection
conn_
;
ev_timer
reneg_shutdown_timer_
;
...
...
src/shrpx_config.cc
View file @
27b250ac
...
...
@@ -1643,6 +1643,11 @@ int option_lookup_token(const char *name, size_t namelen) {
break
;
case
35
:
switch
(
name
[
34
])
{
case
'e'
:
if
(
util
::
strieq_l
(
"frontend-http2-optimize-window-siz"
,
name
,
34
))
{
return
SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE
;
}
break
;
case
'r'
:
if
(
util
::
strieq_l
(
"frontend-http2-dump-response-heade"
,
name
,
34
))
{
return
SHRPX_OPTID_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER
;
...
...
@@ -1705,6 +1710,10 @@ int option_lookup_token(const char *name, size_t namelen) {
case
41
:
switch
(
name
[
40
])
{
case
'e'
:
if
(
util
::
strieq_l
(
"frontend-http2-optimize-write-buffer-siz"
,
name
,
40
))
{
return
SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WRITE_BUFFER_SIZE
;
}
if
(
util
::
strieq_l
(
"tls-ticket-key-memcached-private-key-fil"
,
name
,
40
))
{
return
SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_PRIVATE_KEY_FILE
;
...
...
@@ -2689,6 +2698,15 @@ int parse_config(Config *config, int optid, const StringRef &opt,
case
SHRPX_OPTID_NO_SERVER_REWRITE
:
config
->
http
.
no_server_rewrite
=
util
::
strieq_l
(
"yes"
,
optarg
);
return
0
;
case
SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WRITE_BUFFER_SIZE
:
config
->
http2
.
upstream
.
optimize_write_buffer_size
=
util
::
strieq_l
(
"yes"
,
optarg
);
return
0
;
case
SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE
:
config
->
http2
.
upstream
.
optimize_window_size
=
util
::
strieq_l
(
"yes"
,
optarg
);
return
0
;
case
SHRPX_OPTID_CONF
:
LOG
(
WARN
)
<<
"conf: ignored"
;
...
...
src/shrpx_config.h
View file @
27b250ac
...
...
@@ -287,6 +287,10 @@ constexpr auto SHRPX_OPT_BACKEND_MAX_BACKOFF =
constexpr
auto
SHRPX_OPT_SERVER_NAME
=
StringRef
::
from_lit
(
"server-name"
);
constexpr
auto
SHRPX_OPT_NO_SERVER_REWRITE
=
StringRef
::
from_lit
(
"no-server-rewrite"
);
constexpr
auto
SHRPX_OPT_FRONTEND_HTTP2_OPTIMIZE_WRITE_BUFFER_SIZE
=
StringRef
::
from_lit
(
"frontend-http2-optimize-write-buffer-size"
);
constexpr
auto
SHRPX_OPT_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE
=
StringRef
::
from_lit
(
"frontend-http2-optimize-window-size"
);
constexpr
size_t
SHRPX_OBFUSCATED_NODE_LENGTH
=
8
;
...
...
@@ -586,6 +590,8 @@ struct Http2Config {
size_t
window_bits
;
size_t
connection_window_bits
;
size_t
max_concurrent_streams
;
bool
optimize_write_buffer_size
;
bool
optimize_window_size
;
}
upstream
;
struct
{
struct
{
...
...
@@ -818,6 +824,8 @@ enum {
SHRPX_OPTID_FRONTEND_HTTP2_DUMP_REQUEST_HEADER
,
SHRPX_OPTID_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER
,
SHRPX_OPTID_FRONTEND_HTTP2_MAX_CONCURRENT_STREAMS
,
SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WINDOW_SIZE
,
SHRPX_OPTID_FRONTEND_HTTP2_OPTIMIZE_WRITE_BUFFER_SIZE
,
SHRPX_OPTID_FRONTEND_HTTP2_READ_TIMEOUT
,
SHRPX_OPTID_FRONTEND_HTTP2_SETTINGS_TIMEOUT
,
SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS
,
...
...
src/shrpx_connection.cc
View file @
27b250ac
...
...
@@ -27,6 +27,7 @@
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif // HAVE_UNISTD_H
#include <netinet/tcp.h>
#include <limits>
...
...
@@ -757,4 +758,55 @@ void Connection::handle_tls_pending_read() {
rlimit
.
handle_tls_pending_read
();
}
int
Connection
::
get_tcp_hint
(
TCPHint
*
hint
)
const
{
#if defined(TCP_INFO) && defined(TCP_NOTSENT_LOWAT)
struct
tcp_info
tcp_info
;
socklen_t
tcp_info_len
=
sizeof
(
tcp_info
);
int
rv
;
rv
=
getsockopt
(
fd
,
IPPROTO_TCP
,
TCP_INFO
,
&
tcp_info
,
&
tcp_info_len
);
if
(
rv
!=
0
)
{
return
-
1
;
}
auto
avail_packets
=
tcp_info
.
tcpi_snd_cwnd
>
tcp_info
.
tcpi_unacked
?
tcp_info
.
tcpi_snd_cwnd
-
tcp_info
.
tcpi_unacked
:
0
;
// http://www.slideshare.net/kazuho/programming-tcp-for-responsiveness
//
// TODO 29 (5 + 8 + 16) is TLS overhead for AES-GCM. For
// CHACHA20_POLY1305, it is 21 since it does not need 8 bytes
// explicit nonce.
auto
writable_size
=
(
avail_packets
+
2
)
*
(
tcp_info
.
tcpi_snd_mss
-
29
);
if
(
writable_size
>
16
_k
)
{
writable_size
=
writable_size
&
~
(
16
_k
-
1
);
}
else
{
if
(
writable_size
<
536
)
{
LOG
(
INFO
)
<<
"writable_size is too small: "
<<
writable_size
;
}
// TODO is this required?
writable_size
=
std
::
max
(
writable_size
,
static_cast
<
uint32_t
>
(
536
*
2
));
}
if
(
LOG_ENABLED
(
INFO
))
{
LOG
(
INFO
)
<<
"snd_cwnd="
<<
tcp_info
.
tcpi_snd_cwnd
<<
", unacked="
<<
tcp_info
.
tcpi_unacked
<<
", snd_mss="
<<
tcp_info
.
tcpi_snd_mss
<<
", rtt="
<<
tcp_info
.
tcpi_rtt
<<
"us"
<<
", rcv_space="
<<
tcp_info
.
tcpi_rcv_space
<<
", writable="
<<
writable_size
;
}
hint
->
write_buffer_size
=
writable_size
;
// TODO tcpi_rcv_space is considered as rwin, is that correct?
hint
->
rwin
=
tcp_info
.
tcpi_rcv_space
;
return
0
;
#else // !defined(TCP_INFO) || !defined(TCP_NOTSENT_LOWAT)
return
-
1
;
#endif // !defined(TCP_INFO) || !defined(TCP_NOTSENT_LOWAT)
}
}
// namespace shrpx
src/shrpx_connection.h
View file @
27b250ac
...
...
@@ -66,6 +66,11 @@ struct TLSConnection {
bool
reneg_started
;
};
struct
TCPHint
{
size_t
write_buffer_size
;
uint32_t
rwin
;
};
template
<
typename
T
>
using
EVCb
=
void
(
*
)(
struct
ev_loop
*
,
T
*
,
int
);
using
IOCb
=
EVCb
<
ev_io
>
;
...
...
@@ -118,6 +123,8 @@ struct Connection {
void
set_ssl
(
SSL
*
ssl
);
int
get_tcp_hint
(
TCPHint
*
hint
)
const
;
TLSConnection
tls
;
ev_io
wev
;
ev_io
rev
;
...
...
src/shrpx_http2_upstream.cc
View file @
27b250ac
...
...
@@ -776,7 +776,9 @@ int send_data_callback(nghttp2_session *session, nghttp2_frame *frame,
// data transferred.
downstream
->
response_sent_body_length
+=
length
;
return
wb
->
rleft
()
>=
MAX_BUFFER_SIZE
?
NGHTTP2_ERR_PAUSE
:
0
;
auto
max_buffer_size
=
upstream
->
get_max_buffer_size
();
return
wb
->
rleft
()
>=
max_buffer_size
?
NGHTTP2_ERR_PAUSE
:
0
;
}
}
// namespace
...
...
@@ -919,7 +921,8 @@ Http2Upstream::Http2Upstream(ClientHandler *handler)
downstream_queue_
(
downstream_queue_size
(
handler
->
get_worker
()),
!
get_config
()
->
http2_proxy
),
handler_
(
handler
),
session_
(
nullptr
)
{
session_
(
nullptr
),
max_buffer_size_
(
MAX_BUFFER_SIZE
)
{
int
rv
;
auto
&
http2conf
=
get_config
()
->
http2
;
...
...
@@ -955,7 +958,9 @@ Http2Upstream::Http2Upstream(ClientHandler *handler)
}
int32_t
window_bits
=
faddr
->
alt_mode
?
31
:
http2conf
.
upstream
.
connection_window_bits
;
faddr
->
alt_mode
?
31
:
http2conf
.
upstream
.
optimize_window_size
?
16
:
http2conf
.
upstream
.
connection_window_bits
;
if
(
window_bits
!=
16
)
{
int32_t
window_size
=
(
1u
<<
window_bits
)
-
1
;
...
...
@@ -983,6 +988,25 @@ Http2Upstream::Http2Upstream(ClientHandler *handler)
prep_
.
data
=
this
;
ev_prepare_start
(
handler_
->
get_loop
(),
&
prep_
);
#if defined(TCP_INFO) && defined(TCP_NOTSENT_LOWAT)
if
(
http2conf
.
upstream
.
optimize_write_buffer_size
)
{
auto
conn
=
handler_
->
get_connection
();
conn
->
tls_dyn_rec_warmup_threshold
=
0
;
uint32_t
pollout_thres
=
1
;
rv
=
setsockopt
(
conn
->
fd
,
IPPROTO_TCP
,
TCP_NOTSENT_LOWAT
,
&
pollout_thres
,
static_cast
<
socklen_t
>
(
sizeof
(
pollout_thres
)));
if
(
rv
!=
0
)
{
if
(
LOG_ENABLED
(
INFO
))
{
auto
error
=
errno
;
LOG
(
INFO
)
<<
"setsockopt(TCP_NOTSENT_LOWAT, "
<<
pollout_thres
<<
") failed: errno="
<<
error
;
}
}
}
#endif // defined(TCP_INFO) && defined(TCP_NOTSENT_LOWAT)
handler_
->
reset_upstream_read_timeout
(
get_config
()
->
conn
.
upstream
.
timeout
.
http2_read
);
...
...
@@ -1032,8 +1056,44 @@ int Http2Upstream::on_read() {
// After this function call, downstream may be deleted.
int
Http2Upstream
::
on_write
()
{
int
rv
;
auto
&
http2conf
=
get_config
()
->
http2
;
if
((
http2conf
.
upstream
.
optimize_write_buffer_size
||
http2conf
.
upstream
.
optimize_window_size
)
&&
handler_
->
get_ssl
())
{
auto
conn
=
handler_
->
get_connection
();
TCPHint
hint
;
rv
=
conn
->
get_tcp_hint
(
&
hint
);
if
(
rv
==
0
)
{
if
(
http2conf
.
upstream
.
optimize_write_buffer_size
)
{
max_buffer_size_
=
std
::
min
(
MAX_BUFFER_SIZE
,
hint
.
write_buffer_size
);
}
if
(
http2conf
.
upstream
.
optimize_window_size
)
{
auto
faddr
=
handler_
->
get_upstream_addr
();
if
(
!
faddr
->
alt_mode
)
{
int32_t
window_size
=
(
1u
<<
http2conf
.
upstream
.
connection_window_bits
)
-
1
;
window_size
=
std
::
min
(
static_cast
<
uint32_t
>
(
window_size
),
hint
.
rwin
*
2
);
rv
=
nghttp2_session_set_local_window_size
(
session_
,
NGHTTP2_FLAG_NONE
,
0
,
window_size
);
if
(
rv
!=
0
)
{
if
(
LOG_ENABLED
(
INFO
))
{
ULOG
(
INFO
,
this
)
<<
"nghttp2_session_set_local_window_size() with window_size="
<<
window_size
<<
" failed: "
<<
nghttp2_strerror
(
rv
);
}
}
}
}
}
}
for
(;;)
{
if
(
wb_
.
rleft
()
>=
MAX_BUFFER_SIZE
)
{
if
(
wb_
.
rleft
()
>=
max_buffer_size_
)
{
return
0
;
}
...
...
@@ -1253,10 +1313,26 @@ ssize_t downstream_data_read_callback(nghttp2_session *session,
auto
downstream
=
static_cast
<
Downstream
*>
(
source
->
ptr
);
auto
body
=
downstream
->
get_response_buf
();
assert
(
body
);
auto
upstream
=
static_cast
<
Http2Upstream
*>
(
user_data
);
const
auto
&
resp
=
downstream
->
response
();
auto
nread
=
std
::
min
(
body
->
rleft
(),
length
);
auto
max_buffer_size
=
upstream
->
get_max_buffer_size
();
auto
buffer
=
upstream
->
get_response_buf
();
if
(
max_buffer_size
<
std
::
min
(
nread
,
static_cast
<
size_t
>
(
256
))
+
9
+
buffer
->
rleft
())
{
if
(
LOG_ENABLED
(
INFO
))
{
ULOG
(
INFO
,
upstream
)
<<
"Buffer is almost full. Skip write DATA"
;
}
return
NGHTTP2_ERR_PAUSE
;
}
nread
=
std
::
min
(
nread
,
max_buffer_size
-
9
-
buffer
->
rleft
());
auto
body_empty
=
body
->
rleft
()
==
nread
;
*
data_flags
|=
NGHTTP2_DATA_FLAG_NO_COPY
;
...
...
@@ -2027,4 +2103,6 @@ void Http2Upstream::cancel_premature_downstream(
downstream_queue_
.
remove_and_get_blocked
(
promised_downstream
,
false
);
}
size_t
Http2Upstream
::
get_max_buffer_size
()
const
{
return
max_buffer_size_
;
}
}
// namespace shrpx
src/shrpx_http2_upstream.h
View file @
27b250ac
...
...
@@ -118,6 +118,8 @@ public:
DefaultMemchunks
*
get_response_buf
();
size_t
get_max_buffer_size
()
const
;
private:
DefaultMemchunks
wb_
;
std
::
unique_ptr
<
HttpsUpstream
>
pre_upstream_
;
...
...
@@ -127,6 +129,7 @@ private:
ev_prepare
prep_
;
ClientHandler
*
handler_
;
nghttp2_session
*
session_
;
size_t
max_buffer_size_
;
bool
flow_control_
;
};
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment