Netty设计特点:

1. io线程模型
使用reactor模式,同步非阻塞。这决定了可以用最少的资源做更多的事。
2. 内存零拷贝
使用直接缓存
3. 内存池设计
申请的内存可以重用,主要指直接内存。
内部实现是用一颗二叉查找树管理内存分配情况。
4. 串形化处理socket读写,避免锁,即一个指定socket的消息是串形化处理的。这样性能比多个线程同时 处理一个socket对应消息要好,因为多线程处理会有锁。
5. 提供对protobuf等高性能序列化协议支持
 
https://blog.csdn.net/tanga842428/article/details/52463248
上下文切换的原因:
  • I/O等待:在多任务系统中,进程主动发起I/O请求,但I/O设备还没有准备好,所以会发生I/O阻塞,进程进入Wait状态。
  • 时间片耗尽:在多任务分时系统中,内核分配给进程的时间片已经耗尽了,进程进入Ready状态,等待内核重新分配时间片后的执行机会。
  • 硬件中断:在抢占式的多任务分时系统中,I/O设备可以在任意时刻发生中断,CPU会停下当前正在执行的进程去处理中断,因此进程进入Ready状态。

netty优化方向:

  • 线程数控制:高并发下如果线程较多时,Context Switch会非常明显,超过CPU核心数的线程不会带来任何好处。不是特别耗时的操作的话,业务线程池也是有害无益的。Netty 5为我们提供了指定底层线程池的机会,这样能更好的控制整个中间件的线程数和调度策略。
  • 非阻塞I/O操作:要想线程少还多做事,避免阻塞是一定要做的。
  • 减少系统调用:虽然Mode Switch比Context Switch的开销要小得多,但我们还是要尽量减少频繁的syscall。
  • 数据零拷贝:从内核空间的Direct Buffer拷贝到用户空间,每次透传都拷贝的话累积起来是个不小的开销。
  • 共享状态保护:中间件内部的并发处理也是决定性能的关键。
    private void doStartNettyServer(int port) throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup(4);
try {
ServerBootstrap b = new ServerBootstrap()
.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.localAddress(port)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(...);
}
}); // Bind and start to accept incoming connections.
ChannelFuture f = b.bind(port).sync(); // Wait until the server socket is closed.
f.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}

透传不需要完整解析消息,只需要知道消息要转发给下游哪个系统就足够了。所以透传时,我们可以只解析出部分消息,消息整体还原封不动地放在Direct Buffer里,最后直接将它写入到连接下游系统的Channel中。所以应用层的Zero Copy实现就分为两部分:Direct Buffer配置和Buffer的零拷贝传递。

    ServerBootstrap b = new ServerBootstrap()
.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.localAddress(port)
.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(...);
}
});

Netty是用引用计数的方式来判断是否回收的,所以要想继续使用ByteBuf而不让Netty释放的话,就要增加它的引用计数。只要我们在ChannelPipeline中的任意一个Handler中调用ByteBuf.retain()将引用计数加1,Netty就不会释放掉它了。我们在连接下游的客户端的Encoder中发送消息成功后再释放掉,这样就达到了零拷贝透传的效果

public class RespEncoder extends MessageToByteEncoder<Resp> {

    @Override
protected void encode(ChannelHandlerContext ctx, Msg msg, ByteBuf out) throws Exception {
// Raw in Msg is retained ByteBuf
out.writeBytes(msg.getRaw(), 0, msg.getRaw().readerIndex());
msg.getRaw().release();
} }

http://laolinshi.iteye.com/blog/2341729

netty在服务器初始化的时候需要设置两个线程池,一个是用来接收客户端的连接(1),另一个是用来处理客户端的读写(一般设置成CPU核数的两倍到三倍)

为了方便系统BUG 的解决,可以为每个线程池设置一个自定义的ThreadFactory,这个factory的作用是根据线程池的类型为创建的线程设置一个特殊的名称,如boss线程的名称是NettyBoss_,工作线程的名称是NettyServerSelector_%d_%d,这样可以在以后的问题排除过程中利用这个名称识别不同的线程,分析这些线程的资源占用率,便于找到问题的根源。

