Reactor 模式的 JAVA NIO 多线程服务器

「NIO系列」——之Reactor模型 通过之前的Unix的IO模型介绍,想必也了解到了5种IO模型。java的NIO是属于同步非阻塞IO,关于IO多路复用,java没有相应的IO模型,但有相应的编程模式,Reactor就是基于NIO中实现多路复用的一种模式。本文将从以下几点阐述Reactor模式reactor是什么为何要用,能解决什么问题如何用,更好的方式其他事件处理模式。... 阅读详情

JAVA NIO 多线程服务器 1.2版
Reactor 模式的 JAVA NIO 多线程服务器

从JDK 1.4开始,Java的标准库中就包含了NIO,即所谓的“New IO”。其中最重要的功能就是提供了“非阻塞”的IO,当然包括了Socket。NonBlocking的IO就是对select(Unix平台下)以及 WaitForMultipleObjects(Windows平台)的封装,提供了高性能、易伸缩的服务架构。

说来惭愧,直到JDK1.4才有这种功能,但迟到者不一定没有螃蟹吃,NIO就提供了优秀的面向对象的解决方案,可以很方便地编写高性能的服务器。

话说回来,传统的Server/Client实现是基于Thread per request,即服务器为每个客户端请求建立一个线程处理,单独负责处理一个客户的请求。比如像Tomcat(新版本也会提供NIO方案)、Resin等Web服务器就是这样实现的。当然为了减少瞬间峰值问题,服务器一般都使用线程池,规定了同时并发的最大数量,避免了线程的无限增长。

但这样有一个问题:如果线程池的大小为100,当有100个用户同时通过HTTP现在一个大文件时,服务器的线程池会用完,因为所有的线程都在传输大文件了,即使第101个请求者仅仅请求一个只有10字节的页面,服务器也无法响应了,只有等到线程池中有空闲的线程出现。

另外,线程的开销也是很大的,特别是达到了一个临界值后,性能会显著下降,这也限制了传统的Socket方案无法应对并发量大的场合,而“非阻塞”的IO就能轻松解决这个问题。

下面只是一个简单的例子:服务器提供了下载大型文件的功能,客户端连接上服务器的12345端口后,就可以读取服务器发送的文件内容信息了。注意这里的服务器只有一个主线程,没有其他任何派生线程,让我们看看NIO是如何用一个线程处理N个请求的。

NIO服务器最核心的一点就是反应器模式:当有感兴趣的事件发生的,就通知对应的事件处理器去处理这个事件,如果没有,则不处理。所以使用一个线程做轮询就可以了。当然这里这是个例子,如果要获得更高性能,可以使用少量的线程,一个负责接收请求,其他的负责处理请求,特别是对于多CPU时效率会更高。

关于使用NIO过程中出现的问题,最为普遍的就是为什么没有请求时CPU的占用率为100%?出现这种问题的主要原因是注册了不感兴趣的事件,比如如果没有数据要发到客户端,而又注册了写事件(OP_WRITE),则在 Selector.select()上就会始终有事件出现,CPU就一直处理了,而此时select()应该是阻塞的。

线程模型

NIO 的选择器采用了多路复用(Multiplexing)技术,可在一个选择器上处理多个套接字,通过获取读写通道来进行 IO 操作。由于网络带宽等原因,在通道的读、写操作中是容易出现等待的,所以在读、写操作中引入多线程,对性能提高明显,而且可以提高客户端的感知服务质量。所以本文的模型将主要通过使用读、写线程池来提高与客户端的数据交换能力。

如下图所示,服务端接受客户端请求后,控制线程将该请求的读通道交给读线程池,由读线程池分配线程完成对客户端数据的读取操作;当读线程完成读操作后,将数据返回控制线程,进行服务端的业务处理;完成业务处理后,将需回应给客户端的数据和写通道提交给写线程池,由写线程完成向客户端发送回应数据的操作。


(NIO 多线程服务器模型)

同时整个服务端的流程处理,建立于事件机制上。在 [接受连接->读->业务处理->写 ->关闭连接 ]这个过程中,触发器将触发相应事件,由事件处理器对相应事件分别响应,完成服务器端的业务处理。
下面我们就来详细看一下这个模型的各个组成部分。

 

 

