参考:

https://www.jianshu.com/p/61df929aa98b

SO_REUSEPORT学习笔记:http://www.blogjava.net/yongboy/archive/2015/02/12/422893.html

代码示例:https://www.programcreek.com/java-api-examples/index.php?api=io.netty.channel.epoll.EpollDatagramChannel

Linux下UDP丢包问题分析思路:https://www.jianshu.com/p/22b0f89937ef

美团的一篇文章:Redis 高负载下的中断优化

当前Linux网络应用程序问题

运行在Linux系统上网络应用程序,为了利用多核的优势,一般使用以下比较典型的多进程/多线程服务器模型:

  1. 单线程listen/accept,多个工作线程接收任务分发,虽CPU的工作负载不再是问题,但会存在:

    • 单线程listener,在处理高速率海量连接时,一样会成为瓶颈
    • CPU缓存行丢失套接字结构(socket structure)现象严重
  2. 所有工作线程都accept()在同一个服务器套接字上呢,一样存在问题:
    • 多线程访问server socket锁竞争严重
    • 高负载下,线程之间处理不均衡,有时高达3:1不均衡比例
    • 导致CPU缓存行跳跃(cache line bouncing)
    • 在繁忙CPU上存在较大延迟

上面模型虽然可以做到线程和CPU核绑定,但都会存在:

  • 单一listener工作线程在高速的连接接入处理时会成为瓶颈
  • 缓存行跳跃
  • 很难做到CPU之间的负载均衡
  • 随着核数的扩展,性能并没有随着提升

SO_REUSEPORT解决了什么问题

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支持多个进程或者线程绑定到同一端口,提高服务器程序的性能,解决的问题:

  • 允许多个套接字 bind()/listen() 同一个TCP/UDP端口

    • 每一个线程拥有自己的服务器套接字
    • 在服务器套接字上没有了锁的竞争
  • 内核层面实现负载均衡
  • 安全层面,监听同一个端口的套接字只能位于同一个用户下面

其核心的实现主要有三点:

  • 扩展 socket option,增加 SO_REUSEPORT 选项,用来设置 reuseport。
  • 修改 bind 系统调用实现,以便支持可以绑定到相同的 IP 和端口
  • 修改处理新建连接的实现,查找 listener 的时候,能够支持在监听相同 IP 和端口的多个 sock 之间均衡选择。

Netty使用SO_REUSEPORT

要想在Netty中使用SO_REUSEPORT特性,需要满足以下两个前提条件

  • linux内核版本 >= 3.9
  • Netty版本 >= 4.0.16

替换Netty中的Nio组件为原生组件

直接在Netty启动类中替换为在Linux系统下的epoll组件

  • NioEventLoopGroup → EpollEventLoopGroup
  • NioEventLoop → EpollEventLoop
  • NioServerSocketChannel → EpollServerSocketChannel
  • NioSocketChannel → EpollSocketChannel
  • 如下所示:
        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());
}

