前面写过一个博文高:高吞吐高并发Java NIO服务的架构,http://maoyidao.iteye.com/admin/blogs/1149015
。这个架构是和MINA一致的,或者可以说MINA是基于同样的思路构架的。想阅读MINA源代码的朋友可用参考这个架构来研究MINAsource code。但是考虑到在已经有比较可靠的开源实现的情况下,现在朋友们很少会自己去实现一个NIO模块。就想到把它做成一个系列,第一篇主要是讲解一种业内普遍采用的NIO架构,及其架构上的两个要点,即:1. 基于提高的性能的线程池设计;2. 基于网络通讯量的通讯完整性校验。这一篇来讲讲在基于MINA搭建高性能NIO服务时如何具体实施以上两点。有部分代码选自openfire(openfire是一个实现XMPP协议的开源项目,其底层通讯模块为MINA)
MINA的使用相信朋友们已经很熟悉了。把MINA应用于高性能分布式应用的时候,表现出来比较突出的问题还是协议解析的问题和容量规划的问题。
1,协议解析的正确性问题
MINA已经给我们提供了非常方便的接口来实现NIO。coding的重点可能会是在协议解析上,即实现ProtocolCodecFactory接口中规定的encoder、decoder。当收到一个NIO包并从中解析出协议消息体并不困难,且容易测试。但高负荷的通信使得协议数据不能在TCP的一个packet中完全到达,即所谓断包。我经历过某团队到性能测试时才发现这一问题,花了很多时间在海量的tcpdump包中寻找问题,花费了很多时间,而且非常痛苦。如能从一开始了解TCP通讯和MINA架构,则轻松很多。
TCP socket发送大数据时会将其拆分成一个个小数据包发出,由于接收窗口和拥塞窗口的限制,这些数据包可能不能一次发送出去。这时后继的数据包等待在发送缓冲区,等待窗口的扩大。比如在Linux系统,通过修改/proc/sys/net/core/rmem_max改变最大的TCP数据接收缓冲,通过修改/proc/sys/net/core/wmem_max改变最大的TCP数据发送缓冲。另外,即使是比较小的数据包,为了达到最好的性能,我们总是尽可能多的把小数据包拼接起来填充每个报文,以减少发送数据包的个数。比如MINA默认的参数会把John Nagle算法设为false。如下代码,socketSessionConfig.isTcpNoDelay()返回false。
SocketAcceptorConfig socketAcceptorConfig = socketAcceptor.getDefaultConfig();
SocketSessionConfig socketSessionConfig = socketAcceptorConfig.getSessionConfig();
socketSessionConfig.setTcpNoDelay(JiveGlobals.getBooleanProperty("xmpp.socket.tcp-nodelay", scocketSessionConfig.isTcpNoDelay()));
这些TCP传输的特性导致NIO收到的packet对于应用协议来说可能是不完整的。因此需要在协议解码类中实现拼接消息的逻辑。
解决办法也很简单,即将这次没有解析完的byte再放到下次数据包头重新解析。openfire的协议解析实现类XMLLightweightParser中startLastMsg记录的就是上次完整消息的解析后ByteBuffer的位置。而StringBuilder类型的buffer则是用来存储上一次收到的数据包中没有解析完的数据。XMPP是XML流协议,解析起来比较复杂,朋友们不看也罢。这些都是代码实现的细节,等用到了再做测试不迟。但抛去其解析协议的复杂逻辑,这不起眼的buffer和startLastMsg则是具有通用性的逻辑。有些协议比如RTSP协议会规定在消息头中传递数据包的长度,其逻辑实现就简洁很多,但也需要实现类似的buffer和startLastMsg的逻辑。
2,容量规划问题。
在本系列第一篇中已经阐述过NIO的瓶颈在于后端架构的实现。当时给出了一个线程计算公式来估计所需的线程。但我在实际情况中发现工程师们往往不愿意做这样的测试,而喜欢简单的将线程池设为CPU核数+1。比如openfire在MINA中设置处理线程池:
// Customize Executor that will be used by processors to process incoming stanzas
ExecutorThreadModel threadModel = ExecutorThreadModel.getInstance("CommServer");
int eventThreads = getCPUCoreNumber();
ThreadPoolExecutor eventExecutor = (ThreadPoolExecutor) threadModel.getExecutor();
eventExecutor.setCorePoolSize(eventThreads + 1);
eventExecutor.setMaximumPoolSize(eventThreads + 1);
eventExecutor.setKeepAliveTime(60, TimeUnit.SECONDS);
commSocketAcceptor.getDefaultConfig().setThreadModel(threadModel);
这里我还是建议朋友们在线程池大小上多花些时间,这对提高单台NIO服务效率是很有好处的。特别是在特定场景下更是如此,
1,每个操作都需要调用数据库。数据库操作是重IO,相对来讲CPU比较闲,这个时候就可以多设置些线程。这里还可以有一个优化,就是在db上增加一个写缓冲。对db采用batch write,还可以合并update操作,速度可以提高很多。
2,每个操作都要访问互锁的资源。这就是说实际上线程之间要争夺一个mutex锁,这样的逻辑最好是可以避免的。或者缩小锁的影响范围。如果实在避免不了,则要考虑是否有必要减少线程数量,因为既然都要抢一个mutex,实际并行的也是一个线程。
当然即使线程池大小适度,也不能完全解决容量问题。比如我这里有一个实际模型
这里需要调用远程资源,可以认为是memcached,也可以是其他socket资源。最后还要通过socket发送到其他模块。这时则要求即使这些资源访问全部挂掉,也不能让线程等待很久,否则前端的queue就会堆积溢出。因此resource的调用必须有很快的超时返回,而socket则必须有心跳和很快的超时返回。在这个项目中,经过容量规划我们认为后端socket的超时应该为3秒。这个时间相对是比较短的,但是为了保证整个系统的健康,是必须的。
在发送端设置发送缓冲的意义我想引用Timyang的一篇博客中所说的,
1.
Socket占满后写socket会block, 或者设置了TCP_NODELAY则不管SOCKET buffer是否占满都会block
2. 如果应用写入操作没有队列概念则应用程序会出现异常,所有操作会阻塞。
3. 强壮的应用会对发送数据做应用层的buffer,像Connection Manager/Openfire之间是使用发送 Thread Pool,在对方不可到达的时段内,内存占用会急剧上升。
4. 如果网络层恢复正常,首先是socket buffer中的数据会被发送,然后应用层堆积的数据也随后发送。
5. 如果网络层长时间不可用,有2种方法可以判断,通过达到SO_SNDTIMEO
Socket返回错误检测,应用层如果需要更早知道错误,可以调低SO_SNDTIMEO。另外一种检测方法是应用检测发送队列达到上限临界值来做进一步
处理。避免服务器内存溢出造成崩溃。实际环境中需要结合这2种方式一起考虑。
对于第五点,我提供的经验是,对LinkedBlockingQueue size做一个定时健康监控。如果前端queue内容发生堆积,说明系统中有锁定的情况;如果后端queue堆积,说明网络状况不好。
总结一下,设计和开发广泛适用于分布式的通讯模块,需处理
1,收不到,
2,收到处理不了,
3,和发不出去
这三种情况下的健壮性。
- 大小: 25.7 KB
分享到:
相关推荐
MINA作为一款优秀的客户端/服务器架构下的Java服务器框架,凭借其强大的功能和灵活性,在开发高性能网络应用程序方面表现突出。本文将深入探讨MINA的核心概念、优势以及如何利用它构建高效稳定的网络应用。 #### ...
在当今的网络编程中,NIO(非阻塞IO)是构建高性能网络应用的一种常见技术。Java NIO提供了一种不同于传统BIO(阻塞IO)的IO处理方式。传统BIO模式下,每个客户端连接通常需要一个单独的线程去处理,这样在面对大量...
本文介绍了一种基于高性能Java NIO的MINA框架,用以构建高性能的即时通讯系统服务器。MINA框架(Multipurpose Infrastructure for Network Applications)是基于Java NIO设计的网络应用程序框架,其特点是采用松耦合...
### 高性能网络架构Mina框架简介 #### 一、Mina框架概述 Mina(Multithreaded Internet Network Application)框架是由Apache软件基金会提供的一个高性能、可伸缩的网络编程框架,它主要应用于Java NIO环境下的...
Jetty、Tomcat和Mina都是Java领域中著名的Web服务器和应用服务器,它们在NIO架构上有着相似的设计模式。本文将从这三个框架中提炼出NIO构架网络服务器的经典模式,并逐一解析它们的核心机制。 首先,Jetty的NIO实现...
MINA(Java Mini Asynchronous Network Application Framework)是一个高性能、异步事件驱动的网络应用程序框架,主要用于简化开发服务器和客户端的网络应用,特别是TCP和UDP协议的应用。MINA为开发者提供了高度抽象...
Apache的Mina(Multipurpose Infrastructure Networked Applications)是一个网络应用框架,可以帮助用户开发高性能和高扩展性的网络应用程序;它提供了一个抽象的、事件驱动的异步API,使Java NIO在各种传输协议...
Apache的Mina(Multipurpose Infrastructure Networked Applications)是一个网络应用框架,可以帮助用户开发高性能和高扩展性的网络应用程序;它提供了一个抽象的、事件驱动的异步API,使Java NIO在各种传输协议...
Apache Mina是一个开源的网络通信应用框架,主要应用于Java平台,它为高性能、高可用性的网络应用程序提供了基础架构。在本文中,我们将深入探讨Mina的高级使用,特别是在文件图片传送、文件发送、XML和JSON报文处理...
根据提供的信息,我们可以详细解析与"Mina2中文文档"相关的各个关键知识点: ### Mina2中文文档概述 #### Introduction ...对于希望利用Mina开发高性能网络应用程序的开发者来说,这是一份非常有价值的参考资料。
Netty 的高性能原理和框架架构是基于其对 JDK 原生 NIO 的改进、设计优雅、高性能和安全等方面的体现。 知识点: 1. Netty 的高性能原理是基于其对 JDK 原生 NIO 的改进的。 2. Netty 的设计优雅体现在其统一 API ...
Mina框架的核心设计理念在于提供一个强大的基础架构,让开发者能够轻松创建高性能和高可用性的网络应用程序。这一框架已被广泛应用于多个领域,如Red5项目中的RTMP协议实现、SubEtha.SMTP项目中的SMTP协议、Apache ...
MINA (Java IO Network Application Framework) 是一个由Apache软件基金会开发的开源网络通信框架,主要应用于构建高性能、高可用性的网络服务器。这个压缩包包含了MINA API文档、自学手册以及开发指南,对于学习和...
* MINA 是基于 NIO 的一个网络应用框架,提供了一个灵活的架构和高性能的 I/O 操作。 Chapter 2 - 基础应用架构 本章节介绍了 MINA 的基础应用架构,包括服务端架构和客户端架构。 * 服务端架构:MINA 提供了一个...
Apache MINA(Multipurpose Infrastructure for Network Applications)是一个高性能的Java网络框架,主要设计用于构建网络应用,如服务器和客户端通信。MINA的核心理念是提供一个与传输协议无关的抽象层,使得...
本文将深入解析Mina的核心概念、架构设计以及其在实际项目中的应用示例。 ### Mina核心概念与架构设计 Mina(Multipurpose Infrastructure Networked Applications)是基于Java NIO构建的高性能非阻塞网络编程框架...
Mina,全称为Apache MINA (Multipurpose Infrastructure for Network Applications),是一个由Apache软件基金会开发的网络应用框架,主要用于简化高效、高性能的网络编程。Mina提供了一种与协议无关的抽象层,允许...
MINA是Apache软件基金会的一个项目,它提供了一个高度可扩展和高性能的网络应用开发框架,适用于多种传输协议,如TCP/IP和UDP/IP,以及SSL/TLS加密。在大数据传输场景中,MINA因其非阻塞I/O模型而被广泛采用,能够...
Apache MINA(Multipurpose Infrastructure for Network Applications)是一个Java框架,用于构建高性能、高可用性的网络应用程序。MINA 提供了一套高级的网络编程抽象层,简化了开发过程,特别是对于那些处理TCP/IP...