diff --git a/src/util.cc b/src/util.cc index c5424217fb5..5a56da9f393 100644 --- a/src/util.cc +++ b/src/util.cc @@ -77,10 +77,42 @@ Status SockSetTcpNoDelay(int fd, int val) { return Status::OK(); } -Status SockSetTcpKeepalive(int fd, int val) { +Status SockSetTcpKeepalive(int fd, int interval) { + int val = 1; if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1) { return Status(Status::NotOK, strerror(errno)); } + +#ifdef __linux__ + // Default settings are more or less garbage, with the keepalive time + // set to 7200 by default on Linux. Modify settings to make the feature + // actually useful. + + // Send first probe after interval. + val = interval; + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) { + return Status(Status::NotOK, std::string("setsockopt TCP_KEEPIDLE: ")+strerror(errno)); + } + + // Send next probes after the specified interval. Note that we set the + // delay as interval / 3, as we send three probes before detecting + // an error (see the next setsockopt call). + val = interval / 3; + if (val == 0) val = 1; + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < 0) { + return Status(Status::NotOK, std::string("setsockopt TCP_KEEPINTVL: ")+strerror(errno)); + } + + // Consider the socket in error state after three we send three ACK + // probes without getting a reply. + val = 3; + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) { + return Status(Status::NotOK, std::string("setsockopt TCP_KEEPCNT: ")+strerror(errno)); + } +#else + ((void) interval); // Avoid unused var warning for non Linux systems. +#endif + return Status::OK(); } diff --git a/src/util.h b/src/util.h index e7ac2ddea24..fd0c5c77a94 100644 --- a/src/util.h +++ b/src/util.h @@ -41,7 +41,7 @@ sockaddr_in NewSockaddrInet(const std::string &host, uint32_t port); Status SockConnect(std::string host, uint32_t port, int *fd); Status SockConnect(std::string host, uint32_t port, int *fd, uint64_t conn_timeout, uint64_t timeout = 0); Status SockSetTcpNoDelay(int fd, int val); -Status SockSetTcpKeepalive(int fd, int val); +Status SockSetTcpKeepalive(int fd, int interval); Status SockSend(int fd, const std::string &data); Status SockReadLine(int fd, std::string *data); int GetPeerAddr(int fd, std::string *addr, uint32_t *port); diff --git a/src/worker.cc b/src/worker.cc index 3a506fe7d5d..b3dda8b5f61 100644 --- a/src/worker.cc +++ b/src/worker.cc @@ -70,7 +70,7 @@ void Worker::newConnection(evconnlistener *listener, evutil_socket_t fd, << " from port: " << worker->svr_->GetConfig()->port << " thread #" << worker->tid_; } - auto s = Util::SockSetTcpKeepalive(fd, 1); + auto s = Util::SockSetTcpKeepalive(fd, 120); if (!s.IsOK()) { LOG(ERROR) << "[worker] Failed to set tcp-keepalive, err:" << s.Msg(); evutil_closesocket(fd);