Netty In Action-第一章 Netty介绍

Netty是基于Java NIO的网络应用框架,它是一个NIO Client-Server框架,使用Netty可以快速开发网络应用,例如服务器和客户端协议。Netty提供了一组新的方式来开发网络应用程序,这种新的方式使得它很容易使用和有很强的扩展性。Netty的内部实现是很复杂的,但是Netty提供了简单易用的api从网络处理代码中解耦业务逻辑。

1为什么使用Netty?

Netty提供了高层次的抽象来简化TCP和UDP服务器的编码,但是你仍然可以使用底层地API。

1.1不是所有的网络框架都是一样的

Netty的“高性能和简单易用”并不意味着编写的程序的性能和可维护性会受到影响。Netty成功的提供了易于开发、高性能和高稳定性,以及较强的扩展性。

1.2Netty的功能非常丰富

Netty除了提供传输和协议,在其他各领域都有发展。Netty为开发者提供了一套完整的工具,看下表:

Development AreaNetty Features
设计
  • 各种传输类型,阻塞和非阻塞套接字统一的API
  • 使用灵活
  • 简单但有功能强大的线程模型
  • 无连接的DatagramSocket支持
  • 链逻辑,易于重用
易于使用
  • 提供大量的文档和例子
  • 出JDK1.6+,没有额外的依赖关系。某些功能依赖JDK1.7+,其他特性可能有相关依赖,但都是可选的。
性能
  • 比Java API更好的吞吐量和更低的延迟
  • 消耗较少的资源
  • 尽量减少不必要的内存拷贝
鲁棒性

鲁棒性可理解为健壮性

  • 链接快或慢或超载不会导致更读的内存泄露错误
  • 在高速的网络程序中不会有不公平的 read/write
安全性
  • 完整的SSL/TLS和StartTLS支持
  • 可以在如Applet或OSGI这些受限制的环境中允许
社区
  • 版本发布频繁
  • 社区活跃

2.异步设计

整个Netty的API都是异步的。对网络应用来说,IO异步是性能的瓶颈,使用异步IO可以较大程度上提高程序性能。

异步处理提倡更有效的使用资源,它允许你创建任务,当有事件发生时获得通知并等待事件完成。这样就不会阻塞,不管事件完成与否都会及时返回,程序可以利用剩余资源做一些其他事情。

2.1 Callbacks(回调)

回调一般是异步处理的一种技术。一个回调被传递到并且执行完该方法。下面代码是一个简单的回调。

package netty.in.action.chapter1;

public class Worker {
    public void doWork() {
        Fetcher fetcher = new MyFetcher(new Data(1,0));
        fetcher.fetchData(new FetcherCallback() {
            @Override
            public void onData(Data data) {
                System.out.println("data received:"+data);
            }

            @Override
            public void onError(Throwable cause) {
                System.err.println("An error accour:" + cause.getMessage());
            }
        });
    }

    public static void main(String[] args) {
        Worker w = new Worker();
        w.doWork();
    }
}
package netty.in.action.chapter1;

public interface Fetcher {
    void fetchData(FetcherCallback callback);
}
package netty.in.action.chapter1;

public class MyFetcher implements Fetcher  {
    Data data;

    public MyFetcher(Data data) {
        this.data = data;
    }

    @Override
    public void fetchData(FetcherCallback callback) {
        try {
            callback.onData(data);
        } catch (Exception e) {
            callback.onError(e.getCause());
        }
    }
}
package netty.in.action.chapter1;

public interface FetcherCallback {
    void onData(Data data);
    void onError(Throwable cause);
}
package netty.in.action.chapter1;

import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;

@lombok.Data
@AllArgsConstructor
@NoArgsConstructor
public class Data {
    private int n;
    private int m;
}

上面的例子只是一个简单的模拟回调,要明白其表达的含义。Fetcher.fetchData()方法需要传递一个FetcherCallback类型的参数,当获得数据或发生异常时。对于每种情况都提供了方法:

  • FetchCallback.onData(),接收数据时被调用
  • FetchCallback.onError(),发生错误时被调用

因为可以将这些方法的执行从“caller”线程移动到其他的线程执行;但也不保证FetchCallback的每个方法都会被执行。

2.2Futures

第二种技术是使用Futures。Futures是一个抽象的概念,它表示一个值,该值可能在某一点变得可用。一个Future要么获得计算完结果,要么获得计算失败后的异常。Java在java.util.concurrent包中附带了Future接口,它使得Executor异步执行。例如下面的代码,每传递一个Runnable对象到ExecutorService.submit()方法就会得到一个回调的Future,你能使用它检测是否执行完成。

package netty.in.action.chapter1.example2;

import java.util.concurrent.*;

public class FutureExample {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executor = Executors.newCachedThreadPool();
        //任务1
        Runnable task1 = new Runnable() {
            @Override
            public void run() {
                System.out.println("I am task1...");
            }
        };

        //任务2
        Callable<Integer> task2 = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                return new Integer(100);
            }
        };

        Future<?> f1 = executor.submit(task1);
        Future<Integer> f2 = executor.submit(task2);
        System.out.println("task1 is completed? " + f1.isDone());
        System.out.println("task2 is completed? " + f2.isDone());

        while(f1.isDone()){
            System.out.println("task1 completed.");
            break;
        }
        //waiting task2 completed
        while(f2.isDone()){
            System.out.println("return value by task2: " + f2.get());
            break;
        }
    }
}

有时候使用Future感觉很丑,因为你需要间隔检查Future是否已完成,而使用回调会直接受到返回通知。事实上,Netty两周都使用,提供两全其美的方案。

4.NIO的问题和Netty中是如何解决这些问题的

本节中将介绍Netty是如何解决NIO中的一些问题和限制。

4.1夸平台和兼容性问题

NIO是一个比较底层的API,它依赖于操作系统的IO API。Java实现了统一的接口来操作IO,其在所有的操作系统中的工作行为是一样的。但是使用NIO经常会发现代码在Linux上运行正常,在Windos上就会有问题。

NIO2看起来很理想,但是NIO2只支持JDK1.7+,若你的程序在JDK1.6上运行,则无法使用NIO2。另外,JAVA7的NIO中没有提供DatagramSocket的支持,所有NIO2只支持TCP程序,不支持UDP程序。

Netty提供了一个统一的接口,统一语义无论在Java6还是在Java7的环境下都是可以运行的,开发者无须关系底层API就可以轻松实现相关功能。

4.2扩展ByteBuff

ByteBuff是一个数据容器,但可惜的是JDK没有开发ByteBuff实现的源码;ByteBuff允许包装一个byte[]来获得一个实例,如果希望减少内存拷贝,那么这种方式是非常有用的。ByteBuff的构造函数是私有的,所以它不能被扩展。Netty提供了自己的ByteBuff实现,Netty通过一些简单的API对ByteBuff进行构造、使用和操作,以此来解决NIO中的一些限制。

4.3NIO对缓冲区的聚合和分散操作可能会内存泄露

很多Channel的实现支持Gather和Scatter。这个功能运行从多个ByteBuff中读出和写入到多个ByteBuff,这样做可以提供性能。操作系统底层知道如何处理这些被写入/读出,并能以有效的方式处理。如果要分割的数据在多个不同的ByteBuffer中,使用Gather/Scatter是比较好的方式。

可惜Gather/Scatter功能会导致内存泄露,直到Java7才解决内存泄露问题。使用这个功能必须小心编码和Java版本。

4.4著名的epoll缺陷

NIO中对epoll问题的解决方案是有限的,Netty提供了更好的解决方案。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值