Linux, 在选中worker线程池的时候可以考虑用epoll代替传统的select,前者对应netty提供的EpollEventLoopGroup,后者对应的是NioEventLoopGroup。之所以要这样做,是因为传统的select基于轮询的方式来进行事件处理,随着fd数量的增加,导致轮询的处理开销增大,实际的事件处理效率就会下降。而epoll是基于通知的方式,每当事件准备就绪的时候,epoll就会通知线程进行处理,这样可以保证及时性的同时也不会随着fd的数量的增加而降低效率。

SO_BACKLOG,这个值通常设置1024,意味着当服务器请求处理线程全满时,用于临时存放已完成三次握手的请求的队列的最大长度。这样就可以服务器处理能力达到饱和的情况下用队列来暂存用户取得请求,等服务器空闲的时候再从队列中取出请求来处理,而不是马上拒接掉。

SO_SNDBUF和SO_RCVBUF,两者可以设置成1048576,即是128K,在性能优化是通常都设置成这样

内存池,需要内存时就从内存池中分配,使用完了再归还给内存池以便可以重复利用。这样就可以避免每次使用内存所带来的内存创建和销毁开销,而且还不容易形成内存碎片。基于这样的目的可以使用netty提供的PooledByteBufAllocator分配器,需要注意的是使用这个分配器分配的内存使用完后必须手动进行释放,否则会造成内存泄漏

WRITE_BUFFER_WATER_MARK,他的作用是限制netty向channel写数据时使用的缓冲区边界。当netty需要往channel中写入数据时会先把数据写入一个Buffer缓冲区,这个缓冲区是各个channel独占的,不共享。等到channel空闲的时候就从缓冲区中读取数据进行发送,当Buffer的数据超过高水位线时就停止写入数据,设置channel的isWritable为false。等到buffer中的数据由于被消费而低于低水位线时设置channel的isWritable为true,又可以重新接受写入的数据。所以设置了这个参数之后,对应用的要求是,每次写数据时先判断channel的isWritable,在 true时才进行写入。

考虑到具体的业务逻辑处理可能涉及到耗时的数据库操作或远程RPC调用,因而在具体的业务处理Handler中启动一个新的线程来处理相关逻辑,如上图的NettyServerHandler。这样可以让这些耗时的操作不阻塞处理Handler的线程,让线程可以及时返回来处理新的操作,同样可以起到提高服务器并发处理能力的目的

class NettyServerHandler extends SimpleChannelInboundHandler<RemotingCommand> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, RemotingCommand msg) throws Exception {
Runnable run = new Runnable() {
@Override
public void run() {
//具体的业务逻辑处理
}
}
submit(run);//在线程池中提交处理任务
}
}

