Netty网络编程(一):初步了解

简介: Netty网络编程(一):初步了解

简介

我们常用浏览器来访问web页面得到相关的信息,通常来说使用的都是HTTP或者HTTPS协议,这些协议的本质上都是IO,客户端的请求就是In,服务器的返回就是Out。但是在目前的协议框架中,并不能完全满足我们所有的需求。比如使用HTTP下载大文件,可能需要长连接等待。

我们也知道IO方式有多种多样的,包括同步IO,异步IO,阻塞IO和非阻塞IO等。不同的IO方式其性能也是不同的,而netty就是一个基于异步事件驱动的NIO框架。


本系列文章将会探讨netty的详细使用,通过原理+例子的具体结合,让大家了解和认识netty的魅力。


netty介绍

netty是一个优秀的NIO框架,大家对IO的第一印像应该是比较复杂,尤其是跟各种HTTP、TCP、UDP协议打交道,使用起来非常复杂。但是netty提供了对这些协议的友好封装,通过netty可以快速而且简洁的进行IO编程。netty易于开发、性能优秀,同时兼具稳定性和灵活性。如果你希望开发高性能的服务,那么使用netty总是没错的。


netty的最新版本是4.1.66.Final,事实上这个版本是官方推荐的最稳定的版本,netty还有5.x的版本,但是官方并不推荐。


如果要在maven项目中使用,可以引入下面的依赖坐标:

    <dependency>
        <groupId>io.netty</groupId>
        <artifactId>netty-all</artifactId>
        <version>4.1.66.Final</version>
    </dependency>

下面我们将会从一个最简单的例子,体验netty的魅力。


netty的第一个服务器

什么叫做服务器?能够对外提供服务的程序就可以被称作是服务器。建立服务器是所有对外服务的第一步,怎么使用netty建立一个服务器呢?服务器主要负责处理各种服务端的请求,netty提供了一个ChannelInboundHandlerAdapter的类来处理这类请求,我们只需要继承这个类即可。


在NIO中每个channel都是客户端和服务器端沟通的通道。ChannelInboundHandlerAdapter定义了在这个channel上可能出现一些事件和情况,如下图所示:


32.png

32.png

如上图所示,channel上可以出现很多事件,比如建立连接,关闭连接,读取数据,读取完成,注册,取消注册等。这些方法都是可以被重写的,我们只需要新建一个类,继承ChannelInboundHandlerAdapter即可。


这里我们新建一个FirstServerHandler类,并重写channelRead和exceptionCaught两个方法,第一个方法是从channel中读取消息,第二个方法是对异常进行处理。

public class FirstServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        // 对消息进行处理
        ByteBuf in = (ByteBuf) msg;
        try {
            log.info("收到消息:{}",in.toString(io.netty.util.CharsetUtil.US_ASCII));
        }finally {
            ReferenceCountUtil.release(msg);
        }
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        // 异常处理
        log.error("出现异常",cause);
        ctx.close();
    }
}

上面例子中,我们收到消息后调用release()方法将其释放,并不进行实际的处理。调用release方法是在消息使用完成之后常用的做法。上面代码将msg进行了ByteBuf的强制转换,如果并不想进行转换的话,可以直接这样使用:

   try {
        // 消息处理
    } finally {
        ReferenceCountUtil.release(msg);
    }

在异常处理方法中,我们打印出异常信息,并关闭异常的上下文。


有了Handler,我们需要新建一个Server类用来使用Handler创建channel和接收消息。接下来我们看一下netty的消息处理流程。


在netty中,对IO进行处理是使用多线程的event loop来实现的。netty中的EventLoopGroup就是这些event loop的抽象类。


我们来观察一下EventLoopGroup的类结构。


33.png

可以看出EventLoopGroup继承自EventExecutorGroup,而EventExecutorGroup继承自JDK自带的ScheduledExecutorService。


所以EventLoopGroup本质是是一个线程池服务,之所以叫做Group,是因为它里面包含了很多个EventLoop,可以通过调用next方法对EventLoop进行遍历。


EventLoop是用来处理注册到该EventLoop的channel中的IO信息,一个EventLoop就是一个Executor,通过不断的提交任务进行执行。当然,一个EventLoop可以注册多个channel,不过一般情况下并不这样处理。


EventLoopGroup将多个EventLoop组成了一个Group,通过其中的next方法,可以对Group中的EventLoop进行遍历。另外EventLoopGroup提供了一些register方法,将Channel注册到当前的EventLoop中。


从上图可以看到,register的返回结果是一个ChannelFuture,Future大家都很清楚,可以用来获得异步任务的执行结果,同样的ChannelFuture也是一个异步的结果承载器,可以通过调用sync方法来阻塞Future直到获得执行结果。


