Netty单机百万连接及高性能优化

无毁的湖光-A 2018-10-08 15:10:23 ⋅ 1443 阅读

来自:https://www.toutiao.com/a6609515667714474503

作者:codeMyWorld


netty

关于netty的学习和介绍,可以去github看官方文档,这里良心推荐《netty实战》和《netty权威指南》两本书,前者对于新手更友好,原理和应用都有讲到,多读读会发现很多高性能的优化点。

netty高性能优化点

最近参加了阿里中间价性能比赛,为了提升netty写的servive mesh的网络通信的性能,最近几天查了书、博客(这里强力推荐netty作者的博客,干货真的很多),自己总结了如下一下优化点。如果有错误希望能指正。

注:这里所讨论的对应的netty版本为netty4

首先要明确要netty优化的几个主要的关注点。

  1. 减少线程切换的开销。

  2. 复用channel,可以选择池化channel

  3. zero copy的应用

  4. 减少并发下的竞态情况

接下来将细数一下总结的优化点

1. 尽可能的复用EventLoopGroup。

这里就要涉及netty的线程模型了。netty实战的第七章里有很细致的阐释。简单说EventLoopGroup包含了指定数量(如果没有指定,默认是cpu核数的两倍,可以从源码中看到)的EvenetLoop,Eve netLoop和channel的关系是一对多,一个channel被分配给一个EventLoop,它生命周期中都会使用这个EventLoop,而EventLoop背后就是线程。见下图。

因此如果需要使用ThreadLocal保存上下文,那么许多channel就会共享同一个上下文。


因此不需要每次都new出一个EventLoopGroup,其本质上是线程分配,可以复用同一个EventLoopGroup,减少资源的使用和线程的切换。特别是在服务端引导一个客户端连接的时候。如下:


2. 使用EventLoop的任务调度

在EventLoop的支持线程外使用channel,用


而不是直接使用channel.writeAndFlush(data);

前者会直接放入channel所对应的EventLoop的执行队列,而后者会导致线程的切换。

3. 减少ChannelPipline的调用长度


前者是将msg从整个ChannelPipline中走一遍,所有的handler都要经过,而后者是从当前handler一直到pipline的尾部,调用更短。

同样,为了减少pipline的长度,如果一个handler只需要使用一次,那么可以在使用过之后,将其从pipline中remove。

4. 减少ChannelHandler的创建

如果channelhandler是无状态的(即不需要保存任何状态参数),那么使用Sharable注解,并在bootstrap时只创建一个实例,减少GC。否则每次连接都会new出handler对象。


同时需要注意ByteToMessageDecoder之类的编解码器是有状态的,不能使用Sharable注解。

5. 减少系统调用(Flush)的调用

flush操作是将消息发送出去,会引起系统调用,应该尽量减少flush操作,减少系统调用的开销。

同时也要减少write的操作, 因为这样消息会流过整个ChannelPipline。

6. 使用单链接

对于两个指定的端点可以使用单一的channel,在第一次创建之后保存channel,然后下次对于同一个IP地址可以复用该channel而不需要重新建立。

你可能需要一个map来保存对于不同ip的channel,但是在初始化时这可能会有一些线程并发的问题。在这篇微信推文(https://mp.weixin.qq.com/s/JRsbK1Un2av9GKmJ8DK7IQ)中有提到对于这个的解决方案,在蚂蚁金服的sofa-bolt项目中有类似情形,不过不太理解。


7. 利用netty零拷贝,在IO操作时使用池化的DirectBuffer

在bootstrap配置参数的时候,使用.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)来指定一个池化的Allocator,并且使用ByteBuf buf = allocator.directBuffer();来获取Bytebuf。

PooledByteBufAllocator,netty会帮你复用(无需release,除非你后面还需要用到同一个bytebuf)而不是每次都重新分配ByteBuf。在IO操作中,分配直接内存而不是JVM的堆空间,就避免了在发送数据时,从JVM到直接内存的拷贝过程,这也就是zero copy的含义。

8. 一些配置参数的设置

ServerBootstrap启动时,通常 bossGroup 只需要设置为 1 即可,因为 ServerSocketChannel 在初始化阶段,只会注册到某一个 eventLoop 上,而这个 eventLoop 只会有一个线程在运行,所以没有必要设置为多线程。而 IO 线程,为了充分利用 CPU,同时考虑减少线上下文切换的开销,通常设置为 CPU 核数的两倍,这也是 Netty 提供的默认值。

在对于响应时间有高要求的场景,使用.childOption(ChannelOption.TCP_NODELAY, true)和.option(ChannelOption.TCP_NODELAY, true)来禁用nagle算法,不等待,立即发送。

9. 小心的使用并发编程技巧

千万不要阻塞EventLoop!包括了Thead.sleep() CountDownLatch 和一些耗时的操作等等,尽量使用netty中的各种future。如果必须尽量减少重量级的锁的的使用。

  • 在使用volatile时,

坏的:


