• 学习 Doug Lea 大神写的——Scalable IO in Java


    学习 Doug Lea 大神写的——Scalable IO in Java


    网络服务

    Web services、分布式对象等等都具有相同的处理结构

    1. Read request
    2. Decode request
    3. Process service
    4. Encode reply
    5. Send reply

    基础的网络设计
    在这里插入图片描述
    每一个处理的 handler 都在各自的线程中处理。

    代码示例

    public class Server01 implements Runnable {
    	@Override public void run() {
    		try {
    			ServerSocket serverSocket = new ServerSocket(9898);
    			while (!Thread.interrupted()) {
    				// serverSocket.accept() 会阻塞到有客户端连接,之后 Handler 会处理
    				new Thread(new Handler(serverSocket.accept())).start();
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    
    	private static class Handler implements Runnable {
    		private final Socket socket;
    
    		Handler(Socket socket) {
    			this.socket = socket;
    		}
    
    		@Override public void run() {
    			try {
    				byte[] input = new byte[1024];
    				// 假设能全部读取出来
    				socket.getInputStream().read(input);
    				byte[] output = process(input);
    				socket.getOutputStream().write(output);
    			} catch (Exception e) {
    				e.printStackTrace();
    			}
    		}
    
    		private byte[] process(byte[] input) {
    			// 里面处理逻辑
    			return new byte[0];
    		}
    	}
    }
    

    这样做的好处是通过 accept 事件来触发任务的执行,将每个任务单独的去执行。但是缺点也很明显如果客户端链接过大那么需要新建若干个线程去执行,每台服务器可以运行的线程数是有限的。那么多线程的上下文切换的消耗也是巨大的。

    Reactor Pattern

    首先我们先来看下什么是事件驱动,在 java AWT 包中广泛的得到了使用。用户在点击一个 button 按钮的时候就会触发一个事件,然后会使用观察者模式来触发 Listener 中的处理事件。
    在这里插入图片描述

    Reactor 设计模式是基于事件驱动的一种实现方式,处理多个客户端并发的向服务端请求服务的场景。每种服务在服务端可能由多个方法组成。reactor 会解耦并发请求的服务并分发给对应的事件处理器来处理。目前,许多流行的开源框架都用到了。类似 AWT 中的 Thread。

    Handlers 执行非阻塞操作的具体类,类似 AWT 中的 ActionListeners。

    Reactor 单线程处理任务的设计
    在这里插入图片描述

    代码示例

    public class Reactor implements Runnable {
    	private final Selector            selector;
    	private final ServerSocketChannel serverSocketChannel;
    
    	public Reactor(int port) throws IOException {
    		selector = Selector.open();
    		serverSocketChannel = ServerSocketChannel.open();
    		serverSocketChannel.socket().bind(new InetSocketAddress(port));
    		serverSocketChannel.configureBlocking(false);
    		SelectionKey selectionKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    		selectionKey.attach(new Acceptor());
    	}
    
    	@Override public void run() {
    		try {
    			while (!Thread.interrupted()) {
    				selector.select();
    				Set<SelectionKey> selectionKeys = selector.selectedKeys();
    				for (SelectionKey selectionKey : selectionKeys) {
    					dispatch(selectionKey);
    					selectionKeys.clear();
    				}
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    
    	private void dispatch(SelectionKey selectionKey) {
    		Runnable runnable = (Runnable) selectionKey.attachment();
    		if (null != runnable) {
    			runnable.run();
    		}
    	}
    
    	private class Acceptor implements Runnable {
    		@Override public void run() {
    			try {
    				SocketChannel socketChannel = serverSocketChannel.accept();
    				if (null != socketChannel) {
    					new Handler(selector, socketChannel);
    				}
    			} catch (IOException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    
    	private class Handler implements Runnable {
    		private final        SocketChannel socketChannel;
    		private final        SelectionKey  selectionKey;
    		private              ByteBuffer    input   = ByteBuffer.allocate(1024);
    		private              ByteBuffer    output  = ByteBuffer.allocate(1024);
    		private static final int           READING = 0, SENDING = 1;
    		private int state = READING;
    
    		Handler(Selector selector, SocketChannel socketChannel) throws IOException {
    			this.socketChannel = socketChannel;
    			this.socketChannel.configureBlocking(false);
    			selectionKey = this.socketChannel.register(selector, 0);
    			selectionKey.attach(this);
    			selectionKey.interestOps(SelectionKey.OP_READ);
    			selector.wakeup();
    		}
    
    		void process() {
    		}
    
    		@Override public void run() {
    			try {
    				if (state == READING) {
    					socketChannel.read(input);
    					process();
    					state = SENDING;
    					selectionKey.interestOps(SelectionKey.OP_WRITE);
    				}
    				if (state == READING) {
    					socketChannel.write(output);
    					selectionKey.cancel();
    				}
    			} catch (Exception e) {
    				e.printStackTrace();
    			}
    		}
    	}
    }
    

    这个程序的重点在于 selectionKey.attach(); 方法每次把需要的对象传入进去,之后在有事件触发的时候会在 dispatch 中 attachment() 获取到这个对象,之后直接调用 run 方法。

    Reactor 多线程处理任务的设计
    在这里插入图片描述

    只需要稍微修改下 Handler 这个类

    // 添加一个线程池开发时请使用自定义或 spring 的线程池
    		private final ExecutorService executorService = Executors.newCachedThreadPool();
        
    // 修改 run 方法
    @Override public void run() {
      try {
        if (state == READING) {
          socketChannel.read(input);
          executorService.execute(new Runnable() {
            @Override public void run() {
              process();
              state = SENDING;
              selectionKey.interestOps(SelectionKey.OP_WRITE);
            }
          });
        }
        if (state == READING) {
          socketChannel.write(output);
          selectionKey.cancel();
        }
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
    

    多个 Reactor
    在这里插入图片描述
    当看到这幅图的时候感觉这不就是 Netty EventLoopGroup 的工作模式吗

    1. mainReactor 不就是 bossGroup
    2. subReactor 不就是 workGroup

    至此粗略的看完了这篇文章,感觉太 6 了,需要后面重复学习,这次只是了解大概。后面学习完会持续更新这篇文章!

  • 相关阅读:
    一个随机数生成函数
    HTTP Post请求过程详解
    md5加密算法c语言版
    android popupwindow 自定义视图
    android 获取顶部状态栏的高度
    android 显示和隐藏输入框
    android tablayout + recycleview 简单使用
    jetpack paging使用
    android 自定义控件 属性配置
    vueLazyload 图片懒加载
  • 原文地址:https://www.cnblogs.com/liufeichn/p/11961613.html
Copyright © 2020-2023  润新知