大并发量 socket 通信的解决方案

简介: 大并发量 socket 通信的解决方案http://www.bieryun.com/1537.html 笔者之前的工作主要是做java的web端开发,后因工作原因参与了一个国家级的大项目,主要负责其中底层通讯的前置机模块。

大并发量 socket 通信的解决方案

笔者之前的工作主要是做java的web端开发,后因工作原因参与了一个国家级的大项目,主要负责其中底层通讯的前置机模块。几经波折,将该系统完成后,结果在第一轮的测试中就惨败退回。其根本原因就在于原设计文档的要求单“通信机”与“终端”(注一)之间的并发量要达到2W以上的连接通信,而实际运行并发量只能达到2600个相差了近十倍左右。经过代码调优、扩展JVM内存等等手段,但因基础数据相差过大,所取得的优化效果十分有限。后考虑在根本着手,只有更改整个系统的通信接口,才有可能达到设计文档上的要求。某天在某个技术QQ群里一次讨论中,有网友向我推荐了一个框架,这就是本文要介绍的主角-MINA。

注一:前置机分成了三个部分,其设计的结构图如下所示:

What is MINA

Apache MINA(Multipurpose Infrastructure for Network Applications) 是 Apache 组织一个较新的项目,它为开发高性能和高可用性的网络应用程序提供了非常便利的框架。当前发行的 MINA 版本支持基于 Java NIO 技术的 TCP/UDP 应用程序开发、串口通讯程序(只在最新的预览版中提供),MINA 所支持的功能也在进一步的扩展中

使用案例:

目前正在使用 MINA 的软件包括有:Apache Directory Project、AMQP(Advanced Message Queuing Protocol)、RED5 Server(Macromedia Flash Media RTMP)、ObjectRADIUS、Openfire 等等。

Apache直属MINA的子项目:

FTPServer,AsyncWeb,SSHD

其实在有人推荐了MINA之后,本人就上网google了一把,搜索一番下来之后才发现自己的眼界过于狭隘了,原来有相同功能的开源框架还真不少,看来以后得继续多泡论坛和QQ了(有正当理由上班泡坛子,聊QQ了,偷笑一个^_^。。。。。。)

同类框架:

Netty2:具有很好的构架,并且该框架非常有名,使用该框架的项目不少,这意味着开发团队持续更新的动力非常大。同时Netty2的文档非常齐全,并且支持JMX。Netty2的缺点就是实现代码的质量不是非常好,最大的缺点就是只支持普通的TCP。

Cindy:起源于Netty2之后,借鉴了Netty2中MessageRecognizer类的设计,在当前的版本中已经全面支持普通TCP/Secure TCP/UDP单播/UDP多播/Pipe,可以使用同一个模型进行同步/异步IO处理。Cindy目前缺点是文档相对较少以及应用的项目比较少。

Grizzl:的设计与一般的nio框架相比是比较不同的,主要不同点在于读和写都是采用blocking方式,并且使用临时selector;线程模型高度可配置。性能据说比MINA还高,但是学习曲线很高。QuickServer: http://www.quickserver.org/

Xscocket:是一个轻量级的解决方案,核心思想是屏蔽,简化nio方式的的开发,并不需要过多的学习。

对于这些框架的基本使用和基础架构,本人都经过了一番研究,发现这些框架要么重者过重,要么轻者过轻。鉴于项目的规模及时间的紧迫,再三比较之下,本人还是选择了学习曲线低,性能优异的MINA. 至于这些优点是如何体现出来的,本文以下内容将继续为您解读。

MINA快速入门

闲话少说,借用大师级的写书经验,咱们先来一个hello world。当然在这前我们还是得先做一些准备的。

预备知识:

JAVA NIO

多线程

Socket

以上知识对本文的阅读理解有一定帮助,但并非一定必需。

资源下载:

MINA2.0:暂时分为1.x和2.x两个主版本,本文只涉及2.X的版本,至于为什么只讲2.X而不讲1.X,比较冠冕堂皇的回答是----因为有一位伟人曾经说过:要以发展的眼光看世界。。。。。。而真实原因嘛。。。。。。咳咳。。。。大家都知道的。。我就不便多言了。下载地址:http://mina.apache.org/downloads.html。项目中使用的是2.03,截止本文发稿为止,最新版本为:2.04

log4j:因为其中缺少log4j的包,所以做试验的朋友还需要去下一个log4j的包。

开发工具:eclipse