可以看到,register方法还可以传入一个ChannelPromise对象,ChannelPromise它同时是ChannelFuture和Promise的子类,Promise又是Future的子类,它是一个特殊的可以控制Future状态的Future。


EventLoopGroup有很多子类的实现,这里我们使用NioEventLoopGroup,Nio使用Selector对channel进行选择。还有一个特性是NioEventLoopGroup可以添加子EventLoopGroup。


对于NIO服务器程序来说,我们需要两个Group,一个group叫做bossGroup,主要用来监控连接,一个group叫做worker group,用来处理被boss accept的连接,这些连接需要被注册到worker group中才能进行处理。


将这两个group传给ServerBootstrap,就可以从ServerBootstrap启动服务了,相应的代码如下:

//建立两个EventloopGroup用来处理连接和消息
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new FirstServerHandler());
                        }
                    })
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);
            // 绑定端口并开始接收连接
            ChannelFuture f = b.bind(port).sync();

我们最开始创建的FirstServerHandler最作为childHandler的处理器在初始化Channel的时候就被添加进去了。


这样,当有新建立的channel时,FirstServerHandler就会被用来处理该channel的数据。


上例中,我们还指定了一些ChannelOption,用于对channel的一些属性进行设定。


最后,我们绑定了对应的端口,并启动服务器。


netty的第一个客户端

上面我们已经写好了服务器,并将其启动,现在还需要一个客户端和其进行交互。


如果不想写代码的话,可以直接telnet localhost 8000和server端进行交互即可,但是这里我们希望使用netty的API来构建一个client和Server进行交互。


构建netty客户端的流程和构建netty server端的流程基本一致。首先也需要创建一个Handler用来处理具体的消息,同样,这里我们也继承ChannelInboundHandlerAdapter。


上一节讲到了ChannelInboundHandlerAdapter里面有很多方法,可以根据自己业务的需要进行重写,这里我们希望当Channel active的时候向server发送一个消息。那么就需要重写channelActive方法,同时也希望对异常进行一些处理,所以还需要重写exceptionCaught方法。如果你想在channel读取消息的时候进行处理,那么可以重写channelRead方法。


创建的FirstClientHandler代码如下:

@Slf4j
public class FirstClientHandler extends ChannelInboundHandlerAdapter {
    private ByteBuf content;
    private ChannelHandlerContext ctx;
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        this.ctx = ctx;
        content = ctx.alloc().directBuffer(256).writeBytes("Hello World".getBytes(StandardCharsets.UTF_8));
        // 发送消息
        sayHello();
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        // 异常处理
        log.error("出现异常",cause);
        ctx.close();
    }
    private void sayHello() {
        // 向服务器输出消息
        ctx.writeAndFlush(content.retain());
    }
}

上面的代码中,我们首先从ChannelHandlerContext申请了一个ByteBuff,然后调用它的writeBytes方法,写入要传输的数据。最后调用ctx的writeAndFlush方法,向服务器输出消息。


接下来就是启动客户端服务了,在服务端我们建了两个NioEventLoopGroup,是兼顾了channel的选择和channel中消息的读取两部分。对于客户端来说,并不存在这个问题,这里只需要一个NioEventLoopGroup即可。


服务器端使用ServerBootstrap来启动服务,客户端使用的是Bootstrap,其启动的业务逻辑基本和服务器启动一致:

