Netty学习

    xiaoxiao2023-10-20  153

    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)