Jdk:1.6x

监视测试工具:Oracle JRockit Mission Control 4.0.1 强烈推荐,简称JRMC,开发过程中,用它解决了很多性能瓶颈的问题,具体使用方法,因为篇幅所限在此不做详述,请自行查询相关文档。

Hello world的关键关键代码:

Server端的Main函数:

 

IoAcceptor acceptor = new NioSocketAcceptor();// 建立监控器

//acceptor.getSessionConfig().setReadBufferSize(2048);

//acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);

acceptor.getFilterChain().addLast("codec",

New ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8"),

LineDelimiter.WINDOWS.getValue(), LineDelimiter.WINDOWS.getValue())));//加载解/编码工厂

acceptor.setHandler(newStandBaowenHandler()); //设置处理类,其主要内容见下。

acceptor.bind(new InetSocketAddress(9998));//绑定的监听端口,可多次绑定,也可同时绑定多个。

StandBaowenHandler的关键代码

public void messageReceived(IoSession session, Object message) throws Exception {           session.getService().getManagedSessions();

String baowenSrc = message.toString();// 原始报文

System.out.println(baowenSrc);

}

鉴于篇幅关系,类没有写全,更具体的内容,请大家参考mina压缩包里自带的demo。但实际上服务端需要手写的部分确实只有以上二块内容,服务端就算是写好了,由次可以看出mina的入门之快。现在我们来测试。打开cmd(这个对于同仁们来说,就不用详述了吧。。。- -!!),输入telnet 127.0.0.1 9998(这里的9998要与上面代码绑定的端口一致)随便输入一些字符,敲下回车后就会在控制台显示您所输入的信息了。

做得这一步的童鞋可以说对MINA的使用可以说已经初步入门了,是不是觉得太简单了?这也太假了吧,这就算入门了?呵呵。。。个人认为这就是MINA的强悍之一,简单的几行代码就搞定了一个服务端,要入门简直是太简单了。但任何事物都有其二面性,真要把一个东西学好,用好,还要不出错,我们需要了解的东西就太多太多了。下面我们就从MINA框架的底层说起,因为在实际应用当中,要解决一些碰到的难点及问题,对框架的整个运作体系必须要有个全面深入的了解。只有这样才能在碰到问题时,有目的,有针对性的去找到症结所在。

MINA深度了解

Mina的应用层

图片1

图1

一个设计成熟的开源框架,总是会仅可能的减少侵入性,并在整个项目中找到合适的位置,而不应对整个项目的构架设计产生过多的影响,图1就是MINA的应用层示意图。从图中和上节的DEMO中我们可以看到,MINA很好的把业务代码和底层的通信隔离了开来,我们要做的仅仅是建立好监听,然后写上我们需要实现的业务逻辑就OK了。

MINA的内部流程

图片2

图2

(1) IoService:这个接口在一个线程上负责套接字的建立,拥有自己的Selector,监听是否有连接被建立。

(2) IoProcessor:这个接口在另一个线程上负责检查是否有数据在通道上读写,也就是说它也拥有自己的Selector,这是与我们使用JAVA NIO 编码时的一个不同之处,

通常在JAVA NIO 编码中,我们都是使用一个Selector,也就是不区分IoService与IoProcessor 两个功能接口。另外,IoProcessor也是MINA框架的核心组件之一.在MINA框架启动时,会用一个线程池来专门生成线程,来负责调用注册在IoService 上的过滤器,并在过滤器链之后调用IoHandler。在默认情况IoProcessor 会用N+1个线程来轮流询问监视的端口是否有数据传送,其中n为cpu的内核个数。按一般的多线程设计概念来说,IoProcessor的线程数是越多越好,但实际上并非如此,因为大家都知道,IO的操作是非常占用资源的,所以项目中的IoProcessor的线程数应该根据实际需要来定,而这个数字可以在生成IoAcceptor对象时进行设定。Eg IoAcceptor acceptor = newNioSocketAcceptor(N);

(3.) IoFilter:这个接口定义一组拦截器,这些拦截器可以包括日志输出、黑名单过滤(参见之前代码示例的注释部份),甚至是在过滤器链中利用AOP写上权限控制(笔者负责的部分没有涉及到这权限部分,但根据笔者对框架的理解要实现这一点应该问题不大,有需要的可以自行试验)。数据的编码(write 方向)与解码(read 方向)等功能,其中数据的encode 与decode是最为重要的、也是您在使用Mina 时最主要关注的地方(笔者曾经为了decode的解码编写,重写了不下十几种实现方式才找到准确无误适合项目的解码方法)。

