基于WebRTC的P2P Server方案选择
11 Oct 2013 • ~1 min read本文主要的目标在于对p2p服务器的功能进行分析,以便对整个p2p架构有一个更加明确的认知。方便我们公司其它相关业务的扩展以及方便对解决方案的高效的评估和讨论。图[ADC]是表示的当前与服务器连接的所有操作过程。本方将讨论以下的几个部分。
- 主要是对p2p服务器的整个功能需求进行大致描述。这一部分主要是对p2p业务逻辑的需求。
- 主要是分析现在所使用的p2p服务器的大致功能、运作模式和优缺点。
- 主要是分析基于XMPP协议的服务器以及使用Openfire作为基础服务器的方案研究。
##P2P服务器的功能需求
实际上p2p存在的本身有很大一部分原因是为了解放传统C/S或者B/S对服务器的依赖。但是基于安全、效率以及可管理性等等各个方面的考虑,一个完善的p2p网络,必须有服务器的参与。在p2p网络当中服务器的功能已经被简化了非常多,但是简化并不意味着简单,成熟的p2p网络服务器的功能丝毫不会比传统架构下的服务器简单多少。不过还好,我们项目当中所需要使用到的p2p服务器,从功能上看并不复杂,充分享受到了p2p网络所带来的方便。
在只考虑当下p2p服务器所需要的功能,而不考虑后续的p2p服务器其它业务功能扩展的情况上来看。现在的p2p服务器主要需要三种功能:
- 登录、注册
- 管理p2p结点信息
- 根据需要转发结点之间的数据
上图所列出来的三点,是p2p服务器所需要的基本功能,但对于一个可用的服务器来说,除了在功能上需要完成需求之外,服务器安全、服务器的效率都是需要重点考虑的因素,这些内容将在后面加以讨论。
##登录和注册
登录服务器本身是一个非常普通的功能,现在大部分的网络程序都需要登录。对于这个需求来说,功能看起来简单。我以前理解的登录功能,就直接使用TCP连接上服务器,然后交换一些认证数据,就算登录成功了。然而实际上并没有相像中的这么简单。根据我们的需求,登录服务器至少需要三点保证,如图[LAR]所示
重连机制
由于p2p节点将长期运行在设备和客户的计算机上面,所以如果出现断网、断电重启以及其它各种未知的影响客户端与服务器通信的情况。那么当一切恢复之后,就需要客户端能够自动登录上服务器,同时服务器也能够识别到客户端。
心跳包机制
由于p2p客户端的特殊性,只有在客户端需要和另一个客户端建立p2p通道的时候才会与服务器发生实质性的数据交互,所以当客户端发生断网这样严重的问题的时候,服务器并不知道客户端已经断线。这个时候就需要服务器有一种机制能够保证在短时间内发现掉线的客户端,同时做出相应的响应。在这种情况下,使用心跳包机制就是最好的解决方案。服务器每隔一断时间向客户端发送特殊的心中数据包,客户端回复这个数据包,表示自己存在。
除了这种情况之外,心跳包还有另一个重要的功能,就是保持连接。在计算机网络当中,理论上建立一个TCP连接,只要双方在线,并且不关闭,那么这个连接就一直存在,双方可以在任何时间发送信息给另一方。但实际上,由于网络当中的路由器、交换机等中间设备的存在,情况就变得有一点不同。路由器和交换机的功能常常是把内网的一个IP地址映射到外网的一个端口上面,并发送到远端。如果建立的是TCP连接,那么路由器和交换机会记住这个外网端口的数据对应的是哪个内网计算机的TCP连接,并保持这个端口畅通,如下图所示。
然而实际上这个保持并不是永远保持,当这个TCP连接通道当中长时间没有数据流动,那么路由器和交换机就很可能认为这个TCP通道已经因为某一种未知的原因断开了连接,就会关闭这个端口映射。这个时候,虽然p2p客户端和服务器本身并没有直接关闭TCP连接,但是他们实际上已经断开了连接,客户端和服务器之间发送的数据将会永远收不到。
如果这个时候是内网的客户端想要发送数据给外网的服务器,它就会发现连接已经断开,根据登录协议会重连服务器,这当然没有问题。但是如果外网的服务器需要向内网的客户端发送数据,那么将永远得不到结果,而外网服务器又无法主动连接内网客户端,这就造成了严重的业务逻辑错误。
在我们的p2p业务逻辑当中,有一大部分业务流程是需要服务器主动发送数据给客户端的,如果这个时候发生这种问题的话,将会无解。所以,为了避免这个情况发生,就必须使用心跳包的机制,保持TCP通道当中有数据流动、避免被NAT设备切断连接。
更新结点信息机制
前面两项功能,基本上是登录服务器协议的正常功能。而在我们的p2p架构当中,由于业务的需要,在登录之后,需要马上向服务器说明当前p2p结点下面管理着哪些可用的本地服务器资源。
- 本地资源序列号
- 本地资源型
- 本地资源IP
- 本地资源端口
- 本地资源序列号
- 本地资源型
- 本地资源IP
- 本地资源端口
正如我们上一次所讨论的那样如上图所示:一个p2p结点下面可能存在一个或者多个服务器资源。当客户端连接的时候,需要提供相应的信息来查找相应的结点下面的服务器,然后才能够进行有效连接。所以,当一个p2p结点登录成功之后的第一件事情就是那自己本地的服务器资源告诉服务器,以便让其它客户端连接选择性连接,而更新的信息内容如表[XXGXGS]所示。
小结
登录服务器实际上有很多不同种类型,我把它叫做登录服务器协议,在当前的网络协议簇当中,邮件服务器登录协议、FTP服务器登录协议、远程登录协议、XMPP登录协议等等各种各样的协议非常多。
这些登录协议有很多的共同点,比如上面所说的重连机制、心跳包机制等等。除此之外,在登录过程当中一般情况下还要协商数据安全机制、加密协议等等重要的信息。
在后面的章节当中,我会分析我们现在的服务器的功能和基于XMPP协议的OpenFire开源服务器的登录部分,提供参考思路。
##服务器信息管理
数据管理一般情况下是服务器的主要业务功能,在p2p架构当中也不例外。在p2p服务器当中,服务器的信息管理从数据的持续时间上划分,可以分为两类数据,一类是永久性数据,一类是临时性数据。永久性数据包括用户的账户、密码、还有其它重要信息等等,而临时数据则主要包括当前节点旗下的本地服务器资源,当节点登录服务器的时候提供给p2p服务器,当结点退出的时候,销毁该信息。如图[MI]所示
永久性数据,在服务器当中一般情况下保存在数据库当中,用户通过服务器与数据库产生交互。永久性数据被我分为两类,用户信息和其它信息。因为当前的p2p架构实际上也并不完整,也许还会加入我们公司其它的业务到服务器当中,所以其它信息主要是为服务器以后的业务扩展留下余地。
对用户账户的管理,包括注册、查询、注销等等一系列的细节性问题暂时不在这里讨论,在以后的具体应用当中,根据实际情况再进行分析。我们管理用户的账户是为了让用户注册账号的时候,把自己的账号与相应的设备关联起来,只有相应的用户才能够看到已经授权的视频。
值得注意的是,按照中维世纪的业务逻辑,在网络环境当中,使用云视通,只需要填写设备的序列号,就可以与相应的设备相连。这种设备没有与用户账户关联的设计我个人认为并不安全。因为任何人只要知道设备的序列号就可以通过云视通软件看到相应的视频。
##数据查询和转发
数据查询和转发,是根据我们的p2p架构的特性所必须要有的功能的。数据查询,主要是客户端想要连接哪个结点,需要通过服务器上所注册的当前结点的信息来查询。数据转发是在在两方结点都不知道对方外网IP地址之前,必须由外网服务器将这些信息转发给双方,才能进行后续的p2p连接。所有的业务流动都可以从图[ADC]当中看到,而图[DTS]所显示的是数据查询和转发的分类功能。
- 客户端 请求本地peer A节点连接服务器XXX,其中XXX是这个服务器的设备序列号。
- 本地peer A 接收到客户端的请求,就会向服务器发送查询请求查询这个序列号是否有服务器资源存在,是哪个节点在管理这个服务器资源。
- 服务器在自己的数据库当中根据序列号查询,一量查询到了之后,就把管理这个服务器资源的节点名称(假设是Peer X)告诉PeerA
- Peer A 得到结果之后,就通过服务器向Peer X发送p2p连接请求。
- Peer X 接收到来自服务器的转发的Peer A的连接请求,判断是否进行连接,假设这里同意连接。那么就通过服务器向Peer A传达同意消息,同时开始收集自己本身的外网IP地址、内网IP地址等等传输地址信息,也通过服务器发送给Peer A。
- Peer A 通过服务器接收到来自Peer X的同意连接消息,就马上收集自己传输地址信息通过服务器发送给Peer X.
- 两个节点进入p2p打洞阶段,假设这里打洞成功,那么就可以进行直连。
- 进入后续的直连交互过程
##P2P服务器选择及方案分析
前一章所分析的情况是我们p2p服务器在业务功能上面所需要的硬性需求。对一个服务器来说,效率和安全是永恒的主题。我现在并没有把这两个重要因素考虑进去,是因为考虑到效率和安全是可以无限扩展的,对效率和安全要求太高可以把服务器做得很大,对效率和安全要求低也可以把服务器做得很小,这是一个非常重要的服务器衡量标准,但并不紧急。
通过前面的分析,已经大概能够知道对p2p服务器有着怎么样的需求。按照实际情况来看,我们的p2p服务器选择面还是挺广的,主要是满足以下三点。
- 能够实现我们的三点业务功能、可扩展性比较好
- 效率和安全有保障
- 移植到我们项目当中容易、代码可行性
这里的可性行主要是指的加入到我们的项目当中并不困难。一个完整的服务器解决方案,必然包括客户端和服务器两部分。例如在满足我们三点业务功能上面来看,在所选择的解决方案当中,客户端能够有现成的代码直接能够登录到服务器、能够有现成的代码直接与服务器进行通信交流。如果我们选择了一种服务器,但是还需要我们自己来全部写与服务器的连接的代码,那样的工作量也太大了。
接下来分析两个可以解决方案,一个是当前项目当中所使用的p2p服务器解决方案,一个是使用基于XMPP协议的开源服务器解决方案。
###PeerConnectionServer服务器
Peerconnectionserver服务器是libjingle中一个示例的服务器部分,通过后面做的相应修改,这个服务器能够针对当前的业务提供相应的功能。他没有遵循任何标准,也没有在安全上面做太多功能,在效率上也没有特别考虑。他的最大的优点就是基本实现了我们p2p架构所需要的三点业务需求。
业务功能和可扩展性要求
peerconnectionserver是我们p2p项目到现在为止一直使用的服务器,它是现在唯一能够全部满足我们业务功能的需求的服务器。
然而这个服务器的业务功能也非常简单,基本上所有的功能就是写死过的。由于我以前在这个服务器上面加入过一个更新结点信息的功能模块,所以我深刻的体会到这个服务器的可扩展性特别差,加入一个新功能很麻烦。
效率和安全
Peerconnectionserver使用的是HTTP协议的方式将数据封装、并传输给服务器,这一切数据都是明文传输,没有经过任何加密措施。同时Peerconnectionserver使用了最原始的套节字、单线程机制来管理整个网络连接,所以在效率上面也没有太大的保障。
代码可行性
这个服务器一直在使用,所以说,不管是客户端的连接、重连、心跳包机制都已经实现,并且经过了很长时间的测试,并没有出现太大的问题。所以在代码可性行上面,它还是能够满足我们的需求的。
###基于XMPP服务器方案与Openfire
XMPP协议是一簇协议,包括两个最主要的RFC 3920(XMPP服务器登录协议)、RFC3921(基于IM的即时通信协议)。XMPP是一种基于XML的协议,它继承了在XML环境中灵活的发展性。因此,基于XMPP的应用具有超强的可扩展性。经过扩展以后的XMPP可以通过发送扩展的信息来处理用户的需求,以及在XMPP的顶端建立如内容发布系统和基于地址的服务等应用程序。
XMPP有非常多的扩展协议,这些协议把XMPP扩展到方方面面,使得XMPP协议在现在的互联网上使用得越来越多,特别是在即时通信领域IM、GTalk、Skype、QQ等等都使用了XMPP协议@xmpp_intro。
XMPP的扩展协议以XEP开头,对于我们项目来说,XMPP的扩展协议XEP-0176可以支持ICE的p2p通信,XMPP还有针对STUN服务的扩展协议、也有针对流媒体转发的扩展协议,这一些对我们项目来说都是非常重要的。
之所以选择XMPP服务器有两个原因:
- XMPP协议本身的标准很容易满足我们项目对服务器的业务功能需求。因为标准所以经得起检验,可以避免很多的细节性问题。同时也是因为标准,在互联网上面有非常多的开源的XMPP服务器实现,我们可以在很大范围内选择他们。
- libjingle 本身是GTalk的一部分,在libjingle当中与服务器通信的协议也是XMPP协议,所以在libjingle当中有完整的基本XMPP协议连接服务器的模块、如果使用这个协议的话,节约很大一部分时间。
OpenFire开源服务器
网上有很多基于XMPP实现的开源服务器,各种语言都存在。它们基本上都实现了XMPP的核心协议,但是对XMPP的扩展协议实现程序各不相同。
在众多的XMPP服务器当中,我选择了Openfire服务器。原因有几点
- Openfire服务器是现在互联网上最常用的开源XMPP服务器,我相信大众的眼光。现在互联网上有非常多针对Openfire服务器进行分析的文章和报告,可以使用Openfire资料非常多,对于开发来说有很多好处。
- Openfire服务器的使用Java开发,搭建非常简单,使用网页管理,各种功能模块考虑得非常合理。
- Openfire服务器单台可以支持上万并发用户,在性能上面有比较好的表现。
业务功能需求和可扩展性
对于登录和注册模块,Libjingle可以直接使用XMPP协议登录到Openfire服务器上面,并且经过我的一些修改,没有任何问题。Openfire服务器实现了XMPP的ping模块,有心跳包机制可以保证,同时libjingle中的XMPP模块对于超时重连这一部分也考虑得非常全面,完全满足我们的需求。
对于信息管理需求,Openfire服务器充分考虑到了不同的业务场景当中的业务差异,所以Openfire有一个插件的功能。我们可以根据自己的实际业务需求,在Openfire的基础上面写相应的业务处理插件。这些插件不会改变Openfire本身的代码和原有的功能,很容易扩展。
对于数据查询和转发方面需求,数据查询功能,我们也可以通过插件的方式来完成。而对于数据转发,XMPP的扩展协议XEP-0176专门对ICE通信过程的数据转发做出规定。可以非常完美的与libjingle集成。但是遗憾的是Openfire并没有实现XEP-0176扩展协议,幸好XEP-0176协议功能非常简单,这个功能也需要我们使用插件的形式加入进去。现在互联网的有一些开源的XMPP服务器实现了XEP-0176协议,但这些开源的服务器常常用的不知名的语言写的,而且使用起来在效率和安全上面没有多大保障,所以就没有考虑这些服务器。
所以综上所述,在业务功能需求和可扩展性方面,Openfire服务器是基本满足我们的要求的。
效率和安全
Openfire是基于MINA的JAVA NIO服务器,官网的数据是支持5000人同时在线,使用connectionManager可以实现支持3.3万人在线。网上有人对Openfire性能做过比较详细的测试,最后得出结论:OpenFire的系统集群有点问题,适合在十万以下的用户访问量,如果到了十万二十万左右的在线用户级别,就可以放弃掉Openfire@openfire_imp。
代码可行性
在这个方面,libjingle有完整的XMPP模块,而且包括后面的ICE协议的传输都使用的XMPP协议来进行的。libjingle本身就是使用的XMPP协议来实现与服务器交互的,现目前项目当中所采用的数据传输,实际上也是把XMPP协议的部分提取出来得到的结果,所以我们的p2p项目使用XMPP协议将会更加的合理、自然。
在服务器端,有一些针对我们项目的功能还没有实现,根据Openfire的开源设计,这一部分难度也不大,可行性很高。