Netty学习之Netty介绍

Netty学习之Netty介绍

前言

本周开始学习Netty,主要的参考资料是《Netty In Action》原版书,这本书写得真好,一开始学习Netty的时候,看得有些云里雾里,后面弄懂之后,再回头看一下这本书,就发现这本书真的言简意赅,精炼地将Netty的各个组件展现出来。

传统的Java网络编程

在传统的Java网络编程中,是基于阻塞形式的IO,在这种形式的IO模型中,由于当数据没有到来的时候,对应的线程会阻塞,所以,需要为每一个请求都分配一个线程(当然,可以通过线程池来复用线程,从而减少线程创建以及销毁的代价),然而,实际上需要消耗的资源还是非常巨大的,比如,如果同一时刻有1k的并发量,那么至少就需要有1k的左右的线程来处理(数据不严谨,但是足够表达意思了)。

如下代码所示

public class OIo {

    private static boolean isStop = false;
    private static ExecutorService executorService;

    public static void main(String[] args) throws IOException {
        ServerSocket server = new ServerSocket(8080);
        // 通过线程池的方式来处理
        executorService = Executors.newCachedThreadPool();
        // 监听多个请求
        while (!isStop) {
            Socket clientSocket = server.accept();
            // 每个请求分配对应的线程来处理
            // 由于是阻塞IO,所以如果不分配线程来处理,会导致同一时间只能处理
            // 一个请求
            executorService.submit(new NetworkHandler(clientSocket));
        }
    }

    static class NetworkHandler implements Runnable {

        private Socket clientSocket;

        public NetworkHandler(Socket socket) {
            this.clientSocket = socket;
        }

        public void run() {
            BufferedReader reader = null;
            try {
                reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
                PrintWriter writer = new PrintWriter(clientSocket.getOutputStream());
                String requestContent, responseContent;
                // 建立连接之后不停读取数据
                while((requestContent = reader.readLine()) != null) {
                    if("DONE".equals(requestContent)) {
                        break;
                    }
                    responseContent = handleRequest(requestContent);
                    writer.write(responseContent);
                }
                writer.flush();
                reader.close();
                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        private String handleRequest(String request) {
            System.out.println("request: " + request);
            return "handed";
        }
    }
}

显然,上面的这种模式在并发比较大的时候是不太适合使用的,于是,在Java4之后,引入了Nio模式,通过选择器(selector)来监视多个channel(多个连接),当数据到达时负责与对应的channel相互,这样,在建立连接的时候,就不需要为每个连接都分配一个线程了,Nio是基于非阻塞形式的IO,所以,相对于传统的阻塞IO,有了很大的性能提升,然而,由于Java中提供的Nio过于底层,而且重复的操作比较多,所以,很多的大牛们就开始对其进行抽象,封装,Netty就是其中的一个了。

Netty介绍

Netty是一个异步的,事件驱动的网络应用框架。

具有众多优秀的特性

  • 通用的API(支持阻塞跟非阻塞socket)
  • 支持TCP、UDP
  • 逻辑以及网络底层处理分隔
  • 灵活及可扩展的事件模型
  • 高度自定义线程模型
  • 高吞吐量,低延迟
  • 消耗资源低
  • 支持SSL/TSL
  • 社区支持

Netty官网:Netty官网

通过上面的介绍,我们应该明确的是Netty是一个网络框架,所以,它所做的事情其实就是网络的处理(建立连接、发送数据等),这一点需要搞清楚(一开始就是没有弄清楚Netty是什么,所以在学习的时候出现了混乱)。而我们使用Netty,一般是在它上面进行我们的业务操作(比如实现IM逻辑、实现RPC等等),当然,如果有精力研究其源码,也是非常不错的,据说Netty的源码也是非常值得学习的(先挖个坑,后面有机会一定学习、分析一波)

在上面Netty的介绍的时候,我们看到了异步事件驱动这两个词,先来谈谈异步,与异步对应的还有一个词,同步

同步指的是当请求发出之后,必须等到请求回来,才完成操作,异步则不是,异步是在请求发出之后,立即结束,不用等到请求返回才结束操作。

与这两个词关联的还有阻塞非阻塞,阻塞是指请求发出之后,如果没有数据,则对应的操作会被阻塞(具体的就是对应的线程会进入阻塞态,等到数据能获取,再变为可运行态),非阻塞则是指即使没有数据到来,此时对应的线程可以进行其他的操作,而不会被动进入阻塞态,即不会被阻塞。

关于这四个词语的更多的解析及举例,可以参考知乎的这篇问答:怎样理解阻塞非阻塞与同步异步的区别

对于事件驱动,一般是与异步相结合的,对于同步而言,由于操作会等到出结果了才返回,所以直接调用就能获取结果了(成功/失败),然而,对于异步而言,由于调用后立即返回,所以根据返回内容是没法知道结果的,那怎么知道结果呢,一般是通过注册回调函数来实现,当有结果的时候,事件发出者/监管者会调用对应的回调函数来通知我们操作已经完成了,也就是说,整个操作的过程中,是通过事件来进行沟通,而不是通过直接调用来沟通。理清楚这一点对于学习Netty非常重要,因为Netty中是通过事件来进行消息的传递的。

Netty核心组件

Netty是由几个核心的组件构成的