(4.) IoHandler:这个接口负责编写业务逻辑,也就是接收、发送数据的地方。只本文的代码实例中,可以看到真正的业务代码只有一句:System.out.println(str);真实项目中当然不可能如此简单,但如果大家把业务处理写好,并写好业务接口,真正要用时,呆需要在此处替换即可,再次见证了MINA分层的彻底。

说了这么多,以上内容也只能让大家对MINA有个基础的了解,对于MINA框架优势的认识可能还不是很多,那么下面的内容,就此展开对比讨论,以便大家对MINA的适用场景及优点有个更全面的了解。

选择MINA的理由

传统socket编程

在传统I/O中,最简单实现高并发服务器的编程方式就是对每一个客户开启一个线程。但是这种方式有如下几个弊端:

客户端上限很大的情况下不能及时响应

服务器硬件资源受限,性能也会急剧下降

受制于操作系统的限制

但这种设计方式优点还是有的:

编码简单,实现容易

一定数量的连接性能比较好。

笔者的项目开发中,最开始就是采用的这种方式,写起来方便,控制起来也方便,但遗憾的是JVM最多只能开到2K多的线程,就会报- can not create new thread的错误。

(实现结构图见下:)

图片3

图3(一对一的结构图。)

改进的Socket编程

为了解决每一个线程一个连接的模型,笔者最开始想到用多个线程处理N个用户,这样既可以保证处理多个用户的同时,线程开销降到系统的临界点。

这样的方式与前一个模型优势在于同样的多线程,但线程数固定,充分运用系统的优势性能,又不存在多余的开销。但是缺点也是显而易见的:

轮询的消耗不可避免。

一但产生io阻塞,其阻塞的时间纯属浪费。

客户数量固定的时候没有前一模型响应快

编码更加复杂。

图片4

图4(一对多)

使用MINA框架的编程

为了解决上述的矛盾,最终的解决方案只能是异步的NIO,而随着笔者对JAVA  NIO的研究发现,要实现异步的NIO,并应用到实际项目中,必须对NIO有着比较深刻的了解和把握,笔者曾尝试着利用JAVA 原生 NIO接口写了一个DEMO(具体的使用方法,感兴趣的童鞋可以GOOGLE一把,你会发现用原生NIO写程序与使用MINA写程序对比起来是多么的痛苦。。。。 -_-!!),但由于笔者在这方面的底子过薄,试验结果不如人意,但要对NIO进行更为深入的学习,时间上面也不允许。直到MINA框架的映入眼帘,以上难题不再是问题。。。。以下是利用MINA的实现方式的一个简图。

图片5

图5

其中IoService接口会专门起一个线程来轮询是否有新的连接产生,一旦有连接产生则通知IoProcessor,而IoProcessor则起n+1个线程来检查连接是否有数据在上面读写(注二)。一旦有连接产生,并有数据读写,则通知decode或ENCODE,进行报文的解码或编码,将处理后的报文再交给业务类进行业务处理。其中IoProcessor是处理请求的分配,包括选择Selector,超时验证,状态记录等。总之这个类和IoService一起配合工作,封装了NIO底层的实现以及MINA框架内部的功能的支持.由于过于复杂,篇幅所限所以不作详细讲解.

结合实例,并根据以上的图文讲解,我们可以很轻易的总结出利用MINA编程的几个大致步骤:

创建一个实现了IoService接口的类

设置一个实现了IoFilter接口的过滤器(如果有需要的情况下)

设置一个IoHandler 接口实现的处理类,用于处理事件(必须)

对IoService绑定一个端口开始工作

关于MINA的大致运行流程及使用步骤,我们就暂时分析到这,具体更细节的关于一些核心类的使用方法及自定义编码器的方法,大家可以直接参考mina中所带的几个案例,写得非常详细,足够解决大家在项目中碰到的大部分问题,接下来要与大家交流的是使用MINA时非常有可能遇到的一些扩展知识。

注二:这一点请特别注意,因IoProcessor也是相当于轮询机制,这导致在报文过长时,或其它原因导致报文不能一次传输完毕的情况下,必须保存同一连接(在MINA中是以IoSession类生成的对象)下的上一次状态,这样才能截取到一个完成的报文,而这也是Decode(编码器)需要做的核心工作,新手往往就在这上面要跌跟斗。

