Netty 快速入门系列 - Chapter 2 Netty3.x 【第四讲】 - 基本原理

Chapter2 章节结构


原理概念图: 

1. Boss 作为一个大门的人员,由于注册Socket Accept 的事件,即是否有客人进入餐厅

2. 如果有客人进入餐厅, Boss 将轮训从多个区中跳出一个Nio Worker, 让worker 完成 注册NIO selector 的 IO read 事件。 


代码:

1. NioSelectorFactoryPool 用于注册有个Boss 和 Worker Executor及 获取下一个Worker

package com.john.netty.learn.ch04;


import java.io.IOException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;


public abstract class AbstractNioSelector implements Runnable {
  /**
   * 线程池
   */
  private Executor executor;


  /**
   * 线程名称
   */
  private String threadName;


  /**
   * 线程管理对象
   */
  private NioSelectorFactoryPool nioSelectorFactoryPool;
  /**
   * 选择器
   */
  protected Selector selector;


  /**
   * 选择器wakenUp状态标记
   */
  private AtomicBoolean wakeup = new AtomicBoolean(false);


  /**
   * 任务队列
   */
  private Queue<Runnable> taskQueue = new ConcurrentLinkedQueue<>();


  public AbstractNioSelector(Executor executor, String threadName, NioSelectorFactoryPool nioSelectorFactoryPool) {
     this.executor = executor;
     this.threadName = threadName;
     this.nioSelectorFactoryPool = nioSelectorFactoryPool;


     openSelector();
  }


  /**
   * 获取selector并启动线程
   */
  private void openSelector() {


     try {


       this.selector = Selector.open();


     } catch (IOException e) {


       e.printStackTrace();
     }


     executor.execute(this);


  }


  @Override
  public void run() {


     Thread.currentThread().setName(threadName);


     while (true) {


       try {


          wakeup.set(false);


          select(this.selector);


          processTaskQueue();


          process(this.selector, nioSelectorFactoryPool);


       } catch (IOException e) {


          e.printStackTrace();


       }


     }


  }


  /**
   * 注册一个任务并激活selector
   * 
   * @param task
   */
  protected void registerTask(Runnable task) {


     if (this.selector == null) {
       return;
     }


     taskQueue.add(task);


     if (wakeup.compareAndSet(false, true)) {


       // 非常关键,避免selector.select()堵塞操作
       this.selector.wakeup();
     }


     if (this.selector == null) {


       taskQueue.remove(task);


       return;
     }


  }


  /**
   * 执行队列里的任务
   */
  private void processTaskQueue() throws IOException {


     if (taskQueue.isEmpty()) {
       return;
     }


     while (true) {


       Runnable task = taskQueue.poll();


       if (task == null) {


          return;
       }


       task.run();
     }


  }


  /**
   * select抽象方法
   * 
   * @param selector
   * @return
   * @throws IOException
   */
  protected abstract void select(Selector selector) throws IOException;


  protected final void process(Selector selector, NioSelectorFactoryPool nioSelectorFactoryPool) throws IOException {


     Set<SelectionKey> selectionKeys = selector.selectedKeys();


     if (selectionKeys.isEmpty()) {


       return;
     }


     for (Iterator<SelectionKey> it = selectionKeys.iterator(); it.hasNext();) {


       SelectionKey selectionKey = it.next();
       it.remove();


       process(selectionKey, selector, nioSelectorFactoryPool);
     }
  }


  /**
   * selector的业务处理
   * 
   * @param selector
   * @throws IOException
   */
  protected abstract void process(SelectionKey selectionKey, Selector selector,
       NioSelectorFactoryPool nioSelectorFactoryPool) throws IOException;


}

2.  AbstractNioSelector  主要方法Run 中  select()、 processTaskQueue() 和 process()

使用Queue 避免线程堵塞, 添加任务的时候,必须调用 wakeup 方法,否则 selector.select() 将堵塞当前线程,无法完成事件注册操作。

package com.john.netty.learn.ch04;


import java.io.IOException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;


public abstract class AbstractNioSelector implements Runnable {
	/**
	 * 线程池
	 */
	private Executor executor;


	/**
	 * 线程名称
	 */
	private String threadName;


