Netty 和nio、io、epoll相关

尚硅谷netty课程
netty 文章
https://www.bbsmax.com/A/WpdKYYemJV/
https://www.freesion.com/article/2828427687/
https://www.cnblogs.com/xuewangkai/p/11158576.html (select/poll/epoll对比)
这里学习一下Netty中的异步操作与任务队列

同步与异步
在这里插入图片描述
在这里插入图片描述

NIO本质上也是同步的,Netty框架是基于NIO的,不过Netty自己完成了异步操作

同步与异步
同步:一个进程执行任务时,会一直等待到当前任务完成才会进行下一个任务
异步:是指进程不需要一直等待下去,而是继续执行下面的操作,不管其他进程的状态,当有信息返回的时候会通知进程进行处理

异步的效率明显会优于同步,网页的AJAX技术就是异步执行的

NIO虽然是多路复用,但是当线程执行某一个操作,其他操作都要进行等待
ChannelFuture为异步操作
在这里插入图片描述
netty nio
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

Bio : 用流读取
inputStream.read

Nio: 用channel读取
FileInputstream,getChannel().read()

Selector类的方法:
select() 返回当前有事件发生的selectkey,至少要有一个IO事件发生否则会导致成为阻塞状态

selectkey() 返回所有的selectkey

在这里插入图片描述

在这里插入图片描述

Selector.select() //阻塞方法
Selector.keys() 返回所有的key
Selector.selectedKeys() 返回当前注册事件的key

Netty模型
在这里插入图片描述
NioEventLoop包含selector和taskQueue,1个NioEventLoop对应1个selector
在这里插入图片描述
ctx可以得到pipeline,pipeline和channel互相包含

在这里插入图片描述NioEventLoop包含selector和taskQueue,与模型框图对应
在这里插入图片描述

第三种例子没讲清楚,需要搜索继续学习
在这里插入图片描述

byteBuffer.rewind(); //倒带 position = 0 mark 作废

第2、4条第一句话注意
在这里插入图片描述

在这里插入图片描述
netty中所有的io操作都是异步的
在这里插入图片描述

在这里插入图片描述
channel出站、入站
在这里插入图片描述
编码器是outBoundHandler
解码器是InBoundHandler
在这里插入图片描述

netty源码分析

bind()方法

bind–>doBind()–>initAndRigister()–>init() -->init(Channel channel)—拿到serverBootStrap中的属性值加在channel的pipeline中,将EventLoop注册到channel立即返回channelFuture结果(无论成功或者失败),后续操作系统会查询channelFuture最终结果(EventLoop注册到channel结果)返回;
bind–>doBind()–>doBind0() 在上一步的注册完成后,bind端口或者ip/域名+端口,并附带返回channelFuture结果

在这里插入图片描述

在这里插入图片描述

ChannelInitialize中泛型为SocketChannel的原因
在这里插入图片描述
fireChannelRead 个人理解是转到下一个Channel 中去read
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
pipeline为双头队列
入站(栈) 从socketchannel 获取给到pipeline
出站(栈) 从pipeline 获取给到socketchannel
在这里插入图片描述

在这里插入图片描述
netty io异步执行方法
注意如下直接在channelRead中处理有可能导致线程阻塞,耗时的方法需要添加在异步操作中,如下边1和2

在这里插入图片描述

异步方案1代码:相当于把整个hanler类对象加入线程池,开销和占用大
//创建业务线程池
//这里我们就创建2个子线程
static final EventExecutorGroup group = new DefaultEventExecutorGroup(2);
ChannelPipeline p = ch.pipeline(); //ch为SocketChannel的对象
p.addLast(group, new EchoServerHandler());
或者
//在handler类的方法中将任务提交到 group线程池,相当于单独把某个方法加入线程池,开销和占用小
static final EventExecutorGroup group = new DefaultEventExecutorGroup(2);
group.submit(new Callable() {
@Override
public Object call() throws Exception {

            //接收客户端信息
            ByteBuf buf = (ByteBuf) msg;
            byte[] bytes = new byte[buf.readableBytes()];
            buf.readBytes(bytes);
            String body = new String(bytes, "UTF-8");
            //休眠10秒
            Thread.sleep(10 * 1000);
            System.out.println("group.submit 的  call 线程是=" + Thread.currentThread().getName());
            ctx.writeAndFlush(Unpooled.copiedBuffer("hello, 客户端~(>^ω^<)喵2", CharsetUtil.UTF_8));
            return null;
        }
    });

