先看一个自己用nio实现的netty示例:
1、初始化boss线程池组:
初始化boss
executor是一个单线程的线程池
selector是多路复用选择器
taskqueue是任务队列,此时还为空
2、初始化worker线程池组,跟boss一样
3、生成ServerSocketChannel
4、往boss里面注册task,并唤醒selector
5、这个就等有客户端请求过来了
public void run() {
Thread.currentThread().setName(this.threadName);
//一直循环
while (true) {
try {
//还没有链接
wakenUp.set(false);
select(selector); //会堵塞
processTaskQueue();
process(selector);
} catch (Exception e) {
// ignore
}
}
}
如果有客户端请求,会走processTaskQueue,这个就是执行任务队列里面的任务
public void run() {
try {
//注册serverChannel到selector
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
} catch (ClosedChannelException e) {
e.printStackTrace();
}
}、
就是把serverChannel注册到boss中的selector中去
process(selector);就是处理请求事件
protected void process(Selector selector) throws IOException {
System.out.println("-------------ssssssssssssssssss---------------");
Set<SelectionKey> selectedKeys = selector.selectedKeys();
System.out.println("-------------selectedKeyslen---------------"+selectedKeys.size());
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("新客户端链接");
}
}
接收客户端的accept事件,获取SocketChannel,往一个work中插入一个task
nextworker.registerNewChannelTask(channel);
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();
}
}
});
}
并且worker线程也会执行下面这个代码,并且把SocketChannel注册到一个work线程池的selecor中,然后执行读事件
@Override
public void run() {
Thread.currentThread().setName(this.threadName);
//一直循环
while (true) {
try {
//还没有链接
System.out.println("----------processTaskQueue--------"+Thread.currentThread().getName());
wakenUp.set(false);
select(selector);
processTaskQueue();
System.out.println("----------processTaskQueue--------"+Thread.currentThread().getName());
process(selector);
} catch (Exception e) {
// ignore
}
}
}
netty源码
其中pipeline是一个双向链表,是单线程执行的,不会引起并发问题:
红色箭头代表出战,tail---->head 绿色箭头代表入站:head---->tail
入站Handle要实现ChannelInboundHandler接口
出站Handle要实现ChannelOutboundHandler接口
selector.selectedKeys()
// 阻塞等待需要处理的事件发生
selector.select();
// 获取selector中注册的全部事件的 SelectionKey 实例
Set<SelectionKey> selectionKeys = selector.selectedKeys();
selector会去监听所有的channel的事件,当有个channel发来了就绪事件,就会触发selector.select()调用,而且会有一个延迟,所以selector.selectedKeys()出来的是一个集合,而不是一个一个的找出就绪事件
netty中boss线程多个
netty中boss线程多个 ,则需要开多个端口,否则只有一个线程生效
如:boss线程数设置为2时:
ChannelFuture cf = bootstrap.bind(9000).sync();
ChannelFuture cf1 = bootstrap.bind(9001).sync();