基于nio和io的面向发布订阅系统的传输方法

文档序号:7663090阅读:175来源:国知局

专利名称::基于nio和io的面向发布订阅系统的传输方法
技术领域
:本发明涉及数据传输领域,主要是一种基于NIO和IO的面向发布订阅系统的传输方法。
背景技术
:发布/订阅(publish/subscribe,简称pub/sub)系统是一种分布式中间件系统,是随着复杂大规模的网络计算而出现的一种技术,用于为分布式系统中的各参与者提供一种松耦合的通信方式。在大规模、高度动态的分布式系统中,参与者在时间以及空间位置上通常都是动态变化的,比如在出差途中使用掌上电脑等移动设备,频繁的加入和退出网络,因此需要一种松耦合的方式参与消息交互和协同工作。而发布/订阅系统的松耦合的特性,恰能解决这个问题。如图1所示,一个发布/订阅系统中一般由消息生产者(publisher),消息消费者(subscriber)和事件通知服务组成。在发布/订阅系统中,消息通常被称为"事件",消息生产者将"事件"发送给事件通知服务;消息消费者则向事件通知服务发出一个"订阅条件",表示对系统的哪些事件感兴趣;而事件通知服务则保证将发布的事件及时、可靠地送给所有感兴趣的消息消费者。而发布者和消费者在进行消息交互时,都不需要知道对方的存在,使得它们在时间和空间被完全解耦,从而能够满足大规模、动态的分布式系统的需要。而在传统的发布/订阅系统中,事件的生产者和消费者之间的传输多采用基于java的老式IO(输入/输出)方式。在这种方式中,服务端将在开始时建立一个监听socket(套接字),socket是主要用来进行客户端和服务器端通讯的工具,当简历一个监听socket后,就等待客户端请求,客户连接后,产生会话,会话完成后,关闭连接;而客户端通过socket对服务端发出连接请求,一旦连接成功,打开会话,会话完成,关闭socket。在这种传输系统中,系统的瓶颈通常在10读写方面,在打开一个IO通道后,read()方法将一直等待在端口一边读取字节内容,如果没有内容进来,read()也是一直等待,这会影响我们程序继续做其他事情,那么改进做法就是开设线程,让线程去等待,但是这样做也是相当耗费资源的,尤其是在大规模分布式系统中,如果我们要接受大量的客户端的连接,必须要开设大量的线程,而这将是对资源的严重浪费。而基于java的NIO(newIO)的出现,则改变了这种现状,使网络传输的协议不再集中在IO读写上。NIO最大的特点有两个一个是块状读写;另外一个是多路复用技术(multiplex)。第一个特点改变了老式10中,每次读写只有一个字节(stream)或两个字节(reader)的方式,提高了读写的速度;第二个特点改变了原来读写socket时候阻塞的同步状况,使socket的读写具有异步的功能。JavaNIO非阻塞技术实际是采取Reactor模式,或者说是Observer模式为我们监察IO端口,如果有内容进来,会自动通知我们,这样,我们就不必开启多个线程死等,从外界看,实现了流畅的IO读写,不堵塞了。NK)有一个主要的类Selector,这个类就好像一个观察者,只要我们把需要探知的socketchannel(套接字通道)告诉Selector,我们接着做别的事情,当有事件发生时,他会通知我们,传回一组SelectionKey,我们读取这些Key,就会获得我们刚刚注册过的socketchannel,然后,我们从这个Channel中读取数据,放心,包准能够读到,接着我们可以处理这些数据。在我们使用NIO的时候,cpu时间多会消耗在Selector的select操作上(NIO的多路复用),而select操作主要调用的是poll函数,这个操作是一个轮询socket的同步操作,它的主要功能就是不断地去查询向该选择器注册的socket,看看有没有可做的操作,如有,则返回,没有,继续不断地轮询。也就说,分配的cpu的时间基本上被消耗在poll上了。之所以如此,是因为没有东西可以写入或从socket中读取;那么,就应该给"提供读写数据的线程"更多的cpu时间,使得每一次的poll都有可作的操作,使时间耗费在读写socket之上,而不是poll之上。然而,从实际情况中看来,poll线程显然比"提供读写数据的线程(wr线程)"占用了更多的cpu时间,假设他们的优先级相同,也假设操作系统是公平的分配cpu时间,那么它们得到的cpu时间应该是一样的,此时,造成它们占用cpu时间不同的原因就很可能是这样在相同的时间内,wr线程提供的数据量根本不够poll线程处理,poll线程处理在相同的时间内处理这些数据绰绰有余,那么,poll线程更多的时间就耗费在轮询上了。到这里,我们假设的是这两个线程分配到了相同的时间,所以,即使poll线程大部分时间是在做poll轮询操作,那么所占用的时间最大值也是有限的,不会超过线程分配得到的时间。但是,如果此时尽管wr线程得到了cpu时间,但是它在处理过程中主动放弃cpu(比如执行wait操作),那么,在cpu时间总量相等的情况下,由于poll线程没有执行wait操作,因此,就会得到更多的cpu时间,这样,大部分的cpu时间都给poll的轮询操作了。如果我们可以如果使wr线程不断的运行,而使poll线程在没有数据的时候放弃cpu时间,就应该可以减少poll线程的轮询操作,而使wr线程得到更多的cpu时间,提供更多的可供读写的数据,提高读写的效率。然而,由于poll轮询操作是一个不间断的过程,是sun公司提供的底层的api,是无法修改的。所以,就要放弃poll操作,也就是要放弃选择器。
发明内容本发明要解决上述技术所存在的缺陷,提供一种基于NIO和IO的面向发布订阅系统的传输方法。本发明解决其技术问题所采用的技术方案这种基于NIO和IO的面向发布订阅系统的传输方法,我们的传输协议在服务端摒弃老式的基于流的IO,而采用了JDK1.4中的NIO,而在客户端仍采用老式的I/O。在服务端我们启用唯一的接收socket连接的线程,这个接收线程包含有一个Selector对象和一个读线程组和写线程组,Selector对象主要从客户端捕捉到各种事件(比如向服务器端写入数据),读线程组和写线程组用来接收来自客户端的socket连接以及向客户端写入数据,这个Selector对象注册了一个ServerSocketChannel(服务器套接字通道)对象,这个ServerSocketChannel对象用来接收来自客户端的socket连接。每个读线程或写线程分别对应一个socket连接,并将每次接收到的socket都封装起来成为一个可读写数据的SelectableChannel对象,调用Java的regist()函数将其再注册到唯一的Selector对象上,当客户端连接关闭的时候,将连接从读写线程中注销,注销后,读写线程则可以绑定新的连接,这样就可以使用固定数目的线程处理任意多的客户端连接。对于选中的用来读写数据的Channel(通道),包括ReadableByteChannel(读通道,用来从客户端读取数据)和WritableByteChannel(写通道,用来向客户端写入数据)进行的操作是非阻塞的,因而所有的线程都可以真正用来发送、接收消息。在客户端连接数量很大的情况下,NIO可以保证线程上下文切换时较小的开销。而在客户端,因为我们每个客户端一般只有一个到服务端的连接,所以摒弃NIO,釆用老式IO操作。主要过程包括如下步骤1)启动服务器端接收SocketChannel连接的接收线程,并启动读、写线程组;2)客户端进行操作时,打开一个Socket连接到服务器端,服务器端的接收线程开始接收连接,并把接收到的SocketChannd向服务器端的读写线程注册,即将连接的SocketChannel绑定到读写线程上;3)客户端将要发送的消息作为数据报的一部分,每个数据报格式为数据报头、消息体,其中数据报头包含一个特殊的标志符和表示消息大小的一个整数,而后将数据报加码为二进制流,然后通过Socket调用write()函数将数据报发送到服务器端;4)服务器端读线程从Socket中读取标志数据报的二进制流,如读取数据为数据报头,则新建一个数据报,并将此流解码为数据报;然后服务器处理此数据报,并生成返回客户端的数据报,利用NIO的WriteableByteChannel,将处理后得到的数据报加码后变为二进制流,发送到客户端;5)客户端从Socket读取此二进制流,同上解码为数据报,并根据数据报格式判断数据报是否需要客户端处理器进行处理,如果需要处理,则将此数据报进行处理,否则,什么也不做。按照以上步骤,就完成了一个消息从消息生产者发送到事件通知服务,再从事件通知服务发送到消息订阅者的过程。本发明有益的效果是基于Java的IO和NIO的混合使用,将NIO用在服务端可以以非阻塞的形式处理连入socket,减少线程数量,从而减少资源的消耗,在客户端利用老式IO与服务端连接,进而大大提高了消息发送的速度,改进了整个系统的性能。对于大规模分布式系统有着重要的意义。图1为发布/订阅系统的概念结构示意图。图2为本发明的传输协议的整体流程图。附图标记在第五步之前,我们应该确定用户名,并且检査连接;④接受线程;⑩服务端发报机;^客户端发报机;[M1读写线程^^客户端处理器;A连接代理;O解码器;^^服务端处理器;§数据报对列口客户端连接代理;0客户端读线程;|SDS|服务端发报服务;O客户端发报服务。具体实施例方式下面结合附图和实施例对本发明作进一步介绍图1中,Client2和CUent3为消息订阅者,两个客户端向事件通知服务订阅消息,而Clientl为消息发布者,负责向事件通知服务发送消息,当事件通知服务接收到消息时,就会向消息订阅者发送其订阅的消息。此时的消息订阅者和消息发布者统称为客户端,而事件通知服务即为服务端。如图2所示,整个系统由客户端和服务端组成,并启动客户端和服务端各自的操作来完成对消息的整个传输过程。上述各部分具体功能如下-接收线程服务器端监视线程,接收客户端连接,并把其注册到相应的读写线程上服务端发报机调用连接向服务器端发送数据报。客户端发报机向客户端发送数据报。读写线程服务端线程,调用连接代理读取客户端数据并将数据写回客户端。客户端处理器处理服务器端返回数据报。连接代理负责向客户端写入数据报,并读取数据报。解码器将二进制流转换为数据报,以及将数据报转换为二进制流。服务端处理器在服务器端处理从客户端接收到的数据报。数据报队列存储数据报。客户端连接代理负责向客户端写入数据报,并读取数据报。客户端读线程读取服务端发送的数据报。服务端发报服务启动服务端服务,等待客户端连接。客户端发报服务启动客户端服务,与服务端建立连接,开始通讯。整个过程可描述如下首先启动服务端服务,这个过程分为2个主要的步骤1、启动接收SocketChannel连接的接收线程(图中接收线程);2、启动处理"读""写"任务的读写线程(图中读写线程);在步骤1中,服务器的启动可以根据设定参数来确定同时启动读写线程数目,并只启动一个接收线程来接收客户端的连接。其中读写线程里维持一个链表,用来存放接收到的客户端连接。当服务端启动以后,服务端就会有一个接收线程监听来自客户端的socket连接,一旦有来自客户端的socket连接,则读、写线程就会开始工作,即通过channel读取socket中数据并将数据写入soclcet。然后就开始创建客户端到服务端的连接,这个过程分为5个主要的步骤1、用户使用通过JNDI査找到的连接工厂来创建连接;2、根据査找到的连接类型从服务端发报机工厂中获得相应的服务端发报机(如图);3、调用服务端发报机的方法,从客户端打开一个Socket连接到服务器端;4、服务器端的接收线程接收连接,并把接收到的SocketChannel向服务器端的读写线程注册;5、启动客户端的通讯层服务,并客户端读线程(如图)。在步骤1中,连接工程主要的功能为建立与服务器端的连接,当连接建立好后,才可以发送、接收消息,其中连接可根据配置文件从连接工厂中创建。在步骤2中,连接类型可从配置文件中得到,而服务端发报机工厂为一创建发报机的对象,发报机则用来向服务端发送消息。在步骤3中,我们给服务端发报机定义了发送数据报的方法,利用socket定义的接口write()来发送各种数据报。在发送之前,这些数据报都经过了加码处理,变为二进制流发送到服务器端。在步骤4中,服务器端的接收线程不停査询看是否有新的连接,如果有新的连接,则将此连接加入读写线程的链表中,并唤醒读写线程来处理链表的连接。步骤5中的通讯层服务启动后才可以读取从服务器端发回的消息,读线程主要用来读取从服务端发回的数据,并交给解码器(如图)处理。当客户端与服务端建立连接后,就开始消息的收发过程,此过程分为5个主要的步骤1、客户端产生消息,把消息封装成数据报中后,调用客户端连接代理(如图)来输出数据报;2、连接代理把数据报发往服务器端;3、服务器端的读写线程从Socket中读到数据,把数据提交给解码器解码;4、解码器解码得到数据报之后,判断这个数据报是否需要提交给服务器端的处理器处理,如果不需要,什么都不做。如果需要,则服务端的处理器会调用相应的方法来处理这个数据报。5、处理完这个数据报之后,通过连接代理把数据报的回执发往客户端。步骤1中产生消息主要是根据SUN公司制定的JMS规范中提供的的API(标准用户接口)生产的,根据JMS规范,在生成消息的时候,可以给消息设置属性。生成消息后,根据前面提到的加码方法,将本消息封装为一个数据报。步骤2中的发送方式主要为调用socket中的标准write0函数。步骤3中的读写线程读取二进制数据,并将此二进制数据交给解码器解码,将二进制文件还原为一个标准的数据报。步骤4中,数据报可分为多种类型,有的数据报中封装消息,有的数据报中封装请求。而服务器端的根据不同的数据报类型会有不同的处理方式,比如有的数据报需要需要提取出消息后将消息存储到数据库中。步骤5中,连接代理会调用解码器将数据报回执解码为二进制数据,通过前面提到的SelectableChannel的write()方法发往客户端。当服务端发送数据报后,客户端会接收服务端发来的数据,并进行处理1、客户端端的读线程从Socket中读到数据,把数据提交给解码器解码;2、解码器解码得到数据报之后,判断这个数据报是否需要提交给客户端的处理器处理,如果不需要,则什么也不做。如果需要,则客户端的处理器会调用相应的方法来处理这个数据报。步骤1中,客户端通过socket读取一段二进制数据,并根据解码器将此二进制数据解为相应的数据报。步骤2中,根据数据报类型进行处理的时候,根据数据报类型的不同采取不同的处理方式。例如-.如果数据报中包含有消息,则将此消息提取出来。这样,就完成了一个消息从消息发布者发送到事件通知服务,再从事件通知服务发送到消息订阅者的过程。上述实施例用来解释说明本发明,而不是对本发明进行限制,在本发明的精神和权利要求的保护范围内,对本发明作出的任何修改和改变,都落入本发明的保护范围。权利要求1、一种基于NIO和IO的面向发布订阅系统的传输方法,其特征在于在服务端采用了JDK1.4中的NIO,启用唯一的接收socket连接的线程,这个接收线程包含有一个Selector对象和一个读线程组和写线程组,Selector对象主要从客户端捕捉到各种事件,读线程组和写线程组用来接收来自客户端的socket连接以及向客户端写入数据,这个Selector对象注册了一个ServerSocketChannel对象,这个ServerSocketChannel对象用来接收来自客户端的socket连接;每个读线程或写线程分别对应一个socket连接,并将每次接收到的socket都封装起来成为一个可读写数据的SelectableChannel对象,调用java的regist()函数将其再注册到唯一的Selector对象上,当客户端连接关闭的时候,将连接从读写线程中注销,注销后,读写线程则可以绑定新的连接;对于选中的用来读写数据的Channel,包括ReadableByteChannel和WritableByteChannel进行的操作是非阻塞的;在客户端采用基于流的IO操作,按照以上步骤,就完成了一个消息从消息生产者发送到事件通知服务,再从事件通知服务发送到消息订阅者的过程。2、根据权利要求1所述的基于NIO和IO的面向发布订阅系统的传输方法,其特征在于在客户端采用基于流的IO操作,主要过程包括如下步骤1)启动服务器端接收SocketChannel连接的接收线程,并启动读、写线程组;2)客户端进行操作时,打开一个Socket连接到服务器端,服务器端的接收线程开始接收连接,并把接收到的SocketChannel向服务器端的读写线程注册,即将连接的SocketChannel绑定到读写线程上;3)客户端将要发送的消息作为数据报的一部分,每个数据报格式为数据报头、消息体,其中数据报头包含一个特殊的标志符和表示消息大小的一个整数,而后将数据报加码为二进制流,然后通过Socket调用write()函数将数据报发送到服务器端;4)服务器端读线程从Socket中读取标志数据报的二进制流,如读取数据为数据报头,则新建一个数据报,并将此流解码为数据报;然后服务器处理此数据报,并生成返回客户端的数据报,利用NIO的WriteableByteChaimel,将处理后得到的数据报加码后变为二进制流,发送到客户端;5)客户端从Socket读取此二进制流,同上解码为数据报,并根据数据报格式判断数据报是否需要客户端处理器进行处理,如果需要处理,则将此数据报进行处理,否则,什么也不做。全文摘要本发明涉及一种基于NIO和IO的面向发布订阅系统的传输方法,在服务端采用NIO,启用唯一的接收socket连接的线程,这个接收线程包含有Selector对象、读线程组和写线程组,Selector对象从客户端捕捉到各种事件,读线程组和写线程组用来接收来自客户端的socket连接以及向客户端写入数据;每个读线程或写线程分别对应一个socket连接,并将每次接收到的socket都封装起来成为一个可读写数据的对象,将其再注册到唯一的Selector对象上,当客户端连接关闭的时,将连接从读写线程中注销,注销后,读写线程则可以绑定新的连接。本发明优点是基于java的IO和NIO的混合使用,将NIO用在服务端减少线程数量,从而减少资源的消耗,在客户端利用IO与服务端连接,进而大大提高了消息发送的速度。文档编号H04L29/08GK101651698SQ20071016475公开日2010年2月17日申请日期2007年12月12日优先权日2007年12月12日发明者健吴,吴朝晖,尹建伟,施东材,莹李,桢王,邓水光申请人:浙江大学
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1