diff --git a/etc/conf/kiwi.conf b/etc/conf/kiwi.conf index 88ea40d..801e678 100644 --- a/etc/conf/kiwi.conf +++ b/etc/conf/kiwi.conf @@ -14,6 +14,10 @@ ips 127.0.0.1 ::1 # Close the connection after a client is idle for N seconds (0 to disable) timeout 0 +# Enable the TCP keep-alive function. Set the value to 0 to disable keep-alive. +# A non-zero value represents the idle time (in seconds) before the first keep-alive probe is sent. +tcp-keepalive 300 + # Directory to store the data of kiwi. db-path ./db/ diff --git a/src/config.cc b/src/config.cc index 3f79f07..ced58f9 100644 --- a/src/config.cc +++ b/src/config.cc @@ -139,6 +139,10 @@ Config::Config() { AddNumberWithLimit("port", false, &port, PORT_LIMIT_MIN, PORT_LIMIT_MAX); AddNumber("raft-port-offset", true, &raft_port_offset); AddNumber("timeout", true, &timeout); + AddNumber("tcp-keepalive", true, &tcp_keepalive); + AddString("db-path", false, &db_path); + AddStringWithFunc("loglevel", &CheckLogLevel, false, &log_level); + AddString("logfile", false, &log_dir); AddString("db-path", false, &db_path); AddStringWithFunc("loglevel", &CheckLogLevel, false, &log_level); AddString("logfile", false, &log_dir); diff --git a/src/config.h b/src/config.h index 8d86269..c5d95f4 100644 --- a/src/config.h +++ b/src/config.h @@ -209,6 +209,13 @@ class Config { * 0 means no timeout limit, otherwise it is the specified number of seconds. */ uint32_t timeout = 0; + + /* + *Enable the tcp keep-alive function. + *0 indicates that the TCP keep-alive function is disabled + */ + uint32_t tcp_keepalive = 300; + /* * Client connect to kiwi server may need password */ diff --git a/src/kiwi.cc b/src/kiwi.cc index ed8ff95..4a01fa4 100644 --- a/src/kiwi.cc +++ b/src/kiwi.cc @@ -205,6 +205,9 @@ bool KiwiDB::Init() { PREPL.SetMasterAddr(g_config.master_ip.c_str(), g_config.master_port); } + auto tcpKeepAlive = g_config.tcp_keepalive; + options_.SetOpTcpKeepAlive(tcpKeepAlive); + options_.SetRwSeparation(true); event_server_ = std::make_unique>>(options_); diff --git a/src/net/base_socket.cc b/src/net/base_socket.cc index 8fd460a..faa9e78 100644 --- a/src/net/base_socket.cc +++ b/src/net/base_socket.cc @@ -37,6 +37,7 @@ void BaseSocket::OnCreate() { SetNonBlock(true); #endif SetNodelay(); + SetTcpKeepAlive(); SetSndBuf(); SetRcvBuf(); } @@ -65,6 +66,49 @@ void BaseSocket::SetNodelay() { } } +void BaseSocket::SetTcpKeepAlive() { + if (tcp_keep_alive_ == 0) { + return; + } + + int enabled = 1; + uint32_t idle = tcp_keep_alive_; + uint32_t intvl = idle / 3; + int cnt = 3; + + if (setsockopt(Fd(), SOL_SOCKET, SO_KEEPALIVE, &enabled, sizeof(enabled)) == -1) { + WARN("SetTcpKeepAlive fd:{} error:{}", Fd(), errno); + return; + } + +#ifdef TCP_KEEPIDLE + if (setsockopt(Fd(), IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(idle))) { + WARN("SetTcpKeepAlive fd:{} error:{}", Fd(), errno); + return; + } +#elif defined(TCP_KEEPALIVE) + /* support MacOS */ + if (setsockopt(Fd(), IPPROTO_TCP, TCP_KEEPALIVE, &idle, sizeof(idle))) { + WARN("SetTcpKeepAlive fd:{} error:{}", Fd(), errno); + return; + } +#endif + +#ifdef TCP_KEEPINTVL + if (setsockopt(Fd(), IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl))) { + WARN("SetTcpKeepAlive fd:{} error:{}", Fd(), errno); + return; + } +#endif + +#ifdef TCP_KEEPCNT + if (setsockopt(Fd(), IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt))) { + WARN("SetTcpKeepAlive fd:{} error:{}", Fd(), errno); + return; + } +#endif +} + void BaseSocket::SetSndBuf(socklen_t winsize) { if (::setsockopt(Fd(), SOL_SOCKET, SO_SNDBUF, reinterpret_cast(&winsize), sizeof(winsize)) == -1) { WARN("SetSndBuf fd:{} error:{}", Fd(), errno); diff --git a/src/net/base_socket.h b/src/net/base_socket.h index 405cc08..a75f547 100644 --- a/src/net/base_socket.h +++ b/src/net/base_socket.h @@ -45,6 +45,8 @@ class BaseSocket : public NetEvent { void SetNodelay(); + void SetTcpKeepAlive(); + void SetSndBuf(socklen_t size = SOCKET_WIN_SIZE); void SetRcvBuf(socklen_t size = SOCKET_WIN_SIZE); @@ -63,12 +65,15 @@ class BaseSocket : public NetEvent { void SetSocketType(int type) { type_ = type; } + inline void SetBSTcpKeepAlive(uint32_t keepAlive) { tcp_keep_alive_ = keepAlive; } + protected: bool NoBlock() const { return noBlock_; } private: int type_ = SOCKET_NONE; // socket type (TCP/UDP) bool noBlock_ = true; + uint32_t tcp_keep_alive_ = 300; // TCP keepalive }; } // namespace net diff --git a/src/net/event_server.h b/src/net/event_server.h index 808796f..4c138c7 100644 --- a/src/net/event_server.h +++ b/src/net/event_server.h @@ -247,11 +247,13 @@ template requires HasSetFdFunction int EventServer::StartThreadManager(bool serverMode) { std::vector> listenSockets; + auto tcpKeepAlive = opt_.GetOpTcpKeepAlive(); if (serverMode) { for (auto &listenAddr : listenAddrs_) { std::shared_ptr listen(ListenSocket::CreateTCPListen()); listen->SetListenAddr(listenAddr); + listen->SetBSTcpKeepAlive(tcpKeepAlive); listenSockets.push_back(listen); if (auto ret = (listen->Init() != static_cast(NetListen::OK))) { listenSockets.clear(); // Clean up all sockets @@ -267,6 +269,7 @@ int EventServer::StartThreadManager(bool serverMode) { auto listenAddr = listen->GetListenAddr(); listen.reset(ListenSocket::CreateTCPListen()); listen->SetListenAddr(listenAddr); + listen->SetBSTcpKeepAlive(tcpKeepAlive); if (auto ret = (listen->Init() != static_cast(NetListen::OK))) { listenSockets.clear(); // Clean up all sockets return ret; diff --git a/src/net/net_options.h b/src/net/net_options.h index 52fc959..3847f8f 100644 --- a/src/net/net_options.h +++ b/src/net/net_options.h @@ -22,10 +22,16 @@ class NetOptions { bool GetRwSeparation() const { return rwSeparation_; } + void SetOpTcpKeepAlive(uint32_t tcpKeepAlive) { tcpKeepAlive_ = tcpKeepAlive; } + + uint32_t GetOpTcpKeepAlive() const { return tcpKeepAlive_; } + private: bool rwSeparation_ = true; // Whether to separate read and write int8_t threadNum_ = 1; // The number of threads + + uint32_t tcpKeepAlive_ = 300; // The timeout of the keepalive connection in seconds }; } // namespace net diff --git a/src/net/thread_manager.h b/src/net/thread_manager.h index 2652da4..e251e3c 100644 --- a/src/net/thread_manager.h +++ b/src/net/thread_manager.h @@ -93,6 +93,7 @@ class ThreadManager { private: const int8_t index_ = 0; // The index of the thread + uint32_t tcpKeepAlive_ = 300; // The timeout of the keepalive connection in seconds std::atomic running_ = true; // Whether the thread is running NetOptions netOptions_;