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