Linux下Netty实现高性能UDP服务(SO_REUSEPORT)的更多相关文章

  1. 【Network】高性能 UDP 服务应该怎么搞?

    参考资料: Netty系列之Netty高性能之道 C++高性能服务框架revover:rudp总体介绍(可靠UDP传输) - zerok的专栏 - 博客频道 - CSDN.NET 高性能异步Socke ...

  2. linux下可以禁用的一些服务

    linux下多软件/多脚本之间的配合: 包括做好 “实体”和“配置”两个方面的事情 “实体”是指实实在在的脚本文件,服务脚本: “配置”是指其他与之交互的.协同工作的软件.脚本,要进行适当的配置,告知 ...

  3. linux下启动和关闭tomcat服务的方式

    Linux下tomcat服务的启动.关闭与错误跟踪,通常通过以下几种方式启动关闭tomcat服务: 切换到tomcat主目录下的bin目录 启动tomcat服务 生产模式: 方式一:直接启动 ./st ...

  4. 3、linux下Socket编程-TCP/UDP

    1.什么是Socket 网络的 Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符.Socket也具有一个类似于打开文件的函数调用Socket(),该函数返 回一个整型的Socke ...

  5. 使用BeetleX在Linux下部署.NET多站点服务

    ​      在windows下常用IIS来部署.NET的多站点服务,但在Linux下就没这么方便了:虽然可以使用一些代理服务器如nginx,jexus等来反代或部署应用,但nginx对.NET应用的 ...

  6. 【修改端口号】linux下修改apache,nginx服务端口号

    一.linux下修改apache端口号 yum安装后,apache配置文件: /etc/httpd/conf/httpd.conf 找到apache目录下的 httpd.conf, 使用vi 打开,找 ...

  7. windows和linux 下将tomcat注册为服务

    参考文献: tomcat注册成windows服务 背景 当前项目需要运行两个Tomcat,每次启动系统以后都要手动进入到tomcat目录执行startup.bat,非常烦,所以想将这两个tomcat直 ...

  8. linux下修改apache,nginx服务端口号

    一.linux下修改apache端口号 yum安装后,apache配置文件: /etc/httpd/conf/httpd.conf 找到apache目录下的 httpd.conf, 使用vi 打开,找 ...

  9. Linux下开启和关闭Telnet服务

    telnet与ssh相比,安全性能并不高,但是在SSH版本升级或者其他的情况下还是需要开启这一服务. linux提供服务是由运行在后台的守护程序(daemon)来执行的,telnet服务是由xinet ...

随机推荐

  1. Python学习笔记九

    Python学习笔记之九 为什么要有操作系统 管理硬件,提供接口. 管理调度进程,并且将多个进程对硬件的竞争变得有序. 操作系统发展史 第一代计算机:真空管和穿孔卡片 没有操作系统,所有的程序设计直接 ...

  2. HTML5+CSS3点击指定按钮显示某些内容效果

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  3. [DIV+CSS] set the screen capture Part 1 (div截取屏幕)

    使用下面的代码来获取屏幕.用DIV加CSS 来控制. 使用mousemove来获取移动的时候DIV的变化, 效果图如下: 使用5个DIV来组成实现截图目的第一部分,现在只是实现了选择的第一部分. HT ...

  4. 【EasyNetQ】- 发布/订阅模式

    EasyNetQ支持的最简单的消息传递模式是发布/ 订阅.这种模式是消除消费者信息提供者的绝佳方式.出版商简单地向全世界说,“这已经发生了”或“我现在有了这些信息”.它不关心是否有人正在倾听,他们可能 ...

  5. python之基于libsvm识别数字验证码

    1. 参考 字符型图片验证码识别完整过程及Python实现 2.图片预处理和手动分类 (1)分析图片 from PIL import Image img = Image.open('nums/ttt. ...

  6. open_basedir php授权目录设置

    php为了安全性考虑,有一项 open_basedir 的设置.根据你web服务器环境,open_basedir可以在几个地方设置. 首先 在php.ini中配置. ;open_basedir = 如 ...

  7. Python 地点转化为经纬度

    1.geopy包下载地点https://pypi.python.org/pypi/geopy 2.安装步骤            运行cmd,切换到D:/python/geopy-1.11.0/目录下 ...

  8. 在Qt中配置TBB以及简单实用

    最近本人在写离线光线追踪渲染器,但是Qt::QtConcurrent的功能有限,所以就尝试使用了一下,顺便分享一些经验. TBB里面的parallel_for非常适合光线追踪渲染器,而QtConcur ...

  9. webpack实现开发、测试、生产等环境的打包切换

    使用webpack构建的工程,在开发过程中不同环境的配置不同,在各种环境的打包切换过程中需要手动修改相关配置达到预期目的.但是每次都手动修改会比较麻烦,本文简单介绍如何通过对webpack进行配置,实 ...

  10. javascript的数组之includes()

    includes()方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回true,否则返回false. var array1 = [1, 2, 3]; console.log(array ...