Add REUSEPORT option to AsyncServerSocket
authorDave Watson <davejwatson@fb.com>
Mon, 1 Dec 2014 20:52:35 +0000 (12:52 -0800)
committerDave Watson <davejwatson@fb.com>
Thu, 11 Dec 2014 16:01:24 +0000 (08:01 -0800)
Summary:
Adds a reuse port option to AsyncServerSocket, so multiple sockets can bind to the same accept port.  Allows for multiple accept threads, so accepts can be greater, since there is no longer a single accept lock.

reuse port option is ifdefd, since this wouldn't build with some of the older kernels otherwise.

Postponed overnight

Test Plan: Builds.  Used in an upcoming diff.

Reviewed By: jsedgwick@fb.com

Subscribers: benj, trunkagent, doug, njormrod, folly-diffs@

FB internal diff: D1710600

Tasks: 54885165788110

Signature: t1:1710600:1418066966:627e03857f9b5ff831f2922add08e90cc525c95c

folly/io/async/AsyncServerSocket.cpp
folly/io/async/AsyncServerSocket.h
folly/io/async/test/AsyncSocketTest.cpp

index 4f2e39bb591d183becaa27b77dbae4bccf652bfc..7482ae4060f646dfa7361856fbfde7d009713b21 100644 (file)
@@ -574,8 +574,18 @@ void AsyncServerSocket::setupSocket(int fd) {
     LOG(ERROR) << "failed to set SO_REUSEADDR on async server socket " << errno;
   }
 
-  // Set keepalive as desired
+  // Set reuseport to support multiple accept threads
   int zero = 0;
+  if (reusePortEnabled_ &&
+      setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(int)) != 0) {
+    LOG(ERROR) << "failed to set SO_REUSEPORT on async server socket "
+               << strerror(errno);
+    folly::throwSystemError(errno,
+                            "failed to bind to async server socket: " +
+                            address.describe());
+  }
+
+  // Set keepalive as desired
   if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
                  (keepAliveEnabled_) ? &one : &zero, sizeof(int)) != 0) {
     LOG(ERROR) << "failed to set SO_KEEPALIVE on async server socket: " <<
index 449b5a4a483484fdd83b5c3bd472df8db4b84395..72c14a79cecee8dc32c6bdbcc3dbf5c7fb821964 100644 (file)
 #include <stddef.h>
 #include <sys/socket.h>
 
+
+// Due to the way kernel headers are included, this may or may not be defined.
+// Number pulled from 3.10 kernel headers.
+#ifndef SO_REUSEPORT
+#define SO_REUSEPORT 15
+#endif
+
 namespace folly {
 
 /**
@@ -510,6 +517,36 @@ class AsyncServerSocket : public DelayedDestruction {
     return keepAliveEnabled_;
   }
 
+  /**
+   * Set whether or not SO_REUSEPORT should be enabled on the server socket,
+   * allowing multiple binds to the same port
+   */
+  void setReusePortEnabled(bool enabled) {
+    reusePortEnabled_ = enabled;
+
+    for (auto& handler : sockets_) {
+      if (handler.socket_ < 0) {
+        continue;
+      }
+
+      int val = (enabled) ? 1 : 0;
+      if (setsockopt(handler.socket_, SOL_SOCKET,
+                     SO_REUSEPORT, &val, sizeof(val)) != 0) {
+        LOG(ERROR) <<
+          "failed to set SO_REUSEPORT on async server socket " << errno;
+        folly::throwSystemError(errno,
+                                "failed to bind to async server socket");
+      }
+    }
+  }
+
+  /**
+   * Get whether or not SO_REUSEPORT is enabled on the server socket.
+   */
+  bool getReusePortEnabled_() const {
+    return reusePortEnabled_;
+  }
+
   /**
    * Set whether or not the socket should close during exec() (FD_CLOEXEC). By
    * default, this is enabled
@@ -675,6 +712,7 @@ class AsyncServerSocket : public DelayedDestruction {
   BackoffTimeout *backoffTimeout_;
   std::vector<CallbackInfo> callbacks_;
   bool keepAliveEnabled_;
+  bool reusePortEnabled_{false};
   bool closeOnExec_;
   ShutdownSocketSet* shutdownSocketSet_;
 };
index 473c87ecbb001eaf0a60a09e250073905e4ece8d..34971dc2efa35338cb08a009cb065fb142af224d 100644 (file)
 #include <iostream>
 
 #include <folly/io/async/AsyncSocket.h>
+#include <folly/io/async/AsyncServerSocket.h>
 #include <folly/io/async/EventBase.h>
 
 #include <gtest/gtest.h>
 
+namespace folly {
+
 TEST(AsyncSocketTest, getSockOpt) {
-  folly::EventBase evb;
-  std::shared_ptr<folly::AsyncSocket> socket =
-    folly::AsyncSocket::newSocket(&evb, 0);
+  EventBase evb;
+  std::shared_ptr<AsyncSocket> socket =
+    AsyncSocket::newSocket(&evb, 0);
 
   int val;
   socklen_t len;
@@ -34,3 +37,31 @@ TEST(AsyncSocketTest, getSockOpt) {
 
   EXPECT_EQ(expectedRc, actualRc);
 }
+
+TEST(AsyncSocketTest, REUSEPORT) {
+  EventBase base;
+  auto serverSocket = AsyncServerSocket::newSocket(&base);
+  serverSocket->bind(0);
+  serverSocket->listen(0);
+  serverSocket->startAccepting();
+
+  try {
+    serverSocket->setReusePortEnabled(true);
+  } catch(...) {
+    LOG(INFO) << "Reuse port probably not supported";
+    return;
+  }
+
+  SocketAddress address;
+  serverSocket->getAddress(&address);
+  int port = address.getPort();
+
+  auto serverSocket2 = AsyncServerSocket::newSocket(&base);
+  serverSocket2->setReusePortEnabled(true);
+  serverSocket2->bind(port);
+  serverSocket2->listen(0);
+  serverSocket2->startAccepting();
+
+}
+
+} // namespace