Node.js是一个基于Chrome JavaScript运行时建立的平台,用于方便地搭建响应速度快、易于扩展的网络应用。本来,js是运行于浏览器端的语言,例如google浏览器chrome,该浏览器执行js的引擎,即解释器,叫做V8。V8 Javascript引擎采用C++编写,开源,可嵌入到其他应用程序中,正是由于这一特点,才催生了node。
Nodejs就以V8为基础,创建了一个新的javascript运行时环境,叫做node(我们写一个helloworld.js在cmd中,node helloworld.js 就可以运行。类似于java helloworld.class 只是省去了javac的编译,因为js本身是解释执行,不生产目标程序)。Node本身是一个javascript解释器,更确切地说,是服务器端的javascript运行时环境,它扩充了浏览器端的javascript运行时环境,开放了一些文件系统和系统命令接口,提供了net,socket编程的api。所有我们可以用node轻松地写出一个http服务器,如下:
Cmd中执行改node程序
以上就是一个相对完整的http服务器,访问端口返回HelloWorld。
NodeJS的作者说,他创造NodeJS的目的是为了实现高性能Web服务器,他首先看重的是事件机制和异步IO模型的优越性,而不是JS。但是他需要选择一种编程语言实现他的想法,这种编程语言不能自带IO功能,并且需要能良好支持事件机制。JS没有自带IO功能,天生就用于处理浏览器中的DOM事件,并且拥有一大群程序员,因此就成为了天然的选择。
Nodejs最重要的特性是事件驱动和非阻塞IO。举个例子
到餐厅点餐,点完餐后拿到了一个号码,拿到号码,我们往往会在位置上等待,而在我们后面点餐的请求会继续得到处理,同样是拿了一个号码然后到一旁等待,接待员能一直进行处理。等到饭菜做号了,会喊号码,我们拿到了自己的饭菜,进行后续的处理(吃饭)。这个喊号码的动作在NodeJS中叫做回调(Callback),能在事件(烧菜,I/O)处理完成后继续执行后面的逻辑(吃饭),这体现了NodeJS的显著特点,异步机制、事件驱动整个过程没有阻塞新用户的连接(点餐),也不需要维护已经点餐的用户与厨师的连接。
基于这样的机制,理论上陆续有用户请求连接,NodeJS都可以进行响应,因此NodeJS能支持比Java、PHP程序更高的并发量虽然维护事件队列也需要成本,再由于NodeJS是单线程,事件队列越长,得到响应的时间就越长,并发量上去还是会力不从心。
总结一下NodeJS是怎么解决并发连接这个问题的:更改连接到服务器的方式,每个连接发射(emit)一个在NodeJS引擎进程中运行的事件(Event),放进事件队列当中,而不是为每个连接生成一个新的OS线程(并为其分配一些配套内存),java,php对每一个请求都是分配一个连接,主要表现在系统资源上,例如内存。
捕鱼服务端在发挥node的异步特性上,做得并不是很多,大部分操作,因为逻辑要求,都被写成了同步操作。
优点: 1. 高并发(最重要的优点)
2. 适合I/O密集型应用
缺点: 1. 不适合CPU密集型应用;CPU密集型应用给Node带来的挑战主要是:由于JavaScript单线程的原因,如果有长时间运行的计算(比如大循环),将会导致CPU时间片不能释放,使得后续I/O无法发起;
解决方案:分解大型运算任务为多个小任务,使得运算能够适时释放,不阻塞I/O调用的发起;
2. 只支持单核CPU,不能充分利用CPU
3. 可靠性低,一旦代码某个环节崩溃,整个系统都崩溃
原因:单进程,单线程
解决方案:(1)Nnigx反向代理,负载均衡,开多个进程,绑定多个端口;
(2)开多个进程监听同一个端口,使用cluster模块;
4. 开源组件库质量参差不齐,更新快,向下不兼容
5. Debug不方便,错误没有stack trace
以下内容在Pomelo(Homein Chinese)均可找到,这里挑选一些概要章节,做一个简介
Pomelo是网易开发的一个Nodejs游戏开源框架。以下是pomelo开源框架负责人的自述
“2011年下半年开始,我们在游戏开发上有了些积累,希望在游戏服务器上有更前沿的研究,在技术选型时我们比较了erlang, node.js,java等多种语言,发现node很适合做开发游戏服务器,它的事件IO模型与单线程应用模型跟游戏服务器简直是绝配。于是在2011年11月,我们正式立项并取名项目为pomelo。
项目的开发经历了原型、框架开发、demo开发,性能优化,不断重构、调优、整理等几个阶段。
我们的原型开发只经历了一个月, 当时做了个很简单的捡宝游戏; 框架的抽取和开发是项目最难的阶段, 要定义抽象出框架模型很难, 而且我们是多进程的应用框架,完全没有蓝本参考,到了2012年4月终于完成了框架雏形;之后的demo开发比较快,我们用了一个半月时间就搭建了一个HTML5客户端的网页版的MMORPG,并在6月底的node party上小试牛刀了一把;然后我们用了两个月时间做压力测试和性能调优,做了一些性能优化的工具,并把遇到的性能瓶颈都解决了;到了9月之后我们的主要工作就是重构、文档,不断地优化接口,并在11月20日左右开源我们的框架。”
2012年底开源的pomelo,目前的商用成功案例已经很多,如中国平安在线客服系统,网易消息推送平台,还有一系列扑克麻将游戏。。。在成功案例有参考。
1. 第一个理念是让游戏(高实时web应用)服务器的开发变得非常简单,而不是解决某类算法或系统上的难题。
2.第二个理念是重视性能和可伸缩性,用户用pomelo开发出来的游戏天生具有很强的伸缩性,扩展也很容易。pomelo在性能优化上也花了很多功夫,并且会持续进行。
3.第三个理念是让第三方很容易扩展,框架用了很多插件式的设计,组件component、路由规则、甚至管理控制台都可以完全由第三方扩展。
针对目前游戏服务器框架产品市场的情况,没有适用于中小型游戏开发的框架,我们推出了pomelo框架,它是基于node.js开发的高性能、可伸缩、轻量级游戏服务器框架,使得游戏服务器的开发变得简单。与其他的类似的框架相比,它的主要优势有以下几点:
在讲了这么多分布式开发的难点之后,引入node.js实在是太自然了,它天生的异步编程模型解决了分布式开发的很多问题:
node.js之所以叫node就是因为它天生就是做多进程开发的, 多个节点(node)互相通讯交织在一起组成的分布式系统是node天生就应该这么干的。它的编程模式里天生就是这种模式,两阶段提交、异步化操作这些看似复杂的工作里在node.js只是一个正常的异步执行流程。例如前面提到的分布式事务、异步化操作在node.js里只是个正常的流程。
node.js的单线程处理能力远比其它语言强大,而单线程处理游戏逻辑是最简单,最不容易出错,而且不可能出现死锁、锁竞争的情况。
游戏是非常io密集型的应用, 采用node.js是最合适的, 可达到最好的可伸缩性。虽然有很高的可伸缩性,却并没有因此损失性能。node.js生来就是为io而生的,而游戏服务器刚好是网络密集型的应用。node.js的网络编程接口简单,抽象程度高。
使用javascript开发可以实现快速迭代。它不仅由于脚本语言的轻量、简单带来了开发效率的提升,还可以与一些类型的客户端共享部分代码,如html5,unity3d的js客户端等。另外,语言的动态性带来了很多框架设计的便利。
一个典型的多进程MMO运行架构, 如下图所示:
一些说明:
pomelo框架的组成如图所示:
下面对架构图的一些说明:
pomelo是个真正多进程、分布式的游戏服务器。因此各游戏server(进程)的管理是pomelo很重要的部分,框架通过抽象使服务器的管理非常容易。server management 部分维护服务器的监控信息,对服务器进行管理等功能;
pomelo中的通信,包括服务器与客户端的通信,也包括服务器群中各个服务器进程之间的通信,也就是服务器间的rpc调用。请求、响应、广播、rpc、session管理等构成了整个游戏框架的脉络,所有游戏流程都构建在这个脉络上。
应用的定义、component管理、上下文配置,这些使pomelo framework的对外接口很简单, 并且具有松耦合、可插拔架构。
在web应用中, 每个服务器是无状态、对等的, 开发者无需通过框架或容器来管理服务器。 但游戏应用不同, 游戏可能需要包含多种不同类型的服务器,每类服务器在数量上也可能有不同的需求。这就需要框架对服务器进行抽象和解耦,支持服务器类型和数量上的扩展。
该架构把游戏服务器做了抽象, 抽象成为两类:前端服务器和后端服务器
前端服务器(frontend)的职责:
后端服务器(backend)的职责:
客户端的请求、响应与web应用是类似的, 但框架是基于长连接的, 实现模式与http请求有一定差别。 广播是游戏服务器最频繁的操作, 需要方便的api, 并且在性能上达到极致。下图的代码是一个request请求示例:
请求的api与web应用的ajax请求很象,基于convention over configuration的原则, 请求不需要任何配置。 如下图所示,请求的route字符串:chat.chatHandler.send, 它可以将请求分发到chat服务器上chatHandler文件定义的send方法。
pomelo的框架里还实现了对request的filter机制,广播/组播机制,以及Channel的支持等,更详细的内容可以参考后面的开发指南部分的相关内容。
尽管框架尽量避免跨进程调用,但进程间的通讯是不可避免的, 因此需要一个方便好用的rpc框架来支撑。架构中各服务器之间的通讯主要是通过底层rpc框架来完成的,该rpc框架主要解决了进程间消息的路由和rpc底层通讯协议的选择两个问题。 服务器间的rpc调用也实现了零配置。rpc框架目前在底层采用socket.io作为通讯协议,但协议对上层是透明的,以后可以替换成任意的协议。
应用的扩展性很重要,pomelo framework支持以component的形式插入任何第三方组件, 也支持加入自定义的路由规则, 自定义的filter,自定义admin module等。component是pomelo的核心,pomelo的核心功能都是由component完成,开发者可定制自己的component,并加载到框架中,以完成其功能。
在本部分,讲述了pomelo框架的整体架构,以及其设计目标。pomelo框架完成了对服务器的抽象,对用户请求响应以及服务器端主动推送消息的抽象,服务器间rpc调用的抽象,可插拔的components抽象。这些抽象使得pomelo非常灵活以及易于使用,易于扩展。
pomelo的通信协议是开放的,又是可定制的,因此,理论上pomelo可以与使用任意协议的任意平台的客户端进行通信。当用户开发客户端时,可以根据相应的通信协议完成与服务端的通信。
为了方便开发者,目前pomelo提供了一些常见客户端平台的开发库。这些平台包括web,iOS, java & android, unity3d, flash以及C语言的库libpomelo等。基本上每种平台都提供了基于socket.io和使用socket/websocket的开发库版本。也欢迎大家提供一些客户端开发库,供大家使用。
目前Pomelo服务器提供两类connector:sioconnector和hybridconnector,分别对于基于socket.io和二进制的通讯。
1.1 sioconnector
支持基于socket.io的通讯协议,也是Pomelo框架默认采用的connector(主要是兼容老版本)。之前基于socket.io的服务器和客户端代码不用修改就可以使用(默认使用)。
1.2hybridconnector
支持socket和websocket,使用二进制通讯协议,并且支持route字典压缩和protobuf压缩的connector,需要在app.js中显式配置。
pomelo的客户端和服务器之间的通讯可以分为三种:
request-response
pomelo中最常用的就是request-response模式,客户端发送请求,服务器异步响应。客户端的请求发送形式类似ajax类似:pomelo.request(url, msg, function(data){});第一个参数为请求地址,完整的请求地址主要包括三个部分:服务器类型、服务端相应的文件名及对应的方法名。第二个参数是消息体,消息体为json格式,第三个参数是回调函数,请求的响应将会把结果置入这个回调函数中返回给客户端。
notify
notify与request—response类似,唯一区别是客户端只负责发送消息到服务器,客户端不接收服务器的消息响应。 pomelo.notify(url, msg);
push
push则是服务器主动向客户端进行消息推送,客户端根据路由信息进行消息区分,转发到后。通常游戏服务器都会发送大量的这类广播。pomelo.on(route, function(data){});
以上是javascript的api, 其它客户端的API基本与这个类型。由于API与ajax极其类似,所有web应用的开发者对此都不陌生。