	/**
	 * 线程管理对象
	 */
	private NioSelectorFactoryPool nioSelectorFactoryPool;
	/**
	 * 选择器
	 */
	protected Selector selector;


	/**
	 * 选择器wakenUp状态标记
	 */
	private AtomicBoolean wakeup = new AtomicBoolean(false);


	/**
	 * 任务队列
	 */
	private Queue<Runnable> taskQueue = new ConcurrentLinkedQueue<>();


	public AbstractNioSelector(Executor executor, String threadName, NioSelectorFactoryPool nioSelectorFactoryPool) {
		this.executor = executor;
		this.threadName = threadName;
		this.nioSelectorFactoryPool = nioSelectorFactoryPool;


		openSelector();
	}


	/**
	 * 获取selector并启动线程
	 */
	private void openSelector() {


		try {


			this.selector = Selector.open();


		} catch (IOException e) {


			e.printStackTrace();
		}


		executor.execute(this);


	}


	@Override
	public void run() {


		Thread.currentThread().setName(threadName);


		while (true) {


			try {


				wakeup.set(false);


				select(this.selector);


				processTaskQueue();


				process(this.selector, nioSelectorFactoryPool);


			} catch (IOException e) {


				e.printStackTrace();


			}


		}


	}


	/**
	 * 注册一个任务并激活selector
	 * 
	 * @param task
	 */
	protected void registerTask(Runnable task) {


		if (this.selector == null) {
			return;
		}


		taskQueue.add(task);


		if (wakeup.compareAndSet(false, true)) {


			// 非常关键,避免selector.select()堵塞操作
			this.selector.wakeup();
		}


		if (this.selector == null) {


			taskQueue.remove(task);


			return;
		}


	}


	/**
	 * 执行队列里的任务
	 */
	private void processTaskQueue() throws IOException {


		if (taskQueue.isEmpty()) {
			return;
		}


		while (true) {


			Runnable task = taskQueue.poll();


			if (task == null) {


				return;
			}


			task.run();
		}


	}


	/**
	 * select抽象方法
	 * 
	 * @param selector
	 * @return
	 * @throws IOException
	 */
	protected abstract void select(Selector selector) throws IOException;


	protected final void process(Selector selector, NioSelectorFactoryPool nioSelectorFactoryPool) throws IOException {


		Set<SelectionKey> selectionKeys = selector.selectedKeys();


		if (selectionKeys.isEmpty()) {


			return;
		}


		for (Iterator<SelectionKey> it = selectionKeys.iterator(); it.hasNext();) {


			SelectionKey selectionKey = it.next();
			it.remove();


			process(selectionKey, selector, nioSelectorFactoryPool);
		}
	}


	/**
	 * selector的业务处理
	 * 
	 * @param selector
	 * @throws IOException
	 */
	protected abstract void process(SelectionKey selectionKey, Selector selector,
			NioSelectorFactoryPool nioSelectorFactoryPool) throws IOException;


}

3. NioServerBoss 实现 registerServerSocketChannel 和 process 方法。

其中: 

a. registerServerSocketChannel  当ServerSocketChannel.bind之后,添加  Task 任务

serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

b. process 方法

当有客人进入饭店, Boss将将通知Worker 来服务这个客人

NioServerWorker nioServerWorker = nioSelectorFactoryPool.nextWorker();

nioServerWorker.registerSocketChannel(socketChannel);

package com.john.netty.learn.ch04;


import java.nio.channels.ClosedChannelException;
import java.nio.channels.ServerSocketChannel;


public interface Boss {


  public void registerServerSocketChannel(final ServerSocketChannel serverSocketChannel)
       throws ClosedChannelException;
}
 



package com.john.netty.learn.ch04;


import java.io.IOException;
import java.net.ServerSocket;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.Executor;


public class NioServerBoss extends AbstractNioSelector implements Boss{


  public NioServerBoss(Executor executor, String threadName, NioSelectorFactoryPool nioSelectorFactoryPool) {
     super(executor, threadName, nioSelectorFactoryPool);


  }


  @Override
  protected void select(Selector selector) throws IOException {


     selector.select();
  }