public class MiniServer extends Thread
{
    private static final Log log = LogFactory.getLog(MiniServer.class);
   
    private final Selector s;
    private final ServerSocketChannel ssc;
    private ExecutorService executor;
   
    public MiniServer(int portnumber,ExecutorService executor) throws IOException
    {
        this.executor=executor;
        s = Selector.open();
        ssc = ServerSocketChannel.open();
        ssc.socket().bind(new InetSocketAddress(portnumber));
        ssc.configureBlocking(false);
        ssc.register(s,SelectionKey.OP_ACCEPT);
    }
   
    public void run()
    {
        try
        {
            while(s.isOpen())
            {
                int nKeys=s.select();
                if(nKeys>0)
                {
                    Iterator<SelectionKey> it = s.selectedKeys().iterator();
                    while (it.hasNext())
                    {
                        SelectionKey key = it.next();
                        it.remove();
                        if (!key.isValid() || !key.channel().isOpen())
                            continue;
                        if(key.isAcceptable())
                        {
                            SocketChannel sc = ssc.accept();
                            if (sc != null)
                            {
                                sc.configureBlocking(false);
                                sc.register(s, SelectionKey.OP_READ, new Reader(executor));
                            }
                        }
                        else if(key.isReadable()||key.isWritable())
                        {
                            Reactor reactor = (Reactor) key.attachment();
                            reactor.execute(key);
                        }
                    }
                }
            }
        }
        catch(IOException e)
        {
            log.info(e);
        }
    }
}


public interface Reactor
{
    void execute(SelectionKey key);
}


public class Reader implements Reactor
{
    private static final Log log = LogFactory.getLog(Reader.class);
   
    private byte[] bytes=new byte[0];
    private ExecutorService executor;
   
    public Reader(ExecutorService executor)
    {
        this.executor=executor;
    }
   
    @Override
    public void execute(SelectionKey key)
    {
        SocketChannel sc = (SocketChannel) key.channel();
        try
        {
            ByteBuffer buffer=ByteBuffer.allocate(1024);
            int len=-1;
            while(sc.isConnected() && (len=sc.read(buffer))>0)
            {
                buffer.flip();
                  byte [] content = new byte[buffer.limit()];
                buffer.get(content);
                bytes=NutUtil.ArrayCoalition(bytes,content);
                buffer.clear();
            }
            if(len==0)
            {
                key.interestOps(SelectionKey.OP_READ);
                key.selector().wakeup();
            }
            else if(len==-1)
            {
                Callable<byte[]> call=new ProcessCallable(bytes);
                Future<byte[]> task=executor.submit(call);
                ByteBuffer output=ByteBuffer.wrap(task.get());
                sc.register(key.selector(), SelectionKey.OP_WRITE, new Writer(output));
            }
        }
        catch(Exception e)
        {
            log.info(e);
        }
    }
}


public class Writer implements Reactor
{
    private static final Log log = LogFactory.getLog(Writer.class);
   
    private ByteBuffer output;
   
    public Writer(ByteBuffer output)
    {
        this.output=output;
    }
   
    public void execute(SelectionKey key)
    {
        SocketChannel sc = (SocketChannel) key.channel();
        try
        {
            while(sc.isConnected() && output.hasRemaining())
            {
                int len=sc.write(output);
                if(len<0)
                {
                    throw new EOFException();
                }
                if(len==0)
                {
                    key.interestOps(SelectionKey.OP_WRITE);
                    key.selector().wakeup();
                    break;
                }
            }
            if(!output.hasRemaining())
            {
                output.clear();
                key.cancel();
                sc.close();
            }
        }
        catch(IOException e)
        {
            log.info(e);
        }
    }
}

 

YOLOv9改进,YOLOv9添加CBAM注意力机制 CBAM是一个简单而有效的注意力模块,用于前馈卷积神经网络。给定一个中间特征图,我们的模块按顺序推断两个独立维度(通道和空间)的注意力图,然后将注意力图乘以输入特征图以进行自适应特征细化。由于 CBAM 是一个轻量级的通用模块,因此它可以无缝集成到任何 CNN 架构中,开销可以忽略不计,并且可以与基础 CNN 一起进行端到端训练。论文链接:(CBAM论文) 阅读详情

