Netty的介绍与简单使用

座无虚席
• 阅读 826

一、Netty的优势

尽管我们前面学习NIO的时候,我已经尽可能的简化代码,但是我们依旧会发现,JDK NIO的开发依旧是极为复杂,在业务开发中我们还要考虑到业务的处理流程、业务的复用、请求的并发量、请求过程中的编解码问题、网络传输中的半包粘包问题等等,会进一步增加NIO开发的难度!

  • Netty是基于上述难题提供了一个统一的解决方案,Netty提供了一个对于Socket编程的统一模板,即使是一个没有网络编程基础的开发人员,也能够很轻松的开发出一个高并发、低延时的程序!
  • Netty提供了很多默认的编解码功能,能够很轻松的实现一些市面上常见的协议比如FTP、HTTP、SMTP、WebSocket等,能够通过很少的几行代码很轻松的解决网络通讯过程中的半包、粘包问题!
  • 定制能力强,Netty优秀的代码风格和强大的扩展能力,可以让我们通过ChannelHandler对通信框架进行灵活的扩展,以及通过管道流(PipLine)实现业务功能的相互隔离与复用!
  • 成熟、稳定,Netty修复了现在JDK已经发现了的,所有的JDK NIO BUG,其中最著名的就是臭名昭著的JDK空轮训BUG!
  • 社区活跃,版本迭代周期短,发现的BUG可以被及时修复,同时,更多的新功能会加入;

二、Netty的架构设计

Netty的介绍与简单使用

这是来自官网的一张架构图,我们可以大致的了解Netty的模块!

  • Core核心层,是Netty的最主要的实现,后续的所有扩展都建立在Core之上,他提供了事件的可扩展模型、网络通讯编程的通用API、数据零拷贝和数据载体的封装和复用!
  • Transport Service服务传输层,Netty提供了底层网络通讯的能力,它抽象了底层网络TCP、UDP等协议的网络通信,使得用户不在为一个网络通信开发的底层技术所头疼,似的注意力能够更加专注于业务!也正是因为这一层的封装,使得NIO/BIO之间能够无缝切换!
  • Protocol Support协议层,Netty几乎实现类市面上的大部分主流协议、包括HTTP、SSL、Protobuf、压缩、大文件传输、WebSocket、文本、二进制等主流协议, 而且Netty支持自定义扩展协议。Netty丰富的协议使得用户的开发成本大大降低,使用内置的协议可以很轻松的开发一个类似于Tomcat的Http服务器!

三、Netty的基本使用和介绍

经过上面的介绍,我们大概了解了Netty的基本架构,下面我们会看一下Netty的基本使用,然后我会逐行解析,希望能够通过这一节课帮助大家入门Netty编程,也为后面的源码学习更好的铺垫一下!

1. 开发一个服务端

我们使用Netty开发一个简单的服务端代码:

服务端接收到客户端的消息,打印出来,然后主动中断连接!

启动服务端源代码:

/**
 * 服务端
 *
 * @author 源码学徒
 * @date 2021年4月19日12:39:21
 */
public class EchoServer {

    public static void main(String[] args) {
        //设置事件循环组
        EventLoopGroup boss = new NioEventLoopGroup(1);
        EventLoopGroup worker = new NioEventLoopGroup();

        try {
            //设置服务启动器
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(boss, worker)
                    .channel(NioServerSocketChannel.class)
                    .localAddress(8989)
                    //设置服务管道
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(new EchoServerHandler());
                        }
                    });
            //同步阻塞 直到绑定完成
            ChannelFuture channelFuture = serverBootstrap.bind().sync();
            //监听通道关闭方法 等待服务端通道关闭完成
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //优雅的关闭线程组
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }
}

服务端读取数据处理器源代码 : EchoServerHandler

/**
 * 服务端打印处理的handler
 *
 * @author huangfu
 * @date 2021年4月19日12:42:34
 */
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf byteBuf = (ByteBuf) msg;
        try {
            //转换为字符串
            String message = byteBuf.toString(StandardCharsets.UTF_8);
            System.out.println(message);
        }finally {
            ReferenceCountUtil.release(byteBuf);
            ctx.channel().close();
        }
    }
}

2. 服务端源代码介绍

EventLoopGroup boss = new NioEventLoopGroup(1);
EventLoopGroup worker = new NioEventLoopGroup();

定义两个事件循环组,还记得我们第五章将的我们对NIO的优化版本吗?现在暂时你可以把它理解为两个selector组

  • boss内部只包含一个selector选择器,用于接收新连接!
  • worker内部默认是CPU*2个selector选择器,用于处理我们的业务逻辑!

boss接收到新连接后,将新连接产生的SocketChannel交给worker线程组处理! 用一个现在比较流行的比喻:boss相当于老板,worker相当于工人,boss不处理工作,只负责在外面跑业务拉活谈合同。接到新活之后,就把这个活交给worker来干,自己再去接下一个活!

这两行代码建议参考上一章的NIO的优化版本学习!

ServerBootstrap serverBootstrap = new ServerBootstrap();

