关于程序员:Netty的介绍与简单使用

52次阅读

共计 6147 个字符,预计需要花费 16 分钟才能阅读完成。

一、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 的模块!

  • 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 官网对于通信管道的扩大:

  • 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!

正文完
 0