相关推荐

GitHub漏洞挖掘与CVE提交实战指南:从发现到编号的完整链路

GitHub开源项目是现代软件供应链的核心节点,其安全漏洞直接影响全球数百万系统的机密性、完整性与可用性。理解漏洞本质需回归CIA三元组原理,而真实可利用性依赖对协议解析层、配置加载区和依赖传递链等高危模块的精准识别。技术价值在于将模糊的‘可疑逻辑’转化为符合MITRE CVE标准的可验证事件,支撑SDL流程、威胁情报建设与供应链风险管理。典型应用场景包括甲方安全工程师推动供应商修复、独立研究者参与负责任披露,以及DevSecOps团队构建自动化风险坐标系。本文基于27次CVE提交经验,聚焦GitHub代码

weixin_33646989的博客 651

Java NIO服务端多线程化问题

工作中有这么一个项目,电动车充电桩使用socket与服务器通信,项目于今年一月份上线,一直平稳运行,近期由于充电桩数量激增,socket服务端压力倍增,导致服务端开放的端口连接不上。分析代码,发现服务端使用的单线程模式。

qq_20157013的博客 1808

FPGA实战:从零构建eMMC控制器(Verilog篇)

本文详细介绍了如何使用Verilog从零构建FPGA上的eMMC控制器,涵盖eMMC接口基础、命令系统解析、关键模块设计及性能优化技巧。通过实战案例,帮助开发者掌握高速数据存储解决方案,提升嵌入式系统开发能力。

weixin_26802933的博客 298

Java基础之《netty(10)—Reactor三种模式》

(3)如果是建立连接请求事件,则由Acceptor通过Accept处理连接请求,然后创建一个Handler对象处理连接完成后的后续业务处理。服务器端用一个线程通过多路复用搞定所有的IO操作(包括连接、读、写等),编码简单,清晰明了,但是如果客户端连接数量较多,将无法支撑。(1)Select是前面I/O复用模型介绍的标准网络编程API,可以实现应用程序通过一个阻塞对象监听多路连接请求。(4)如果不是建立连接事件,则Reactor会分发调用连接对应的Handler来响应。在处理高并发的时候,存在瓶颈。

csj50的专栏 1084

FPGA实现PI控制

FPGA实现PI控制

m0_57319166的博客 3897

MatLab:数字图像处理实验

图像处理基本处理实验目的实验环境实验内容题目一代码结果题目二平移平移代码平移结果水平镜像水平镜像代码水平镜像结果垂直镜像垂直镜像代码垂直镜像结果放大放大代码放大结果缩小缩小代码缩小结果旋转旋转代码旋转结果双线性旋转旋转代码旋转结果 实验目的 1、熟悉并掌握MATLAB工具的使用;  2、实现图像的读取、显示、存储、平移、镜像、放大、缩小及旋转操作; 3、掌握常用的插值方法,并了解其优缺点。 实验环境 MatLab2016 实验内容 题目一 读入一幅RGB图像,变换为灰度图像和二值图像,并在同一个窗口内分别显

luoyao_的博客 9696

Java I/O中的Reactor模式

单Reactor单线程,酒店的门童,服务员是同一个人,全程为客户服务单Reactor多线程,1个门童,多个服务员,门童之负责接待,转给服务员主从Reactor多线程,多个门童,多个服务员。

0102的博客 1164

UE5网络同步优化:GetLifetimeReplicatedProps高级应用指南

本文深入探讨了UE5中GetLifetimeReplicatedProps函数的高级应用与优化策略。文章超越了基础变量注册,重点阐述了如何通过精细的复制条件控制、频率管理及分层复制机制,构建高效的数据同步通道,从而优化网络带宽与游戏性能,是提升多人游戏体验的实用指南。

weixin_29007243的博客 533

Tomcat源码解析系列三:Tomcat的Reactor机制-http请求连接处理机制

Tomcat获取连接的时候使用阻塞获取连接的方式,没有使用reactor模型。防止由于selector.select(selectorTimeout)导致的cpu空转,避免了cpu空转的场景。Tomcat处理长连接超时以及获取read事件的时候使用了reactor模式,非阻塞获取可读的socket,提高请求处理的能力。