ServerBootstrap是Netty为我们提供的一个快速启动配置类,为什么Netty相较于NIO开发比较简单,就是因为Netty为我们提供了一个网络编程的代码的模板,那么想要使用这些模板,就必须要告诉模板一些必要的参数,供模板读取,而ServerBootstrap正式这个作用,通过初始化的时候,我们设置各种参数,我们将之保存到ServerBootstrap中,后续Netty服务启动的时候,会读取我们初始化的时候配置的各种参数,完成自己的初始化

 serverBootstrap.group(boss, worker)

将我们前面初始化的两个事件循环组绑定起来,保存到serverBootstrap中,供后续读取!

.channel(NioServerSocketChannel.class)

我们这里开发的是一个服务端程序,所以我们使用的是NioServerSocketChannel,在Netty中分为两种SocketChannel,一种是NioServerSocketChannel一种是NioSocketChannel,两者都继承与JDK的ServerSocketChannel和SocketChannel,是Netty官方对于通讯管道的扩展:

Netty的介绍与简单使用

  • NioServerSocketChannel: 服务端通道
  • NioSocketChannel: 客户端通道

有关于两个通道,我们后面会详细分析,这里你们先记着,NioServerSocketChannel代表着服务端通道!NioSocketChannel代表着客户端通道,不要搞混了!

.localAddress(8989)

设置一个端口号,服务端对外暴露的端口号!

.childHandler(new ChannelInitializer<SocketChannel>() {
    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        socketChannel.pipeline().addLast(new EchoServerHandler());
    }
});

这个代码及其重要,后面也会重点提及,这里主要是做服务编排的,想客户端Socket绑定一个通道,并添加我们的业务处理类 xxxxHandler,当一个客户端连接上服务端后,后续所有的业务处理,都会由这里注册的各种Handler来处理,这就是Netty提供的能够让开发人员专注于业务开发的主要逻辑所在,后面会对这一块代码进行一个重点的讲解,这里也只需要记住,我们注册这些Handler后,客户端发来的数据都会在这些Handler中流转处理!

ChannelFuture channelFuture = serverBootstrap.bind().sync();

上面的初始化完成了,开始进行服务器启动和端口绑定,根据上面设置的端口号绑定,细心的同学可能会发现我还调用了一个sync方法,Netty是基于异步事件来开发的,这里我们进行bind调用之后,因为是异步执行,所以我们并不知道什么时候完成,所以这里调用了一个阻塞的方法(sync),一直阻塞等待绑定完成后才继续往下走!

channelFuture.channel().closeFuture().sync();

获取一个服务端的通道对象,并且对服务端的通道对象添加一个关闭的监听,并调用阻塞方法(sync),调用后,程序会一直阻塞再这里,等待服务端管道关闭,才会继续往下走!一般来说,服务端管道除非我们主动停机活因为异常崩溃,否则服务端管道会一直存活,那么改程序将会一直阻塞在这里,形成一个服务!

boss.shutdownGracefully();
worker.shutdownGracefully();

优雅停机,该段程序会将通道标记为不可用状态,等待程序处理完毕后,释放Netty所创建的所有资源!

public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {....}

当服务端察觉到客户端发来数据时,回调该逻辑!

3. 开发一个客户端

/**
 * 打印程序客户端
 *
 * @author huangfu
 * @date 2021年4月19日13:58:16
 */
public class EchoClient {
    public static void main(String[] args) {
        EventLoopGroup worker = new NioEventLoopGroup();

        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.remoteAddress("127.0.0.1", 8989)
                    .group(worker)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new EchoClientHandler());
                        }
                    });

            ChannelFuture channelFuture = bootstrap.connect().sync();
            channelFuture.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            worker.shutdownGracefully();
        }
    }
}

客户端处理Handler

/**
 * @author huangfu
 * @date
 */
public class EchoClientHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ByteBuf buffer = ByteBufAllocator.DEFAULT.buffer();
        buffer.writeBytes("Hello Netty".getBytes(StandardCharsets.UTF_8));
        ctx.channel().writeAndFlush(buffer);
    }
}

4. 客户端源代码介绍

客户端的代码和服务端的代码基本一致,所以这里不做全部讲解,只做不一样的讲解:

EventLoopGroup worker = new NioEventLoopGroup();

这里客户端只有一个事件循环组,为什么?因为客户端不存在新连接嘛!

Bootstrap bootstrap = new Bootstrap();

这个里和服务端的ServerBootstrap基本一致,是为了保存客户端的配置所设置的一个类!

ChannelFuture channelFuture = bootstrap.connect().sync();

连接服务端,并等待链接完成!

Handler的重载方法也发生了变化:
public void channelActive(ChannelHandlerContext ctx) throws Exception {....}

这里的含义是,当客户端被激活后既链接到服务端后,回调该逻辑!

细心的同学在练习的时候可能会发现一点问题,我们发现客户端会有 handler和childHandler两种方法

.handler()
//设置服务管道
.childHandler()
  • handler: 是绑定在 ServerSocketChannel之上的,负责服务端的逻辑处理!
  • childHandler: 是绑定在SockerChannel之上的,当客户端绑定成功后,会产生一个SocketChannel对象会调用该handler的绑定!

