深入理解Netty核心类及其作用

 更新时间:2023年04月26日 09:40:27   作者:顽石九变  
本文介绍了Netty框架中的核心类及其作用,包括Channel、EventLoop、ChannelPipeline、ByteBuf等,通过对这些类的深入理解,可以更好地使用Netty进行网络编程开发

MessageToByteEncoder

MessageToByteEncoder是一个抽象编码器,子类可重写encode方法把对象编码为ByteBuf输出。

MessageToByteEncoder继承自ChannelOutboundHandlerAdapter,encode在出站是被调用。

public class MyMessageEncoder extends MessageToByteEncoder<MessagePO> {
    @Override
    protected void encode(ChannelHandlerContext ctx, MessagePO msg, ByteBuf out) throws Exception {
        System.out.println("MyMessageEncoder.encode,被调用");
        String json = JSONObject.toJSONString(msg);
        out.writeInt(json.getBytes(StandardCharsets.UTF_8).length);
        out.writeBytes(json.getBytes(StandardCharsets.UTF_8));
    }
}

ByteToMessageDecoder

ByteToMessageDecoder是一种ChannelInboundHandler,可以称为解码器,负责将byte字节流(ByteBuf)转换成一种Message,Message是应用可以自己定义的一种Java对象。

ByteToMessageDecoder:用于将字节转为消息,需要检测缓冲区是否有足够的字节。

public class MyMessageDecoder extends ByteToMessageDecoder {
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        System.out.println("MyMessageDecoder.decode,被调用");
        while (in.readableBytes() >= 4){
            int num = in.readInt();
            System.out.println("解码出一个整数:"+num);
            out.add(num);
        }
    }
}

ReplayingDecoder

ReplayingDecoder:继承自ByteToMessageDecoder,不需要检测缓冲区是否有足够的字节,但是ReplayingDecoder的速度略慢于ByteToMessageDecoder,而且并不是所有的ByteBuf都支持。

项目复杂度高用ReplayingDecoder,否则使用ByteToMessageDecoder。

public class MyMessageDecoder extends ReplayingDecoder<Void> {
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        System.out.println("MyMessageDecoder.decode,被调用");
        int length = in.readInt();
        byte[] content = new byte[length];
        in.readBytes(content);
        String json = new String(content,StandardCharsets.UTF_8);
        MessagePO po = JSONObject.parseObject(json,MessagePO.class);
        out.add(po);
    }
}

MessageToMessageEncoder

用于从一种消息编码为另外一种消息,例如从POJO到POJO,是一种ChannelOutboundHandler

MessageToMessageDecoder

从一种消息解码为另一种消息,例如POJO到POJO,是一种ChannelInboundHandler

MessageToMessageCodec

整合了MessageToMessageEncoder 和 MessageToMessageDecoder

public class RequestMessageCodec extends MessageToMessageCodec<String, RequestData> {
    @Override
    protected void encode(ChannelHandlerContext ctx, RequestData msg, List<Object> out) throws Exception {
        System.out.println("RequestMessageCodec.encode 被调用 " + msg);
        String json = JSONObject.toJSONString(msg);
        out.add(json);
    }
    @Override
    protected void decode(ChannelHandlerContext ctx, String msg, List<Object> out) throws Exception {
        System.out.println("RequestMessageCodec.decode 被调用 " + msg);
        RequestData po = JSONObject.parseObject(msg, RequestData.class);
        out.add(po);
    }
}

ChannelInitializer

ChannelInitializer是一种特殊的ChannelInboundHandler,可以通过一种简单的方式(调用initChannel方法)来初始化Channel。

通常在Bootstrap.handler(ChannelHandler)ServerBootstrap.handler(ChannelHandler)ServerBootstrap.childHandler(ChannelHandler)中给Channel设置ChannelPipeline。

注意:当initChannel被执行完后,会将当前的handler从Pipeline中移除。

Bootstrap bootstrap = new Bootstrap().group(group)//设置线程组
    .channel(NioSocketChannel.class)//设置客户端通道的实现类
    .handler(new ChannelInitializer<SocketChannel>() {
        @Override
        protected void initChannel(SocketChannel ch) throws Exception {
            ch.pipeline().addLast(new NettyClientHandler());//加入自己的处理器
        }
    });
ServerBootstrap bootstrap = new ServerBootstrap().group(bossGroup, workerGroup)
    .channel(NioServerSocketChannel.class)//使用NioServerSocketChannel作为服务器的通道实现
    .option(ChannelOption.SO_BACKLOG, 128)//设置线程队列等待连接的个数
    .childOption(ChannelOption.SO_KEEPALIVE, true)//设置保持活动连接状态
//      .handler(null)//该Handler对应bossGroup
    .childHandler(new ChannelInitializer<SocketChannel>() {//给workerGroup的EventLoop对应的管道设置处理器
        @Override
        protected void initChannel(SocketChannel ch) throws Exception {
            ch.pipeline().addLast(new NettyServerHandler());
        }
    });

SimpleChannelInboundHandler

SimpleChannelInboundHandler继承自ChannelInboundHandlerAdapter,可以通过泛型来规定消息类型。

处理入站的数据我们只需要实现channelRead0方法。

SimpleChannelInboundHandler在接收到数据后会自动release掉数据占用的Bytebuffer资源,ChannelInboundHandlerAdapter不会自动释放。

public class MyClientHandler extends SimpleChannelInboundHandler<MessagePO> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, MessagePO msg) throws Exception {
        System.out.println("收到服务端消息:" + msg);
    }
}