  @Override
  protected void process(SelectionKey selectionKey, Selector selector, NioSelectorFactoryPool nioSelectorFactoryPool)
       throws IOException {


     System.out.println("新的客户已连接...");


     ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel();


     SocketChannel socketChannel = serverSocketChannel.accept();
     socketChannel.configureBlocking(false);


     // next work and add task...


     NioServerWorker nioServerWorker = nioSelectorFactoryPool.nextWorker();


     nioServerWorker.registerSocketChannel(socketChannel);


  }


  public void registerServerSocketChannel(final ServerSocketChannel serverSocketChannel)
       throws ClosedChannelException {


     /**
      * 
      * 不能直接调用,由于selector.select()堵塞,导致serverSocketChannel.register无法运行;
      * 
      * Refer to AbstractNioSelector.run() -- 使用线程启动,select()方法先运行
      * 
      * serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
      */


     this.registerTask(new Runnable() {


       @Override
       public void run() {
          try {


            System.out.println("注册客户建立连接的操作-开始...");
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println("注册客户建立连接的操作-结束...");
          } catch (ClosedChannelException e) {


            e.printStackTrace();
          }


       }
     });


  }


}

 


4. NioServerWorker process 用于 IO read 处理。

package com.john.netty.learn.ch04;


import java.nio.channels.SocketChannel;


public interface Worker {


  public void registerSocketChannel(final SocketChannel socketChannel);
}
 



package com.john.netty.learn.ch04;


import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.concurrent.Executor;


public class NioServerWorker extends AbstractNioSelector implements Worker {


  public NioServerWorker(Executor executor, String threadName, NioSelectorFactoryPool nioSelectorFactoryPool) {
     super(executor, threadName, nioSelectorFactoryPool);


  }


  @Override
  protected void select(Selector selector) throws IOException {


     selector.select();
  }


  @Override
  protected void process(SelectionKey selectionKey, Selector selector, NioSelectorFactoryPool nioSelectorFactoryPool)
       throws IOException {


     if (selectionKey.isReadable()) {


       handlerRead(selectionKey);
     }


  }


  private void handlerRead(SelectionKey selectionKey) throws IOException {


     SocketChannel socketChannel = (SocketChannel) selectionKey.channel();


     ByteBuffer buffer = ByteBuffer.allocate(1024);


     int length = socketChannel.read(buffer);


     // 客户端已经关闭连接
     if (length == -1) {


       System.out.println("客户端关闭");
       selectionKey.cancel();


       return;
     }


     System.out.println("接收到消息:" + new String(buffer.array()));


     buffer.flip();


     socketChannel.write(buffer);


  }


  public void registerSocketChannel(final SocketChannel socketChannel) {


     final Selector selector = this.selector;


     this.registerTask(new Runnable() {


       @Override
       public void run() {


          try {
            System.out.println("注册客户数据的读取任务...");


            socketChannel.register(selector, SelectionKey.OP_READ);


          } catch (ClosedChannelException e) {


            e.printStackTrace();
          }
       }
     });
  }



}


5. NioServerBootstrap 启动 NIO Server, 注意:  

nioSelectorFactoryPool.nextBoss().registerServerSocketChannel(serverSocketChannel);

 
package com.john.netty.learn.ch04;


import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.util.concurrent.Executors;


public class NioServerBootstrap {


  public NioServerBootstrap(NioSelectorFactoryPool nioSelectorFactoryPool) {


     this.nioSelectorFactoryPool = nioSelectorFactoryPool;
  }


  public void bind(int port) throws IOException {


     serverSocketChannel = ServerSocketChannel.open();


     serverSocketChannel.configureBlocking(false);


     serverSocketChannel.socket().bind(new InetSocketAddress(port));


     nioSelectorFactoryPool.nextBoss().registerServerSocketChannel(serverSocketChannel);


     System.out.println("启动服务" + port);
  }


  private ServerSocketChannel serverSocketChannel = null;


  private NioSelectorFactoryPool nioSelectorFactoryPool;


  public static void main(String[] args) throws IOException {


     NioSelectorFactoryPool nioSelectorFactoryPool = new NioSelectorFactoryPool(Executors.newCachedThreadPool(),
          Executors.newCachedThreadPool());


     new NioServerBootstrap(nioSelectorFactoryPool).bind(23);
  }
}

所有源码下载 :https://download.csdn.net/download/netcobol/10308871
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值