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

DragonFly BSD论坛
dragonflybsd
帖子: 20
注册时间: 10 5月 2016, 20:20

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

帖子dragonflybsd » 30 7月 2016, 18:09

在Linux 3.9+及DragonFlyBSD上支持SO_REUSEPORT扩展:

http://gitweb.dragonflybsd.org/dragonfl ... d7a2d441c9

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



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

http://nginx.com/blog/socket-sharding-n ... ase-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 */

...

}

回到 “DragonFly BSD论坛”

在线用户

用户浏览此论坛: 没有注册用户 和 1 访客