BIO、NIO、Netty演化总结之二(手撸一个极简版netty)

2024-02-23 16:12

本文主要是介绍BIO、NIO、Netty演化总结之二(手撸一个极简版netty),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

之前的一片文章里面总结了一下IO模型的演进(BIO、NIO、Netty演化总结-CSDN博客),里面给了一个示例AsyncNonBlockingServerWithThreadPool,最近想了想,发现这个代码跟netty的模型还是有一些出入,说是netty的雏形好像有点牵强,于是想了一下,还是决定写一个更接近netty的极简版代码,仅供交流,有不对的地方欢迎指正,不喜勿喷,直接上代码

public class MyBossGroup {//多路复用器private Selector selector;private ServerSocketChannel serverChannel;//读写处理线程(对应netty里面的worker线程组)private MyWorkerGroup[] myWorkerGroups = new MyWorkerGroup[10];//计数器,用于从线程组中挑选一个线程来处理事件private final AtomicLong idx = new AtomicLong();public MyBossGroup(int port) throws IOException {// 创建选择器和服务器通道selector = Selector.open();serverChannel = ServerSocketChannel.open();serverChannel.bind(new InetSocketAddress(port));serverChannel.configureBlocking(false);// 注册服务器通道到选择器,并注册接收连接事件serverChannel.register(selector, SelectionKey.OP_ACCEPT);for (int i = 0; i < myWorkerGroups.length; i++) {myWorkerGroups[i] = new MyWorkerGroup();}}public void start() throws IOException {System.out.println("Server started.");while (true) {// 阻塞等待事件发生selector.select();// 处理连接事件Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();while (keyIterator.hasNext()) {SelectionKey key = keyIterator.next();keyIterator.remove();// 接收连接事件handleAccept(key);}}}private void handleAccept(SelectionKey key) throws IOException {ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();SocketChannel clientChannel = serverChannel.accept();clientChannel.configureBlocking(false);//挑选一个线程,将clientChannel绑定到这个线程中去MyWorkerGroup myWorkerGroup = myWorkerGroups[(int) Math.abs(idx.getAndIncrement() % myWorkerGroups.length)];//已经建立连接的socket交给worker线程组myWorkerGroup.register(clientChannel);System.out.println("New client connected: " + clientChannel.getRemoteAddress());}public static void main(String[] args) {try {MyBossGroup server = new MyBossGroup(8080);server.start();} catch (IOException e) {e.printStackTrace();}}
}

这个MyBossGroup就是我们在编写netty应用程序的时候的bossgroup的核心逻辑,负责接收客户端连接,并且将连接的socket注册到worker线程组中,下面的MyWorkerGroup就是编写netty应用程序的时候的workergroup的核心逻辑,负责数据的读写:


public class MyWorkerGroup {private Selector selector;private Thread thread;private ByteBuffer buffer;public MyWorkerGroup() {try {selector = Selector.open();} catch (IOException e) {e.printStackTrace();}thread = new Thread(new MyRunnable());buffer = ByteBuffer.allocate(1024);}class MyRunnable implements Runnable {@Overridepublic void run() {while (true) {try {selector.select();// 处理连接事件Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();while (keyIterator.hasNext()) {SelectionKey key = keyIterator.next();keyIterator.remove();SocketChannel clientChannel = (SocketChannel) key.channel();if (key.isReadable()) {buffer.clear();int bytesRead = 0;try {bytesRead = clientChannel.read(buffer);} catch (IOException e) {e.printStackTrace();closeSocketChannel(key, clientChannel);continue;}if (bytesRead == -1) {closeSocketChannel(key, clientChannel);try {System.out.println("Client disconnected: " + clientChannel.getRemoteAddress());} catch (IOException e) {e.printStackTrace();}continue;}buffer.flip();byte[] data = new byte[buffer.remaining()];buffer.get(data);System.out.println("Received message from client: " + new String(data));clientChannel.write(ByteBuffer.wrap((new String("server_response:") + new String(data)).getBytes()));System.err.println("current_thread:" + thread.getName());} else {closeSocketChannel(key, clientChannel);}}} catch (Exception e) {e.printStackTrace();}}}private void closeSocketChannel(SelectionKey key, SocketChannel socketChannel) {try {System.out.println("Client disconnected: " + socketChannel.getRemoteAddress());// 客户端关闭连接key.cancel();socketChannel.close();} catch (IOException e) {e.printStackTrace();}}}public void register(SocketChannel socketChannel) {//这里唤醒是因为当连接数量超过myWorkerGroups数组长度的时候,可能会因为selector.select()阻塞,导致下面的register方法无法运行selector.wakeup();try {socketChannel.register(selector, SelectionKey.OP_READ);} catch (ClosedChannelException e) {e.printStackTrace();}if (!thread.isAlive()) {thread.start();}}
}

可以看到两个group其核心逻辑都是一个死循环,监听selector里面的事件,只是在netty里面将这两个死循环合并到了一个类里面,也就是NioEventLoop的run方法,每一个NioEventLoop独立维护一个自己的selector和任务队列(在这里没有体现),客户端连接第一次连接过来的socket绑定到一个NioEventLoop之后,后面这个socket的读写事件就全部由这个NioEventLoop负责,这样就有几个好处:

1、selector.select是一个阻塞的方法,由于每一个workergroup独立维护自己的selector,不会相互影响

2、每一个连接的客户端在绑定workergroup的时候实际上就是绑定了一个selector,这样每一个workergroup所负责管理的客户端连接的socket之间也相互不影响

这篇关于BIO、NIO、Netty演化总结之二(手撸一个极简版netty)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/739192

相关文章

Java通过驱动包(jar包)连接MySQL数据库的步骤总结及验证方式

《Java通过驱动包(jar包)连接MySQL数据库的步骤总结及验证方式》本文详细介绍如何使用Java通过JDBC连接MySQL数据库,包括下载驱动、配置Eclipse环境、检测数据库连接等关键步骤,... 目录一、下载驱动包二、放jar包三、检测数据库连接JavaJava 如何使用 JDBC 连接 mys

JavaSE正则表达式用法总结大全

《JavaSE正则表达式用法总结大全》正则表达式就是由一些特定的字符组成,代表的是一个规则,:本文主要介绍JavaSE正则表达式用法的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录常用的正则表达式匹配符正则表China编程达式常用的类Pattern类Matcher类PatternSynta

SQL中JOIN操作的条件使用总结与实践

《SQL中JOIN操作的条件使用总结与实践》在SQL查询中,JOIN操作是多表关联的核心工具,本文将从原理,场景和最佳实践三个方面总结JOIN条件的使用规则,希望可以帮助开发者精准控制查询逻辑... 目录一、ON与WHERE的本质区别二、场景化条件使用规则三、最佳实践建议1.优先使用ON条件2.WHERE用

Nginx Location映射规则总结归纳与最佳实践

《NginxLocation映射规则总结归纳与最佳实践》Nginx的location指令是配置请求路由的核心机制,其匹配规则直接影响请求的处理流程,下面给大家介绍NginxLocation映射规则... 目录一、Location匹配规则与优先级1. 匹配模式2. 优先级顺序3. 匹配示例二、Proxy_pa

Android学习总结之Java和kotlin区别超详细分析

《Android学习总结之Java和kotlin区别超详细分析》Java和Kotlin都是用于Android开发的编程语言,它们各自具有独特的特点和优势,:本文主要介绍Android学习总结之Ja... 目录一、空安全机制真题 1:Kotlin 如何解决 Java 的 NullPointerExceptio

MySQL基本查询示例总结

《MySQL基本查询示例总结》:本文主要介绍MySQL基本查询示例总结,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录Create插入替换Retrieve(读取)select(确定列)where条件(确定行)null查询order by语句li

解决Java异常报错:java.nio.channels.UnresolvedAddressException问题

《解决Java异常报错:java.nio.channels.UnresolvedAddressException问题》:本文主要介绍解决Java异常报错:java.nio.channels.Unr... 目录异常含义可能出现的场景1. 错误的 IP 地址格式2. DNS 解析失败3. 未初始化的地址对象解决

Linux区分SSD和机械硬盘的方法总结

《Linux区分SSD和机械硬盘的方法总结》在Linux系统管理中,了解存储设备的类型和特性是至关重要的,不同的存储介质(如固态硬盘SSD和机械硬盘HDD)在性能、可靠性和适用场景上有着显著差异,本文... 目录一、lsblk 命令简介基本用法二、识别磁盘类型的关键参数:ROTA查询 ROTA 参数ROTA

Qt实现网络数据解析的方法总结

《Qt实现网络数据解析的方法总结》在Qt中解析网络数据通常涉及接收原始字节流,并将其转换为有意义的应用层数据,这篇文章为大家介绍了详细步骤和示例,感兴趣的小伙伴可以了解下... 目录1. 网络数据接收2. 缓冲区管理(处理粘包/拆包)3. 常见数据格式解析3.1 jsON解析3.2 XML解析3.3 自定义

Python实现图片分割的多种方法总结

《Python实现图片分割的多种方法总结》图片分割是图像处理中的一个重要任务,它的目标是将图像划分为多个区域或者对象,本文为大家整理了一些常用的分割方法,大家可以根据需求自行选择... 目录1. 基于传统图像处理的分割方法(1) 使用固定阈值分割图片(2) 自适应阈值分割(3) 使用图像边缘检测分割(4)