Node.js高效分布式实时应用的技术措施

搜论文-编辑:admin-

Node.js高效分布式实时应用的技术措施

  0 引言

  随着互联网技术的迅速发展,智能手机用户数量不断攀升,云处理大数据时代已经来临。大部分复杂的计算处理和海量数据存取的任务不可避免地集中到云端的数据中心,而各种智能移动终端设备则更多地发挥展示数据结果和接收用户数据输入的作用。原有B/S架构下的JSP、ASP.NET、PHP等主流技术在分布式计算上遇到了一定的技术瓶颈,而Node.js应用平台的适时出现,为针对大量用户的实时服务提供了一个难得的可选技术方案。

  传统的网站服务器针对每个连接客户端分配一个线程提供网络服务。由于服务器的内存总会有限,所以随着并发用户数量不断增长,服务器的响应速度将会明显下降。为了保证正常服务,可采用的简单措施有两类:从硬件方面增加服务器内存和CPU数量,而软件方面则主要是通过重复利用线程池中已有的线程来减少线程新建的时间。

  Node.js是一个建立在Chrome浏览器的JavaScript运行环境中的快速应用构建平台,容易开发出具有良好扩展性的网络应用程序。 Node.js使用一个事件驱动的非阻塞I/O模型,使得它轻量、高效、完美地将数据密集型实时应用程序运行在各种分布式设备上 [1]。Node.js可以在所有主流操作系统上顺畅运行。

  1 分布式处理策略

  分布式应用一般采用C/S(客户端/服务器)通信模式,即客户向服务器发出服务请求,服务器接收到请求后,提供相应的服务。此模型通常假定的情况是:网络中各设备拥有的资源不均衡,需要互相协作共同完成任务:拥有众多资源的服务主机提供计算、存储、查询等服务,而资源相对较少的客户则发出服务请求,展示处理结果。两端进程通信是异步的,通常使用较为成熟的Socket技术来简化进程间的网络通信。曾开发过分布式应用的程序员都会觉得分布式处理比单机处理复杂很多:既要考虑到服务器本身的软、硬件故障,还要考虑网络不稳定等因素。如果要构建高可靠性的实时应用系统,系统的所有构件都必须满足双系统的冗余要求[2]。

  为了简化问题,可以将具体的应用客户端与服务端逻辑进行抽象,本文主要讨论分布式应用的高效可靠的设计策略,同时也考虑这些技术的实现代价,从而使得中小型企业也能采用这些技术来解决实际问题。最简单的分布式处理模型。

  这是所有分布应用的基础,虽然部署简单,但是只要服务器出现任何故障,一切对外服务就会中断,这对于银行及电信等企业来说是无法接受的。

  在银行及电信等企业,会对关键的服务器实行双机热备机制,确保关键服务稳定运行。随着硬件的快速发展,基于Intel处理器的服务器运算能力不断增强,这使得中小型企业不一定要采购中小型机来作为服务器,采用HP或者联想等公司的服务器来也能完成同样任务,还可以降低采购成本。

  当采用Node.js平台后,可以使用多台普通配置的服务器去代替那些中小型机或者工作站,图2展示了使用这种策略的分布处理模型。

  在这种模型中,每个特定的服务应该运行在至少两台服务器上,从而避免单点故障的发生。

  在Web2.0没有广泛应用之前,有3个主要的分布式处理技术:Sun公司(目前已被Oracle公司收购)的J2EE、微软的DCOM和UNIX厂商联盟的CORBA。不管是哪种模型,开发调试都比较困难。比如基于CORBA开发分布式应用,首先按照接口定义语言(IDL)编写服务接口方法申明,将开发好的服务进行登记注册,运行时由智能代理去发现具体的服务器地址与端口。客户端开发人员调用远程服务和本地调用方法区别不大,但是初次执行时间较长。这种开发方法的扩展性不够强,所有的客户端均需要各类服务的IDL,如果服务划分的粒度较细且客户端数量又较多时,服务的维护就显得尤为困难。

  Node.js平台的出现,为在Web2.0时代进行分布式处理提供了很好的技术手段。笔者参考网易公司的开源网络游戏引擎 Pomelo[3],提出了两种简单而高效可靠的分布式处理策略:基于Redis消息中心的抢占式处理策略和基于ZeroMQ的任务路由策略。在阐述这两种策略前,先介绍基于Web2.0的客户端与服务器通讯的常用手段。

  2 使用Socket.IO简化网络开发

  为了对桌面应用和移动终端应用提供统一的通讯支持,可以选择使用Socket.IO,避免在客户端安装一个Node.js运行环境。

  目前的主流浏览器都能支持WebSocket,这样就可以直接使用标准的Socket编程方式进行客户端与服务器的通讯。Socket.IO的诞生统一了网络分布应用的前后端通讯方式,即便比较老式的浏览器也能运行基于“Socket”的网络交互。

  Socket.IO的第一个版本在Node.js出现不久就被开发出来。目前1.2版本也已经发布,还提供了对二进制数据的传输支持,方便了图片、声音文件的传送,降低了网络应用的编写复杂度。

  Socket.IO其实也是Web上的事件发生器(EventEmitter)。Socket.IO的最新版本代码已经不再处理传输与浏览器兼容的问题,这些工作已经并入到新模块Engine.IO里。Engine.IO是一套类WebSocket风格的API实现。Socket.IO的服务端只有1 200多行代码,客户端代码只有代码不到1 000行。

  在分布式应用中,客户端可使用Socket.IO连接后端服务器来获取资料。使用 Socket.IO时,不用关心包、帧、TCP等底层琐碎细节,只需关心事件的收发。在Node.js上使用Socket.IO开发一个简单聊天应用只需要很少的几行代码[4]。

  3 负载均衡的软件实现策略

  Intel处理器的运算能力越来越强,多台普通PC服务器互相协作就能完成以前小型机才能胜任的处理任务。

  由于Node.js优秀的设计思想,对于像传统的HTTP这样的Web服务,使用一台标准服务器也能轻松应对上万个并发访问。但是如果每次的处理是比较费时的计算,比如游戏中的A*寻路算法等,就需要通过软件将这些费时的服务分离到单独的服务器上去处理,避免请求排队积压的现象发生。

  由于服务分布于多台服务器上,不可避免地会出现分布式处理典型的服务登记与管理问题。一种简单的策略如图3所示,选取两台专用的服务器作为代理,负责所有客户的接入,在网易公司的游戏引擎Pomelo中称之为接入服务器。当客户端是真实的Socket长连接时,还可以起到负载均衡的作用。

  3.1 借助Redis作为消息中心

  Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。如图4所示,只需选择一台普通配置的服务器,配置适当的内存,客户端和服务工作进程就都直接连接到该服务器,这台服务器其实充当了网络消息中心。

  图4 使用Redis服务器作为消息中心

  使用该策略的软件处理步骤如下:①所有服务进程向Redis订阅自己可处理请求服务名称的通知;②客户端把服务请求发到可用的服务代理进程上;③服务代理进程发送请求到Redis内存消息队列并产生作业编号;④服务代理进程通过Redis发布广播通知给所有后台服务进程;⑤多个服务进程收到通之后,按照先到先得的处理原则从消息队列中提取一个任务并处理;⑥服务代理进程处理完毕后将结果放到一个单独结果表中并通知代理进程,该结果表实际上是一个以作业号作为key的散列表;⑦服务代理进程从结果表中取出自己的结果并转发给客户端。

  该方案的优点是服务器配置简单,客户端只需知道代理的地址,并定期检测服务器的可用性,该策略可以根据并发客户量的大小在线动态增加服务进程的数量。但缺点是要多安装Redis服务器,而Redis很可能成为新的瓶颈并可能造成单点故障。一个简单的方案是将Redis服务进程直接安装在代理服务器上并使用 Redis提供的负载均衡方案。

  3.2 借助ZeroMQ实现

  在服务器所在隔离区内部的通信还有更加有效的方法。一种可选方案是使用ZeroMQ进行局域网内服务进程的高效协作。

  ZeroMQ是一个开源、跨语言、十分灵活简洁、高性能的灵活的网络通讯库。ZeroMQ是以消息为单位进行收发,它确保进程每次发出或者收到的都是一个完整的消息块。不用编程人员考虑缓冲区之类的繁琐事项。ZeroMQ的“零”代表网络传输延迟短、无需专用消息队列、开发成本低等意思[5]。ZeroMQ 以统一接口支持多种底层通信方式:线程间通信、进程间通信、跨主机通信。比如,如果要把多进程软件用于跨主机环境中,只需将通信协议由 “ipc://xxx”改为”tcp://ip:port”即可,而不需要改动其余代码。ZeroMQ 有超过 20 种以上的开发语言绑定,诸如 Node.js、C、C++、Java、.NET等,并且还能支持全部主流操作系统。通常分布式系统较为复杂,开发中不会局限于一种开发语言或者一种平台,所以在技术选择时,ZeroMQ 跨语言、跨平台的特性就有很强的吸引力。由于 ZeroMQ是用C++编写,并且ZeroMQ协议格式定义得很简单,所以它的性能远远高于其它消息队列软件 [6]。

  不管是客户端还是服务端,使用ZeroMQ通讯库后,就不必像传统的通讯方式先启动服务端,然后才能运行客户端。两端均在内存中配有自己的发送队列,所以客户端可以先发送请求而不必等到服务器启动后才发送,一定程度上降低了对两端处理速度的匹配要求。当网络出现故障后,ZeroMQ会自动尝试重新连接,大大降低了网络编程难度。

  ZeroMQ内建立了同步请求与答复、异步请求与答复、发布订阅等常用通信模式。如果客户端使用同步请求模式(REQ),那么在没有得到服务端的结果之前不能再次发送请求。为了更好地同时服务多个客户的并发请求,可使用ZeroMQ提供的dealer-router模式,在代理服务器上同时运行路由进程。此时的客户请求代理进程和服务进程实际上都扮演了“客户端”角色,而路由进程则扮演了“服务器”角色。通常只需要两个不在同一台物理机器上的路由进程。工作进程启动后先发握手信号给路由进程,表明自己可以处理任务了。以后每次把处理结果发给路由进程时实际上已经表明了本工作进程处于正常工作状态,可以继续接受后续任务。当有多个同样的工作进程可用时,路由进程按照FIFO(先进先出)原则,将下一个请求分配给队列中的第一个可用服务进程。

  使用这种解决方案不必安装Redis服务器,只要安装zmq模块即可,可运行两个独立的路由进程来避免单点故障,使用该方案同样可以在运行时动态增加后台服务进程并且数据传送速度很快。但是这种方案有一定的学习曲线,并且调试比较复杂。

  4 Node.js分布应用测试模拟

  使用Node.js开发分布式应用时,一开始不必配置多台服务器。目前大部分服务器都配置多核CPU,可以直接利用cluster模块来实现并发测试。 Node.js从0.8版本开始,内置了cluster模块。在使用cluster时,最重要的两个概念是master和worker。其中master 为总控主进程,作为服务管理者,worker是具体服务进程。可根据CPU的数量,启动相应数量的worker。

  使用内置的集群处理代码十分简单:

  var cluster = require('cluster');

  var os = require('os');

  var total = os.cpus().length;

  var MyFun=function(){};//具体处理函数

  if (cluster.isMaster){

  for (var i = 0;i < total; i ++)

  cluster.fork();

  } else{

  // 启动程序

  MyFun();

  }

  5 结语

  在大数据时代,计算机分布式处理将是必然发展趋势。Node.js平台的优点引人关注,但也存在一些缺点。如果是计[dylW.net专业提供教育论文代写的服务,欢迎光临www.dylW.NeT]算密集型应用,就无法体现 Node.js 提供的非阻塞 I/O 的优点。本文研究了在Node.js平台上

  利用Redis和ZeroMQ技术实现高效可靠的分布式实时应用的方法,认为Redis“抢占式”服务思路清晰但是会安装更多服务并且会浪费一些通信带宽,而ZeroMQ路由策略通信效率高,更适合大数据处理环境,但是此方法需要掌握较多概念,有一定的学习曲线。

  参考文献

  \[1\] Node.js官方网站[EB/OL].http://www.nodejs.org/.

  [2] 胡华平.分布式实时系统的高可靠性研究与实现[J].计算机研究与发展,1998(9):841-845.

  [3] 网易pomelo框架官方网站[EB/OL]. https://github. com/Net Ease/pomelo/wiki/Home-in-Chinese.

  [4] Socket.IO官方网站[EB/OL].http://socket.io/get-started/chat/.

  [5] MQ-the guide[EB/OL].http://zguide.zeromq.org/page:all.

  [6] DCCMX.史上最快消息内核—ZeroMQ[EB/OL].http://itindex.net/detail/4067-%E6%B6%88%E6%81%AF-%E5%86%85%E6%A0%B8-zeromq,2011.