当前位置: 移动技术网 > IT编程>开发语言>Java > Netty:初识Netty

Netty:初识Netty

2020年04月02日  | 移动技术网IT编程  | 我要评论

核战爆发令,家具设计师,电子杂志免费下载

总结了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的重要的模块,简单介绍一下各个模块,在后续的文章中将详细介绍每个模块组件。

 

参考资料:

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网