NIO实现Netty框架
简介:Netty就是有 看门服务员(线程+selector) 是迎接客人(接受请求),点菜服务员 (线程+selector)去 处理客人的点菜等等。客人就相当于一个客户端的请求。
各个类的流程
一、主启动类流程:(Start.java)
1、创建 线程池管理者 : 用于管理 Boss (看门服务员) 和 Worker (点菜服务员)的两个线程池。
2、 把线程池管理者交给服务类
ServerBootstrap bootstrap = new ServerBootstrap(nioSelectorRunnablePool);
3、服务类监听端口
bootstrap.bind(new InetSocketAddress(10101));
二、线程池管理者 构造方法:
提示:一个线程池中可能有多个线程 => 有多个看门的服务员 和 多个点菜的服务员
1、初始化 boss (看门服务员)线程池
private void initBoss(Executor boss, int count) {
this.bosses = new NioServerBoss[count];
// 给每一个元素进行初始化
for (int i = 0; i < bosses.length; i++) {
//(线程池, 线程的名字, 线程管理者对象)
bosses[i] = new NioServerBoss(boss, "boss thread"+(i+1), this);
}
}
2、初始化 worker (点菜服务员)线程池
private void initWorker(Executor worker, int count) {
this.workeres = new NioServerWorker[count];
for (int i = 0; i < workeres.length; i++) {
// (线程池,线程的名字,线程管理者对象)
workeres[i] = new NioServerWorker(worker, "worker thread " + (i+1), this);
}
}
提示:
AbstractNioSelector、NioServerWorker、NioServerBoss、Boss、Worker、Runnable 实体类之间的关系图
Boss(看门服务员接口)用来监听端口 Worker(点菜服务员接口) 实际的读写处理类
NioServerBoss(看门服务员实体类) NioServerWorker(点菜服务员实体类)
AbstractNioSelector( 看门服务员 与 点 菜服务员 的抽象类)
三、NioServerBoss 处理流程
1、AbstrcatNioSelector的构造方法有 openSelector方法:
executor.execute(this); // 执行当前线程 ,就是 Boss 线程
就会执行 抽象类 的run方法 -> run方法中的 select 与 process 在不同的服务员中有不同的处理方式
public void run() {
while (true) {
wakenUp.set(false);
select(selector);
processTaskQueue();
// 进行业务处理 (因为Boss和worker的工作任务时不同的)
process(selector);
}
}
2、
3、
4、
所有代码示例:
主启动类:
/**
* 使用NIO麻烦netty框架
*/
public class Start {
// 主启动类
public static void main(String[] args) {
//初始化线程(用来管理所有的线程池)
NioSelectorRunnablePool nioSelectorRunnablePool =
new NioSelectorRunnablePool(Executors.newCachedThreadPool(), Executors.newCachedThreadPool()); // 新建两个线程池,相当于一个是boss(看门服务员),一个是worker(点菜服务员)
//获取服务类 (把线程管理者交给服务类)
ServerBootstrap bootstrap = new ServerBootstrap(nioSelectorRunnablePool);
//绑定端口 (服务类监听端口)
bootstrap.bind(new InetSocketAddress(10101));
System.out.println("start");
}
/*
* 我们可以打开CMD窗口 输入 telnet 127.0.0.1 10101进行连接 (可以自己多打开几个)
*/
}
线程管理者:
public class NioSelectorRunnablePool {
/**
* boss线程数组(多个线程,多个看门服务员)
*/
private final AtomicInteger bossIndex = new AtomicInteger();
private Boss[] bosses;
/**
* worker线程数组
*/
private final AtomicInteger workerIndex = new AtomicInteger();
private Worker[] workeres;
// 初始化两个线程池,一个是boss,一个是worker
public NioSelectorRunnablePool(Executor boss, Executor worker) {
initBoss(boss, 1);
// 系统核心数量 * 2
initWorker(worker, Runtime.getRuntime().availableProcessors() * 2);
}
/**
* 初始化boss线程
*/
private void initBoss(Executor boss, int count) {
this.bosses = new NioServerBoss[count];
// 给每一个元素进行初始化
for (int i = 0; i < bosses.length; i++) {
// 对应三个参数, (线程池,线程的名字,线程管理者对象)
bosses[i] = new NioServerBoss(boss, "boss thread " + (i+1), this);
}
}
/**
* 初始化worker线程
*/
private void initWorker(Executor worker, int count) {
this.workeres = new NioServerWorker[count];
for (int i = 0; i < workeres.length; i++) {
// 对应三个参数, (线程池,线程的名字,线程管理者对象)
workeres[i] = new NioServerWorker(worker, "worker thread " + (i+1), this);
}
}
/**
* 获取一个 worker 服务员来为客人服务
*/
public Worker nextWorker() {
// Math.abs() 返回绝对值
return workeres[Math.abs(workerIndex.getAndIncrement() % workeres.length)];
// 通过循环给每一个 worker 进行分配工作
}
/**
* 获取一个boss 来为客人欢迎光临
*/
public Boss nextBoss() {
return bosses[Math.abs(bossIndex.getAndIncrement() % bosses.length)];
}
}
服务类:
/**
* 服务类
*/
public class ServerBootstrap {
private NioSelectorRunnablePool selectorRunnablePool;
public ServerBootstrap(NioSelectorRunnablePool selectorRunnablePool) {
this.selectorRunnablePool = selectorRunnablePool;
}
/**
* 绑定端口
*/
public void bind(final SocketAddress localAddress){
try {
// 获得一个ServerSocket通道
ServerSocketChannel serverChannel = ServerSocketChannel.open();
// 设置通道为非阻塞
serverChannel.configureBlocking(false);
// 将该通道对应的ServerSocket绑定到port端口
serverChannel.socket().bind(localAddress);
//获取一个boss线程
Boss nextBoss = selectorRunnablePool.nextBoss();
//向boss注册一个ServerSocket通道
nextBoss.registerAcceptChannelTask(serverChannel);
} catch (Exception e) {
e.printStackTrace();
}
}
}
AbstractNioSelector:Boss 和 Worker 的抽象类
/**
* 抽象selector线程类
*/
public abstract class AbstractNioSelector implements Runnable {
/**
* 线程池
*/
private final Executor executor;
/**
* 选择器
*/
protected Selector selector;
/**
* 选择器wakenUp状态标记
*/
protected final AtomicBoolean wakenUp = new AtomicBoolean();
/**
* 任务队列 (这个queue是一个 线程安全 的 queue)
*/
private final Queue<Runnable> taskQueue = new ConcurrentLinkedQueue<Runnable>();
/**
* 线程名称
*/
private String threadName;
/**
* 线程管理对象
*/
protected NioSelectorRunnablePool selectorRunnablePool;
AbstractNioSelector(Executor executor, String threadName, NioSelectorRunnablePool selectorRunnablePool) {
// 线程池
this.executor = executor;
// 线程名
this.threadName = threadName;
// 线程管理者
this.selectorRunnablePool = selectorRunnablePool;
openSelector();
}
/**
* 获取selector并启动线程
*/
private void openSelector() {
try {
// selector打开,一个线程有selector才可以为多个客人服务的能力
this.selector = Selector.open();
} catch (IOException e) {
throw new RuntimeException("Failed to create a selector.");
}
// 用线程池执行 当前对象 的run方法 this只的是当前的 NioServerBoss 或 NioServerWorker 对象
// AbstractNioSelector 是 实现 Runnable的,所以肯定有run方法,相当于执行run方法
executor.execute(this);
}
@Override
public void run() {
// 为当前线程设置名字
Thread.currentThread().setName(this.threadName);
while (true) {
try {
wakenUp.set(false);
select(selector);
processTaskQueue();
// 进行业务处理 (因为Boss和worker的工作任务时不同的)
process(selector);
} catch (Exception e) {
// ignore
}
}
}
/**
* 注册一个任务并激活selector
*
* @param task
*/
protected final void registerTask(Runnable task) {
taskQueue.add(task);
Selector selector = this.selector;
if (selector != null) {
// 看是不是 false 状态,如果是false改为true ,不是就跳过
if (wakenUp.compareAndSet(false, true)) {
selector.wakeup();
// 加入一个任务时,要重新 激活 selector的状态
}
} else {
taskQueue.remove(task);
}
}
/**
* 执行队列里的任务
*/
private void processTaskQueue() {
for (;;) {
// 将queue里的一个一个 Runnable(任务) 取出来 运行
final Runnable task = taskQueue.poll();
if (task == null) {
break;
}
task.run();
}
}
/**
* 获取线程管理对象
*/
public NioSelectorRunnablePool getSelectorRunnablePool() {
return selectorRunnablePool;
}
/**
* select抽象方法 ,不同的对象有不同的方法
*/
protected abstract int select(Selector selector) throws IOException;
/**
* selector的业务处理
*/
protected abstract void process(Selector selector) throws IOException;
}
Boss接口:
public interface Boss {
/**
* 加入一个新的ServerSocket
*/
public void registerAcceptChannelTask(ServerSocketChannel serverChannel);
}
Worker接口:
public interface Worker {
/**
* 加入一个新的客户端会话
*/
public void registerNewChannelTask(SocketChannel channel);
}
Boss实体类:
/**
* boss实现类
* AbstractNioSelector 是一个单线程的抽象类
*/
public class NioServerBoss extends AbstractNioSelector implements Boss{
public NioServerBoss(Executor executor, String threadName, NioSelectorRunnablePool selectorRunnablePool) {
super(executor, threadName, selectorRunnablePool);
}
@Override
protected void process(Selector selector) throws IOException {
// 取出所有的key
Set<SelectionKey> selectedKeys = selector.selectedKeys();
if (selectedKeys.isEmpty()) {
return;
}
for (Iterator<SelectionKey> i = selectedKeys.iterator(); i.hasNext();) {
SelectionKey key = i.next();
i.remove();
ServerSocketChannel server = (ServerSocketChannel) key.channel();
// 新客户端
SocketChannel channel = server.accept();
// 设置为非阻塞
channel.configureBlocking(false);
// 通过线程管理者对象 获取一个worker (类似于大门的服务生,把顾客引到一个区域后要交给这个区域的服务员)
Worker nextworker = getSelectorRunnablePool().nextWorker();
// 注册新客户端接入任务 类似于往别的线程池队列里加任务,让线程池自己去执行 (类似通知这个服务员与服务它)
nextworker.registerNewChannelTask(channel);
// 往别的线程队列中加任务,可以更好的避免 高并发出现的问题,不会直接去调用其他的线程, 让线程之间的耦合减低
System.out.println("新客户端链接");
}
}
public void registerAcceptChannelTask(final ServerSocketChannel serverChannel){
final Selector selector = this.selector;
registerTask(new Runnable() {
@Override
public void run() {
try {
//注册serverChannel到selector
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
} catch (ClosedChannelException e) {
e.printStackTrace();
}
}
});
}
@Override
protected int select(Selector selector) throws IOException {
return selector.select();
}
}
Worker实体类:
package com.cn;
/**
* worker实现类
* @author -琴兽-
*/
public class NioServerWorker extends AbstractNioSelector implements Worker{
public NioServerWorker(Executor executor, String threadName, NioSelectorRunnablePool selectorRunnablePool) {
super(executor, threadName, selectorRunnablePool);
}
@Override
protected void process(Selector selector) throws IOException {
Set<SelectionKey> selectedKeys = selector.selectedKeys();
if (selectedKeys.isEmpty()) {
return;
}
Iterator<SelectionKey> ite = this.selector.selectedKeys().iterator();
while (ite.hasNext()) {
SelectionKey key = (SelectionKey) ite.next();
// 移除,防止重复处理
ite.remove();
// 得到有读写事件发生的Socket通道(客户端)
SocketChannel channel = (SocketChannel) key.channel();
// 数据总长度
int ret = 0;
boolean failure = true;
ByteBuffer buffer = ByteBuffer.allocate(1024);
//读取数据
try {
ret = channel.read(buffer);
failure = false;
} catch (Exception e) {
// ignore
}
//判断是否连接已断开
if (ret <= 0 || failure) { // 如果没有读取到数据就是客户端断开
key.cancel(); // 取消关联
System.out.println("客户端断开连接");
}else{
System.out.println("收到数据:" + new String(buffer.array()));
//回写 数据 给 客户端
ByteBuffer outBuffer = ByteBuffer.wrap("收到\n".getBytes());
channel.write(outBuffer);// 将消息回送给客户端
}
}
}
/**
* 加入一个新的socket客户端
* 加入一个任务
*/
public void registerNewChannelTask(final SocketChannel channel){
final Selector selector = this.selector;
registerTask(new Runnable() {
@Override
public void run() {
try {
//将客户端注册到selector中
channel.register(selector, SelectionKey.OP_READ);
} catch (ClosedChannelException e) {
e.printStackTrace();
}
}
});
}
@Override
protected int select(Selector selector) throws IOException {
return selector.select(500);
}
}