欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Netty:初识Netty

程序员文章站 2022-12-08 19:28:19
前文总结了NIO的内容,有了NIO的一些基础之后,我们就可以来看下Netty。Netty是Java领域的高性能网络传输框架,RPC的技术核心就是网络传输和序列化,所以Netty给予了RPC在网络传输领域巨大的支持。 一个简单的Netty代码实现 网络传输基于的是TCP协议,所以会有服务端和客户端之分 ......

总结了nio的内容,有了nio的一些基础之后,我们就可以来看下netty。netty是java领域的高性能网络传输框架,rpc的技术核心就是网络传输和序列化,所以netty给予了rpc在网络传输领域巨大的支持。 

一个简单的netty代码实现

网络传输基于的是tcp协议,所以会有服务端和客户端之分,而netty是网络传输框架,所以一个完整的netty代码至少是有服务端和客户端的。本文代码基于netty4.1.15。

服务端:

public class demoserver {
    public static void main(string[] args) throws exception {
        eventloopgroup bossgroup = new nioeventloopgroup();
        eventloopgroup workergroup = new nioeventloopgroup();
        try {
            serverbootstrap serverbootstrap = new serverbootstrap();
            serverbootstrap.group(bossgroup,workergroup).
                    channel(nioserversocketchannel.class).
                    childhandler(new channelinitializer<socketchannel>() {
                        @override
                        protected void initchannel(socketchannel ch) throws exception {
                            ch.pipeline().addlast(new stringdecoder(charsetutil.utf_8));
                            ch.pipeline().addlast(new stringencoder(charsetutil.utf_8));
                            ch.pipeline().addlast(new demoserverhandler());
                        }
                    });

            channelfuture future = serverbootstrap.bind(8899).sync();
            future.channel().closefuture().sync();
        }finally {
            bossgroup.shutdowngracefully();
            workergroup.shutdowngracefully();
        }
    }
}
public class demoserverhandler extends simplechannelinboundhandler<string> {
    @override
    protected void channelread0(channelhandlercontext ctx, string msg) throws exception {
        system.out.println("" + ctx.channel().remoteaddress() + "," + msg);
        ctx.channel().writeandflush("from server" + uuid.randomuuid());
    }

    @override
    public void exceptioncaught(channelhandlercontext ctx, throwable cause) throws exception {
        cause.printstacktrace();
        ctx.close();
    }
}

客户端:

public class democlient {
    public static void main(string[] args) throws exception{
        eventloopgroup eventloopgroup = new nioeventloopgroup();

        try{
            bootstrap bootstrap = new bootstrap();
            bootstrap.group(eventloopgroup).channel(niosocketchannel.class).handler(new channelinitializer<socketchannel>() {
                @override
                protected void initchannel(socketchannel ch) throws exception {
                    ch.pipeline().addlast(new stringdecoder(charsetutil.utf_8));
                    ch.pipeline().addlast(new stringencoder(charsetutil.utf_8));
                    ch.pipeline().addlast(new democlienthandler());
                }
            });

            channelfuture channelfuture = bootstrap.connect("localhost",8899).sync();
            channelfuture.channel().closefuture().sync();


        }finally {
            eventloopgroup.shutdowngracefully();
        }
    }
}
public class democlienthandler extends simplechannelinboundhandler<string> {
    @override
    protected void channelread0(channelhandlercontext ctx, string msg) throws exception {
        system.out.println("" + ctx.channel().remoteaddress());
        system.out.println("client output:" + msg);
        ctx.writeandflush("from client" + localdatetime.now());
    }

    @override
    public void channelactive(channelhandlercontext ctx) throws exception {
        string msg = "来自客户端的问候!";
        ctx.writeandflush(msg);
    }

    @override
    public void exceptioncaught(channelhandlercontext ctx, throwable cause) throws exception {
        cause.printstacktrace();
        ctx.close();
    }
}

来看下这个简单的代码做了什么:

1、服务端启动后,客户端先向服务端发起连接建立;

