Netty学习

Netty是基于Java NIO的网络应用框架,,使用Netty可以快速开发网络应用,例如服务器和客户端协议。Netty提供了一种新的方式来开发网络应用程序,这种方式使得他很容易使用和很强的扩展性,Netty的内部实现很复杂,但是他提供了简单的API从网络代码中解耦业务逻辑,Netty是完全基于NIO实现的,所以整个Netty都是异步的。
在这里插入图片描述
什么是Netty?

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

Netty的“quick and easy(高性能和简单易用)”并不意味着编写的程序的性能和可维护性会受到影响。从Netty中实现的协议如FTP,SMTP,HTTP,WebSocket,SPDY以及各种二进制和基于文本的传统协议中获得的经验导致Netty的创始人要非常小心它的设计。Netty成功的提供了易于开发,高性能和高稳定性,以及较强的扩展性。

异步设计

整个Netty的API都是异步的,异步不是一个新的机制,这个机制出来已经有一段时间了。

下面就讨论一下最常用的方法

Callbacks(回调)

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

1.	package netty.in.action;  
2.	  
3.	public class Worker {  
4.	  
5.	    public void doWork() {  
6.	        Fetcher fetcher = new MyFetcher(new Data(1, 0));  
7.	        fetcher.fetchData(new FetcherCallback() {  
8.	            @Override  
9.	            public void onError(Throwable cause) {  
10.	                System.out.println("An error accour: " + cause.getMessage());  
11.	            }  
12.	  
13.	            @Override  
14.	            public void onData(Data data) {  
15.	                System.out.println("Data received: " + data);  
16.	            }  
17.	        });  
18.	    }  
19.	  
20.	    public static void main(String[] args) {  
21.	        Worker w = new Worker();  
22.	        w.doWork();  
23.	    }  
24.	  
25.	}  


1.	package netty.in.action;  
2.	  
3.	public interface Fetcher {  
4.	    void fetchData(FetcherCallback callback);  
5.	}  

1.	package netty.in.action;  
2.	  
3.	public class MyFetcher implements Fetcher {  
4.	      
5.	    final Data data;  
6.	      
7.	    public MyFetcher(Data data){  
8.	        this.data = data;  
9.	    }  
10.	  
11.	    @Override  
12.	    public void fetchData(FetcherCallback callback) {  
13.	        try {  
14.	            callback.onData(data);  
15.	        } catch (Exception e) {  
16.	            callback.onError(e);  
17.	        }  
18.	    }  
19.	  
20.	}  


1.	package netty.in.action;  
2.	  
3.	public interface FetcherCallback {  
4.	    void onData(Data data) throws Exception;  
5.	    void onError(Throwable cause);  
6.	}  


1.	package netty.in.action;  
2.	  
3.	public class Data {  
4.	      
5.	    private int n;  
6.	    private int m;  
7.	  
8.	    public Data(int n,int m){  
9.	        this.n = n;  
10.	        this.m = m;  
11.	    }  
12.	  
13.	    @Override  
14.	    public String toString() {  
15.	        int r = n/m;  
16.	        return n + "/" + m +" = " + r;  
17.	    }  
18.	}  

上面只是一个简单的模拟回调,Fetcher,fetchData()方法只需要传递一个FetcherCallBack类型的参数,

当获得数据或发生错误是被回调。对于每种情况都提供了统一方法

  • FetcherCallback.onData(),接受数据时被调用
  • FetcherCallback.onError(),发生错误时候被调用

因为可以将这些方法执行从“caller”线程移动到其他线程执行,但不会保证FetcherCallback的每个方法都执行。回调过程有问题都是链式调用,很多不同方法会导致线性代码,有人认为链式调用导致代码难以阅读,但这是一种风格和习惯问题。例如,基于Javascript的Node.js越来越受欢迎,它使用了大量的回调,许多人都认为它的这种方式利于阅读和编写。

Futures

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