Running666的博客 781

从零开始:用ADS 2023手把手教你设计2.4GHz Wi-Fi LNA(基于ATF-54143,附模型文件)

本文详细介绍了如何使用ADS 2023软件设计2.4GHz Wi-Fi频段的低噪声放大器(LNA),基于ATF-54143晶体管。内容涵盖模型导入、直流工作点确定、稳定性优化、噪声匹配等关键步骤,并提供可下载的模型文件包,帮助工程师快速掌握LNA设计技巧。

weixin_42531128的博客 320

C++笔记之emplace_back(),push_back()性能比较i++ 、 ++i

操作是否需要临时对象/拷贝性能需要(构造临时对象)较慢不需要(直接构造)较快i++需要(保存旧值)对于内置类型无影响,但对于自定义类型(如迭代器、大对象)会有拷贝开销++i不需要相对更快(尤其是自定义类型)都存在"是否需要额外的临时对象/拷贝"这一性能差异点和++i都是更高效的版本(避免临时对象/拷贝)和i++都可能产生额外的临时对象(前者构造临时对象,后者保存旧值)对于内置类型(如int),i++和++i性能几乎没有差异(编译器会优化);但对于自定义类型(尤其是迭代器等重量级对象),

小勇博客 98

【Netty】从0到1(五):NIO-多线程优化

这前几篇文章中,都是采用单线程进行设计,虽然可以运行,但是没有充分利用 CPU 的性能,并且如果有一个事件的处理时间较长,则会影响其他事件的处理。例如,开发一个项目,如果团队只有一个全栈工程师,那么他需要先完成前端,再完成后端,只能按部就班的完成任务,如果前端开发遭遇困难,花费了很多时间,则会大大拉长项目开发周期,而如果一个团队里有前端工程师和后端工程师,则前后端的开发能同步进行,这样会大大提高开发效率。

z1ztai的博客 387

【RT-DETR实战】117、华为昇腾CANN部署探索:从模型转换到推理踩坑实录

本文总结了RT-DETR模型在昇腾310B芯片上部署的关键挑战与解决方案。主要遇到四大问题:1)模型转换时需注意ONNX导出设置,建议先固定尺寸验证;2)内存布局差异导致推理异常,需精确匹配NHWC/NCHW格式;3)流水线并发优化中内存复用和异步处理是关键;4)调试阶段需利用专用工具逐层比对。作者提出六条实用建议,包括小尺寸验证、预处理对齐、内存池设计等,强调部署是持续适配过程,需建立完整的测试体系应对工具链迭代。

企鹅码农的专属播客,分享10多年的实战案例和经验解读,包含YOLO、RT-DETR、AI等各个工作领域。 192

Java进阶(五)Java I/O模型从BIO到NIO和Reactor模式

本文介绍了Java中的四种I/O模型,同步阻塞,同步非阻塞,多路复用,异步阻塞。同时将NIO和BIO进行了对比,并详细分析了基于NIO的Reactor模式,包括经典单线程模型以及多线程模式和多Reactor模式。 原创文章,转载请务必将下面这段话置于文章开头处(保留超链接)。 本文转发自技术世界,原文链接 http://www.jasongj.com/java/nio_reactor/ J...

GarfieldEr007的专栏 749

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

《netty实战》读书笔记一---selector、reactor模型、NIO与零拷贝

首先先自己熟悉一下selector、epoll、NIO编程、reactor模型 I/O 模型基本说明 I/O 模型简单的理解:就是用什么样的通道进行数据的发送和接收,很大程度上决定了程序通信的性能 Java共支持3种网络编程模型/IO模式:BIO、NIO、AIO Java BIO : 同步并阻塞(传统阻塞型),服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进...

阿拉斯加大闸蟹的博客 3891

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

java reactor nio_Java NIO Reactor模式

一、NIO介绍:NIO模型:1、Channel为连接通道,相当于一个客户端与服务器的一个连接,Selector为通道管理器,将Channel注册到Selector上,Selector管理着这些Channel,当管理的某个通道有事件到达时,Selector将会通知应用程序去处理;Selector工作的线程和处理网络事件的应用是在不同的线程中;2、NIO 有一个主要的类Selector,这个类似一个观...