Netty - 1的更多相关文章

  1. 谈谈如何使用Netty开发实现高性能的RPC服务器

    RPC(Remote Procedure Call Protocol)远程过程调用协议,它是一种通过网络,从远程计算机程序上请求服务,而不必了解底层网络技术的协议.说的再直白一点,就是客户端在不必知道 ...

  2. 基于netty http协议栈的轻量级流程控制组件的实现

    今儿个是冬至,所谓“冬大过年”,公司也应景五点钟就放大伙儿回家吃饺子喝羊肉汤了,而我本着极高的职业素养依然坚持留在公司(实则因为没饺子吃没羊肉汤喝,只能呆公司吃食堂……).趁着这一个多小时的时间,想跟 ...

  3. 从netty-example分析Netty组件续

    上文我们从netty-example的Discard服务器端示例分析了netty的组件,今天我们从另一个简单的示例Echo客户端分析一下上个示例中没有出现的netty组件. 1. 服务端的连接处理,读 ...

  4. 源码分析netty服务器创建过程vs java nio服务器创建

    1.Java NIO服务端创建 首先,我们通过一个时序图来看下如何创建一个NIO服务端并启动监听,接收多个客户端的连接,进行消息的异步读写. 示例代码(参考文献[2]): import java.io ...

  5. 从netty-example分析Netty组件

    分析netty从源码开始 准备工作: 1.下载源代码:https://github.com/netty/netty.git 我下载的版本为4.1 2. eclipse导入maven工程. netty提 ...

  6. Netty实现高性能RPC服务器优化篇之消息序列化

    在本人写的前一篇文章中,谈及有关如何利用Netty开发实现,高性能RPC服务器的一些设计思路.设计原理,以及具体的实现方案(具体参见:谈谈如何使用Netty开发实现高性能的RPC服务器).在文章的最后 ...

  7. Netty构建分布式消息队列(AvatarMQ)设计指南之架构篇

    目前业界流行的分布式消息队列系统(或者可以叫做消息中间件)种类繁多,比如,基于Erlang的RabbitMQ.基于Java的ActiveMQ/Apache Kafka.基于C/C++的ZeroMQ等等 ...

  8. 基于Netty打造RPC服务器设计经验谈

    自从在园子里,发表了两篇如何基于Netty构建RPC服务器的文章:谈谈如何使用Netty开发实现高性能的RPC服务器.Netty实现高性能RPC服务器优化篇之消息序列化 之后,收到了很多同行.园友们热 ...

  9. Netty构建分布式消息队列实现原理浅析

    在本人的上一篇博客文章:Netty构建分布式消息队列(AvatarMQ)设计指南之架构篇 中,重点向大家介绍了AvatarMQ主要构成模块以及目前存在的优缺点.最后以一个生产者.消费者传递消息的例子, ...

  10. JAVA通信系列三:Netty入门总结

    一.Netty学习资料 书籍<Netty In Action中文版> 对于Netty的十一个疑问http://news.cnblogs.com/n/205413/ 深入浅出Nettyhtt ...

随机推荐

  1. UnicodeString基本操作(Ring3)

    // Unicode_String_Ring3.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include "Unicode ...

  2. Recyclerview 实现上拉加载更多

    LinearLayoutManager layoutManager; layoutManager = new LinearLayoutManager(getActivity()); layoutMan ...

  3. [UE4]AnimOffset偏移动画

    在每个在偏移动画要用到的动画文件中设置中设置上图属性. 也可以选择多个动画文件:

  4. windows下pem转ppk

    下载puttygen.exe,启动 下载路径:链接:https://pan.baidu.com/s/1hstORTa 密码:kvfi pem -> ppk :通过PuTTYgen 转换 1. I ...

  5. typescript静态属性,静态方法,抽象类,多态

    /* 1.vscode配置自动编译 1.第一步 tsc --inti 生成tsconfig.json 改 "outDir": "./js", 2.第二步 任务 ...

  6. SSL&HTTPS简单介绍

    这篇是最近看SSL和HTTPS的一个简单性总结,其中内容大部分都是参考网络上的内容,自己归纳整理了下. SSL介绍 HTTPS介绍 HTTP请求数据工作流程: l  用户在浏览器中输入网址,并告诉浏览 ...

  7. SAS数据集推送到sql server 数据库 实现代码段

    libname fdsas ODBC datasrc=fdsas user=fdsas password=fdsas123 preserve_tab_names=yes connection=shar ...

  8. 通过表达式树把datareader和datatable转换为实体

    续上两篇文章,使用emit构造dynamic method,把 datareader转换为实体,以避免直接使用反射来实现带来的性能损失.代码看似没有纰漏,但是实际上我在framwork4下运行时,调用 ...

  9. tp5 post接到的json被转义怎么解决???

    $data =input('post.');//用户唯一标识$goods = $data['goods']; $shopcuxiao=$data['shopcuxiao']; $goods=htmls ...

  10. Java - 20 Java 继承

    Java 继承 继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类.继承可以理解为一个对象从另一个对象获取属性的过程. 如果类A是类B的父类,而类B是类C的父类,我们也称C是A的子 ...