`
WildFly
  • 浏览: 64361 次
社区版块
存档分类
最新评论

Undertow服务器基础分析 - XNIO

阅读更多

我们从名字上就能看出这是一个NIO思想为基础的IO框架,X是指这个框架可以有多种实现,我们可以从代码库 https://github.com/xnio 中发现一个项目xnio-native,里面有用C实现的nio层,就能体会到这个X的含义,可以直接基于操作系统C库。目前在Xnio中默认的实现是nio-impl,也就是JDK的NIO。我们可以认为xnio是在JDK的NIO之上,进行了扩展,融入了一些JBoss开发人员对于并发访问异步通信的理解和思考,总结出一套API,并用基于JDK NIO进行实现。

要学习XNIO,必须得有JDK NIO的基础知识,本文假设读者已经学习过NIO,如果还没有可以阅读参考书籍[1],[2]。另外对Netty有所了解的话,就会融会贯通,比较容易的理解XNIO的基本原理,因为两个项目有很多相似之处。

XNIO有两个重要的概念:
1. Channel,是传输管道的抽象概念,在NIO的Channel上进行的扩展加强,使用ChannelListener API进行事件通知。在创建Channel时,就赋予IO线程,用于执行所有的ChannelListener回调方法。
2. 区分IO线程和工作线程,创建一个工作线程池可以用来执行阻塞任务。一般情况下,非阻塞的Handler由IO线程执行,而阻塞任务比如Servlet则被调度到工作线程池执行。这样就很好的区分了阻塞和非阻塞的两种情形。

我们知道NIO的基本要求是不阻塞当前线程的执行,对于非阻塞请求的结果,可以用两种方式获得:一种是对于请求很快返回一个引用(如JDK中Future,XNIO中称为IoFuture,其中很多方法是类似的),过一段时间再查询结果;还有一种是当结果就绪时,调用事先注册的回调方法来通知(如NIO2的CompletionHandler,XNIO的ChannelListener)。显而易见后者效率更高一些,避免了数据未就绪情景下的无用处理过程。但JDK7之前无法将函数作为方法参数,所以只能用Java的匿名内部类来模拟函数式方法,造成代码嵌套层次过多,难以理解和维护,所以Netty和XNIO这样的框架通过调度方法调用过程,简化了编程工作。

XNIO和Netty的最主要的一个区别是,XNIO继承重用了JDK NIO的ByteBuffer类,而不像Netty另起炉灶,完全重建自己的ByteBuf体系。我们知道NIO的ByteBuffer使用时有个状态切换的过程,读和写要显式的通过调用slice, reset等方法切换,就和unix使用vi编辑器编辑和处理文本需要用'i'和Esc切换状态类似。Netty通过读写指针索引值移除了这个“不便操作”,但XNIO保留了和JDK NIO一致的做法。

无论NIO还是Netty,都有heap buffer和direct buffer的概念,前者可以认为是byte数组的封装,缓冲区存放在堆上,后者可以直接通过调用操作系统的系统调用在内存上分配缓冲,这样在一些IO操作时,比如从网卡上读出大量数据,再写到硬盘文件中,就不必拷贝数据到应用层,直接在操作系统内核或者驱动上进行数据复制。ByteBuffer的管理和应用是NIO最核心的思想,开发人员应该根据应用类型,对其反复调优,做到占用资源最少和效率最大。

XNIO和Netty都对ByteBuffer进行池化管理,简单来说就是开发者在程序开始时就计划好读写缓存区大小,统一分配好放到池中,Xnio中有Pool和Pooled接口用来管理池化缓存区。开发过高并发应用就知道,JVM GC经常出现并难以控制是很头疼的问题。我们通常在接收网络数据时,往往简单的new出一块数据区,填充,解析,使用,最后丢弃,这种方法随着大量的数据读入,必然造成GC反复出现。重用缓存区就可以在这个方面解决一部分问题。

和Netty的ChannelHandler不同,XNIO对应的ChannelListener只有一个方法handleEvent(),也就意味着所有的事件都要经由这个方法。在实际实行过程中,会进行若干状态机的转变,比如在服务器端,开始时accept状态就绪,当连接建立后转变为可读或者可写状态。请参见下面的例子。

在一些情况下,阻塞的IO调用也是很有用的,比如事务过程中。XNIO也提供了阻塞方法awaitReadable()和awaitWritable()。

利用Stream channel,可以在数据源头和目的地之间直接读写数据,有一种zero-copy(零拷贝)的方式,即读写过程使用同一块缓存区,这样就不必进行数据拷贝移动过程。XNIO通过封装NIO FileChannel中的方法transferTo和transferFrom来实现。

还有一种Messgae channel,用来传输帧数据,因为有些报文格式是固定长度或者按照某种已知帧式格式定义的,缓冲区的长度可以按照报文帧来定义,当数据填满后就及时发送,减少了数据长度的计算工作,代码逻辑简洁很多,所以这种channel用于传递'消息',websocket就是这样的。

阅读XNIO时,有几个词出现频率很高,Source表示信息源头,Sink是信息目的地,Conduit是源头到目的地管道的抽象。

前面提过,XNIO中有两类线程:
WORKER_IO_THREADS, IO thread处理非阻塞任务,要保证不做阻塞操作,因为很多连接同时用到这类线程,类似于nodejs中的loop,这个线程只要有任务就去执行,实际配置时每个CPU一个线程比较好。
WORKER_TASK_CORE_THREADS,用于执行阻塞任务,从线程池中获得,任务完成后返回到线程池中。因为不同应用对应的服务器负载不同,所以不易给出具体数值,一般建议每个CPU core设置10个。

代码分析,摘自
https://github.com/ecki/xnio-samples/blob/master/src/main/java/org/xnio/samples/SimpleEchoServer.java

服务器:

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;

import org.xnio.ChannelListener;
import org.xnio.IoUtils;
import org.xnio.OptionMap;
import org.xnio.Xnio;
import org.xnio.XnioWorker;
import org.xnio.channels.AcceptingChannel;
import org.xnio.channels.Channels;
import org.xnio.channels.ConnectedStreamChannel;

public final class SimpleEchoServer {

    public static void main(String[] args) throws Exception {

        // 定义读数据listener
        final ChannelListener<ConnectedStreamChannel> readListener = 
            new ChannelListener<ConnectedStreamChannel>() {
            public void handleEvent(ConnectedStreamChannel channel) {
                //分配缓冲
                final ByteBuffer buffer = ByteBuffer.allocate(512);
                int res;
                try {
                    while ((res = channel.read(buffer)) > 0) {
                        //切换到写的状态并用阻塞的方式写回
                        buffer.flip();
                        Channels.writeBlocking(channel, buffer);
                    }
                    // 保证全部送出
                    Channels.flushBlocking(channel);
                    if (res == -1) {
                        channel.close();
                    } else {
                        channel.resumeReads();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                    IoUtils.safeClose(channel);
                }
            }
        };
        // 创建接收 listener.
        final ChannelListener<AcceptingChannel<ConnectedStreamChannel>> acceptListener = 
            new ChannelListener<AcceptingChannel<ConnectedStreamChannel>>() {
            public void handleEvent(
                    final AcceptingChannel<ConnectedStreamChannel> channel) {
                try {
                    ConnectedStreamChannel accepted;
                    // channel就绪,准备接收连接请求
                    while ((accepted = channel.accept()) != null) {
                        System.out.println("accepted " + accepted.getPeerAddress());
                        // 已经连接,设置读数据listener
                        accepted.getReadSetter().set(readListener);
                        // 恢复读的状态
                        accepted.resumeReads();
                    }
                } catch (IOException ignored) {
                }
            }
        };

        //创建Xnio实例,并构造XnioWorker
        final XnioWorker worker = Xnio.getInstance().createWorker(OptionMap.EMPTY);
        // 创建server,在本地12345端口上侦听
        AcceptingChannel<? extends ConnectedStreamChannel> server = worker
                .createStreamServer(new InetSocketAddress(12345),
                        acceptListener, OptionMap.EMPTY);
        // 开始接受连接
        server.resumeAccepts();
        System.out.println("Listening on " + server.getLocalAddress());
    }
}

 
客户端:

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;

import org.xnio.IoFuture;
import org.xnio.IoUtils;
import org.xnio.OptionMap;
import org.xnio.Xnio;
import org.xnio.XnioWorker;
import org.xnio.channels.Channels;
import org.xnio.channels.ConnectedStreamChannel;

public final class SimpleHelloWorldBlockingClient {

    public static void main(String[] args) throws Exception {
        final Charset charset = Charset.forName("utf-8");
        //创建Xnio实例,并构造XnioWorker
        final Xnio xnio = Xnio.getInstance();
        final XnioWorker worker = xnio.createWorker(OptionMap.EMPTY);

        try {
            //连接服务器,本地12345端口,注意返回值是IoFuture类型,并不阻塞,返回后可以做些别的事情
            final IoFuture<ConnectedStreamChannel> futureConnection = worker.connectStream(
                new InetSocketAddress("localhost", 12345), null, OptionMap.EMPTY);
            final ConnectedStreamChannel channel = futureConnection.get(); // get是阻塞调用
            try {
                // 发送消息
                Channels.writeBlocking(channel, ByteBuffer.wrap("Hello world!\n".getBytes(charset)));
                // 保证全部送出
                Channels.flushBlocking(channel);
                // 发送EOF
                channel.shutdownWrites();
                System.out.println("Sent greeting string! The response is...");
                ByteBuffer recvBuf = ByteBuffer.allocate(128);
                // 接收消息
                while (Channels.readBlocking(channel, recvBuf) != -1) {
                    recvBuf.flip();
                    final CharBuffer chars = charset.decode(recvBuf);
                    System.out.print(chars);
                    recvBuf.clear();
                }
            } finally {
                IoUtils.safeClose(channel);
            }
        } finally {
            worker.shutdown();
        }
    }
}

 
[1]: JavaNIO http://www.amazon.com/Java-Nio-Ron-Hitchens/dp/0596002882
[2]: Pro Java 7 NIO.2 http://www.amazon.com/Pro-Java-NIO-2-Anghel-Leonard/dp/1430240113

0
0
分享到:
评论

相关推荐

    undertow-2.2.18.Final.zip

    4. **集成XNIO**:XNIO是 Undertow 使用的一个底层I/O库,它提供了一种跨平台的方式来处理非阻塞I/O操作,支持多种I/O模型如NIO、 APR(Apache Portable Runtime)等。 5. **WebSocket支持**:Undertow提供了全面的...

    undertow-examples-1.0.0.Alpha2.zip

    2. **非阻塞I/O**:Undertow基于XNIO,利用Java NIO提供非阻塞I/O,提高服务器性能,尤其在处理大量并发连接时。 3. **可扩展性**:Undertow允许用户自定义处理器链,以满足特定的业务需求。 4. **WebSocket支持**:...

    藏经阁-中国银联的开源应用之路.pdf

    * JBoss Modules、JBoss MSC、JBoss logging、JBoss Web、JBoss EJB、Weld、HornetQ、Undertow、Arquillian、Ironjacamar (JCA)、JBoss XNIO、JBoss Remoting、Resteasy、Infinispan、JBoss Forge等组件。...

    【KUKA 机器人资料】:激光跟踪焊接机器人系统技术方案.pdf

    KUKA机器人相关资料

    基于Matlab的模拟退火算法在旅行商问题(TSP)优化中的应用及其实现

    内容概要:本文详细介绍了利用Matlab实现模拟退火算法来优化旅行商问题(TSP)。首先阐述了TSP的基本概念及其在路径规划、物流配送等领域的重要性和挑战。接着深入讲解了模拟退火算法的工作原理,包括高温状态下随机探索、逐步降温过程中选择较优解或以一定概率接受较差解的过程。随后展示了具体的Matlab代码实现步骤,涵盖城市坐标的定义、路径长度的计算方法、模拟退火主循环的设计等方面。并通过多个实例演示了不同参数配置下的优化效果,强调了参数调优的重要性。最后讨论了该算法的实际应用场景,如物流配送路线优化,并提供了实用技巧和注意事项。 适合人群:对路径规划、物流配送优化感兴趣的科研人员、工程师及高校学生。 使用场景及目标:适用于需要解决复杂路径规划问题的场合,特别是涉及多个节点间最优路径选择的情况。通过本算法可以有效地减少路径长度,提高配送效率,降低成本。 其他说明:文中不仅给出了完整的Matlab代码,还包括了一些优化建议和技术细节,帮助读者更好地理解和应用这一算法。此外,还提到了一些常见的陷阱和解决方案,有助于初学者避开常见错误。

    基于STM32的永磁同步电机Simulink代码生成与57次谐波抑制的霍尔FOC控制

    内容概要:本文详细介绍了如何利用Simulink进行自动代码生成,在STM32平台上实现带57次谐波抑制功能的霍尔场定向控制(FOC)。首先,文章讲解了所需的软件环境准备,包括MATLAB/Simulink及其硬件支持包的安装。接着,阐述了构建永磁同步电机(PMSM)霍尔FOC控制模型的具体步骤,涵盖电机模型、坐标变换模块(如Clark和Park变换)、PI调节器、SVPWM模块以及用于抑制特定谐波的陷波器的设计。随后,描述了硬件目标配置、代码生成过程中的注意事项,以及生成后的C代码结构。此外,还讨论了霍尔传感器的位置估算、谐波补偿器的实现细节、ADC配置技巧、PWM死区时间和换相逻辑的优化。最后,分享了一些实用的工程集成经验,并推荐了几篇有助于深入了解相关技术和优化控制效果的研究论文。 适合人群:从事电机控制系统开发的技术人员,尤其是那些希望掌握基于Simulink的自动代码生成技术,以提高开发效率和控制精度的专业人士。 使用场景及目标:适用于需要精确控制永磁同步电机的应用场合,特别是在面对高次谐波干扰导致的电流波形失真问题时。通过采用文中提供的解决方案,可以显著改善系统的稳定性和性能,降低噪声水平,提升用户体验。 其他说明:文中不仅提供了详细的理论解释和技术指导,还包括了许多实践经验教训,如霍尔传感器处理、谐波抑制策略的选择、代码生成配置等方面的实际案例。这对于初学者来说是非常宝贵的参考资料。

    基于S7-200 PLC和组态王的机械手搬运控制系统设计与调试

    内容概要:本文详细介绍了基于西门子S7-200 PLC和组态王的机械手搬运控制系统的实现方案。首先,文章展示了梯形图程序的关键逻辑,如急停连锁保护、水平移动互锁以及定时器的应用。接着,详细解释了IO分配的具体配置,包括数字输入、数字输出和模拟量接口的功能划分。此外,还讨论了接线图的设计注意事项,强调了电磁阀供电和继电器隔离的重要性。组态王的画面设计部分涵盖了三层画面结构(总览页、参数页、调试页)及其动画脚本的编写。最后,分享了调试过程中遇到的问题及解决方案,如传感器抖动、输出互锁设计等。 适合人群:从事自动化控制领域的工程师和技术人员,尤其是对PLC编程和组态软件有一定基础的读者。 使用场景及目标:适用于自动化生产线中机械手搬运控制系统的开发与调试。目标是帮助读者掌握从硬件接线到软件逻辑的完整实现过程,提高系统的稳定性和可靠性。 其他说明:文中提供了大量实践经验,包括常见的错误和解决方案,有助于读者在实际工作中少走弯路。

    西门子1200PLC污水处理项目:PLC程序、通讯配置与HMI设计详解

    内容概要:本文详细介绍了基于西门子1200PLC的污水处理项目,涵盖了PLC程序结构、通信配置、HMI设计以及CAD原理图等多个方面。PLC程序采用梯形图和SCL语言相结合的方式,实现了复杂的控制逻辑,如水位控制、曝气量模糊控制等。通讯配置采用了Modbus TCP和Profinet双协议,确保了设备间高效稳定的通信。HMI设计则注重用户体验,提供了详细的报警记录和趋势图展示。此外,CAD图纸详尽标注了设备位号,便于后期维护。操作说明书中包含了应急操作流程和定期维护建议,确保系统的长期稳定运行。 适合人群:从事工业自动化领域的工程师和技术人员,尤其是对PLC编程、HMI设计和通信配置感兴趣的从业者。 使用场景及目标:适用于污水处理厂及其他类似工业控制系统的设计、实施和维护。目标是帮助工程师掌握完整的项目开发流程,提高系统的可靠性和效率。 其他说明:文中提供的具体代码片段和设计思路对于理解和解决实际问题非常有价值,建议读者结合实际项目进行深入学习和实践。

    5电平三相MMC的VSG控制与MATLAB-Simulink仿真:调频调压效果验证

    内容概要:本文详细介绍了基于5电平三相模块化多电平变流器(MMC)的虚拟同步发电机(VSG)控制系统的构建与仿真。首先,文章描述了MMC的基本结构和参数设置,包括子模块电容电压均衡策略和载波移相策略。接着,深入探讨了VSG控制算法的设计,特别是有功-频率和无功-电压下垂控制的具体实现方法。文中还展示了通过MATLAB-Simulink进行仿真的具体步骤,包括设置理想的直流电源和可编程三相源来模拟电网扰动。仿真结果显示,VSG控制系统能够在面对频率和电压扰动时迅速恢复稳定,表现出良好的调频调压性能。 适合人群:从事电力电子、电力系统自动化及相关领域的研究人员和技术人员。 使用场景及目标:适用于研究和开发新型电力电子设备,特别是在新能源接入电网时提高系统的稳定性。目标是通过仿真验证VSG控制的有效性,为实际应用提供理论支持和技术指导。 其他说明:文章提供了详细的代码片段和仿真配置,帮助读者更好地理解和重现实验结果。此外,还提到了一些常见的调试技巧和注意事项,如选择合适的仿真步长和参数配对调整。

    工业自动化中基于PLC1200的SCL与梯形图混编立体库及码垛系统的通信与控制

    内容概要:本文详细介绍了在一个复杂的工业自动化项目中,如何利用西门子S7-1200 PLC为核心,结合基恩士视觉相机、ABB机器人以及G120变频器等多种设备,构建了一个高效的立体库码垛系统。文中不仅探讨了不同设备之间的通信协议(如Modbus TCP和Profinet),还展示了SCL和梯形图混合编程的具体应用场景和技术细节。例如,通过SCL进行视觉坐标解析、机器人通信心跳维护等功能的实现,而梯形图则用于处理简单的状态切换和安全回路。此外,作者分享了许多实际调试过程中遇到的问题及其解决方案,强调了良好的注释习惯对于提高代码可维护性的关键作用。 适用人群:从事工业自动化领域的工程师和技术人员,尤其是对PLC编程、机器人控制及多种通信协议感兴趣的从业者。 使用场景及目标:适用于需要整合多种工业设备并确保它们能够稳定协作的工作环境。主要目标是在保证系统高精度的同时降低故障率,从而提升生产效率。 其他说明:文中提到的一些具体技术和方法可以作为类似项目的参考指南,帮助开发者更好地理解和应对复杂的工业控制系统挑战。

    【KUKA 机器人资料】:KUKA机器人_Interbus_输入输出端口配置说明书.pdf

    KUKA机器人相关资料

    java脱敏工具类,敏感数据脱敏

    java脱敏工具类

    基于自抗扰控制的表贴式永磁同步电机双环控制系统设计与实现

    内容概要:本文详细介绍了基于自抗扰控制(ADRC)的表贴式永磁同步电机(SPMSM)双环控制系统的建模与实现方法。该系统采用速度环一阶ADRC控制和电流环PI控制相结合的方式,旨在提高电机在复杂工况下的稳定性和响应速度。文章首先解释了选择ADRC的原因及其优势,接着展示了ADRC和PI控制器的具体实现代码,并讨论了在Matlab/Simulink环境中搭建模型的方法和注意事项。通过对不同工况下的仿真测试,验证了该控制策略的有效性,特别是在负载突变情况下的优越表现。 适合人群:从事电机控制、自动化控制及相关领域的研究人员和技术人员,尤其是对自抗扰控制感兴趣的工程师。 使用场景及目标:适用于需要高精度、高响应速度的工业伺服系统和其他高性能电机应用场景。目标是提升电机在复杂环境下的稳定性和抗扰能力,减少转速波动和恢复时间。 其他说明:文中提供了详细的代码示例和调试技巧,帮助读者更好地理解和实施该控制策略。同时,强调了在实际应用中需要注意的问题,如参数调整、输出限幅等。

    java设计模式之责任链的demo

    java设计模式之责任链的使用demo

    电力电子领域中两相交错并联Buck/Boost变换器的三种控制方式及其仿真分析

    内容概要:本文详细介绍了两相交错并联Buck/Boost变换器的硬件结构和三种控制方式(开环、电压单环、双环)的实现方法及仿真结果。文中首先描述了该变换器的硬件结构特点,即四个MOS管组成的H桥结构,两相电感交错180度工作,从而有效减少电流纹波。接着,针对每种控制方式,具体讲解了其配置步骤、关键参数设置以及仿真过程中需要注意的问题。例如,在开环模式下,通过固定PWM占空比来观察原始波形;电压单环则引入PI控制器进行电压反馈调节;双环控制进一步增加了电流内环,实现了更为精确的电流控制。此外,文章还探讨了单向结构的特点,并提供了仿真技巧和避坑指南。 适合人群:从事电力电子研究的技术人员、高校相关专业师生。 使用场景及目标:适用于希望深入了解两相交错并联Buck/Boost变换器的工作原理和技术细节的研究者,旨在帮助他们掌握不同控制方式的设计思路和仿真方法。 其他说明:文中不仅提供了详细的理论解释,还有丰富的实例代码片段,便于读者理解和实践。同时,作者分享了许多宝贵的实践经验,有助于避免常见的仿真错误。

    第十六届蓝桥杯大赛软件赛省赛第二场 C/C++ 大学 A 组

    第二场c++A组

    数控磨床编程.ppt

    数控磨床编程.ppt

    COMSOL数值模拟:N2和CO2混合气体在THM热流固三场耦合下增强瓦斯抽采的技术研究与应用

    内容概要:本文详细介绍了利用COMSOL软件进行N2和CO2混合气体在热-流-固三场耦合作用下增强煤层气抽采的数值模拟。首先,通过设定煤岩材料参数,如热导率、杨氏模量等,构建了煤岩物理模型。接着,引入达西定律和Maxwell-Stefan扩散方程,建立了混合气体运移方程,考虑了气体膨胀系数和吸附特性。在应力场求解方面,采用自适应步长和阻尼系数调整,确保模型稳定。同时,探讨了温度场与气体运移的耦合机制,特别是在低温条件下CO2注入对煤体裂隙扩展的影响。最后,通过粒子追踪和流线图展示了气体运移路径和抽采效率的变化。 适合人群:从事煤层气开采、数值模拟以及相关领域的科研人员和技术工程师。 使用场景及目标:适用于需要优化煤层气抽采工艺的研究机构和企业,旨在通过数值模拟提高抽采效率并减少环境影响。 其他说明:文中提供了详细的MATLAB和COMSOL代码片段,帮助读者理解和复现模型。此外,强调了模型参数选择和求解器配置的重要性,分享了作者的实际经验和常见问题解决方法。

    计算给定G、相位裕度、交叉频率和安全裕度要求的引线补偿器

    基于Bode的引线补偿器设计 计算给定G、相位裕度、交叉频率和安全裕度要求的引线补偿器。 计算给定电厂G、PM和Wc要求的铅补偿器,并运行ControlSystemDesigner进行验证。

    【KUKA 机器人TCP测量】:mp2_tool_fixed_en.ppt

    KUKA机器人相关文档

Global site tag (gtag.js) - Google Analytics