1.	package netty.in.action;  
2.	  
3.	import java.util.concurrent.Callable;  
4.	import java.util.concurrent.ExecutorService;  
5.	import java.util.concurrent.Executors;  
6.	import java.util.concurrent.Future;  
7.	  
8.	public class FutureExample {  
9.	      
10.	    public static void main(String[] args) throws Exception {  
11.	        ExecutorService executor = Executors.newCachedThreadPool();  
12.	        Runnable task1 = new Runnable() {  
13.	            @Override  
14.	            public void run() {  
15.	                //do something  
16.	                System.out.println("i am task1.....");  
17.	            }  
18.	        };  
19.	        Callable<Integer> task2 = new Callable<Integer>() {  
20.	            @Override  
21.	            public Integer call() throws Exception {  
22.	                //do something  
23.	                return new Integer(100);  
24.	            }  
25.	        };  
26.	        Future<?> f1 = executor.submit(task1);  
27.	        Future<Integer> f2 = executor.submit(task2);  
28.	        System.out.println("task1 is completed? " + f1.isDone());  
29.	        System.out.println("task2 is completed? " + f2.isDone());  
30.	        //waiting task1 completed  
31.	        while(f1.isDone()){  
32.	            System.out.println("task1 completed.");  
33.	            break;  
34.	        }  
35.	        //waiting task2 completed  
36.	        while(f2.isDone()){  
37.	            System.out.println("return value by task2: " + f2.get());  
38.	            break;  
39.	        }  
40.	    }  
41.	  
42.	}  

Netty核心概念

​ 一个Netty程序开始于Bootstrap类,bootstrap类是Netty提供的一个可以通过简单配置来设置或“引导”程序的一个重要的类。Netty中设计了Handler来处理特定的“event”和设置Netty中的事件,从而来处理多个协议和数据。事件可以描述成一个非常通用的方法,因为你可以自定义一个handler,

​ ChannelInboundHandler这个类是用来接收消息,当你有消息传送过来,你可以决定怎么处理。当程序需要返回消息时可以在ChannelInboundHandler里write/flush数据。可以任务程序的业务逻辑都是在ChannelInboundHandler中来处理的

​ Netty连接客户端端或绑定服务器需要知道如何发送或接收消息,这是通过不同类型的handlers来做的,多个Handlers是怎么配置的?Netty提供了ChannelInitializer类用来配置Handlers。ChannelInitializer是通过ChannelPipeline来添加ChannelHandler的,如发送和接收消息,这些Handlers将确定发的是什么消息。ChannelInitializer自身也是一个ChannelHandler,在添加完其他的handlers之后会自动从ChannelPipeline中删除自己。

所有Netty程序都是基于ChannelPipeline。ChannelPipeline和EventLoop和EventGroup密切相关,因为他们三个都是和事件处理相关,所以这就是为什么他们处理IO的工作由EventLoop管理的原因。

​ Netty中所有的IO操作都是异步执行的,例如你连接一个主机默认是异步完成的;写入/发送消息也是同样是异步。也就是说操作不会直接执行,而是会等一会执行,因为你不知道返回的操作结果是成功还是失败,但是需要有检查是否成功的方法或者是注册监听来通知;Netty使用Futures和ChannelFutures来达到这种目的。Future注册一个监听,当操作成功或失败时会通知。ChannelFuture封装的是一个操作的相关信息,操作被执行时会立刻返回ChannelFuture。

什么是Bootstarp?

“引导”是Netty中配置程序的过程,当你需要连接客户端或服务器绑定指定端口时需要使用bootstrap。如前面所述,“引导”有两种类型,一种是用于客户端的Bootstrap(也适用于DatagramChannel),一种是用于服务端的ServerBootstrap。不管程序使用哪种协议,无论是创建一个客户端还是服务器都需要使用“引导”。
两种bootsstraps之间有一些相似之处,其实他们有很多相似之处,也有一些不同。Bootstrap和ServerBootstrap之间的差异:
• Bootstrap用来连接远程主机,有1个EventLoopGroup
• ServerBootstrap用来绑定本地端口,有2个EventLoopGroup
事件组(Groups),传输(transports)和处理程序(handlers)分别在本章后面讲述,我们在这里只讨论两种"引导"的差异(Bootstrap和ServerBootstrap)。第一个差异很明显,“ServerBootstrap”监听在服务器监听一个端口轮询客户端的“Bootstrap”或DatagramChannel是否连接服务器。通常需要调用“Bootstrap”类的connect()方法,但是也可以先调用bind()再调用connect()进行连接,之后使用的Channel包含在bind()返回的ChannelFuture中。
第二个差别也许是最重要的。客户端bootstraps/applications使用一个单例EventLoopGroup,而ServerBootstrap使用2个EventLoopGroup(实际上使用的是相同的实例),它可能不是显而易见的,但是它是个好的方案。一个ServerBootstrap可以认为有2个channels组,第一组包含一个单例ServerChannel,代表持有一个绑定了本地端口的socket;第二组包含所有的Channel,代表服务器已接受了的连接。

ChannelPipeline

​ ChannelPipeline是ChannelHandler实例的列表,用于处理或者截获通道的接受和发送数据。ChannelPipeline提供了一种高级的截取过滤器模式,让用户可以在ChannelPipeline中完全控制一个事件及如何处理ChannelHandler与ChannelPipeline的交互。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值