weixin_29300805的博客 336

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Reactor详解

在开篇之前,我们对JavaNIO 的使用方式不做过多介绍,这种API的介绍方式网上太多了,没必要详细介绍,我们假设NIO的使用方式,你能够熟练运用。这是NIO系列第三篇: 【NIO系列】——之TCP探秘 【NIO系列】——之IO模型 通过之前的Unix的IO模型介绍,想必也了解到了5种IO模型。java的NIO是属于同步非阻塞IO,关于IO多路复用,java没有相应的IO模型,但有相应的编程模式,Reactor 就是基于NIO中实现多路复用的一种模式。本文将从以下几点阐述Reactor模式: .

好习惯成就伟大 737

java nio proactor_reactor模式与java nio

Reactor是由Schmidt, Douglas C提出的一种模式,在高并发服务器实现中广泛采用。改模式采用事件驱动方式,当事件出现时,后调用相应的事件处理代码(Event Handler)。这个模式是高并发服务器的基础,如nginx和lighttpd。这两种对大并发,但每个请求处理都很快的场景非常适合。通常的web访问就是这个特点。结构包括了5个部分,其中handle和Synchronou...

weixin_30162121的博客 161

AVA NIO 多线程服务器 1.2版

reactor 模式的 java nio 多线程服务器 public class miniserver extends thread{ private static final log log = logfactory.getlog(miniserver.class); private final selector s; private final serversocke...

技术总结 82

Java NIO线程模型分析--单Reactor单线程

涉及到Buffer, ServerSocketChannel, SocketChannel, Selector,SelectionKey等类的使用。 ServerSocketChannel API: SocketChannel API: Client端代码: package com.bruce.javanio; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteB...

wdquan19851029的专栏 857

单线程 Reactor 模式

单线程 Reactor 是Reactor 模式最简单的版本,即 Reactor 和所有 Handler 都运行在同一个线程中。

2301_79609207的博客 1206

还愿之Netty-Reactor模式

还愿之Netty-Reactor模式一. 什么是reactor模式1. 多线程时io的致命缺陷2. reactor模式简介二. 单线程的Reactor模型三. 多线程的Reactor模型四. 主从的Reactor模型 一. 什么是reactor模式 1. 多线程时io的致命缺陷 如果以bio的思想去设计一个服务器,用一个while循环,不断的去监听端口是否有套接字的连接。如果有连接建立,就进行业务处理,代码如下所示。 while(true) { Socket socket = ServerSocket

Anbal_Zheng的博客 353

BIO到NIO

同步阻塞IO 单线程BIO 最原始的网络编程思路就是服务器用一个while循环,不断监听端口是否有新的套接字连接,如果有,那么就调用一个处理函数处理。 while(true){ socket = accept(); handle(socket) } 这种方法的最大问题是无法并发,效率太低。如果当前的请求没有处理完,那么后面的请求只能被阻塞,服务器的吞吐量太低。 多线程BIO 针对上面的问题,很自然想到了使用多线程处理IO,也就是很经典的connection per thread,每一个连接

weixin_37850264的博客 165

什么是Netty、Reactor线程模型和nio

Netty是提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。也就是说,Netty是一个基于NIO的客户、服务器端的编程框架。 例如:dubbo的底层通信是利用netty来实现的。 Reactor是一种线程模式,Reactor是什么? The reactor design_pattern is anevent_handlingpattern for handling service requests delivered concurrently t...

当我谈编程时我谈些什么 314

请收藏好:Doug Lea写的理解 Java NIO 与 Reactor 模式最权威的资料

但每个步骤的性质和开销各不相同,例如:XML 解析、文件传输、Web 页面生成、计算服务等。示例:数据库和事务监控器、多人游戏、聊天等。但分派速度可能较慢,而且通常。,主要适用于多处理器环境。

m0_50180963的博客 374
上一篇: LUN---Logical Unit Number
下一篇: Linux 中的零拷贝技术
Ice008
博客等级 码龄19年 34粉丝 36原创
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值