参考: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()); }