异步方案 2:
static final EventExecutorGroup group = new DefaultEventExecutorGroup(2);
//EchoServerHandler()加入到group中的一个线程
p.addLast(group, new EchoServerHandler());
ChannelInitializer的中EchoServerHandler的方法中加入如下
//普通方式
//接收客户端信息
ByteBuf buf = (ByteBuf) msg;
byte[] bytes = new byte[buf.readableBytes()];
buf.readBytes(bytes);
String body = new String(bytes, “UTF-8”);
//休眠10秒
Thread.sleep(10 * 1000);
System.out.println(“普通调用方式的 线程是=” + Thread.currentThread().getName());
ctx.writeAndFlush(Unpooled.copiedBuffer(“hello, 客户端~(>ω<)喵2”, CharsetUtil.UTF_8));
在这里插入图片描述

在这里插入图片描述

  1. 将任务加入ctx下的pipeline下的channel下的eventLoop下的SchduledTaskQueue中
    3.非当前REACTOR线程调用CHANNEL的方法
    例如:
    在推送系统的业务线程里面,根据用户的标识,找到对应的 Channel 引用,然后调用 Write 类方法向该用户推送消息,就会进入到这种场景。最终的 Write 会提交到任务队列中后被异步消费

可以使用一个集合管理所有的SocketChannel,当我们想要推送消息时,将业务加入到该Channel对应的NioEventLoop的TaskQueue或者ScheduleTaskQueue

其实操作与上面两种类似
服务器使用集合保存管理所有的Channel,ctx.channel()获得想要的通道, ctx.channel().eventLoop().schedule()或者.execute()操作调用Write方法向该用户推送消息

(其实我们前面已经往客户端推送消息了,这里只是对推送的通道做一定的限制)
上述截图普通任务方案1和定时2代码如下(非异步)
//解决方案1 用户程序自定义的普通任务,这种方法式和几个System.out.println(“EchoServer Handler 的线程是=” + Thread.currentThread().getName());打印出来显示是同一个线程,不是异步,这种方案不行

System.out.println(“EchoServer Handler 的线程是=” + Thread.currentThread().getName());
ctx.channel().eventLoop().execute(new Runnable() {
@Override
public void run() {

            try {
                Thread.sleep(5 * 1000);
                ctx.writeAndFlush(Unpooled.copiedBuffer("hello, 客户端~(>^ω^<)喵2", CharsetUtil.UTF_8));
                System.out.println("EchoServer Handler 的线程是=" + Thread.currentThread().getName());
                System.out.println("channel code=" + ctx.channel().hashCode());
            } catch (Exception ex) {
                System.out.println("发生异常" + ex.getMessage());
            }
        }
    });

    ctx.channel().eventLoop().execute(new Runnable() {
        @Override
        public void run() {

            try {
                Thread.sleep(5 * 1000);
                ctx.writeAndFlush(Unpooled.copiedBuffer("hello, 客户端~(>^ω^<)喵3", CharsetUtil.UTF_8));
                System.out.println("channel code=" + ctx.channel().hashCode());
                System.out.println("EchoServer Handler 的线程是=" + Thread.currentThread().getName());
            } catch (Exception ex) {
                System.out.println("发生异常" + ex.getMessage());
            }
        }
    });

    //解决方案2 : 用户自定义定时任务 -》 该任务是提交到 scheduleTaskQueue中

    ctx.channel().eventLoop().schedule(new Runnable() {
        @Override
        public void run() {

            try {
                Thread.sleep(5 * 1000);
                ctx.writeAndFlush(Unpooled.copiedBuffer("hello, 客户端~(>^ω^<)喵4", CharsetUtil.UTF_8));
                System.out.println("channel code=" + ctx.channel().hashCode());
            } catch (Exception ex) {
                System.out.println("发生异常" + ex.getMessage());
            }
        }
    }, 5, TimeUnit.SECONDS);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值