SO_REUSEPORT: strange kernel balancer behaviour

trafdev trafdev at mail.ru
Tue Jul 23 07:09:45 UTC 2013


It's like shared acceptor FD and N processes:

Listening proc:

bool HttpServer::Listen(unsigned short port, uint listen_backlog) {
    LOG4CXX_TRACE(kLogger, "Listen");
    if ((sockd_acceptor_ = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        LOG4CXX_ERROR_ERRNO(kLogger, "socket");
        return false;
    }
    struct sockaddr_in sa_in;
    memset(&sa_in, 0, sizeof(sa_in));
    sa_in.sin_family = AF_INET;
    sa_in.sin_port = htons(port);
    sa_in.sin_addr.s_addr = htonl(INADDR_ANY);
    int yes = 1;
    if (setsockopt(sockd_acceptor_, SOL_SOCKET, SO_REUSEPORT, &yes, 
sizeof (yes)) == -1) {
        LOG4CXX_ERROR_ERRNO(kLogger, "setsockopt");
        return false;
    }
    if (bind(sockd_acceptor_, (const struct sockaddr*)&sa_in, 
sizeof(sa_in)) == -1) {
        LOG4CXX_ERROR_ERRNO(kLogger, "bind");
        return false;
    }
    if (listen(sockd_acceptor_, listen_backlog) == -1) {
        LOG4CXX_ERROR_ERRNO(kLogger, "socket listen");
        return false;
    }
    if (!fcntl_set(sockd_acceptor_, O_NONBLOCK))
        return false;

    sockd_acceptor_watcher_.set<HttpServer, &HttpServer::AcceptConnCb> 
(this);
    sockd_acceptor_watcher_.start(sockd_acceptor_, ev::READ);

    int wrk = thread::hardware_concurrency();
    while (--wrk) {
        pid_t pid = fork();
        if (pid == 0) {
            LOG4CXX_INFO(kLogger, "process " << wrk <<  " started");
            ev::default_loop().post_fork();
            ev::default_loop().run();
            return true;
        }
    }

    ev::default_loop().run();
    return true;
}


Accept conn callback:

void HttpServer::AcceptConnCb(ev::io &w, int revents) {
    LOG4CXX_TRACE(kLogger, "AcceptConnCb");
    CHECK_LIBEV_ERROR
    struct sockaddr_in addr;
    socklen_t addr_len = sizeof (addr);
    int sockd;
    if ((sockd = accept(sockd_acceptor_, (struct sockaddr *) &addr,
            &addr_len)) == -1) {
        if (errno == EAGAIN || errno == EWOULDBLOCK) {
            return;
        } else {
            LOG4CXX_ERROR_ERRNO(kLogger, "accept");
            return;
        }
    }
    if (!fcntl_set(sockd, O_NONBLOCK))
        return;
    char str[INET_ADDRSTRLEN];
    if (!inet_ntop(AF_INET, &(addr.sin_addr), str, INET_ADDRSTRLEN)) {
        LOG4CXX_ERROR(kLogger, "inet_ntop");
        return;
    }
    // Internal logic follows... ConnIn* c = 
ConnPool::Instance().Add(sockd, str);
    // c->Recv(this);
    return;
}

Accept conn callback is called in N processes on each connection, only 
one wins,
others exit by errno == EAGAIN case. Overhead is almost zero.
Problem is that "wins" distribution is far from equal.


On Mon Jul 22 23:37:57 2013, Adrian Chadd wrote:
> eg:
>
> * one process, one listen thread, multiple dispatch threads?
> * n processes, one listen FD per process, all listening on the same IP:port?
> * one process, each thread listening on the same IP:port?
> * something else?
>
> Thanks,
>
>
>
> -adrian
>
>


More information about the freebsd-net mailing list