扩展知识

这部分的内容要说起来跟MINA的使用关联不大,但实际情况是用上了MINA框架的项目基本上多多少少都会涉及到这一块,那就是多线程的编程。多线程的编程历来是JAVA编程中的重难点,很多新手碰到此类编程问题,往往都找不出原因所在,甚至一些有多年编程经验的程序员也会在这上面偶而犯下错,在这里笔者也没有能力通过很短的篇来解说多线程,那么就向大家介绍几个类及一些小常识吧,希望能给大家带来帮助。

相关文章
|
6月前
|
缓存 监控 Java
Java Socket编程最佳实践:优化客户端-服务器通信性能
【6月更文挑战第21天】Java Socket编程优化涉及识别性能瓶颈,如网络延迟和CPU计算。使用非阻塞I/O(NIO)和多路复用技术提升并发处理能力,减少线程上下文切换。缓存利用可减少I/O操作,异步I/O(AIO)进一步提高效率。持续监控系统性能是关键。通过实践这些策略,开发者能构建高效稳定的通信系统。
178 1
|
4月前
|
Python
python socket 简单通信
python socket 简单通信
46 1
|
4月前
|
网络协议 安全 网络安全
网络编程:基于socket的TCP/IP通信。
网络编程:基于socket的TCP/IP通信。
259 0
|
6月前
|
IDE Java 开发工具
从零开始学Java Socket编程:客户端与服务器通信实战
【6月更文挑战第21天】Java Socket编程教程带你从零开始构建简单的客户端-服务器通信。安装JDK后,在命令行分别运行`SimpleServer`和`SimpleClient`。服务器监听端口,接收并回显客户端消息;客户端连接服务器,发送“Hello, Server!”并显示服务器响应。这是网络通信基础,为更复杂的网络应用打下基础。开始你的Socket编程之旅吧!
99 3
|
6月前
|
Java 数据挖掘 开发者
Java网络编程进阶:Socket通信的高级特性与应用
【6月更文挑战第21天】Java Socket通信是分布式应用的基础,涉及高级特性如多路复用(Selector)和零拷贝,提升效率与响应速度。结合NIO和AIO,适用于高并发场景如游戏服务器和实时数据分析。示例展示了基于NIO的多路复用服务器实现。随着技术发展,WebSockets、HTTP/2、QUIC等新协议正变革网络通信,掌握Socket高级特性为应对未来挑战准备。
54 1
|
2月前
|
网络协议 Linux 应用服务中间件
Socket通信之网络协议基本原理
【10月更文挑战第10天】网络协议定义了机器间通信的标准格式,确保信息准确无损地传输。主要分为两种模型:OSI七层模型与TCP/IP模型。
|
6月前
|
Java
Java Socket编程与多线程:提升客户端-服务器通信的并发性能
【6月更文挑战第21天】Java网络编程中,Socket结合多线程提升并发性能,服务器对每个客户端连接启动新线程处理,如示例所示,实现每个客户端的独立操作。多线程利用多核处理器能力,避免串行等待,提升响应速度。防止死锁需减少共享资源,统一锁定顺序,使用超时和重试策略。使用synchronized、ReentrantLock等维持数据一致性。多线程带来性能提升的同时,也伴随复杂性和挑战。
107 0
|
6月前
|
安全 Java 网络安全
Java Socket编程教程:构建安全可靠的客户端-服务器通信
【6月更文挑战第21天】构建安全的Java Socket通信涉及SSL/TLS加密、异常处理和重连策略。示例中,`SecureServer`使用SSLServerSocketFactory创建加密连接,而`ReliableClient`展示异常捕获与自动重连。理解安全意识,如防数据截获和中间人攻击,是首要步骤。通过良好的编程实践,确保网络应用在复杂环境中稳定且安全。
107 0
|
3月前
|
网络协议 Linux 应用服务中间件
Socket通信之网络协议基本原理
【9月更文挑战第14天】网络协议是机器间交流的约定格式,确保信息准确传达。主要模型有OSI七层与TCP/IP模型,通过分层简化复杂网络环境。IP地址全局定位设备,MAC地址则在本地网络中定位。网络分层后,数据包层层封装,经由不同层次协议处理,最终通过Socket系统调用在应用层解析和响应。
|
6月前
|
Java API 开发者
Java网络编程基础与Socket通信实战
Java网络编程基础与Socket通信实战

热门文章

最新文章

相关实验场景

更多