Netty学习之旅------线程模型前置篇Reactor反应堆设计模式实现(基于java

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
img

正文

clientChannel.read(buf);//

buf.put(b);

clientChannel.register(selector, SelectionKey.OP_WRITE, buf);//注册写事件

}

} catch(Throwable e) {

e.printStackTrace();

System.out.println(“客户端主动断开连接。。。。。。。”);

ssc.register(selector, SelectionKey.OP_ACCEPT);

}

}

}

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

}

Nio客户端代码:

package threadmode.r1;

import java.io.IOException;

import java.net.InetSocketAddress;

import java.nio.ByteBuffer;

import java.nio.channels.SelectionKey;

import java.nio.channels.Selector;

import java.nio.channels.SocketChannel;

import java.util.Iterator;

import java.util.Set;

/**

  • @author dingwei2

*/

public class NioClient {

public static void main(String[] args) {

// TODO Auto-generated method stub

SocketChannel clientClient;

Selector selector = null;

try {

clientClient = SocketChannel.open();

clientClient.configureBlocking(false);

selector = Selector.open();

clientClient.register(selector, SelectionKey.OP_CONNECT);

clientClient.connect(new InetSocketAddress(“127.0.0.1”,9080));

Set ops = null;

while(true) {

try {

selector.select();

ops = selector.selectedKeys();

for (Iterator it = ops.iterator(); it.hasNext()😉 {

SelectionKey key = it.next();

it.remove();

if(key.isConnectable()) {

System.out.println(“client connect”);

SocketChannel sc = (SocketChannel) key.channel();

// 判断此通道上是否正在进行连接操作。

// 完成套接字通道的连接过程。

if (sc.isConnectionPending()) {

sc.finishConnect();

System.out.println(“完成连接!”);

ByteBuffer buffer = ByteBuffer.allocate(1024);

buffer.put(“Hello,Server”.getBytes());

buffer.flip();

sc.write(buffer);

}

sc.register(selector, SelectionKey.OP_READ);

} else if(key.isWritable()) {

System.out.println(“客户端写”);

SocketChannel sc = (SocketChannel)key.channel();

ByteBuffer buffer = ByteBuffer.allocate(1024);

buffer.put(“hello server.”.getBytes());

buffer.flip();

sc.write(buffer);

} else if(key.isReadable()) {

System.out.println(“客户端收到服务器的响应…”);

SocketChannel sc = (SocketChannel)key.channel();

ByteBuffer buffer = ByteBuffer.allocate(1024);

int count = sc.read(buffer);

if(count > 0 ) {

buffer.flip();

byte[] response = new byte[buffer.remaining()];

buffer.get(response);

System.out.println(new String(response));

}

}

}

} catch(Throwable e) {

e.printStackTrace();

}

}

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

1.2 多线程模型

=========

Reactor多线程模型(多个Nio线程处理网络读写)(此图来源与网络)。

多线程模型,就是1个线程Acceptor接受客户端的连接,然后由一组IO线程(Reactor)来执行网络的读写。下面贴出其实现。

其中NioServer中的Acceptor为接受客户端连接线程。

其中NioReactorThreadGroup为一组IO线程,NioReactorThread为具体IO线程的实现。

package threadmode.r2;

import java.io.IOException;

import java.net.InetSocketAddress;

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;

public class NioServer {

public static void main(String[] args) {

// TODO Auto-generated method stub

new Thread(new Acceptor()).start();

}

/**

  • 连接线程模型,反应堆,转发器 Acceptor

  • @author dingwei2

*/

private static final class Acceptor implements Runnable {

private NioReactorThreadGroup nioReactorThreadGroup;

public Acceptor() {

nioReactorThreadGroup = new NioReactorThreadGroup();

}

public void run() {

// TODO Auto-generated method stub

System.out.println(“服务端启动成功,等待客户端接入”);

ServerSocketChannel ssc = null;

Selector selector = null;

try {

ssc = ServerSocketChannel.open();

ssc.configureBlocking(false);

ssc.bind(new InetSocketAddress(“127.0.0.1”, 9080));

selector = Selector.open();

ssc.register(selector, SelectionKey.OP_ACCEPT);

Set ops = null;

while (true) {

try {

selector.select(); // 如果没有感兴趣的事件到达,阻塞等待

ops = selector.selectedKeys();

} catch (Throwable e) {

e.printStackTrace();

break;

}

// 处理相关事件

for (Iterator it = ops.iterator(); it.hasNext()😉 {

SelectionKey key = it.next();

it.remove();

try {

if (key.isAcceptable()) { // 客户端建立连接

System.out.println(“收到客户端的连接请求。。。”);

ServerSocketChannel serverSc = (ServerSocketChannel) key.channel();// 这里其实,可以直接使用ssl这个变量

SocketChannel clientChannel = serverSc.accept();

clientChannel.configureBlocking(false);

nioReactorThreadGroup.dispatch(clientChannel); // 转发该请求

}

} catch (Throwable e) {

e.printStackTrace();

System.out.println(“客户端主动断开连接。。。。。。。”);

}

}

}

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

}

package threadmode.r2;

import java.nio.channels.SocketChannel;

import java.util.concurrent.atomic.AtomicInteger;

/**

  • nio 线程组;简易的NIO线程组

  • @author dingwei2

*/

public class NioReactorThreadGroup {

private static final AtomicInteger requestCounter = new AtomicInteger(); //请求计数器

private final int nioThreadCount; // 线程池IO线程的数量

private static final int DEFAULT_NIO_THREAD_COUNT;

private NioReactorThread[] nioThreads;

static {

// DEFAULT_NIO_THREAD_COUNT = Runtime.getRuntime().availableProcessors() > 1

// ? 2 * (Runtime.getRuntime().availableProcessors() - 1 ) : 2;

DEFAULT_NIO_THREAD_COUNT = 4;

}

public NioReactorThreadGroup() {

this(DEFAULT_NIO_THREAD_COUNT);

}

public NioReactorThreadGroup(int threadCount) {

if(threadCount < 1) {

threadCount = DEFAULT_NIO_THREAD_COUNT;

}

this.nioThreadCount = threadCount;

this.nioThreads = new NioReactorThread[threadCount];

for(int i = 0; i < threadCount; i ++ ) {

this.nioThreads[i] = new NioReactorThread();

this.nioThreads[i].start(); //构造方法中启动线程,由于nioThreads不会对外暴露,故不会引起线程逃逸

}

System.out.println(“Nio 线程数量:” + threadCount);

}

public void dispatch(SocketChannel socketChannel) {

if(socketChannel != null ) {

next().register(socketChannel);

}

}

protected NioReactorThread next() {

return this.nioThreads[ requestCounter.getAndIncrement() % nioThreadCount ];

}

public static void main(String[] args) {

// TODO Auto-generated method stub

}

}

package threadmode.r2;

import java.io.IOException;

import java.nio.ByteBuffer;

import java.nio.channels.SelectionKey;

import java.nio.channels.Selector;

import java.nio.channels.SocketChannel;

import java.util.ArrayList;

import java.util.Iterator;

import java.util.List;

import java.util.Set;

import java.util.concurrent.locks.ReentrantLock;

/**

  • Nio 线程,专门负责nio read,write

  • 本类是实例行代码,不会对nio,断线重连,写半包等场景进行处理,旨在理解 Reactor模型(多线程版本)

  • @author dingwei2

*/

public class NioReactorThread extends Thread {

private static final byte[] b = “hello,服务器收到了你的信息。”.getBytes(); //服务端给客户端的响应

private Selector selector;

private List waitRegisterList = new ArrayList(512);

private ReentrantLock registerLock = new ReentrantLock();

public NioReactorThread() {

try {

this.selector = Selector.open();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

/**

  • socket channel

  • @param socketChannel

*/

public void register(SocketChannel socketChannel) {

if(socketChannel != null ) {

try {

registerLock.lock();

waitRegisterList.add(socketChannel);

} finally {

registerLock.unlock();

}

}

}

//private

public void run() {

while(true) {

Set ops = null;

try {

selector.select(1000);

ops = selector.selectedKeys();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

continue;

}

//处理相关事件

for (Iterator it = ops.iterator(); it.hasNext()😉 {

SelectionKey key = it.next();

it.remove();

try {

if (key.isWritable()) { //向客户端发送请求

SocketChannel clientChannel = (SocketChannel)key.channel();

ByteBuffer buf = (ByteBuffer)key.attachment();

buf.flip();

clientChannel.write(buf);

System.out.println(“服务端向客户端发送数据。。。”);

//重新注册读事件

clientChannel.register(selector, SelectionKey.OP_READ);

} else if(key.isReadable()) { //接受客户端请求

System.out.println(“服务端接收客户端连接请求。。。”);

SocketChannel clientChannel = (SocketChannel)key.channel();

ByteBuffer buf = ByteBuffer.allocate(1024);

System.out.println(buf.capacity());

clientChannel.read(buf);//

buf.put(b);

clientChannel.register(selector, SelectionKey.OP_WRITE, buf);//注册写事件

}

} catch(Throwable e) {

e.printStackTrace();

System.out.println(“客户端主动断开连接。。。。。。。”);

}

}

//注册事件

if(!waitRegisterList.isEmpty()) {

try {

registerLock.lock();

for (Iterator it = waitRegisterList.iterator(); it.hasNext()😉 {

SocketChannel sc = it.next();

try {

sc.register(selector, SelectionKey.OP_READ);

} catch(Throwable e) {

e.printStackTrace();//ignore

}

it.remove();

}

} finally {

registerLock.unlock();

}

}

}

}

}

NioClient与Reactor,单线程版本一样,在这不重复给出。

上述示例代码中,其实并不是完成按照Reacor设计模式而来的,重头戏请看1.3,主从多线程模型(Reacor)实现

1.3 主从多线程模型(Reactor)

====================

主从多线程模型(此图来源与网络)

重点关注点如下:

  • Acceeptor:职责维护java.nio.ServerSocketChannel类,绑定服务端监听端口,然后将该通道注册到MainRector中;

  • Main Reactor,监听客户端连接的反应堆,这里使用jdk并发中的Executors.newSingleThreadExecutor线程池来实现,监听客户端的连接事件(OP_ACCEPT)

  • Sub Reactor,目前没有使用jdk的并发池,这里用的SubReactorThreadGroup,其实现是数组,当然这里也可以使用jdk线程池,SubReactor的每一个线程都是IO线程,用来处理读,写事件。所有的IO线程公用一个业务线程池(基于juc)实现,用来处理业务逻辑,也就是运行Handel的地方。

Handel:具体业务逻辑实现,本例就是获取客户端的信息后,在请求信息后面追加一段文字,便返回给客户端。相关源码实现:

NioServer(Acceptor)的实现源码:

package persistent.prestige.demo.netty.threadmode.t3;

import java.io.IOException;

import java.net.InetSocketAddress;

import java.nio.channels.ServerSocketChannel;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

/**

  • Reactor 主从Reactor模式实现

  • Acceptor,其实个人认为,这里就是服务端角色

  • @author Administrator

*/

public class NioServer {

private static final int DEFAULT_PORT = 9080;

public static void main(String[] args) {

new Thread(new Acceptor()).start();

}

private static class Acceptor implements Runnable {

// main Reactor 线程池,用于处理客户端的连接请求

private static ExecutorService mainReactor = Executors.newSingleThreadExecutor();

public void run() {

// TODO Auto-generated method stub

ServerSocketChannel ssc = null;

try {

ssc = ServerSocketChannel.open();

ssc.configureBlocking(false);

ssc.bind(new InetSocketAddress(DEFAULT_PORT));

//转发到 MainReactor反应堆

dispatch(ssc);

System.out.println(“服务端成功启动。。。。。。”);

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

private void dispatch(ServerSocketChannel ssc) {

mainReactor.submit(new MainReactor(ssc));

}

}

}

MainReactor 源码如下:

package persistent.prestige.demo.netty.threadmode.t3;

import java.io.IOException;

import java.nio.channels.SelectableChannel;

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;

/**

  • 主Reactor,主要用来处理连接请求的反应堆

  • @author Administrator

*/

public class MainReactor implements Runnable {

private Selector selector;

private SubReactorThreadGroup subReactorThreadGroup;

public MainReactor(SelectableChannel channel) {

try {

selector = Selector.open();

channel.register(selector, SelectionKey.OP_ACCEPT);

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

subReactorThreadGroup = new SubReactorThreadGroup(4);

}

public void run() {

System.out.println(“MainReactor is running”);

// TODO Auto-generated method stub

while (!Thread.interrupted()) {

Set ops = null;

try {

selector.select(1000);

ops = selector.selectedKeys();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

// 处理相关事件

for (Iterator it = ops.iterator(); it.hasNext()😉 {

SelectionKey key = it.next();

it.remove();

try {

if (key.isAcceptable()) { // 客户端建立连接

System.out.println(“收到客户端的连接请求。。。”);

ServerSocketChannel serverSc = (ServerSocketChannel) key.channel();// 这里其实,可以直接使用ssl这个变量

SocketChannel clientChannel = serverSc.accept();

clientChannel.configureBlocking(false);

subReactorThreadGroup.dispatch(clientChannel); // 转发该请求

}

} catch (Throwable e) {

e.printStackTrace();

System.out.println(“客户端主动断开连接。。。。。。。”);

}

}

}

}

}

SubReactor组,IO线程池实现:

package persistent.prestige.demo.netty.threadmode.t3;

import java.nio.channels.SelectionKey;

import java.nio.channels.SocketChannel;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.atomic.AtomicInteger;

/**

  • nio 线程组;简易的NIO线程组

  • @author dingwei2

*/

public class SubReactorThreadGroup {

private static final AtomicInteger requestCounter = new AtomicInteger(); //请求计数器

private final int nioThreadCount; // 线程池IO线程的数量

private static final int DEFAULT_NIO_THREAD_COUNT;

private SubReactorThread[] nioThreads;

private ExecutorService businessExecutePool; //业务线程池

static {

// DEFAULT_NIO_THREAD_COUNT = Runtime.getRuntime().availableProcessors() > 1

// ? 2 * (Runtime.getRuntime().availableProcessors() - 1 ) : 2;

DEFAULT_NIO_THREAD_COUNT = 4;

}

public SubReactorThreadGroup() {

this(DEFAULT_NIO_THREAD_COUNT);

}

public SubReactorThreadGroup(int threadCount) {

写在最后

学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯。所以:贵在坚持!

最后再分享的一些BATJ等大厂20、21年的面试题,把这些技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,上面只是以图片的形式给大家展示一部分。

蚂蚁金服三面直击面试官的Redis三连,Redis面试复习大纲在手,不慌

Mybatis面试专题

蚂蚁金服三面直击面试官的Redis三连,Redis面试复习大纲在手,不慌

MySQL面试专题

蚂蚁金服三面直击面试官的Redis三连,Redis面试复习大纲在手,不慌

并发编程面试专题

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

private static final AtomicInteger requestCounter = new AtomicInteger(); //请求计数器

private final int nioThreadCount; // 线程池IO线程的数量

private static final int DEFAULT_NIO_THREAD_COUNT;

private SubReactorThread[] nioThreads;

private ExecutorService businessExecutePool; //业务线程池

static {

// DEFAULT_NIO_THREAD_COUNT = Runtime.getRuntime().availableProcessors() > 1

// ? 2 * (Runtime.getRuntime().availableProcessors() - 1 ) : 2;

DEFAULT_NIO_THREAD_COUNT = 4;

}

public SubReactorThreadGroup() {

this(DEFAULT_NIO_THREAD_COUNT);

}

public SubReactorThreadGroup(int threadCount) {

写在最后

学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯。所以:贵在坚持!

最后再分享的一些BATJ等大厂20、21年的面试题,把这些技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,上面只是以图片的形式给大家展示一部分。

[外链图片转存中…(img-YGnCAZXZ-1713424132354)]

Mybatis面试专题

[外链图片转存中…(img-mPA0xXUj-1713424132355)]

MySQL面试专题

[外链图片转存中…(img-fOeUsVjE-1713424132355)]

并发编程面试专题

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-b8NfSq4R-1713424132355)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值