面试题 Netty

Netty中的Channel和JDK NIO下的Channel之间的关系?

Netty是一个基于JDK NIO之上的一个封装框架,它的所有功能还是要基于JDK NIO包的这些基础组件来完成的,所以Netty的Channel内部包含着一个JDK NIO包的一个Channel对象,并且会将内部NIO Channel对象设置为非阻塞模式。

客户端启动类BootStrap它的Connect的过程?

将Channel注册到线程NioEventLoop中。

首先BootStrap启动时需要一些配置,核心的有Netty工作线程池EventGroup,还有Channel实现类,及Channel内部维护的Pipeline依赖的处理器单元,这些都是在启动前我们要配置好的。
BootStrap connect过程,第一件事就是根据我门配置的Channel实现类Class进行反射创建出来Netty Channel对象,再下一步就是初始化Channel内部的Pipeline将BootStrap上配置的Handler(处理单元),把这些Handler装载到Channel内部的Pipeline管理中。后续处理IO时会用到。然后就是将Channel注册到线程NioEventLoop中。

将Channel注册到线程NioEventLoop中,这一步都做了哪些?

NIO API最核心的一个类就是Selector选择器,它调用的操作系统函数完成一对多监听,在Java中使用的是Selector这个对象完成的调用,也就是说 NioEventLoop内一定是持有一个Selector对象的,用来监听socket的。

Channel注册到线程NioEventLoop时,它底层就做了一件事,就是将Netty Channel对象内部维护的JDK NIO Channel注册到NioEventLoop#Selector对象内。(一针见血)

NioEventLoop组件的工作过程?(详细)

【NioEventLoop对IO事件的处理流程】
NioEventLoop内部的线程创建后,就去执行它自身的run方法,这里面是个for(;;)死循环,循环内它首先需要计算一个IO的选择策略,这个策略表示调用selector.selectNow(…)还是selector.select(…)方法,selectNow(…)是非阻塞的,select是阻塞直到有监听的socket就绪,使用哪种策略主要是看NioEventLoop的任务队列内是否有本地任务需要执行,如果有本地任务需要执行,Netty会调用selector.selectNow()非阻塞方法,接下来处理selectKeys了,SelectKeys集合表示的是本次选择器筛选出来的就绪状态的Channel,通过迭代SelectKeys集合,处理每一个SelectKey,从Key中拿到关联的Channel,然后检查就绪的事件是读就绪,还是写就绪。

如果是读事件,那么就会把这个socket缓冲区内的数据load到一个ByteBuf中,调用当前Channel#Pipeline处理管道的传播读(事件的)接口方法,就是Pipeline#fireChannelRead()方法。就这样从Socket中收到的数据,就进入到Channel的Pipeline中。
在Pipeline中再依次调用InBound接口的Handler处理器,进行读就绪事件的处理。

它不仅可以处理IO事件,也可以处理普通事件。

你对Pipeline设计模式的理解?

Tomcat与Netty一样也在使用,Pipeline是管道的意思,我们可以在这里面创建任意数量的处理单元,
当一个事件要进行处理时,它会从第一个处理单元Handler中开始处理,依次向后传递。
比如:
【In事件处理】
第一个处理单元就是解码,将数据按照相应的(传输)协议进行解析,如Http协议,将二进制数据转为字符串后,处理Http中的Data数据。
第二个处理单元就是转换数据结构,比如将JSON数据转成业务传输层面的POJO对象。
第三个处理单元就是对加密字段进行解密;
第四个处理单元就是真正业务层面上的BusinessHandler处理了。
【Out事件处理】
当客户端向服务端发数据,
第一就是加密Handler,对POJO它需要加密的字段进行加密,然后向外传递到下一个处理器单元。
第二就是转成json字符串。
第三就是编码器,根据传输的协议,将数据按照指定协议格式化;
最后写入到Channel中,将数据刷到Socket写缓冲区内,操作系统会根据TCP连接将数据传输到服务端。

Handler对象的mask字段

每个Handler对象都会有一个int类型的mask字段,这个字段表示的是二进制的,当前Handler对上层InBound或者OutBond基类指定的这个接口方法有没有进行复写Override,如果有复写,那么在mask二进制位中这个方法的bit是1或0表示。这样设计避免空调用,向后传递时直接找出响应事件的Handler,中间跳过未实现的Handler,从大流量服务器来看,这个优化很好。

Netty是全异步的框架,你知道它是怎么做到异步处理的吗?