  • Channels,NIO的基本组件,表示一个打开的连接(网络,文件),可以用于处理一个或者多个不同的I/O操作(读/写)
  • Callbacks,一个方法,提供给另一个方法的引用,用于后者在合适的时候调用,Netty使用回调函数处理事件,当回调函数触发时,事件能够被实现了ChannelHandler接口的处理器处理(其实就是事件发生时,回调函数被调用)(被动)
  • Futures,提供了另外一种通知应用操作已经完成的方法(主动),异步操作的结果占位符,通过该方式,可以在后面访问到异步操作的结果,由于JDK中的Future比较笨重,Netty提供了自己的实现ChannelFuture,用于当异步操作执行的时候。并且允许我们向ChannelFuture注册一个或者多个ChannelFutureListener对象,当操作完成时,这些对象的回调方法operationComplete()会被调用,Netty的每个outBoundI/O均返回一个ChannelFuture,也即不阻塞
  • Events and Handlers,Netty使用不同的事件通知我们状态的改变或者操作的结果,然后我们可以采用不同的机制来处理不同的事件,如:日志记录、数据传输、流控制、应用逻辑等,由于Netty是网络框架,所以事件是根据inbound或者outbound数据流来区分的,如:连接建立或者断开、数据读取、用户事件、错误事件;远程端口打开或者关闭、写或者刷新数据
  • EventLoop,连接到每个Channel用于处理事件,包括:注册事件、分发事件到ChannelHandler、调度行动,每个EventLoop本身由一个线程来驱动,用于处理一个Channel中所有的I/O事件,并且在其生命周期中不会改变

基本上理清楚上面几个组件的作用以及相互的关系,就能掌握Netty的使用了。

第一个Netty应用

为了形象地认识Netty,这里通过一个简单的小例子来理解,顺便配置下环境

首先导入Netty依赖

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

对应的小程序

public class EchoServer {

    private final int PORT = 8080;

    public static void main(String[] args) {
        new EchoServer().start();
    }

    public void start() {
        // 创建事件处理器
        EventLoopGroup group = new NioEventLoopGroup();
        // 创建启动器
        ServerBootstrap bootstrap = new ServerBootstrap();
        // 配置启动器
        bootstrap.group(group)
                // 配置建立连接的处理器
                .channel(NioServerSocketChannel.class)
                // 配置连接建立后的处理器
                // 当一个新连接建立后,就会新建一个childHandler
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        // 将逻辑处理器添加到channel对应的pipeline中
                        ch.pipeline().addLast(new EchoServerHandler());
                    }
                });
        try {
            // 绑定地址并且建立连接,sync()表示等到连接绑定完成
            ChannelFuture future = bootstrap.bind(PORT).sync();
            // 阻塞直至连接关闭
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            try {
                // 关闭连接,释放资源
                group.shutdownGracefully().sync();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 逻辑处理器
     * 这里是直接将收到的数据发送回去
     */
    class EchoServerHandler extends ChannelInboundHandlerAdapter {

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            ByteBuf data = (ByteBuf) msg;
            System.out.println("request: " + data.toString(CharsetUtil.UTF_8));
            // 将收到的数据发送回去给客户端
            ctx.writeAndFlush(data);
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            cause.printStackTrace();
            // 关闭连接
            ctx.close();
        }
    }
}

启动服务之后,直接通过telnet程序进行测试即可。

总结

本小节主要是进行Netty的介绍,包括传统阻塞IO的缺点以及Netty的几个特性,最后通过一个简单的小例子展示了Netty的基础用法,后面将详细学习Netty的几个组件及具体作用。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,783评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,360评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,942评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,507评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,324评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,299评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,685评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,358评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,652评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,704评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,465评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,318评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,711评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,991评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,265评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,661评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,864评论 2 335

推荐阅读更多精彩内容