改造现有网络服务程序支持SO_REUSEPORT

dragonflyseallyhs 发布于 2015/06/19 14:16
阅读 488
收藏 0

DragonflyBSD微信公众号:BSDchina 或 BSD操作系统 加关注

DragonflyBSD邮箱:seallyhs@dragonflybsd.org


Linux 3.9+DragonFlyBSD上支持SO_REUSEPORT扩展:

http://gitweb.dragonflybsd.org/dragonfly.git/commit/740d1d9f7b7bf9c9c021abb8197718d7a2d441c9

https://lwn.net/Articles/542629/

 

该扩展大幅提高了accept(2)的性能和并使接收到的套接字在不同的进程和线程间更平均的分布。nginx的性能提升:

http://nginx.com/blog/socket-sharding-nginx-release-1-9-1/

 

以下介绍两个比较简单的方法来改造现有网络服务程序来支持SO_REUSEPORT

 

为方便阅读,错误处理和IPv6套接字创建略去。

 

服务器监听套接字一般通过如下方式建立:

 

static int

create_listen_socket(const struct sockaddr_in *in)

{

    int serv_s, on;

 

    serv_s = socket(AF_INET, SOCK_STREAM, 0);

 

    on = 1;

    setsockopt(serv_s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

 

    /* Set other socket options */

    ...

 

    on = 1;

    ioctl(serv_s, FIONBIO, &on, sizeof(on));

 

    bind(serv_s, (const struct sockaddr *)in, sizeof(*in));

    listen(serv_s, -1);

 

    return serv_s;

}

 

主函数和worker函数大至会是这样:

 

static void

worker(int s)

{

    /*

     * BSD kqueue or Linux epoll with 's',

     * and maybe with "accept mutex".

     */

    ...

}

 

int

main(...)

{

    struct sockaddr_in in;

    int serv_s;

 

    ...

    /* setup 'in' */

    ...

 

    serv_s = create_listen_socket(&in);

    for (i = 0; i < NWORKER; ++i) {

        pid_t pid;

 

        pid = fork();

        if (pid == 0) {

            worker(serv_s);

            exit(0);

        }

    }

 

    /* e.g. wait children */

    ...

}

 

 

我们来进行改造吧!

 

 

方法一:

 

第一步,修改create_listen_socket()

 

setsockopt(serv_s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))改为

setsockopt(serv_s, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on))

 

第二步,修改worker()。如果worker()原先有使用accept mutex的,移除accept mutex相关代码:

 

static void

worker(const struct sockaddr_in *in)

{

    int s;

 

    s = create_listen_socket(in); /* <===== newly added */

 

    /* Original code */

    /*

     * BSD kqueue or Linux epoll with 's';

     * NOTE: DO NOT USE "accept mutex"

     */

    ...

}

 

第三步,修改main()

 

int

main(...)

{

    struct sockaddr_in in;

#if 0

    /* It was in the original code; no longer used */

    int serv_s;

#endif

 

    /* Original code */

    ...

 

#if 0

    /* Workers create their own listen sockets */

    serv_s = create_listen_socket(&in);

#endif

    for (i = 0; i < NWORKER; ++i) {

        pid_t pid;

 

        pid = fork();

        if (pid == 0) {

            worker(&in); /* <===== was worker(serv_s) */

            exit(0);

        }

    }

 

    /* Original code */

    /* e.g. wait children */

    ...

}

 

 

方法二(nginx SO_REUSEPORT支持使用类似逻辑):

 

第一步,同方法一第一步。

 

第二步,修改worker()。如果worker()原先有使用accept mutex的,移除accept mutex相关代码。如果worker()没有使用accept mutex,则无需修改。

 

static void

worker(int s)

{

    /* Original code */

    /*

     * BSD kqueue or Linux epoll with 's';

     * NOTE: DO NOT USE "accept mutex"

     */

    ...

}

 

第三步,修改main()

 

int

main(...)

{

    struct sockaddr_in in;

#if 0

    /* It was in the original code; replaced by listen socket array */

    int serv_s;

#else

    int serv_s_arr[NWORKER];

#endif

 

    /* Original code */

    ...

 

#if 0

    /* We are going to create set of listen sockets */

    serv_s = create_listen_socket(&in);

#else

    for (i = 0; i < NWORKER; ++i)

        serv_s_arr[i] = create_listen_socket(&in);

#endif

    for (i = 0; i < NWORKER; ++i) {

        pid_t pid;

 

        pid = fork();

        if (pid == 0) {

            worker(serv_s_arr[i]); /* was worker(serv_s) */

            exit(0);

        }

    }

 

    /* Original code */

    /* e.g. wait children */

    ...

}

加载中
返回顶部
顶部