參考:php
https://www.jianshu.com/p/61df929aa98bhtml
SO_REUSEPORT學習筆記:http://www.blogjava.net/yongboy/archive/2015/02/12/422893.htmljava
代碼示例:https://www.programcreek.com/java-api-examples/index.php?api=io.netty.channel.epoll.EpollDatagramChannellinux
Linux下UDP丟包問題分析思路:https://www.jianshu.com/p/22b0f89937efgit
美團的一篇文章:Redis 高負載下的中斷優化github
運行在Linux系統上網絡應用程序,爲了利用多核的優點,通常使用如下比較典型的多進程/多線程服務器模型:bootstrap
上面模型雖然能夠作到線程和CPU核綁定,但都會存在:api
linux man文檔中一段文字描述其做用:緩存
The new socket option allows multiple sockets on the same host to bind to the same port, and is intended to improve the performance of multithreaded network server applications running on top of multicore systems.安全
SO_REUSEPORT支持多個進程或者線程綁定到同一端口,提升服務器程序的性能,解決的問題:
其核心的實現主要有三點:
要想在Netty中使用SO_REUSEPORT特性,須要知足如下兩個前提條件
直接在Netty啓動類中替換爲在Linux系統下的epoll組件
group = new EpollEventLoopGroup();//NioEventLoopGroup ->EpollEventLoopGroup bootstrap = new Bootstrap(); bootstrap.group(group) .channel(EpollDatagramChannel.class) // NioServerSocketChannel -> EpollDatagramChannel .option(ChannelOption.SO_BROADCAST, true) .option(EpollChannelOption.SO_REUSEPORT, true) // 配置EpollChannelOption.SO_REUSEPORT .option(ChannelOption.SO_RCVBUF, 1024 * 1024 * bufferSize) .handler( new ChannelInitializer<Channel>() { @Override protected void initChannel(Channel channel) throws Exception { ChannelPipeline pipeline = channel.pipeline(); // .... } });
netty提供了方法Epoll.isAvailable()來判斷是否可用epoll
使用原生epoll組件替換nio原來的組件後,須要屢次綁定同一個端口。
if (Epoll.isAvailable()) { // linux系統下使用SO_REUSEPORT特性,使得多個線程綁定同一個端口 int cpuNum = Runtime.getRuntime().availableProcessors(); log.info("using epoll reuseport and cpu:" + cpuNum); for (int i = 0; i < cpuNum; i++) { ChannelFuture future = bootstrap.bind(UDP_PORT).await(); if (!future.isSuccess()) { throw new Exception("bootstrap bind fail port is " + UDP_PORT); } } }
更多例子:https://www.programcreek.com/java-api-examples/index.php?api=io.netty.channel.epoll.EpollDatagramChannel
也能夠參考:https://github.com/netty/netty/issues/1706
Bootstrap bootstrap = new Bootstrap() .group(new EpollEventLoopGroup(5)) .channel(EpollDatagramChannel.class) .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT) .option(EpollChannelOption.SO_REUSEPORT, true) .handler(channelInitializer); ChannelFuture future; for(int i = 0; i < 5; ++i) { future = bootstrap.bind(host, port).await(); if(!future.isSuccess()) throw new Exception(String.format("Fail to bind on [host = %s , port = %d].", host, port), future.cause()); }