zoukankan      html  css  js  c++  java
  • netty介绍与构成

    什么是 Netty

    Netty 是一个利用 Java 的高级网络的能力,隐藏其背后的复杂性而提供一个易于使用的 API 的客户端/服务器框架。Netty 提供高性能和可扩展性,让你可以自由地专注于你真正感兴趣的东西,你的独特的应用!

    在这一章我们将解释 Netty 在处理一些高并发的网络问题体现的价值。然后,我们将介绍基本概念和构成 Netty 的工具包。

    Netty 介绍

    一个应用想要支持成千上万并发的客户端,在以前,这样的想法会被认为是荒谬。而在今天,我们认为这是理所当然的。事实上,开发者知道,总是会有这样的需求——以较低的成本交付来换取更大的吞吐量和可用性。

    我们不要低估最后一点的重要性。我们从漫长的痛苦的经验学习到,低级别的 API 不仅暴露了高级别直接使用的复杂性,而且引入了过分依赖于这项技术所造成的短板。因此,面向对象的一个基本原则:通过抽象来隐藏背后的复杂性。

    这一原则已见成效,框架的形式封装解决方案成为了常见的编程任务,他们中有许多典型的分布式系统。现在大多数专业的 Java 开发人员都熟悉一个或多个这些框架(比如 Spring),并且许多已成为不可或缺的,使他们能够满足他们的技术要求以及他们的计划。

    谁在用 Netty

    Netty 是一个广泛使用的 Java 网络编程框架(Netty 在 2011 年获得了Duke's Choice Award,见https://www.java.net/dukeschoice/2011)。它活跃和成长于用户社区,像大型公司 Facebook 和 Instagram 以及流行 开源项目如 Infinispan, HornetQ, Vert.x, Apache Cassandra 和 Elasticsearch 等,都利用其强大的对于网络抽象的核心代码。

    反过来,Netty 也从这些开源项目中获益。随着这些项目的作用,Netty 也不断提高了其应用的范围和灵活性,比如已经实现了的协议就有 FTP, SMTP, HTTP, WebSocket 和 SPDY 以及其他二进制和基于文本的协议。

    在初创公司中 Firebase 和 Urban Airship 在使用 Netty。前者 Firebase 是使用 long-lived HTTP 连接,后者是使用 各种推送通知。

    当你使用 Twitter,你会使用 Finagle,这个是基于 Netty API 提供给内部系统通讯。Facebook 使用 Netty 来提供于 Nifty 类似的功能 Apache Thrift 服务。这些公司可扩展性和高性能的表现得益于 Netty 的贡献。

    这些例子的真实案例会在后面几章讲到。

    2011 年 Netty 项目从 Red Hat 独立开来从而让广泛的开发者社区贡献者参与进来。Red Hat ,Twitter 继续使用 Netty ,并且成为保持其最活跃的贡献者之一。

    下面展示了 Netty 技术和方法的特点

    • 设计
      • 针对多种传输类型的统一接口 - 阻塞和非阻塞
      • 简单但更强大的线程模型
      • 真正的无连接的数据报套接字支持
      • 链接逻辑支持复用
    • 易用性
      • 大量的 Javadoc 和 代码实例
      • 除了在 JDK 1.6 + 额外的限制。(一些特征是只支持在Java 1.7 +。可选的功能可能有额外的限制。)
    • 性能
      • 比核心 Java API 更好的吞吐量,较低的延时
      • 资源消耗更少,这个得益于共享池和重用
      • 减少内存拷贝
    • 健壮性
      • 消除由于慢,快,或重载连接产生的 OutOfMemoryError
      • 消除经常发现在 NIO 在高速网络中的应用中的不公平的读/写比
    • 安全
      • 完整的 SSL / TLS 和 StartTLS 的支持
      • 运行在受限的环境例如 Applet 或 OSGI
    • 社区
      • 发布的更早和更频繁
      • 社区驱动

    异步和事件驱动

    所有的网络应用程序需要被设计为可扩展性,可以被界定为“一个系统,网络能力,或过程中能够处理越来越多的工作方式或可扩大到容纳增长的能力”(见 Bondi, André B. (2000). "Characteristics of scalability and their impact on performance")。我们已经说过,Netty 帮助您利用非阻塞 I/O 完成这一目标,通常称为“异步 I/O”

    我们将使用“异步”和其同源词在这本书中大量的使用,所以这是介绍他们的一个很好的时候。异步,即非同步事件,当然是跟你日常生活的类似。例如,您可以发送电子邮件;可能得到或者得不到任何回应,或者当你发送一个您可能会收到一个消息。异步事件也可以有一个有序的关系。例如,你通常不会收到一个问题的答案直到提出一个问题,但是你并没有阻止同时一些其他的东西。

    在日常生活中异步就这样发生了,所以我们不会经常想到。但让计算机程序的工作方式,来实现我们提出了的特殊的问题,会有一点复杂。在本质上,一个系统是异步和“事件驱动”将会表现出一个特定的,对我们来说,有价值的 行为:它可以响应在任何时间以任何顺序发生的事件

    构成部分

    正如我们前面解释的,非阻塞 I/O 不会强迫我们等待操作的完成。在这种能力的基础上,真正的异步 I/O 起到了更进一步的作用:一个异步方法完成时立即返回并直接或稍后通知用户。

    正如我们将看到的,在一个网络环境的异步模型可以更有效地利用资源,可以快速连续执行多个调用。

    Channel

    Channel 是 NIO 基本的结构。它代表了一个用于连接到实体如硬件设备、文件、网络套接字或程序组件,能够执行一个或多个不同的 I/O 操作(例如读或写)的开放连接。

    现在,把 Channel 想象成一个可以“打开”或“关闭”,“连接”或“断开”和作为传入和传出数据的运输工具。

    Callback (回调)

    callback (回调)是一个简单的方法,提供给另一种方法作为引用,这样后者就可以在某个合适的时间调用前者。这种技术被广泛使用在各种编程的情况下,最常见的方法之一通知给其他人操作已完成。

    Netty 内部使用回调处理事件时。一旦这样的回调被触发,事件可以由接口 ChannelHandler 的实现来处理。如下面的代码,一旦一个新的连接建立了,调用 channelActive(),并将打印一条消息。

    public class ConnectHandler extends ChannelInboundHandlerAdapter {
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {   //1
            System.out.println(
                    "Client " + ctx.channel().remoteAddress() + " connected");
        }
    }
    

    1.当建立一个新的连接时调用 ChannelActive()

    Future

    Future 提供了另外一种通知应用操作已经完成的方式。这个对象作为一个异步操作结果的占位符,它将在将来的某个时候完成并提供结果。

    JDK 附带接口 java.util.concurrent.Future ,但所提供的实现只允许您手动检查操作是否完成或阻塞了。这是很麻烦的,所以 Netty 提供自己了的实现,ChannelFuture,用于在执行异步操作时使用。

    ChannelFuture 提供多个附件方法来允许一个或者多个 ChannelFutureListener 实例。这个回调方法 operationComplete() 会在操作完成时调用。事件监听者能够确认这个操作是否成功或者是错误。如果是后者,我们可以检索到产生的 Throwable。简而言之, ChannelFutureListener 提供的通知机制不需要手动检查操作是否完成的。

    每个 Netty 的 outbound I/O 操作都会返回一个 ChannelFuture;这样就不会阻塞。这就是 Netty 所谓的“自底向上的异步和事件驱动”。

    下面例子简单的演示了作为 I/O 操作的一部分 ChannelFuture 的返回。当调用 connect() 将会直接是非阻塞的,并且调用在背后完成。由于线程是非阻塞的,所以无需等待操作完成,而可以去干其他事,因此这令资源利用更高效。

    Channel channel = ...;
    //不会阻塞
    ChannelFuture future = channel.connect(
        new InetSocketAddress("192.168.0.1", 25));
    

    1.异步连接到远程地址

    下面代码描述了如何利用 ChannelFutureListener 。首先,连接到远程地址。接着,通过 ChannelFuture 调用 connect() 来 注册一个新ChannelFutureListener。当监听器被通知连接完成,我们检查状态。如果是成功,就写数据到 Channel,否则我们检索 ChannelFuture 中的Throwable。

    注意,错误的处理取决于你的项目。当然,特定的错误是需要加以约束 的。例如,在连接失败的情况下你可以尝试连接到另一个。

    Channel channel = ...;
    //不会阻塞
    ChannelFuture future = channel.connect(            //1
            new InetSocketAddress("192.168.0.1", 25));
    future.addListener(new ChannelFutureListener() {  //2
    @Override
    public void operationComplete(ChannelFuture future) {
        if (future.isSuccess()) {                    //3
            ByteBuf buffer = Unpooled.copiedBuffer(
                    "Hello", Charset.defaultCharset()); //4
            ChannelFuture wf = future.channel().writeAndFlush(buffer);                //5
            // ...
        } else {
            Throwable cause = future.cause();        //6
            cause.printStackTrace();
        }
    }
    });
    

    1.异步连接到远程对等节点。调用立即返回并提供 ChannelFuture。

    2.操作完成后通知注册一个 ChannelFutureListener 。

    3.当 operationComplete() 调用时检查操作的状态。

    4.如果成功就创建一个 ByteBuf 来保存数据。

    5.异步发送数据到远程。再次返回ChannelFuture。

    6.如果有一个错误则抛出 Throwable,描述错误原因。

    Event 和 Handler

    Netty 使用不同的事件来通知我们更改的状态或操作的状态。这使我们能够根据发生的事件触发适当的行为。

    这些行为可能包括:

    • 日志
    • 数据转换
    • 流控制
    • 应用程序逻辑

    由于 Netty 是一个网络框架,事件很清晰的跟入站或出站数据流相关。因为一些事件可能触发传入的数据或状态的变化包括:

    • 活动或非活动连接
    • 数据的读取
    • 用户事件
    • 错误

    出站事件是由于在未来操作将触发一个动作。这些包括:

    • 打开或关闭一个连接到远程
    • 写或冲刷数据到 socket

    每个事件都可以分配给用户实现处理程序类的方法。这说明了事件驱动的范例可直接转换为应用程序构建块。

    图1.3显示了一个事件可以由一连串的事件处理器来处理

    Figure 1.3 Event Flow

    Netty 的 ChannelHandler 是各种处理程序的基本抽象。想象下,每个处理器实例就是一个回调,用于执行对各种事件的响应。

    在此基础之上,Netty 也提供了一组丰富的预定义的处理程序,您可以开箱即用。比如,各种协议的编解码器包括 HTTP 和 SSL/TLS。在内部,ChannelHandler 使用事件和 future 本身,创建具有 Netty 特性抽象的消费者。

    整合

    FUTURE, CALLBACK 和 HANDLER

    Netty 的异步编程模型是建立在 future 和 callback 的概念上的。所有这些元素的协同为自己的设计提供了强大的力量。

    拦截操作和转换入站或出站数据只需要您提供回调或利用 future 操作返回的。这使得链操作简单、高效,促进编写可重用的、通用的代码。一个 Netty 的设计的主要目标是促进“关注点分离”:你的业务逻辑从网络基础设施应用程序中分离。

    SELECTOR, EVENT 和 EVENT LOOP

    Netty 通过触发事件从应用程序中抽象出 Selector,从而避免手写调度代码。EventLoop 分配给每个 Channel 来处理所有的事件,包括

    • 注册感兴趣的事件
    • 调度事件到 ChannelHandler
    • 安排进一步行动

    该 EventLoop 本身是由只有一个线程驱动,它给一个 Channel 处理所有的 I/O 事件,并且在 EventLoop 的生命周期内不会改变。这个简单而强大的线程模型消除你可能对你的 ChannelHandler 同步的任何关注,这样你就可以专注于提供正确的回调逻辑来执行。该 API 是简单和紧凑。

  • 相关阅读:
    面试题:面试题归类 已看1 背1
    面试题:SSH项目总结 !=!=未看 没用
    面试题: 大公司面试 !=!=未看
    Java 重写paint绘图
    c语言中数组的定义和java中数组定义的一些区别
    Java GUI界面补充总结(不定期补充)
    JFrame 的层次结构 及 背景设置说明
    为什么内部类调用的外部变量必须是final修饰的?
    Java Calendar类的使用总结【转】
    String类为什么可以直接赋值
  • 原文地址:https://www.cnblogs.com/love-menglong/p/13094696.html
Copyright © 2011-2022 走看看