主要是Promise接口,它是Future接口的增强,Future接口都很熟悉了比如在使用线程池时,使用submit提交任务就会返回一个Future句柄,外部线程可以使用Future.get()去获取任务的执行结果,这个方法会阻塞调用线程,直到任务执行完毕或异常。
原生Channel的缺点不支持异步,原因是它内部没有线程资源,所以没法异步。
Netty中,作为Promise接口实现ChannelPromise,它内部有一个Channel对象,Netty的Channel对象会注册到一个NioEventLoop上,同时这个Channel也会持有NioEventLoop对象,EventLoop对象是有线程资源的,所以EventLoop作为ChannelPromise内部对象,也间接的表示ChannelPromise有线程资源,可以注册一些Listener对象。就表示这个Promise关联任务完成后,接下来要执行的事情。
(赞)

你对Netty线程池的理解?(深)

默认情况下Netty线程池最多可以有当前CPU核心数*2的线程数量,NioEventLoopGroup和NioEventLoop是池和单个线程的关系,Group线程池有个接口方法是next()方法,这个方法会反回一个EventLoop线程对象,看源码会发现NioEventLoopGroup这个线程池内部真正的Tread线程,都是延迟创建的。

虽然NioEventLoopGroup会将NioEventLoop创建,但NioEventLoop内部的线程对象不会创建,等到EventLoop对象接收到第一个需要执行任务时,才创建Thread对象。

如果你看NioEventLoop对象的继承关系(SingleThreadEventExecutor 接口),就会发现EventLoop根本不是个单线程,它更倾向于一个单线程的线程池,EventLoop内有自己的队列,它即处理Selector工作,也处理任务队列内的普通任务。

NioEventLoop它内部的任务队列,做过哪些优化?

原来使用普通队列LinkedBlockingQueue,这种队列性能不高,采用的是条件Condition队列来实现的,同时只能有一个线程池进行读写,针对这点,Netty它没有使用JUC中的Queue,而是使用JCTools包提供的Queue,这个包里面有很多Queue实现类,根据类名的前缀去区分,比如SpscXxxxQueue就表示这个队列是单个生产者单个消费者场景使用;MpscXxxQueue是多个生产者单个消费者这个场景使用。JCTools中的源码核心技术点一定是CAS的。

其他

Netty 服务端 boss 启2个线程,worker也启2个线程,这时共有几个线程?
在JDK中启动一个JVM查看工具,可以查看到共3个线程,1个boss,2个worker。初始化了1个,第2个EventLoop 在数组中(堆里面的对象)线程未创建。

Netty中ctx.writeAndFlush与ctx.channel().writeAndFlush的区别

https://blog.csdn.net/FishSeeker/article/details/78447684

ctx.writeAndFlush(new Object())
从当前的handler直接发出这个消息;
代表object从当前的handler流向head节点;

ctx.chanel().writeAndFlush(new Object())
从整个pipeline最后一个outHandler发出。
代表object从tail节点流向head节点;

总结

netty是基于NIO的异步事件驱动的高性能网络通讯框架,它对JDK中的NIO做了封装和优化,提供了更好的性能的同时降低了使用的难度。作为一个异步NIO框架,Netty的所有IO操作都是异步非阻塞的。

Netty是Reactor模型的一种实现,采用Reactor主从线程模型:一组线程池(boss)接收请求,一组线程池(worker)处理IO。
Reactor模型可以分为三种,主要由多路复用器(Acceptor)、事件分发器(Dispatcher)、事件处理器(Handler)组成。

server启动说明
1、EventLoopGroup是基于JDK的线程池进行封装的实现,它包含一组 EventLoop,Channel 通过注册到 EventLoop 中执行操作。
1.1 NioEventLoopGroup是一个线程池循环处理器,它是多线程事件驱动IO操作类。netty服务端有两个NioEventLoopGroup。一个称为boss,负责接收连接请求;另外一个称为worker,负责处理具体的IO操作等。
默认情况下,boss线程池中线程数量为1个,worker中的线程数量为2*CPU。

2、ServerBootstrap是服务端启动的引导类。

3、指定使用NioServerSocketChannel来建立请求连接。

4、构造一系列channelHandler处理链来组成ChannelPipeline。ChannelPipeline控制着ChannelHandler的流转。

5、option用来配置一些channel的参数,配置的参数会被ChannelConfig使用。
6、编写的TestServerHandler可以继承ChannelInboundHandlerAdapter,复写了channelRead()和exceptionCaught()方法。

一个ChannelPipeline中可以有多个ChannelHandler实例,而每一个ChannelHandler实例与ChannelPipeline之间的桥梁就是ChannelHandlerContext实例。
ChannelHandlerContext里就包含着ChannelHandler中的上下文信息,每一个ChannelHandler被添加都ChannelPipeline中都会创建一个与其对应的ChannelHandlerContext。ChannelHandlerContext的功能就是用来管理它所关联的ChannelHandler和在同一个ChannelPipeline中ChannelHandler的交互。

参考

https://blog.csdn.net/u011428598/article/details/83152619?utm_medium=distribute.wap_relevant.none-task-blog-baidujs_title-11

Netty 接受请求过程源码分析
https://www.cnblogs.com/stateis0/p/9062141.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值