EventLoopGroup group = new NioEventLoopGroup();
try {
    Bootstrap b = new Bootstrap();
    b.group(group)
     .channel(NioSocketChannel.class)
     .handler(new ChannelInitializer<SocketChannel>() {
         @Override
         protected void initChannel(SocketChannel ch) throws Exception {
             ChannelPipeline p = ch.pipeline();
             p.addLast(new FirstClientHandler());
         }
     });
    // 连接服务器
    ChannelFuture f = b.connect(HOST, PORT).sync();

运行服务器和客户端

有了上述的准备工作,我们就可以运行了。首先运行服务器,再运行客户端。


如果没有问题的话,应该会输出下面的内容:

[nioEventLoopGroup-3-1] INFO com.yyb.FirstServerHandler - 收到消息:Hello World

总结

一个完整的服务器、客户端的例子就完成了。我们总结一下netty的工作流程,对于服务器端,首先建立handler用于对消息的实际处理,然后使用ServerBootstrap对EventLoop进行分组,并绑定端口启动。对于客户端来说,同样需要建立handler对消息进行处理,然后调用Bootstrap对EventLoop进行分组,并绑定端口启动。


有了上面的讨论就可以开发属于自己的NIO服务了。是不是很简单? 后续文章将会对netty的架构和背后的原理进行深入讨论,敬请期待。


相关文章
|
8天前
|
监控 前端开发 安全
Netty 高性能网络编程框架技术详解与实践指南
本文档全面介绍 Netty 高性能网络编程框架的核心概念、架构设计和实践应用。作为 Java 领域最优秀的 NIO 框架之一,Netty 提供了异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。本文将深入探讨其 Reactor 模型、ChannelPipeline、编解码器、内存管理等核心机制,帮助开发者构建高性能的网络应用系统。
57 0
|
4月前
|
弹性计算 网络协议 Java
Netty基础—2.网络编程基础二
本文介绍了网络编程的基本概念和三种主要模式:BIO(阻塞IO)、AIO(异步IO)和NIO(非阻塞IO)。BIO模型通过为每个客户端连接创建一个线程来处理请求,适合客户端较少的情况,但在高并发下性能较差。AIO模型通过异步IO操作,允许操作系统处理IO,适合高并发场景,但编码复杂且Linux支持有限。NIO模型通过Selector实现多路复用,适合高并发且性能要求高的场景。文章还详细介绍了NIO中的Buffer、Selector、Channel等核心组件,并提供了NIO的实战开发流程和代码示例。
|
4月前
|
监控 网络协议 Java
Netty基础—1.网络编程基础一
本文详细介绍了网络通信的基础知识,涵盖OSI七层模型、TCP/IP协议族及其实现细节。首先解释了OSI模型各层功能,如物理层负责数据通路建立与传输,数据链路层提供无差错传输等。接着探讨了TCP/IP协议,包括TCP和UDP的特点、三次握手与四次挥手过程,以及如何通过确认应答和序列号确保数据可靠性。还分析了HTTP请求的传输流程和报文结构,并讨论了短连接与长连接概念。 此外,解析了Linux下的IO模型,包括阻塞IO、非阻塞IO、IO复用(select/poll/epoll)、信号驱动IO和异步IO的特点与区别,强调了epoll在高并发场景下的优势及其水平触发和边缘触发两种工作模式。
|
4月前
|
网络协议 算法 Java
Netty基础—3.基础网络协议
本文详细梳理了计算机网络的基础知识,涵盖从物理层到应用层的各层协议及其功能。内容包括七层模型与四层模型对比、IP地址与子网划分、TCP三次握手及四次挥手过程、Socket编程原理、HTTP/HTTPS协议的工作机制等。同时深入探讨了Linux IO模型(阻塞、非阻塞、IO多路复用)及其应用场景,并分析了select、poll、epoll的区别。此外,还涉及Java IO读写的底层流程及同步异步、阻塞非阻塞的概念。这些知识点为理解网络通信和高性能服务器开发提供了全面的理论支持。
|
编解码 分布式计算 网络协议
Netty高性能网络框架(一)
Netty高性能网络框架(一)
|
9月前
|
JSON 算法 Java
Nettyの网络聊天室&扩展序列化算法
通过本文的介绍,我们详细讲解了如何使用Netty构建一个简单的网络聊天室,并扩展序列化算法以提高数据传输效率。Netty的高性能和灵活性使其成为实现各种网络应用的理想选择。希望本文能帮助您更好地理解和使用Netty进行网络编程。
119 12
|
10月前
|
消息中间件 编解码 网络协议
Netty从入门到精通:高性能网络编程的进阶之路
【11月更文挑战第17天】Netty是一个基于Java NIO(Non-blocking I/O)的高性能、异步事件驱动的网络应用框架。使用Netty,开发者可以快速、高效地开发可扩展的网络服务器和客户端程序。本文将带您从Netty的背景、业务场景、功能点、解决问题的关键、底层原理实现,到编写一个详细的Java示例,全面了解Netty,帮助您从入门到精通。
1110 0
|
存储 机器人 Linux
Netty(二)-服务端网络编程常见网络IO模型讲解
Netty(二)-服务端网络编程常见网络IO模型讲解
|
Java 应用服务中间件 Linux
(九)Java网络编程无冕之王-这回把大名鼎鼎的Netty框架一网打尽!
现如今的开发环境中,分布式/微服务架构大行其道,而分布式/微服务的根基在于网络编程,而Netty恰恰是Java网络编程领域的无冕之王。Netty这个框架相信大家定然听说过,其在Java网络编程中的地位,好比JavaEE中的Spring。
426 3
|
移动开发 网络协议 算法
(十)Netty进阶篇:漫谈网络粘包、半包问题、解码器与长连接、心跳机制实战
在前面关于《Netty入门篇》的文章中,咱们已经初步对Netty这个著名的网络框架有了认知,本章的目的则是承接上文,再对Netty中的一些进阶知识进行阐述,毕竟前面的内容中,仅阐述了一些Netty的核心组件,想要真正掌握Netty框架,对于它我们应该具备更为全面的认知。
582 2