DefaultEventLoopGroup

在向pipline中添加ChannelHandler时,可以提供一个新的线程组,Handler业务会在该线程中执行。

当加ChannelHandler需要执行多线程并发业务时,DefaultEventLoopGroup可以派上大用处。

如果没有设置DefaultEventLoopGroup,默认使用的是EventLoopGroup workerGroup = new NioEventLoopGroup();

DefaultEventLoopGroup businessGroup = new DefaultEventLoopGroup(100);
...
addLast(businessGroup, new MyNettyServerHandler())
/**
 * 读取客户端发送过来的消息
 */
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    ByteBuf byteBuf = (ByteBuf) msg;
    System.out.println("收到客户信息:" + byteBuf.toString(CharsetUtil.UTF_8));
    System.out.println("客户端地址:" + ctx.channel().remoteAddress());
    System.out.println("处理线程:" + Thread.currentThread().getName());
    ctx.executor().parent().execute(()->{
        try {
            System.out.println("parent().execute Thread = " + Thread.currentThread().getName());
            TimeUnit.SECONDS.sleep(2L);
            System.out.println("parent任务执行完成1");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });
    ctx.executor().parent().execute(()->{
        try {
            System.out.println("parent().execute Thread = " + Thread.currentThread().getName());
            TimeUnit.SECONDS.sleep(2L);
            System.out.println("parent任务执行完成2");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });
    ctx.executor().parent().execute(()->{
        try {
            System.out.println("parent().execute Thread = " + Thread.currentThread().getName());
            TimeUnit.SECONDS.sleep(2L);
            System.out.println("parent任务执行完成3");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });
}

以上代码执行日志如下:

收到客户信息:Hello 服务端
客户端地址:/127.0.0.1:60345
处理线程:defaultEventLoopGroup-4-1
parent().execute Thread = defaultEventLoopGroup-4-2
parent().execute Thread = defaultEventLoopGroup-4-3
程序继续~~ defaultEventLoopGroup-4-1
parent().execute Thread = defaultEventLoopGroup-4-4
parent任务执行完成1
parent任务执行完成3
parent任务执行完成2

EventLoop定时任务

可以在Handler中通过方法ctx.channel().eventLoop().schedule()添加定时任务

ctx.channel().eventLoop().schedule(()->{
    try {
        System.out.println("Thread.currentThread().getName() = " + Thread.currentThread().getName());
        TimeUnit.SECONDS.sleep(2L);
        System.out.println("定时任务执行完成");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
},10L,TimeUnit.SECONDS);

到此这篇关于深入理解Netty核心类及其作用的文章就介绍到这了,更多相关Netty核心类内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 详解javaweb中jstl如何循环List中的Map数据

    详解javaweb中jstl如何循环List中的Map数据

    这篇文章主要介绍了详解javaweb中jstl如何循环List中的Map数据的相关资料,希望通过本文能帮助到大家,让大家理解掌握这部分内容,需要的朋友可以参考下
    2017-10-10
  • BMIDE环境导入项目报编码错误解决方案

    BMIDE环境导入项目报编码错误解决方案

    这篇文章主要介绍了BMIDE环境导入项目报编码错误解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-10-10
  • Java实现Word/Pdf/TXT转html的实例代码

    Java实现Word/Pdf/TXT转html的实例代码

    本文主要介绍了Java实现Word/Pdf/TXT转html的实例代码,代码简单易懂,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-02-02
  • Java中StringBuilder与StringBuffer的区别

    Java中StringBuilder与StringBuffer的区别

    在Java编程中,字符串的拼接是一项常见的操作。为了有效地处理字符串的拼接需求,Java提供了两个主要的类:StringBuilder和StringBuffer,本文主要介绍了Java中StringBuilder与StringBuffer的区别,感兴趣的可以了解一下
    2023-08-08
  • 基于Spring Boot保护Web应用程序

    基于Spring Boot保护Web应用程序

    这篇文章主要介绍了基于Spring Boot保护Web应用程序,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-03-03
  • 详解Spring依赖注入的三种方式以及优缺点

    详解Spring依赖注入的三种方式以及优缺点

    IoC 和 DI 是 Spring 中最重要的两个概念,其中 IoC(Inversion of Control)为控制反转的思想,而 DI(Dependency Injection)依赖注入为其(IoC)具体实现。那么 DI 实现依赖注入的方式有几种?这些注入方式又有什么不同?本文就来和大家一起详细聊聊
    2022-08-08
  • JAVA利用递归删除文件代码实例

    JAVA利用递归删除文件代码实例

    这篇文章主要介绍了JAVA利用递归删除文件代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-10-10
  • 通过AOP拦截Spring Boot日志并将其存入数据库功能实现

    通过AOP拦截Spring Boot日志并将其存入数据库功能实现

    本文介绍了如何使用Spring Boot和AOP技术实现拦截系统日志并保存到数据库中的功能,包括配置数据库连接、定义日志实体类、定义日志拦截器、使用AOP拦截日志并保存到数据库中等步骤,感兴趣的朋友一起看看吧
    2023-08-08
  • 使用IDEA创建一个vert.x项目的方法

    使用IDEA创建一个vert.x项目的方法

    这篇文章主要介绍了使用IDEA创建一个vert.x项目的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-09-09
  • Spring项目中使用Junit单元测试并配置数据源的操作

    Spring项目中使用Junit单元测试并配置数据源的操作

    这篇文章主要介绍了Spring项目中使用Junit单元测试并配置数据源的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09

最新评论