总结

本节课比较简单,主要是对Netty的基本使用有一个比较简单的认知,希望大家课下多练习,争取会简单的使用Netty!

点赞
收藏
评论区
推荐文章
一分钟学会、三分钟上手、五分钟应用,快速上手责任链框架详解 | 京东云技术团队
责任链模式是开发过程中常用的一种设计模式,在SpringMVC、Netty等许多框架中均有实现。我们日常的开发中如果要使用责任链模式,通常需要自己来实现,但自己临时实现的责任链既不通用,也很容易产生框架与业务代码耦合不清的问题,增加CodeReview的成本。
Stella981 Stella981
3年前
Netty如何实现同一个端口接收TCP和HTTP请求
前言在java的网络编程世界里,Netty的地位可谓是举足轻重,说到基于NIO的网络编程,Netty几乎成为企业的首选,本文不会过多介绍Netty的基本使用等知识,本文着重介绍在Netty中如何实现同一个端口,既能接收TCP请求,也能接收Http请求。由于一些特殊的原因,我要实现一款消息中间件,暂时称为“企业消息总线”吧。简单描述一下场景,对如
Stella981 Stella981
3年前
Dubbo处理TCP拆包粘包问题
Dubbo处理TCP拆包粘包问题在TCP网络传输工程中,由于TCP包的缓存大小限制,每次请求数据有可能不在一个TCP包里面,或者也可能多个请求的数据在一个TCP包里面。那么如果合理的decode接受的TCP数据很重要,需要考虑TCP拆包和粘包的问题。我们知道在Netty提供了各种Decoder来解决此类问题,比如LineBasedFrameDecod
Wesley13 Wesley13
3年前
Java BIO、NIO与AIO的介绍(学习过程)
JavaBIO、NIO与AIO的介绍因为netty是一个NIO的框架,所以在学习netty的过程中,开始之前。针对于BIO,NIO,AIO进行一个完整的学习。学习资源分享:Netty学习:https://www.bilibili.com/video/BV1DJ411m7NR?from
Stella981 Stella981
3年前
BIO、NIO、AIO系列二:Netty
一、概述Netty是一个Java的开源框架。提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。Netty是一个NIO客户端,服务端框架。允许快速简单的开发网络应用程序。例如:服务端和客户端之间的协议,它简化了网络编程规范。二、NIO开发的问题
Wesley13 Wesley13
3年前
NIO网络编程中重复触发读(写)事件
一、前言  公司最近要基于Netty构建一个TCP通讯框架,因Netty是基于NIO的,为了更好的学习和使用Netty,特意去翻了之前记录的NIO的资料,以及重新实现了一遍NIO的网络通讯,不试不知道,一试发现好多细节没注意,导致客户端和服务端通讯的时候出现了一些非常莫名其妙的问题,这边我记录下耗了我一晚上的问题~二、正文
Stella981 Stella981
3年前
Netty 入门初体验
Netty简介Netty是一款异步的事件驱动的网络应用程序框架,支持快速开发可维护的高性能的面向协议的服务器和客户端。Netty主要是对java的nio包进行的封装为什么要使用Netty上面介绍到Netty是一款高性能的网络通讯框架,那么我们为什么要使用Netty,换句话说,
Stella981 Stella981
3年前
Netty入门2之
在上一章中我们认识了netty,他有三大优点:并发高,传输快,封装好。在这一章我们来用Netty搭建一个HttpServer,从实际开发中了解netty框架的一些特性和概念。netty.png认识Http请求在动手写Netty框架之前,我们先要了解http请求的组成,如下图:HTTPresponsecomponentpa
Stella981 Stella981
3年前
Netty概述
1.Netty概念异步事件驱动框架,用于快速开发高性能服务端和客户端封装了JDK底层BIO和NIO模型,提供高度可用的API自带编解码器解决拆包粘包问题,用户只用关心业务逻辑精心设计的reactor线程模型支持高并发海量连接自带各种协议栈让你处理任何一种通用协议都几乎不用亲自动手
Stella981 Stella981
3年前
Netty之大名鼎鼎的EventLoop
EventLoopGroup与Reactor:前面的章节中我们已经知道了,一个Netty程序启动时,至少要指定一个EventLoopGroup(如果使用到的是NIO,通常是指NioEventLoopGroup),那么,这个NioEventLoopGroup在Netty中到底扮演着什么角色呢?我们知道,Netty是Reactor模型的
Stella981 Stella981
3年前
Netty堆外内存泄露排查与总结
导读Netty是一个异步事件驱动的网络通信层框架,用于快速开发高可用高性能的服务端网络框架与客户端程序,它极大地简化了TCP和UDP套接字服务器等网络编程。Netty底层基于JDK的NIO,我们为什么不直接基于JDK的NIO或者其他NIO框架:1.使用JDK自带的NIO需要了解太多的概念,编程复杂。2
座无虚席
座无虚席
Lv1
天地心从数点见,河山春借一枝回。
文章
2
粉丝
0
获赞
0