从npm到Node.js再到WebSocket的理解
npm
在没有npm之前程序员们是怎么共享代码的呢?
- 通过网址来共享代码 比如你需要jQuery你就需要去jQuery官网下载,放到自己的页面上使用
- 通过GitHub提供代码供人们下载
这样做的缺点是什么呢?
当一个网站依赖的代码越来越多,程序员发现这是一件很麻烦的事情:
- 去 jQuery 官网下载 jQuery
- 去 BootStrap 官网下载 BootStrap
- 去 Underscore 官网下载 Underscore
- ……
有些程序员就受不鸟了,一个拥有三大美德的程序员 Isaac Z. Schlueter (以下简称 Isaaz)给出一个解决方案:用一个工具把这些代码集中到一起来管理吧!
这个工具就是他用 JavaScript (运行在 Node.js 上)写的 npm,全称是 Node Package Manager
具体步骤
NPM 的思路大概是这样的:
- 买个服务器作为代码仓库(registry),在里面放所有需要被共享的代码
- 发邮件通知 jQuery、Bootstrap、Underscore 作者使用 npm publish 把代码提交到 registry 上,分别取名 jquery、bootstrap 和 underscore(注意大小写)
- 社区里的其他人如果想使用这些代码,就把 jquery、bootstrap 和 underscore 写到 package.json 里,然后运行 npm install ,npm 就会帮他们下载代码
- 下载完的代码出现在 node_modules 目录里,可以随意使用了。
这些可以被使用的代码被叫做「包」(package),这就是 NPM 名字的由来:Node Package(包) Manager(管理器)。
所以现在,你可以使用 npm install jquery 来下载 jQuery 代码。 现在用 npm 来分享代码已经成了前端的标配。
接下来讲什么是Node.js
先说浏览器和服务器之间是怎么沟通的
浏览器给网站发请求的过程一直没怎么变过。当浏览器给网站发了请求。服务器收到了请求,然后开始搜寻被请求的资源。如果有需要,服务器还会查询一下数据库,最后把响应结果传回浏览器。不过,在传统的web服务器中(比如Apache),每一个请求都会让服务器创建一个新的进程来处理这个请求。
后来有了Ajax。有了Ajax,我们就不用每次都请求一个完整的新页面了,取而代之的是,每次只请求需要的部分页面信息就可以了。这显然是一个进步。但是比如你要建一个FriendFeed这样的社交网站(类似人人网那样的刷朋友新鲜事的网站),你的好友会随时的推送新的状态,然后你的新鲜事会实时自动刷新。要达成这个需求,我们需要让用户一直与服务器保持一个有效连接。目前最简单的实现方法,就是让用户和服务器之间保持长轮询(long polling)。 。
HTTP请求不是持续的连接,你请求一次,服务器响应一次,然后就完了。长轮训是一种利用HTTP模拟持续连接的技巧。具体来说,只要页面载入了,不管你需不需要服务器给你响应信息,你都会给服务器发一个Ajax请求。这个请求不同于一般的Ajax请求,服务器不会直接给你返回信息,而是它要等着,直到服务器觉得该给你发信息了,它才会响应。比如,你的好友发了一条新鲜事,服务器就会把这个新鲜事当做响应发给你的浏览器,然后你的浏览器就刷新页面了。浏览器收到响应刷新完之后,再发送一条新的请求给服务器,这个请求依然不会立即被响应。于是就开始重复以上步骤。利用这个方法,可以让浏览器始终保持等待响应的状态。虽然以上过程依然只有非持续的Http参与,但是我们模拟出了一个看似持续的连接状态
我们再看传统的服务器(比如Apache)。每次一个新用户连到你的网站上,你的服务器就得开一个连接。每个连接都需要占一个进程,这些进程大部分时间都是闲着的(比如等着你好友发新鲜事,等好友发完才给用户响应信息。或者等着数据库返回查询结果什么的)。虽然这些进程闲着,但是照样占用内存。这意味着,如果用户连接数的增长到一定规模,你服务器没准就要耗光内存直接瘫了。
这种情况怎么解决?解决方法就是刚才上边说的:非阻塞和事件驱动。这些概念在我们谈的这个情景里面其实没那么难理解。你把非阻塞的服务器想象成一个loop循环,这个loop会一直跑下去。一个新请求来了,这个loop就接了这个请求,把这个请求传给其他的进程(比如传给一个搞数据库查询的进程),然后响应一个回调(callback)。完事了这loop就接着跑,接其他的请求。这样下来。服务器就不会像之前那样傻等着数据库返回结果了。 如果数据库把结果返回来了,loop就把结果传回用户的浏览器,接着继续跑。在这种方式下,你的服务器的进程就不会闲着等着。从而在理论上说,同一时刻的数据库查询数量,以及用户的请求数量就没有限制了。服务器只在用户那边有事件发生的时候才响应,这就是事件驱动。
FriendFeed是用基于Python的非阻塞框架Tornado (知乎也用了这个框架) 来实现上面说的新鲜事功能的。不过,Node.js就比前者更妙了。Node.js的应用是通过javascript开发的,然后直接在Google的变态V8引擎上跑。用了Node.js,你就不用担心用户端的请求会在服务器里跑了一段能够造成阻塞的代码了。因为javascript本身就是事件驱动的脚本语言。你回想一下,在给前端写javascript的时候,更多时候你都是在搞事件处理和回调函数。javascript本身就是给事件处理量身定制的语言。
Node.js还是处于初期阶段。如果你想开发一个基于Node.js的应用,你应该会需要写一些很底层代码。但是下一代浏览器很快就要采用WebSocket技术了,从而长轮询也会消失。在Web开发里,Node.js这种类型的技术只会变得越来越重要。
总结一点就是:node.js一种javascript的运行环境,能够使得javascript脱离浏览器运行。
websocket
“WebSocket 是一项先进的技术,它可以在用户的浏览器和服务器之间打开交互式通信会话。通过 WebSocket,您可以向服务器发送消息并实时接收响应,而无需通过传统的轮询服务器的方式来获取服务器上的响应。”
这是一段 Mozilla 在开发人员文档页面上对于 WebSocket 的介绍。简单的来说,WebSocket 可以使浏览器在一段时间内保持与服务器的连接,它实现了浏览器与服务器全双工(full-duplex)通信,即允许服务器主动发送信息给客户端。对于例如动态更新位置数据、拉取热点新闻、在浏览器中构建高性能游戏以及收集更多点击流数据等等需要保持实时数据交换的场景,这个特性就特别地友好。
相信大家都有遇到过这样的场景,服务器上有些资源经常会进行更新,客户端需要尽量及时的获取到这些更新,在传统的 HTTP 协议中,如果客户端不发起一个 Request 请求,那么服务器是没有办法向客户端主动发起数据的。那么我们是如何解决客户端和服务器之间的数据更新问题的呢?
比较常见的做法就是 AJAX 轮询。客户端设置一个定时器,在一定时间内客户端会向服务器发起一个 AJAX 请求,询问服务器是否有更新,如果有更新就及时返回数据。通过定时器,客户端可以反复的去轮询服务器上相关的资源有没有更新,从而实现近乎“实时”的通讯。
AJAX 轮询虽然缩短了客户端和服务器之间数据同步的延迟时间,但同时也带来了一些性能消耗的问题。例如,如果客户端较多,那么服务器同时接收轮询请求就会增多,这就会对服务器造成巨大的压力,轮询所产生的流量,也会对网络造成一定的消耗,另外服务器资源更新频率较为频繁,但客户端定时器时间间隔较大,或者服务器长时间没有更新资源,这就会导致资源更新不及时、带宽耗费等等问题。
但有了 WebSocket 之后就不同了,在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。这样,即使没有来自客户端的明确请求,服务器也可以向客户端发送信息,实现反向的通讯。这就是服务器与客户端之间的“全双工”通信。
WebSocket 工作原理
客户端(或多个客户端)先通过向服务器发送 HTTP 请求开始,请求中包括了 WebSocket 支持的版本号、协议的版本号、原始地址、主机地址等等一些字段给服务器端,向服务器表明客户端正在尝试建立 WebSocket 连接。
如果服务器检查数据包数据和格式正确,客户端和服务器端的协议版本号匹配,就接受本次握手连接,并给出相应的数据回复,同样回复的数据包也是采用 HTTP 协议传输。等待客户端确认后,就将初始 HTTP 连接升级为 WebSocket 连接,并且为每个客户端维护该连接,从而实现双向通讯
WebSocket 的优势
比起传统的轮询方式,WebSocket 可以更好的节省服务器资源和带宽,并且能够进行更加实时地通讯,优势如下:
- 减小带宽开销。服务器和客户端在连接建立后,相比起 HTTP 请求,交换数据时用于协议控制的数据包头部相对较小,一般只有 2 字节;
- 增强实时性。服务器可以随时主动给客户端下发数据,相对于 HTTP 请求需要等待客户端发起请求服务端才能响应,延迟明显更少,和传统的轮询比较,WebSocket 也可以在短时间内更有效率地传递数据;
- 维持连接状态。在一些需要身份认证的场景下, HTTP 请求可能需要在每个请求都携带状态信息(服务器不记录每次的请求和响应信息),而 WebSocket 一次连接建立后就会保持住会话状态,这就使其成为一种有状态的协议,后续通信时就可以省略部分状态信息;
- 更灵活的扩展支持。根据 RFC6455 协议,开发者可以对 WebSocket 自定义二进制帧,相对 HTTP,可以更轻松地处理二进制内容,此外开发者也自行扩展协议、实现部分自定义的子协议。
- 更好的压缩效果。WebSocket 在适当的扩展支持下,可以沿用之前内容的上下文,在传递类似的数据时,可以显著地提高压缩率。
WebSocket适用场景
- 需要及时响应的场景。当客户端需要对服务端发生的改变做出快速响应(尤其是客户端无法预测的响应)时,WebSocket 是非常适合的。例如开发一个客服系统,这往往要求实现多个用户实时沟通。如果使用 WebSocket,则每个对话都可以实时发送和接收消息。与 HTTP 相比,WebSocket 不需要考虑发送和接收的每个消息的 HTTP 请求/响应导致的开销,从而会有更高的执行效率。
- 需要实时查询的场景。例如一名篮球迷想要查询比赛结果,如果比赛是上周结束的,那么比赛结果是固定的,HTTP 在这种情况下就非常适合。但是,如果是当前正在进行的比赛,得分会不断变化,并且更新频繁,在这种情况下,WebSocket 就是更好的选择。
- 小负载的高频消息传递。如今越来越多的开发人员正在通过移动设备的 GPS 功能来记录 Web 应用程序的方位感知。如果我们需要记录一段时间内用户的位置信息,高频率发送更加细粒度的位置数据,从而起到实时分享功能(例如运动类 APP),WebSocket 所使用的 TCP 连接会让数据交换飞起来。
- 多人协同的场景。例如近几年发展迅速的在线教育,学生可以足不出户,即可与老师以及其他同学一起进行实时沟通与交流,诸如布置作业、师生互动、问题讨论等等强实时交互类的场景都可交由 WebSocket 协议支撑完成,从而满足低延迟,高及时的场景要求。
目前又拍云已经可以支持 WebSocket 无缝接入,依托于又拍云 CDN 1100+ 全球节点,10Tbps 带宽储备,国内主流运营商支持,通过 TCP 协议优化、链路优化、内容优化、智能调度等技术手段,大大提升加速性能。全自助化配置管理,配置策略全网 10 秒内生效;提供全方位的 API 接口,支持多样化管理,只需简单的配置,就可以迅速接入,享受全站加速。