多线程主从模型角色
多线程主从模型主要的应用场景,就是netty
1、MainReactor 主反应器角色。该角色只处理OP_ACCEPT 的IO事件,只轮循主Selector。并将事件分发出去,由Acceptor角色执行
2、Acceptor接受者。Acceptor将分发过来的事件,创建SocketChannel,并将OP_WRITE事件注册到多个SubSelector中。
3、SubReactor 子反应器角色(多个子反应器对象)。每个子反应器对应一个Selector,只处理该Selctor中的OP_WRITE事件。
4、Handler 处理角色。ServerSocketChannel 接收到请求,创建出SocketChannel,并用SocketChannel与客户端通信
代码
主线程类
/**
* 主从多线程模型优点:
* 1、每一个selector注册器都有1个线程来监听
* 主注册器只接收socket连接的请求
* 子注册器处理IO的读写事件
* 2、TchHandler 处理事件的时候,可以用线程池来实现。
*
*
* <p>
* TcpReactor tcpReactor=new TcpReactor(9000);
* 这时,已经有多个线程在监听多个selector注册器了,每个线程监听1个
* TcpReactor 中的run方法 while循环监听的是客户端请求事件
* TcpSubReactor中的run方法 while循环监听的是子注册器中的读写事件
*
* </p>
*
*
* */
public class Main {
public static void main(String[] args) {
try{
TcpReactor tcpReactor=new TcpReactor(9000);
tcpReactor.run();
}catch (Exception e){
}
}
}
1、当TcpReactor tcpReactor=new TcpReactor(9000);初始化的时候,就已经将所有的注册器创建好了
1个主注册器,N个子注册器
selectionKey.attach(new Acceptor(ssc)); 这个Acceptor 和单线程的区别是,只将ServerSocketChannel 当做入参创建Acceptor
2、TcpReactor 类只轮循主Selector,并将OP_ACCEPT事件分发出去。TcpReactor 中的run方法中的
Runnable runnable=(Runnable)selectionKey.attachment();永远都是Acceptor对象。不可能是TcpHandler对象
/**
* 主反应器:只接收OP_ACCEPT事件的反应器
*
*
*
* */
public class TcpReactor implements Runnable{
private final ServerSocketChannel ssc; //服务端socket通道
/**
* 主从多线程中会有多个注册器
* 这个注册器是在主线程中的。
*
* */
private final Selector selector;
public TcpReactor(int port)throws Exception{
//创建选择器对象
selector=Selector.open();
//打开服务端通道
ssc=ServerSocketChannel.open();
//设置非阻塞,只有非阻塞的通道才能使用NIO的IO多路复用,才能注册到选择器中,否则报错,与BIO中的阻塞对立
ssc.configureBlocking(false);
//将通道注册到selector中,设置为感兴趣的IO事件模式,返回SelectionKey,相当于存在map中的key
SelectionKey selectionKey= ssc.register(selector,SelectionKey.OP_ACCEPT);
/**
* 给selectionKey设置一个附加对象,当有OP_ACCEPT的IO事件发生的时候,它的处理对象就是Acceptor
* 这个多线程主从模型,和单线程的入参不一样。这个Acceptor的构造参数就只有1个,ssc,没有selector注册器了
* 执行到这里的时候,启动了2个子线程。2个子注册器Selector
*
* */
selectionKey.attach(new Acceptor(ssc));
InetSocketAddress address=new InetSocketAddress(port);
//通道绑定端口
ssc.socket().bind(address);
}
/**
*
* 该方法只接收socket客户端的请求,分发出去。
* 并且在多线程模式下,只处理主注册器的(多个注册器,1个主注册器,N个子线程注册器)IO事件
*
* */
@Override
public void run() {
//在线程中断前持续执行
while (!Thread.interrupted()){
System.out.println("接收连接。。。。。。");
try{
//查看注册器中有没有事件发生,如果没有事件发生,该方法阻塞到这里。
//该方法会将查询出来的IO事件放到Set<SelectionKey> selectionKeys集合中。Selector类中就有该集合。
selector.select();
}catch (Exception e){
}
System.out.println("发生了事件,开始执行所有注册在selector中的IO事件");
Set<SelectionKey> selectionKeys=selector.selectedKeys();
//遍历所有发生的事件
Iterator<SelectionKey> it=selectionKeys.iterator();
while (it.hasNext()){
//从迭代器中读取事件
SelectionKey selectionKey=it.next();
//该处相当于一个路由器,分发处理
dispatch(selectionKey);
it.remove();
}
}
}
/**
* 多线程模式下,该方法只处理主注册器中的IO事件,该IO事件是IO_ACCEPT 客户端请求的事件
*
*
* */
private void dispatch(SelectionKey selectionKey) {
/**
*
* 这个runnable的对象,一定是Acceptor对象,不可能是TcpHandler对象
*
*
* */
Runnable runnable=(Runnable)selectionKey.attachment();
if(runnable!=null){
runnable.run();
}
}
}
3、Acceptor类中的run方法,会将接收过来的OP_ACCEPT事件,创建SocketChannel,并将OP_WRITE事件设置为感兴趣事件,
注册到子Selector中,并且选择键的附件是TcpHandler对象
public class Acceptor implements Runnable{
private final ServerSocketChannel ssc;
//服务器CPU核数
private final Integer cores=2;
private final Selector[] selectors=new Selector[cores];
private TcpSubReactor[] subReactors=new TcpSubReactor[cores];
private Thread[] threads=new Thread[cores];
private int index=0;
public Acceptor(ServerSocketChannel ssc){
//服务端的sokcet
this.ssc=ssc;
try{
for(int i=0;i<cores;i++){
selectors[i]=Selector.open();
subReactors[i]=new TcpSubReactor(selectors[i],ssc,i);
threads[i]=new Thread(subReactors[i]);
//启动线程,执行run方法
threads[i].start();
}
}catch (Exception e){
}
}
@Override
public synchronized void run() {
try {
//serverSocket接收客户端的请求,创建一个socket 和一个socketChannel和客户端的socket通信
SocketChannel socketChannel=ssc.accept();
if(socketChannel!=null){
//设置socketChannel是非阻塞的,与BIO中的阻塞对立
socketChannel.configureBlocking(false);
//注册到selector中,并设置感兴趣的IO事件是读写。 OP_READ 是<=1 OP_WRITE 是<=2
SelectionKey selectionKey= socketChannel.register(selectors[index], SelectionKey.OP_WRITE);
//将TcpHandler处理对象设置为附件,当有IO事件是 读写事件的时候,会创建TcpHandler对象
selectionKey.attach(new TcpHandler(selectionKey,socketChannel));
index++;
}
} catch (Exception e) {
}
}
}
4、每一个子反应器对象都对应一个子注册器。Acceptor中的TcpSubReactor[]集合和Thread[]集合在主线程刚开始就已经创建好了,
并且Thread[]集合已经开始启动运行了。
TcpSubReactor 中的run()函数,while循环一直在监听着对应的子注册器。如果子注册器中有新注册的OP_WRITE IO事件,
这时就会调用分发方法dispatch(),
Runnable runnable=(Runnable)selectionKey.attachment(); 中的对象是TcpHandler对象。
/**
* 子反应器:接收OP_WRITE事件的反应器。
*
*
* */
public class TcpSubReactor implements Runnable{
private final ServerSocketChannel ssc;
private final Selector selector;
private int num;
public TcpSubReactor(Selector selector,ServerSocketChannel ssc,int num){
this.selector=selector;
this.ssc=ssc;
this.num=num;
}
/**
* TcpReactor tcpReactor=new TcpReactor(9000);
* 初始化TcpReactor的时候,该run方法已经开始跑了。
* 但是它只接收子注册器中的IO事件。
*
* */
@Override
public void run() {
//在线程中断前持续执行
while (!Thread.interrupted()){
System.out.println("接收连接。。。。。。");
try{
/**
* 查看注册器中有没有事件发生,如果没有事件发生,该方法阻塞到这里。
* 该方法会将查询出来的IO事件放到Set<SelectionKey> selectionKeys集合中。Selector类中就有该集合。
* 当有客户端连接的时候
* */
selector.select();
}catch (Exception e){
}
System.out.println("发生了事件,开始执行所有注册在selector中的IO事件");
Set<SelectionKey> selectionKeys=selector.selectedKeys();
//遍历所有发生的事件
Iterator<SelectionKey> it=selectionKeys.iterator();
while (it.hasNext()){
//从迭代器中读取事件
SelectionKey selectionKey=it.next();
//该处相当于一个路由器,分发处理
dispatch(selectionKey);
it.remove();
}
}
}
private void dispatch(SelectionKey selectionKey) {
/**
*
* 如果是接收客户端连接的话。
* selectKey存储的是Acceptor对象,该对象是继承了Runnable接口的。
* 所以,也可以执行run方法。在主线程中执行
*
*
*
*
* */
Runnable runnable=(Runnable)selectionKey.attachment();
if(runnable!=null){
runnable.run();
}
}
}
TcpHandler对象是服务端ServerSocketChannel创建的SocketChannel (服务端)与客户端进行通信。
public class TcpHandler implements Runnable{
private SelectionKey selectionKey;
private SocketChannel socketChannel;
public TcpHandler(SelectionKey selectionKey, SocketChannel socketChannel){
//selectionKey选择键
this.selectionKey=selectionKey;
//服务端创建的和客户端保持通信的 socketChannel 通道
this.socketChannel=socketChannel;
}
@Override
public void run() {
try {
read();
} catch (Exception e) {
e.printStackTrace();
}
}
private void read() throws Exception {
byte[] bytes=new byte[1024];
ByteBuffer byteBuffer=ByteBuffer.wrap(bytes);
int numBytes=socketChannel.read(byteBuffer);//读取字符串
if(numBytes == -1){
closeChannel();
return;
}
String str=new String(bytes);
if(StringUtils.isNotBlank(str)){
Thread.sleep(2000);
System.out.println("正在处理业务逻辑。。。。。。。。。。。。。。。。。。");
System.out.println( socketChannel.getRemoteAddress().toString()+ ">>>>接收到的客户端的信息是:"+ str);
send(str);
}
selectionKey.selector().wakeup();//使一个阻塞的selector立即返回
}
private void send(String str) throws IOException {
String returnStr="根据读取的数据返回响应:"+socketChannel.getLocalAddress().toString()+"\r\n";
ByteBuffer byteBuffer=ByteBuffer.wrap(returnStr.getBytes());
while (byteBuffer.hasRemaining()){
socketChannel.write(byteBuffer);//回传给client的回应字符串,发送buffer的postion位置,到limit位置
}
}
private void closeChannel(){
try{
selectionKey.cancel();
socketChannel.close();
}catch (Exception e){
}
}
}