好的:先将volatile变量保存到方法栈中,jdk源码中大量的使用了这种技巧。


  • 使用Atomic*FieldUpdater替换Atomic*。关于这个可以参考http://normanmaurer.me/blog/2013/10/28/Lesser-known-concurrent-classes-Part-1/。简单说,如果使用Atomic*,对于每个连接都会创建一个对象,而如果使用Atomic*FieldUpdater则会省去这部分的开销,只有一个static final变量。


10. 响应顺序的处理

当使用了单链接,就有一个必须要解决的问题,将请求和响应顺序对应起来。因为所有的操作都是异步的,TCP是基于字节流的,所以channel接收到的数据无法保证和发送顺序一致。这个的解决方案就是,对于每个请求指定一个id,对于响应也携带该id。如果后发的请求的响应先到,则将其缓存起来(可以使用一个并发的队列),然后等待该id之前的所有响应全部接收到,再按序返回。


具体实现可以参见nifty中的NiftyDispatcher类。

---------------END----------------

后续的内容同样精彩

长按关注“IT实战联盟”哦



全部评论: 0

    我有话说:

    打造千级流量秒杀系统

    服务单机性能从 2 连接优化到 5 连接。在小米期间负责过国际电商秒杀系统性能优化,提升并发性能 30%。

    如何实现单服务器300个长连接的?

    有没有试验过单机能抗300个长连接的操作?分享一下

    搞对数据库连接池,这次从100优化到3ms!阿里架构师都说好

    我在研究HikariCP(一个数据库连接池)时无意间在HikariCP的Github wiki上看到了一篇文章(即前面给出的链接),这篇文章有力地消除了我一直以来的疑虑,看完之后感觉神清气爽。故在此

    为什么单线程的Redis能够达到级的QPS?

    达到数十级别的 QPS(暂时忽略阿里对 R...

    「蘑菇街技术」每个人都想听的技术解析--Netty

    高山,而Netty却是照亮山路的明灯。大多数没有深...

    创业团队如何设计支撑并发的数据库架构?

    我们来聊一下对于一个支撑日活用户的高并系统,他的数据库架构应该如何设计?

    pgagroal 1.1.0 发布,高性能数据库连接

    pgagroal 1.1.0 已经发布。 pgagroal 是一个 PostgreSQL 的原生协议连接池,具有高性能、 限制用户和数据库连接的数量、支持预填充、删除贡献连接连接验证等

    IntelliJ IDEA 开启很慢,运行不流畅,大项目卡顿?一招配置解决!

    来源:Java面试题精选 一、前言 IDEA默认启动配置主要考虑低配置用户,参数不高(默认最低128m,最高512m),导致启动慢,然后运行也不流畅,这里我们需要优化下启动和运行配置;但是在工作中

    京东技术:如何实现TPS?详解JMQ4的存储设计

    JMQ是京东中间件团队自研的消息中间件,诞生于2014年,服务京东近个应用,2018年11.11大促期间的峰值流量超过5000亿条消息

    Java Web实战篇-轻松提高千级数据库查询效率

    通过优化数据库设计、java后台和数据库优化达到提高千级数据查询的效率。

    Nginx服务器高性能优化--轻松实现10并发访问量

    作者:章为忠学架构https://www.toutiao.com/i6804346550882402828 前面讲了如何配置Nginx虚拟主机,如何配置服务日志等很多基础的内容,大家可以去这里看看nginx系列文章:https://www.cn...

    Netty 4.1.59.Final 发布,异步事件驱动的网络应用框架

    Netty 4.1.59.Final 已经发布。Netty 是一个异步事件驱动的网络应用框架,主要用于可维护的高性能协议服务器和客户端的快速开发。 该版本除了修复各种错误之外,还包含一个安全修复程序

    Netty 4.1.60.Final 发布,异步事件驱动的网络应用框架

    Netty 4.1.60.Final 已经发布。Netty 是一个异步事件驱动的网络应用框架,主要用于可维护的高性能协议服务器和客户端的快速开发。本次更新除了修复各种错误之外,还包含了一个安全修复

    Netty 4.1.61.Final 发布,异步事件驱动的网络应用框架

    Netty 4.1.61.Final 已经发布。Netty 是一个异步事件驱动的网络应用框架,主要用于可维护的高性能协议服务器和客户端的快速开发。本次更新除了修复各种错误之外,还包含了一个安全修复

    Netty 4.1.65.Final 发布,异步事件驱动的网络应用框架

    Netty 4.1.65.Final 已经发布。Netty 是一个异步事件驱动的网络应用框架,主要用于可维护的高性能协议服务器和客户端的快速开发。本次更新除了修复各种错误之外,还包含了一个安全修复

    Netty 最佳实践

    李林锋https://www.infoq.cn/article/netty-million-level-push-service-design-points/ 1. 背景 1.1. 话题来源 最近

    高性能 NoSQL

    NoSQL是SQL的补充,本文介绍几种经典NoSQL的使用场景特点