nghttpx: Support multiple HTTP/2 session per worker

Currently, we use same number of HTTP/2 sessions per worker with given
backend addresses.  New option to specify the number of HTTP/2 session
per worker will follow.
This commit is contained in:
Tatsuhiro Tsujikawa 2015-03-10 23:11:22 +09:00
parent c5860fc6f4
commit 446de923f3
16 changed files with 83 additions and 38 deletions

View File

@ -918,6 +918,7 @@ void fill_default_config() {
mod_config()->no_server_push = false; mod_config()->no_server_push = false;
mod_config()->host_unix = false; mod_config()->host_unix = false;
mod_config()->http2_downstream_connchk = false; mod_config()->http2_downstream_connchk = false;
mod_config()->http2_downstream_connections_per_worker = 0;
} }
} // namespace } // namespace
@ -2064,6 +2065,11 @@ int main(int argc, char **argv) {
} }
} }
if (get_config()->http2_downstream_connections_per_worker == 0) {
mod_config()->http2_downstream_connections_per_worker =
get_config()->downstream_addrs.size();
}
if (get_config()->rlimit_nofile) { if (get_config()->rlimit_nofile) {
struct rlimit lim = {static_cast<rlim_t>(get_config()->rlimit_nofile), struct rlimit lim = {static_cast<rlim_t>(get_config()->rlimit_nofile),
static_cast<rlim_t>(get_config()->rlimit_nofile)}; static_cast<rlim_t>(get_config()->rlimit_nofile)};

View File

@ -576,6 +576,9 @@ void ClientHandler::set_should_close_after_write(bool f) {
void ClientHandler::pool_downstream_connection( void ClientHandler::pool_downstream_connection(
std::unique_ptr<DownstreamConnection> dconn) { std::unique_ptr<DownstreamConnection> dconn) {
if (!dconn->poolable()) {
return;
}
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
CLOG(INFO, this) << "Pooling downstream connection DCONN:" << dconn.get(); CLOG(INFO, this) << "Pooling downstream connection DCONN:" << dconn.get();
} }
@ -605,7 +608,7 @@ ClientHandler::get_downstream_connection() {
} }
auto dconn_pool = worker_->get_dconn_pool(); auto dconn_pool = worker_->get_dconn_pool();
auto http2session = worker_->get_http2_session(); auto http2session = worker_->next_http2_session();
if (http2session) { if (http2session) {
dconn = make_unique<Http2DownstreamConnection>(dconn_pool, http2session); dconn = make_unique<Http2DownstreamConnection>(dconn_pool, http2session);
@ -628,12 +631,8 @@ ClientHandler::get_downstream_connection() {
SSL *ClientHandler::get_ssl() const { return conn_.tls.ssl; } SSL *ClientHandler::get_ssl() const { return conn_.tls.ssl; }
Http2Session *ClientHandler::get_http2_session() const { ConnectBlocker *ClientHandler::get_connect_blocker() const {
return worker_->get_http2_session(); return worker_->get_connect_blocker();
}
ConnectBlocker *ClientHandler::get_http1_connect_blocker() const {
return worker_->get_http1_connect_blocker();
} }
void ClientHandler::direct_http2_upgrade() { void ClientHandler::direct_http2_upgrade() {

View File

@ -93,8 +93,7 @@ public:
void remove_downstream_connection(DownstreamConnection *dconn); void remove_downstream_connection(DownstreamConnection *dconn);
std::unique_ptr<DownstreamConnection> get_downstream_connection(); std::unique_ptr<DownstreamConnection> get_downstream_connection();
SSL *get_ssl() const; SSL *get_ssl() const;
Http2Session *get_http2_session() const; ConnectBlocker *get_connect_blocker() const;
ConnectBlocker *get_http1_connect_blocker() const;
// Call this function when HTTP/2 connection header is received at // Call this function when HTTP/2 connection header is received at
// the start of the connection. // the start of the connection.
void direct_http2_upgrade(); void direct_http2_upgrade();

View File

@ -260,6 +260,7 @@ struct Config {
size_t http2_downstream_window_bits; size_t http2_downstream_window_bits;
size_t http2_upstream_connection_window_bits; size_t http2_upstream_connection_window_bits;
size_t http2_downstream_connection_window_bits; size_t http2_downstream_connection_window_bits;
size_t http2_downstream_connections_per_worker;
size_t downstream_connections_per_host; size_t downstream_connections_per_host;
size_t downstream_connections_per_frontend; size_t downstream_connections_per_frontend;
// actual size of downstream_http_proxy_addr // actual size of downstream_http_proxy_addr

View File

@ -58,6 +58,9 @@ public:
virtual void on_upstream_change(Upstream *uptream) = 0; virtual void on_upstream_change(Upstream *uptream) = 0;
virtual int on_priority_change(int32_t pri) = 0; virtual int on_priority_change(int32_t pri) = 0;
// true if this object is poolable.
virtual bool poolable() const = 0;
void set_client_handler(ClientHandler *client_handler); void set_client_handler(ClientHandler *client_handler);
ClientHandler *get_client_handler(); ClientHandler *get_client_handler();
Downstream *get_downstream(); Downstream *get_downstream();

View File

@ -62,6 +62,10 @@ public:
virtual void on_upstream_change(Upstream *upstream) {} virtual void on_upstream_change(Upstream *upstream) {}
virtual int on_priority_change(int32_t pri); virtual int on_priority_change(int32_t pri);
// This object is not poolable because we dont' have facility to
// migrate to another Http2Session object.
virtual bool poolable() const { return false; }
int send(); int send();
void attach_stream_data(StreamData *sd); void attach_stream_data(StreamData *sd);

View File

@ -40,6 +40,7 @@
#include "shrpx_ssl.h" #include "shrpx_ssl.h"
#include "shrpx_http.h" #include "shrpx_http.h"
#include "shrpx_worker.h" #include "shrpx_worker.h"
#include "shrpx_connect_blocker.h"
#include "http2.h" #include "http2.h"
#include "util.h" #include "util.h"
#include "base64.h" #include "base64.h"
@ -140,14 +141,14 @@ void writecb(struct ev_loop *loop, ev_io *w, int revents) {
} // namespace } // namespace
Http2Session::Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx, Http2Session::Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx,
Worker *worker) ConnectBlocker *connect_blocker, Worker *worker)
: conn_(loop, -1, nullptr, get_config()->downstream_write_timeout, : conn_(loop, -1, nullptr, get_config()->downstream_write_timeout,
get_config()->downstream_read_timeout, 0, 0, 0, 0, writecb, readcb, get_config()->downstream_read_timeout, 0, 0, 0, 0, writecb, readcb,
timeoutcb, this), timeoutcb, this),
worker_(worker), ssl_ctx_(ssl_ctx), session_(nullptr), worker_(worker), connect_blocker_(connect_blocker), ssl_ctx_(ssl_ctx),
data_pending_(nullptr), data_pendinglen_(0), addr_idx_(0), session_(nullptr), data_pending_(nullptr), data_pendinglen_(0),
state_(DISCONNECTED), connection_check_state_(CONNECTION_CHECK_NONE), addr_idx_(0), state_(DISCONNECTED),
flow_control_(false) { connection_check_state_(CONNECTION_CHECK_NONE), flow_control_(false) {
read_ = write_ = &Http2Session::noop; read_ = write_ = &Http2Session::noop;
on_read_ = on_write_ = &Http2Session::noop; on_read_ = on_write_ = &Http2Session::noop;
@ -237,6 +238,14 @@ int Http2Session::initiate_connection() {
int rv = 0; int rv = 0;
if (state_ == DISCONNECTED) { if (state_ == DISCONNECTED) {
if (connect_blocker_->blocked()) {
if (LOG_ENABLED(INFO)) {
DCLOG(INFO, this)
<< "Downstream connection was blocked by connect_blocker";
}
return -1;
}
auto worker_stat = worker_->get_worker_stat(); auto worker_stat = worker_->get_worker_stat();
addr_idx_ = worker_stat->next_downstream; addr_idx_ = worker_stat->next_downstream;
++worker_stat->next_downstream; ++worker_stat->next_downstream;
@ -261,6 +270,7 @@ int Http2Session::initiate_connection() {
get_config()->downstream_http_proxy_addr.storage.ss_family); get_config()->downstream_http_proxy_addr.storage.ss_family);
if (conn_.fd == -1) { if (conn_.fd == -1) {
connect_blocker_->on_failure();
return -1; return -1;
} }
@ -270,6 +280,7 @@ int Http2Session::initiate_connection() {
SSLOG(ERROR, this) << "Failed to connect to the proxy " SSLOG(ERROR, this) << "Failed to connect to the proxy "
<< get_config()->downstream_http_proxy_host.get() << get_config()->downstream_http_proxy_host.get()
<< ":" << get_config()->downstream_http_proxy_port; << ":" << get_config()->downstream_http_proxy_port;
connect_blocker_->on_failure();
return -1; return -1;
} }
@ -329,6 +340,7 @@ int Http2Session::initiate_connection() {
conn_.fd = util::create_nonblock_socket( conn_.fd = util::create_nonblock_socket(
downstream_addr.addr.storage.ss_family); downstream_addr.addr.storage.ss_family);
if (conn_.fd == -1) { if (conn_.fd == -1) {
connect_blocker_->on_failure();
return -1; return -1;
} }
@ -337,6 +349,7 @@ int Http2Session::initiate_connection() {
const_cast<sockaddr *>(&downstream_addr.addr.sa), const_cast<sockaddr *>(&downstream_addr.addr.sa),
downstream_addr.addrlen); downstream_addr.addrlen);
if (rv != 0 && errno != EINPROGRESS) { if (rv != 0 && errno != EINPROGRESS) {
connect_blocker_->on_failure();
return -1; return -1;
} }
@ -358,12 +371,14 @@ int Http2Session::initiate_connection() {
downstream_addr.addr.storage.ss_family); downstream_addr.addr.storage.ss_family);
if (conn_.fd == -1) { if (conn_.fd == -1) {
connect_blocker_->on_failure();
return -1; return -1;
} }
rv = connect(conn_.fd, const_cast<sockaddr *>(&downstream_addr.addr.sa), rv = connect(conn_.fd, const_cast<sockaddr *>(&downstream_addr.addr.sa),
downstream_addr.addrlen); downstream_addr.addrlen);
if (rv != 0 && errno != EINPROGRESS) { if (rv != 0 && errno != EINPROGRESS) {
connect_blocker_->on_failure();
return -1; return -1;
} }
@ -1377,7 +1392,9 @@ void Http2Session::signal_write() {
LOG(INFO) << "Start connecting to backend server"; LOG(INFO) << "Start connecting to backend server";
} }
if (initiate_connection() != 0) { if (initiate_connection() != 0) {
SSLOG(FATAL, this) << "Could not initiate backend connection"; if (LOG_ENABLED(INFO)) {
SSLOG(INFO, this) << "Could not initiate backend connection";
}
disconnect(true); disconnect(true);
} }
break; break;
@ -1513,6 +1530,8 @@ int Http2Session::connected() {
return -1; return -1;
} }
connect_blocker_->on_success();
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
SSLOG(INFO, this) << "Connection established"; SSLOG(INFO, this) << "Connection established";
} }

View File

@ -47,6 +47,7 @@ namespace shrpx {
class Http2DownstreamConnection; class Http2DownstreamConnection;
class Worker; class Worker;
class ConnectBlocker;
struct StreamData { struct StreamData {
Http2DownstreamConnection *dconn; Http2DownstreamConnection *dconn;
@ -54,7 +55,8 @@ struct StreamData {
class Http2Session { class Http2Session {
public: public:
Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx, Worker *worker); Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx,
ConnectBlocker *connect_blocker, Worker *worker);
~Http2Session(); ~Http2Session();
int check_cert(); int check_cert();
@ -192,6 +194,7 @@ private:
// Used to parse the response from HTTP proxy // Used to parse the response from HTTP proxy
std::unique_ptr<http_parser> proxy_htp_; std::unique_ptr<http_parser> proxy_htp_;
Worker *worker_; Worker *worker_;
ConnectBlocker *connect_blocker_;
// NULL if no TLS is configured // NULL if no TLS is configured
SSL_CTX *ssl_ctx_; SSL_CTX *ssl_ctx_;
nghttp2_session *session_; nghttp2_session *session_;

View File

@ -1200,8 +1200,6 @@ Downstream *Http2Upstream::find_downstream(int32_t stream_id) {
return downstream_queue_.find(stream_id); return downstream_queue_.find(stream_id);
} }
nghttp2_session *Http2Upstream::get_http2_session() { return session_; }
// WARNING: Never call directly or indirectly nghttp2_session_send or // WARNING: Never call directly or indirectly nghttp2_session_send or
// nghttp2_session_recv. These calls may delete downstream. // nghttp2_session_recv. These calls may delete downstream.
int Http2Upstream::on_downstream_header_complete(Downstream *downstream) { int Http2Upstream::on_downstream_header_complete(Downstream *downstream) {

View File

@ -64,8 +64,6 @@ public:
void remove_downstream(Downstream *downstream); void remove_downstream(Downstream *downstream);
Downstream *find_downstream(int32_t stream_id); Downstream *find_downstream(int32_t stream_id);
nghttp2_session *get_http2_session();
int rst_stream(Downstream *downstream, uint32_t error_code); int rst_stream(Downstream *downstream, uint32_t error_code);
int terminate_session(uint32_t error_code); int terminate_session(uint32_t error_code);
int error_reply(Downstream *downstream, unsigned int status_code); int error_reply(Downstream *downstream, unsigned int status_code);

View File

@ -130,7 +130,7 @@ int HttpDownstreamConnection::attach_downstream(Downstream *downstream) {
} }
if (conn_.fd == -1) { if (conn_.fd == -1) {
auto connect_blocker = client_handler_->get_http1_connect_blocker(); auto connect_blocker = client_handler_->get_connect_blocker();
if (connect_blocker->blocked()) { if (connect_blocker->blocked()) {
if (LOG_ENABLED(INFO)) { if (LOG_ENABLED(INFO)) {
@ -768,7 +768,7 @@ int HttpDownstreamConnection::on_write() {
} }
int HttpDownstreamConnection::on_connect() { int HttpDownstreamConnection::on_connect() {
auto connect_blocker = client_handler_->get_http1_connect_blocker(); auto connect_blocker = client_handler_->get_connect_blocker();
if (!util::check_socket_connected(conn_.fd)) { if (!util::check_socket_connected(conn_.fd)) {
conn_.wlimit.stopw(); conn_.wlimit.stopw();

View File

@ -59,6 +59,8 @@ public:
virtual void on_upstream_change(Upstream *upstream); virtual void on_upstream_change(Upstream *upstream);
virtual int on_priority_change(int32_t pri) { return 0; } virtual int on_priority_change(int32_t pri) { return 0; }
virtual bool poolable() const { return true; }
int on_connect(); int on_connect();
void signal_write(); void signal_write();

View File

@ -821,8 +821,6 @@ Downstream *SpdyUpstream::find_downstream(int32_t stream_id) {
return downstream_queue_.find(stream_id); return downstream_queue_.find(stream_id);
} }
spdylay_session *SpdyUpstream::get_http2_session() { return session_; }
// WARNING: Never call directly or indirectly spdylay_session_send or // WARNING: Never call directly or indirectly spdylay_session_send or
// spdylay_session_recv. These calls may delete downstream. // spdylay_session_recv. These calls may delete downstream.
int SpdyUpstream::on_downstream_header_complete(Downstream *downstream) { int SpdyUpstream::on_downstream_header_complete(Downstream *downstream) {

View File

@ -60,8 +60,6 @@ public:
void remove_downstream(Downstream *downstream); void remove_downstream(Downstream *downstream);
Downstream *find_downstream(int32_t stream_id); Downstream *find_downstream(int32_t stream_id);
spdylay_session *get_http2_session();
int rst_stream(Downstream *downstream, int status_code); int rst_stream(Downstream *downstream, int status_code);
int error_reply(Downstream *downstream, unsigned int status_code); int error_reply(Downstream *downstream, unsigned int status_code);

View File

@ -51,17 +51,20 @@ void eventcb(struct ev_loop *loop, ev_async *w, int revents) {
Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx, Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx,
ssl::CertLookupTree *cert_tree, ssl::CertLookupTree *cert_tree,
const std::shared_ptr<TicketKeys> &ticket_keys) const std::shared_ptr<TicketKeys> &ticket_keys)
: loop_(loop), sv_ssl_ctx_(sv_ssl_ctx), cl_ssl_ctx_(cl_ssl_ctx), : next_http2session_(0), loop_(loop), sv_ssl_ctx_(sv_ssl_ctx),
cert_tree_(cert_tree), ticket_keys_(ticket_keys), cl_ssl_ctx_(cl_ssl_ctx), cert_tree_(cert_tree), ticket_keys_(ticket_keys),
connect_blocker_(make_unique<ConnectBlocker>(loop_)),
graceful_shutdown_(false) { graceful_shutdown_(false) {
ev_async_init(&w_, eventcb); ev_async_init(&w_, eventcb);
w_.data = this; w_.data = this;
ev_async_start(loop_, &w_); ev_async_start(loop_, &w_);
if (get_config()->downstream_proto == PROTO_HTTP2) { if (get_config()->downstream_proto == PROTO_HTTP2) {
http2session_ = make_unique<Http2Session>(loop_, cl_ssl_ctx, this); auto n = get_config()->http2_downstream_connections_per_worker;
} else { for (; n > 0; --n) {
http1_connect_blocker_ = make_unique<ConnectBlocker>(loop_); http2sessions_.push_back(make_unique<Http2Session>(
loop_, cl_ssl_ctx, connect_blocker_.get(), this));
}
} }
} }
@ -185,10 +188,22 @@ WorkerStat *Worker::get_worker_stat() { return &worker_stat_; }
DownstreamConnectionPool *Worker::get_dconn_pool() { return &dconn_pool_; } DownstreamConnectionPool *Worker::get_dconn_pool() { return &dconn_pool_; }
Http2Session *Worker::get_http2_session() const { return http2session_.get(); } Http2Session *Worker::next_http2_session() {
if (http2sessions_.empty()) {
return nullptr;
}
ConnectBlocker *Worker::get_http1_connect_blocker() const { auto res = http2sessions_[next_http2session_].get();
return http1_connect_blocker_.get(); ++next_http2session_;
if (next_http2session_ >= http2sessions_.size()) {
next_http2session_ = 0;
}
return res;
}
ConnectBlocker *Worker::get_connect_blocker() const {
return connect_blocker_.get();
} }
struct ev_loop *Worker::get_loop() const { struct ev_loop *Worker::get_loop() const {

View File

@ -29,6 +29,7 @@
#include <mutex> #include <mutex>
#include <deque> #include <deque>
#include <vector>
#include <thread> #include <thread>
#ifndef NOTHREADS #ifndef NOTHREADS
#include <future> #include <future>
@ -94,8 +95,8 @@ public:
void set_ticket_keys(std::shared_ptr<TicketKeys> ticket_keys); void set_ticket_keys(std::shared_ptr<TicketKeys> ticket_keys);
WorkerStat *get_worker_stat(); WorkerStat *get_worker_stat();
DownstreamConnectionPool *get_dconn_pool(); DownstreamConnectionPool *get_dconn_pool();
Http2Session *get_http2_session() const; Http2Session *next_http2_session();
ConnectBlocker *get_http1_connect_blocker() const; ConnectBlocker *get_connect_blocker() const;
struct ev_loop *get_loop() const; struct ev_loop *get_loop() const;
SSL_CTX *get_sv_ssl_ctx() const; SSL_CTX *get_sv_ssl_ctx() const;
SSL_CTX *get_cl_ssl_ctx() const; SSL_CTX *get_cl_ssl_ctx() const;
@ -104,6 +105,8 @@ public:
bool get_graceful_shutdown() const; bool get_graceful_shutdown() const;
private: private:
std::vector<std::unique_ptr<Http2Session>> http2sessions_;
size_t next_http2session_;
#ifndef NOTHREADS #ifndef NOTHREADS
std::future<void> fut_; std::future<void> fut_;
#endif // NOTHREADS #endif // NOTHREADS
@ -121,8 +124,7 @@ private:
ssl::CertLookupTree *cert_tree_; ssl::CertLookupTree *cert_tree_;
std::shared_ptr<TicketKeys> ticket_keys_; std::shared_ptr<TicketKeys> ticket_keys_;
std::unique_ptr<Http2Session> http2session_; std::unique_ptr<ConnectBlocker> connect_blocker_;
std::unique_ptr<ConnectBlocker> http1_connect_blocker_;
bool graceful_shutdown_; bool graceful_shutdown_;
}; };