2、连接建立后,触发客户端的channelactive方法,该方法向服务端发出了一条信息,这条信息默认在网络中是会转成字节的形式来传输的,因为tcp的数据传输都是基于字节的,这个过程也叫做编码;

3、服务端收到信息后,会被服务端的handler,其实就是stringdecoder先做处理,从字节变成了字符,这个过程也叫做解码;

4、此时对于demoserverhandler来说,信息已经变成了符合自己的string类型,所以channelread0方法会被调用,输出信息,同时向客户端发出信息,信息又会转成字节的信息传向客户端;

5、客户端收到信息后,会被客户端解码成字符,触发客户端的channelread0方法,输出客户端地址和收到的信息,再向服务端发送时间戳;

6、循环往复上述3-5,死循环;

 

模块化

从上述这个简单的demo中,我们可以提取出netty的核心模块:

1、channel、eventloop、channelfuture

channel接口:基本的io操作(bind()/connect()/read()/write())依赖于底层网络传输所提供的原语。在我们这个demo中,能看到nioserversocketchannel和niosocketchannel,nioserversocketchannel使用基于nio选择器的实现来接受新连接,niosocketchannel使用基于nio选择器的实现来建立和处理新连接。

eventloop接口:eventloop定义了netty的核心抽象,用于处理连接的生命周期中所发生的事件。eventloop是协调设计的一部分,采用了两个基本的api:并发和网络编程。在我们这个demo中,能看到nioeventloop,nioeventloop就是一个reactor,是整个netty的一个核心。

channelfuture接口:netty中的所有的io操作都是异步的,因为一个操作可能不会立即返回,所以我们需要一种用于在之后某个时间点确定其结果的方法。为此,netty提供了channelfuture接口,其addlistener()方法注册了一个channelfuturelistener,以便在某个操作完成是得到通知。

2、channelhandler、channelpipeline

channelhandler接口:从应用程序开发人员的角度来看,channelhandler是netty的主要组件,它充当了所有处理入站和出站数据的应用程序逻辑的容器,因为channelhandler的方法是由事件来触发的。在我们这个demo中,democlienthandler、demoserverhandler就是两个自定义的channelhandler,democlienthandler在连接一建立的时候,就触发了channelactive方法,然后demoserverhandler在channelread0方法中读取了其传输的信息。

channelpipeline接口:channelpipeline为channelhandler链提供了容器,并定义了用于该链上传播入站和出站事件流的api。当channel被创建时,它会被自动的分配到它所专属的channelpipeline。在我们的demo中,一个channelinitializer的实例被注册到serverbootstrap或者bootstrap,当它的initchannel方法被调用的时候,channelinitializer将在channelpipeline中安装一组自定义的channelhandler,最后channelinitializer将它自己从channelpipeline中移除。

3、bytebuf

网络数据的基本单位是字节,nio提供了bytebuffer作为网络数据的字节容器,但是bytebuffer本身设计并不优雅,使用繁琐,netty使用bytebuf来替代bytebuffer,在我们的demo中,不能直接看到bytebuf,但是在netty提供的内置编解码器stringdecoder/stringencoder中,操作的对象就是bytebuf。

4、serverbootstrap、bootstrap

serverbootstrap和bootstrap是一个非常抽象的概念。serverbootstrap是netty创建服务器的辅助类,负责封装服务启动的一系列操作。和serverbootstrap一样,bootstrap也是封装客户端向服务端发送请求的一系列操作。

5、codec

通过netty发送和接收一个消息的时候,就会发生一次数据转换,入站消息会被解码,也就是从字节转换为原本的形式,如果是出站消息,就会从一种形式变成字节,这个就是编码,编解码的根本原因就是因为网络数据就是一系列的字节。在我们的demo中,stringencoder和stringdecoder就是编解码器。

 

最后总结一下:

1、通过一个简单的demo,介绍了一下netty,站在应用的角度看了下netty是如何运行的;

2、从demo中提取出netty的重要的模块,简单介绍一下各个模块,在后续的文章中将详细介绍每个模块组件。

 

参考资料: