深入浅出Node.js(五):初探Node.js的异步I/O实现
之前介绍了Node.js的事件机制,也许读者对此尚会觉得意犹未尽,因为仅仅只是简单的事件机制,并不能道尽Node.js的神奇。如果Node.js是一盘别开生面的磁带,那么事件与异步分别是其A面和B面,它们共同组成了Node.js的别样之处。本文将翻转Node.js到B面,与你共同聆听。 异步I/O 在操作系统中,程序运行的空间分为内核空间和用户空间。我们常常提起的异步I/O,其实质是用户空间中的程序不用依赖内核空间中的I/O操作实际完成,即可进行后续任务。以下伪代码模仿了一个从磁盘上获取文件和一个从网络中获取文件的操作。异步I/O的效果就是getFileFromNet的调用不依赖于getFile调用的结束。
1 2 |
getFile("file_path"); getFileFromNet("url"); |
如果以上两个任务的时间分别为m和n。采用同步方式的程序要完成这两个任务的时间总花销会是m + n。但是如果是采用异步方式的程序,在两种I/O可以并行的状况下(比如网络I/O与文件I/O),时间开销将会减小为max(m, n)。 异步I/O的必要性 有的语言为了设计得使应用程序调用方便,将程序设计为同步I/O的模型。这意味着程序中的后续任务都需要等待I/O的完成。在等待I/O完成的过程中,程序无法充分利用CPU。为了充分利用CPU,和使I/O可以并行,目前有两种方式可以达到目的: 多线程单进程 多线程的设计之处就是为了在共享的程序空间中,实现并行处理任务,从而达到充分利用CPU的效果。多线程的缺点在于执行时上下文交换的开销较大,和状态同步(锁)的问题。同样它也使得程序的编写和调用复杂化。 单线程多进程 为了避免多线程造成的使用不便问题,有的语言选择了单线程保持调用简单化,采用启动多进程的方式来达到充分利用CPU和提升总体的并行处理能力。 它的缺点在于业务逻辑复杂时(涉及多个I/O调用),因为业务逻辑不能分布到多个进程之间,事务处理时长要远远大于多线程模式。 前者在性能优化上还有回旋的余地,后者的做法纯粹是一种加三倍服务器的行为。 而且现在的大型Web应用中,单机的情形是十分稀少的,一个事务往往需要跨越网络几次才能完成最终处理。如果网络速度不够理想,m和n值都将会变大,这时同步I/O的语言模型将会露出其最脆弱的状态。 这种场景下的异步I/O将会体现其优势,max(m, n)的时间开销可以有效地缓解m和n值增长带来的性能问题。而当并行任务更多的时候,m + n + …与max(m, n, …)之间的孰优孰劣更是一目了然。从这个公式中,可以了解到异步I/O在分布式环境中是多么重要,而Node.js天然地支持这种异步I/O,这是众多云计算厂商对其青睐的根本原因。 操作系统对异步I/O的支持 我们听到Node.js时,我们常常会听到异步,非阻塞,回调,事件这些词语混合在一起。其中,异步与非阻塞听起来似乎是同一回事。从实际效果的角度说,异步和非阻塞都达到了我们并行I/O的目的。但是从计算机内核I/O而言,异步/同步和阻塞/非阻塞实际上时两回事。 I/O的阻塞与非阻塞 阻塞模式的I/O会造成应用程序等待,直到I/O完成。同时操作系统也支持将I/O操作设置为非阻塞模式,这时应用程序的调用将可能在没有拿到真正数据时就立即返回了,为此应用程序需要多次调用才能确认I/O操作完全完成。 I/O的同步与异步 I/O的同步与异步出现在应用程序中。如果做阻塞I/O调用,应用程序等待调用的完成的过程就是一种同步状况。相反,I/O为非阻塞模式时,应用程序则是异步的。 异步I/O与轮询技术 当进行非阻塞I/O调用时,要读到完整的数据,应用程序需要进行多次轮询,才能确保读取数据完成,以进行下一步的操作。 轮询技术的缺点在于应用程序要主动调用,会造成占用较多CPU时间片,性能较为低下。现存的轮询技术有以下这些: read select poll epoll pselect kqueue read是性能最低的一种,它通过重复调用来检查I/O的状态来完成完整数据读取。select是一种改进方案,通过对文件描述符上的事件状态来进行判断。操作系统还提供了poll、epoll等多路复用技术来提高性能。 轮询技术满足了异步I/O确保获取完整数据的保证。但是对于应用程序而言,它仍然只能算时一种同步,因为应用程序仍然需要主动去判断I/O的状态,依旧花费了很多CPU时间来等待。 上一种方法重复调用read进行轮询直到最终成功,用户程序会占用较多CPU,性能较为低下。而实际上操作系统提供了select方法来代替这种重复read轮询进行状态判断。select内部通过检查文件描述符上的事件状态来进行判断数据是否完全读取。但是对于应用程序而言它仍然只能算是一种同步,因为应用程序仍然需要主动去判断I/O的状态,依旧花费了很多CPU时间等待,select也是一种轮询。 理想的异步I/O模型 理想的异步I/O应该是应用程序发起异步调用,而不需要进行轮询,进而处理下一个任务,只需在I/O完成后通过信号或是回调将数据传递给应用程序即可。 幸运的是,在Linux下存在一种这种方式,它原生提供了一种异步非阻塞I/O方式(AIO)即是通过信号或回调来传递数据的。 不幸的是,只有Linux下有这么一种支持,而且还有缺陷(AIO仅支持内核I/O中的O_DIRECT方式读取,导致无法利用系统缓存。参见:http://forum.nginx.org/read.php?2,113524,113587#msg-113587 以上都是基于非阻塞I/O进行的设定。另一种理想的异步I/O是采用阻塞I/O,但加入多线程,将I/O操作分到多个线程上,利用线程之间的通信来模拟异步。Glibc的AIO便是这样的典型http://www.ibm.com/developerworks/linux/library/l-async/。然而遗憾在于,它存在一些难以忍受的缺陷和bug。可以简单的概述为:Linux平台下没有完美的异步I/O支持。 所幸的是,libev的作者Marc Alexander Lehmann重新实现了一个异步I/O的库:libeio。libeio实质依然是采用线程池与阻塞I/O模拟出来的异步I/O。 那么在Windows平台下的状况如何呢?而实际上,Windows有一种独有的内核异步IO方案:IOCP。IOCP的思路是真正的异步I/O方案,调用异步方法,然后等待I/O完成通知。IOCP内部依旧是通过线程实现,不同在于这些线程由系统内核接手管理。IOCP的异步模型与Node.js的异步调用模型已经十分近似。 以上两种方案则正是Node.js选择的异步I/O方案。由于Windows平台和*nix平台的差异,Node.js提供了libuv来作为抽象封装层,使得所有平台兼容性的判断都由这一层次来完成,保证上层的Node.js与下层的libeio/libev及IOCP之间各自独立。Node.js在编译期间会判断平台条件,选择性编译unix目录或是win目录下的源文件到目标程序中。 下文我们将通过解释Windows下Node.js异步I/O(IOCP)的简单例子来探寻一下从JavaScript代码到系统内核之间都发生了什么。 Node.js的异步I/O模型 很多同学在遇见Node.js后必然产生过对回调函数究竟如何被调用产生过好奇。在文件I/O这一块与普通的业务逻辑的回调函数不同在于它不是由我们自己的代码所触发,而是系统调用结束后,由系统触发的。下面我们以最简单的fs.open方法来作为例子,探索Node.js与底层之间是如何执行异步I/O调用和回调函数究竟是如何被调用执行的。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
fs.open = function(path, flags, mode, callback) { callback = arguments[arguments.length - 1]; if (typeof(callback) !== 'function') { callback = noop; } mode = modeNum(mode, 438 /*=0666*/); binding.open(pathModule._makeLong(path), stringToFlags(flags), mode, callback); }; |
fs.open的作用是根据指定路径和参数,去打开一个文件,从而得到一个文件描述符,是后续所有I/O操作的初始操作。 在JavaScript层面上调用的fs.open方法最终都透过node_file.cc调用到了libuv中的uv_fs_open方法,这里libuv作为封装层,分别写了两个平台下的代码实现,编译之后,只会存在一种实现被调用。 请求对象 在uv_fs_open的调用过程中,Node.js创建了一个FSReqWrap请求对象。从JavaScript传入的参数和当前方法都被封装在这个请求对象中,其中回调函数则被设置在这个对象的oncomplete_sym属性上。
1 |
req_wrap->object_->Set(oncomplete_sym, callback); |
对象包装完毕后,调用QueueUserWorkItem方法将这个FSReqWrap对象推入线程池中等待执行。
1 |
QueueUserWorkItem(&uv_fs_thread_proc, req, WT_EXECUTELONGFUNCTION) |
QueueUserWorkItem接受三个参数,第一个是要执行的方法,第二个是方法的上下文,第三个是执行的标志。当线程池中有可用线程的时候调用uv_fs_thread_proc方法执行。该方法会根据传入的类型调用相应的底层函数,以uv_fs_open为例,实际会调用到fs__open方法。调用完毕之后,会将获取的结果设置在req->result上。然后调用PostQueuedCompletionStatus通知我们的IOCP对象操作已经完成。
1 |
PostQueuedCompletionStatus((loop)->iocp, 0, 0, &((req)->overlapped)) |
PostQueuedCompletionStatus方法的作用是向创建的IOCP上相关的线程通信,线程根据执行状况和传入的参数判定退出。 至此,由JavaScript层面发起的异步调用第一阶段就此结束。 事件循环 在调用uv_fs_open方法的过程中实际上应用到了事件循环。以在Windows平台下的实现中,启动Node.js时,便创建了一个基于IOCP的事件循环loop,并一直处于执行状态。
1 |
uv_run(uv_default_loop()); |
每次循环中,它会调用IOCP相关的GetQueuedCompletionStatus方法检查是否线程池中有执行完的请求,如果存在,poll操作会将请求对象加入到loop的pending_reqs_tail属性上。 另一边这个循环也会不断检查loop对象上的pending_reqs_tail引用,如果有可用的请求对象,就取出请求对象的result属性作为结果传递给oncomplete_sym执行,以此达到调用JavaScript中传入的回调函数的目的。 至此,整个异步I/O的流程完成结束。其流程如下: 事件循环和请求对象构成了Node.js的异步I/O模型的两个基本元素,这也是典型的消费者生产者场景。在Windows下通过IOCP的GetQueuedCompletionStatus、PostQueuedCompletionStatus、QueueUserWorkItem方法与事件循环实。对于*nix平台下,这个流程的不同之处在与实现这些功能的方法是由libeio和libev提供。 参考: 《nodejs异步IO的实现》http://cnodejs.org/blog/?p=244 《linux AIO (异步IO) 那点事儿》http://cnodejs.org/blog/?p=2426 《libev 设计分析》http://cnodejs.org/blog/?p=2489 《Node Roadmap》http://nodejs.org/nodeconf.pdf 《多路复用select(2)与事件通知poll(2)、epoll(7)内核源码初探》http://blog.dccmx.com/2011/04/select-poll-epoll-in-kernel/ 《使用异步 I/O […]
View Details深入浅出Node.js(三):深入Node.js的模块机制
专栏的第三篇文章《深入Node.js的模块机制》。之前介绍了Node.js安装的基础知识,本文将深入Node.js的模块机制。 Node.js模块的实现 之前在网上查阅了许多介绍Node.js的文章,可惜对于Node.js的模块机制大都着墨不多。在后续介绍模块的使用之前,我认为有必要深入一下Node.js的模块机制。 CommonJS规范 早在Netscape诞生不久后,JavaScript就一直在探索本地编程的路,Rhino是其代表产物。无奈那时服务端JavaScript走的路均是参考众多服务器端语言来实现的,在这样的背景之下,一没有特色,二没有实用价值。但是随着JavaScript在前端的应用越来越广泛,以及服务端JavaScript的推动,JavaScript现有的规范十分薄弱,不利于JavaScript大规模的应用。那些以JavaScript为宿主语言的环境中,只有本身的基础原生对象和类型,更多的对象和API都取决于宿主的提供,所以,我们可以看到JavaScript缺少这些功能: JavaScript没有模块系统。没有原生的支持密闭作用域或依赖管理。 JavaScript没有标准库。除了一些核心库外,没有文件系统的API,没有IO流API等。 JavaScript没有标准接口。没有如Web Server或者数据库的统一接口。 JavaScript没有包管理系统。不能自动加载和安装依赖。 于是便有了CommonJS(http://www.commonjs.org)规范的出现,其目标是为了构建JavaScript在包括Web服务器,桌面,命令行工具,及浏览器方面的生态系统。 CommonJS制定了解决这些问题的一些规范,而Node.js就是这些规范的一种实现。Node.js自身实现了require方法作为其引入模块的方法,同时NPM也基于CommonJS定义的包规范,实现了依赖管理和模块自动安装等功能。这里我们将深入一下Node.js的require机制和NPM基于包规范的应用。 简单模块定义和使用 在Node.js中,定义一个模块十分方便。我们以计算圆形的面积和周长两个方法为例,来表现Node.js中模块的定义方式。
1 2 3 4 5 6 7 |
var PI = Math.PI; exports.area = function (r) { return PI * r * r; }; exports.circumference = function (r) { return 2 * PI * r; }; |
将这个文件存为circle.js,并新建一个app.js文件,并写入以下代码:
1 2 |
var circle = require('./circle.js'); console.log( 'The area of a circle of radius 4 is ' + circle.area(4)); |
可以看到模块调用也十分方便,只需要require需要调用的文件即可。 在require了这个文件之后,定义在exports对象上的方法便可以随意调用。Node.js将模块的定义和调用都封装得极其简单方便,从API对用户友好这一个角度来说,Node.js的模块机制是非常优秀的。 模块载入策略 Node.js的模块分为两类,一类为原生(核心)模块,一类为文件模块。原生模块在Node.js源代码编译的时候编译进了二进制执行文件,加载的速度最快。另一类文件模块是动态加载的,加载速度比原生模块慢。但是Node.js对原生模块和文件模块都进行了缓存,于是在第二次require时,是不会有重复开销的。其中原生模块都被定义在lib这个目录下面,文件模块则不定性。
1 |
node app.js |
由于通过命令行加载启动的文件几乎都为文件模块。我们从Node.js如何加载文件模块开始谈起。加载文件模块的工作,主要由原生模块module来实现和完成,该原生模块在启动时已经被加载,进程直接调用到runMain静态方法。
1 2 3 4 5 |
// bootstrap main module. Module.runMain = function () { // Load the main module--the command line argument. Module._load(process.argv[1], null, true); }; |
_load静态方法在分析文件名之后执行
1 |
var module = new Module(id, parent); |
并根据文件路径缓存当前模块对象,该模块实例对象则根据文件名加载。
1 |
module.load(filename); |
实际上在文件模块中,又分为3类模块。这三类文件模块以后缀来区分,Node.js会根据后缀名来决定加载方法。 .js。通过fs模块同步读取js文件并编译执行。 .node。通过C/C++进行编写的Addon。通过dlopen方法进行加载。 .json。读取文件,调用JSON.parse解析加载。 这里我们将详细描述js后缀的编译过程。Node.js在编译js文件的过程中实际完成的步骤有对js文件内容进行头尾包装。以app.js为例,包装之后的app.js将会变成以下形式:
1 2 3 4 |
(function (exports, require, module, __filename, __dirname) { var circle = require('./circle.js'); console.log('The area of a circle of radius 4 is ' + circle.area(4)); }); |
这段代码会通过vm原生模块的runInThisContext方法执行(类似eval,只是具有明确上下文,不污染全局),返回为一个具体的function对象。最后传入module对象的exports,require方法,module,文件名,目录名作为实参并执行。 这就是为什么require并没有定义在app.js 文件中,但是这个方法却存在的原因。从Node.js的API文档中可以看到还有__filename、__dirname、module、exports几个没有定义但是却存在的变量。其中__filename和__dirname在查找文件路径的过程中分析得到后传入的。module变量是这个模块对象自身,exports是在module的构造函数中初始化的一个空对象({},而不是null)。 在这个主文件中,可以通过require方法去引入其余的模块。而其实这个require方法实际调用的就是load方法。 load方法在载入、编译、缓存了module后,返回module的exports对象。这就是circle.js文件中只有定义在exports对象上的方法才能被外部调用的原因。 以上所描述的模块载入机制均定义在lib/module.js中。 require方法中的文件查找策略 由于Node.js中存在4类模块(原生模块和3种文件模块),尽管require方法极其简单,但是内部的加载却是十分复杂的,其加载优先级也各自不同。 从文件模块缓存中加载 尽管原生模块与文件模块的优先级不同,但是都不会优先于从文件模块的缓存中加载已经存在的模块。 从原生模块加载 原生模块的优先级仅次于文件模块缓存的优先级。require方法在解析文件名之后,优先检查模块是否在原生模块列表中。以http模块为例,尽管在目录下存在一个http/http.js/http.node/http.json文件,require(“http”)都不会从这些文件中加载,而是从原生模块中加载。 原生模块也有一个缓存区,同样也是优先从缓存区加载。如果缓存区没有被加载过,则调用原生模块的加载方式进行加载和执行。 从文件加载 当文件模块缓存中不存在,而且不是原生模块的时候,Node.js会解析require方法传入的参数,并从文件系统中加载实际的文件,加载过程中的包装和编译细节在前一节中已经介绍过,这里我们将详细描述查找文件模块的过程,其中,也有一些细节值得知晓。 require方法接受以下几种参数的传递: http、fs、path等,原生模块。 ./mod或../mod,相对路径的文件模块。 /pathtomodule/mod,绝对路径的文件模块。 mod,非原生模块的文件模块。 在进入路径查找之前有必要描述一下module path这个Node.js中的概念。对于每一个被加载的文件模块,创建这个模块对象的时候,这个模块便会有一个paths属性,其值根据当前文件的路径计算得到。我们创建modulepath.js这样一个文件,其内容为:
1 |
console.log(module.paths); |
我们将其放到任意一个目录中执行node modulepath.js命令,将得到以下的输出结果。
1 2 3 4 |
[ '/home/jackson/research/node_modules', '/home/jackson/node_modules', '/home/node_modules', '/node_modules' ] |
Windows下:
1 |
[ 'c:\\nodejs\\node_modules', 'c:\\node_modules' ] |
可以看出module path的生成规则为:从当前文件目录开始查找node_modules目录;然后依次进入父目录,查找父目录下的node_modules目录;依次迭代,直到根目录下的node_modules目录。 除此之外还有一个全局module path,是当前node执行文件的相对目录(../../lib/node)。如果在环境变量中设置了HOME目录和NODE_PATH目录的话,整个路径还包含NODE_PATH和HOME目录下的.node_libraries与.node_modules。其最终值大致如下:
1 |
[NODE_PATH,HOME/.node_modules,HOME/.node_libraries,execPath/../../lib/node] |
下图是笔者从源代码中整理出来的整个文件查找流程: 简而言之,如果require绝对路径的文件,查找时不会去遍历每一个node_modules目录,其速度最快。其余流程如下: 从module path数组中取出第一个目录作为查找基准。 直接从目录中查找该文件,如果存在,则结束查找。如果不存在,则进行下一条查找。 尝试添加.js、.json、.node后缀后查找,如果存在文件,则结束查找。如果不存在,则进行下一条。 尝试将require的参数作为一个包来进行查找,读取目录下的package.json文件,取得main参数指定的文件。 尝试查找该文件,如果存在,则结束查找。如果不存在,则进行第3条查找。 如果继续失败,则取出module path数组中的下一个目录作为基准查找,循环第1至5个步骤。 如果继续失败,循环第1至6个步骤,直到module path中的最后一个值。 如果仍然失败,则抛出异常。 整个查找过程十分类似原型链的查找和作用域的查找。所幸Node.js对路径查找实现了缓存机制,否则由于每次判断路径都是同步阻塞式进行,会导致严重的性能消耗。 包结构 前面提到,JavaScript缺少包结构。CommonJS致力于改变这种现状,于是定义了包的结构规范(http://wiki.commonjs.org/wiki/Packages/1.0 )。而NPM的出现则是为了在CommonJS规范的基础上,实现解决包的安装卸载,依赖管理,版本管理等问题。require的查找机制明了之后,我们来看一下包的细节。 一个符合CommonJS规范的包应该是如下这种结构: 一个package.json文件应该存在于包顶级目录下 二进制文件应该包含在bin目录下。 JavaScript代码应该包含在lib目录下。 […]
View Details深入浅出Node.js(一):什么是Node.js
【编者按】:Node.js从2009年诞生至今,已经发展了两年有余,其成长的速度有目共睹。从在github的访问量超过Rails,到去年底Node.jsS创始人Ryan Dalh加盟Joyent获得企业资助,再到今年发布Windows移植版本,Node.js的前景获得了技术社区的肯定。InfoQ一直在关注Node.js的发展,在今年的两次Qcon大会(北京站和杭州站)都有专门的讲座。为了更好地促进Node.js在国内的技术推广,我们决定开设“深入浅出Node.js”专栏,邀请来自Node.js领域的布道师、开发人员、技术专家来讲述Node.js的各方面内容,让读者对Node.js有更深入的了解,并且能够积极投入到新技术的讨论和实践中。 专栏的第一篇文章《什么是Node.js》尝试从各个角度来阐述Node.js的基本概念、发展历史、优势等,对该领域不熟悉的开发人员可以通过本文了解Node.js的一些基础知识。 从名字说起 有关Node.js的技术报道越来越多,Node.js的写法也是五花八门,有写成NodeJS的,有写成Nodejs的,到底哪一种写法最标准呢,我们不妨遵循官方的说法。在Node.js的官方网站上,一直将其项目称之为”Node“或者”Node.js“,没有发现其他的说法,”Node“用的最多,考虑到Node这个单词的意思和用途太广泛,容易让开发人员误解,我们采用了第二种称呼——”Node.js“,js的后缀点出了Node项目的本意,其他的名称五花八门,没有确切的出处,我们不推荐使用。 Node.js不是JS应用、而是JS运行平台 看到Node.js这个名字,初学者可能会误以为这是一个Javascript应用,事实上,Node.js采用C++语言编写而成,是一个Javascript的运行环境。为什么采用C++语言呢?据Node.js创始人Ryan Dahl回忆,他最初希望采用Ruby来写Node.js,但是后来发现Ruby虚拟机的性能不能满足他的要求,后来他尝试采用V8引擎,所以选择了C++语言。既然不是Javascript应用,为何叫.js呢?因为Node.js是一个Javascript的运行环境。提到Javascript,大家首先想到的是日常使用的浏览器,现代浏览器包含了各种组件,包括渲染引擎、Javascript引擎等,其中Javascript引擎负责解释执行网页中的Javascript代码。作为Web前端最重要的语言之一,Javascript一直是前端工程师的专利。不过,Node.js是一个后端的Javascript运行环境(支持的系统包括*nux、Windows),这意味着你可以编写系统级或者服务器端的Javascript代码,交给Node.js来解释执行,简单的命令类似于:
1 |
#node helloworld.js |
Node.js采用了Google Chrome浏览器的V8引擎,性能很好,同时还提供了很多系统级的API,如文件操作、网络编程等。浏览器端的Javascript代码在运行时会受到各种安全性的限制,对客户系统的操作有限。相比之下,Node.js则是一个全面的后台运行时,为Javascript提供了其他语言能够实现的许多功能。 Node.js采用事件驱动、异步编程,为网络服务而设计 事件驱动这个词并不陌生,在某些传统语言的网络编程中,我们会用到回调函数,比如当socket资源达到某种状态时,注册的回调函数就会执行。Node.js的设计思想中以事件驱动为核心,它提供的绝大多数API都是基于事件的、异步的风格。以Net模块为例,其中的net.Socket对象就有以下事件:connect、data、end、timeout、drain、error、close等,使用Node.js的开发人员需要根据自己的业务逻辑注册相应的回调函数。这些回调函数都是异步执行的,这意味着虽然在代码结构中,这些函数看似是依次注册的,但是它们并不依赖于自身出现的顺序,而是等待相应的事件触发。事件驱动、异步编程的设计(感兴趣的读者可以查阅笔者的另一篇文章《Node.js的异步编程风格》),重要的优势在于,充分利用了系统资源,执行代码无须阻塞等待某种操作完成,有限的资源可以用于其他的任务。此类设计非常适合于后端的网络服务编程,Node.js的目标也在于此。在服务器开发中,并发的请求处理是个大问题,阻塞式的函数会导致资源浪费和时间延迟。通过事件注册、异步函数,开发人员可以提高资源的利用率,性能也会改善。 从Node.js提供的支持模块中,我们可以看到包括文件操作在内的许多函数都是异步执行的,这和传统语言存在区别,而且为了方便服务器开发,Node.js的网络模块特别多,包括HTTP、DNS、NET、UDP、HTTPS、TLS等,开发人员可以在此基础上快速构建Web服务器。以简单的helloworld.js为例:
1 2 3 4 5 |
var http = require('http'); http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World\n'); }).listen(80, "127.0.0.1"); |
上面的代码搭建了一个简单的http服务器(运行示例部署在http://helloworld.cnodejs.net/中,读者可以访问),在本地监听80端口,对于任意的http请求,服务器都返回一个头部状态码为200、Content-Type'值为text/plain'的”Hello World“文字响应。从这个小例子中,我们可以看出几点: Node.js的网络编程比较便利,提供的模块(在这里是http)开放了容易上手的API接口,短短几行代码就可以构建服务器。 体现了事件驱动、异步编程,在createServer函数的参数中指定了一个回调函数(采用Javascript的匿名函数实现),当有http请求发送过来时,Node.js就会调用该回调函数来处理请求并响应。当然,这个例子相对简单,没有太多的事件注册,在以后的文章中读者会看到更多的实际例子。 Node.js的特点 下面我们来说说Node.js的特点。事件驱动、异步编程的特点刚才已经详细说过了,这里不再重复。 Node.js的性能不错。按照创始人Ryan Dahl的说法,性能是Node.js考虑的重要因素,选择C++和V8而不是Ruby或者其他的虚拟机也是基于性能的目的。Node.js在设计上也是比较大胆,它以单进程、单线程模式运行(很吃惊,对吧?这和Javascript的运行方式一致),事件驱动机制是Node.js通过内部单线程高效率地维护事件循环队列来实现的,没有多线程的资源占用和上下文切换,这意味着面对大规模的http请求,Node.js凭借事件驱动搞定一切,习惯了传统语言的网络服务开发人员可能对多线程并发和协作非常熟悉,但是面对Node.js,我们需要接受和理解它的特点。由此我们是否可以推测出这样的设计会导致负载的压力集中在CPU(事件循环处理?)而不是内存(还记得Java虚拟机抛出OutOfMemory异常的日子吗?),眼见为实,不如来看看淘宝共享数据平台团队对Node.js的性能测试: 物理机配置:RHEL 5.2、CPU 2.2GHz、内存4G Node.js应用场景:MemCache代理,每次取100字节数据 连接池大小:50 并发用户数:100 测试结果(socket模式):内存(30M)、QPS(16700)、CPU(95%) 从上面的结果,我们可以看到在这样的测试场景下,qps能够达到16700次,内存仅占用30M(其中V8堆占用22M),CPU则达到95%,可能成为瓶颈。此外,还有不少实践者对Node.js做了性能分析,总的来说,它的性能让人信服,也是受欢迎的重要原因。既然Node.js采用单进程、单线程模式,那么在如今多核硬件流行的环境中,单核性能出色的Node.js如何利用多核CPU呢?创始人Ryan Dahl建议,运行多个Node.js进程,利用某些通信机制来协调各项任务。目前,已经有不少第三方的Node.js多进程支持模块发布,专栏后面的文章会详细讲述Node.js在多核CPU下的编程。 Node.js的另一个特点是它支持的编程语言是Javascript。关于动态语言和静态语言的优缺点比较在这里不再展开讨论。只说三点: Javascript作为前端工程师的主力语言,在技术社区中有相当的号召力。而且,随着Web技术的不断发展,特别是前端的重要性增加,不少前端工程师开始试水”后台应用“,在许多采用Node.js的企业中,工程师都表示因为习惯了Javascript,所以选择Node.js。 Javascript的匿名函数和闭包特性非常适合事件驱动、异步编程,从helloworld例子中我们可以看到回调函数采用了匿名函数的形式来实现,很方便。闭包的作用则更大,看下面的代码示例:
1 2 3 4 5 6 7 8 9 10 |
var hostRequest = http.request(requestOptions,function(response) { var responseHTML =''; response.on('data', function (chunk) { responseHTML = responseHTML + chunk; }); response.on('end',function(){ console.log(responseHTML); // do something useful }); }); |
在上面的代码中,我们需要在end事件中处理responseHTML变量,由于Javascript的闭包特性,我们可以在两个回调函数之外定义responseHTML变量,然后在data事件对应的回调函数中不断修改其值,并最终在end事件中访问处理。 Javascript在动态语言中性能较好,有开发人员对Javacript、Python、Ruby等动态语言做了性能分析,发现Javascript的性能要好于其他语言,再加上V8引擎也是同类的佼佼者,所以Node.js的性能也受益其中。 Node.js发展简史 2009年2月,Ryan Dahl在博客上宣布准备基于V8创建一个轻量级的Web服务器并提供一套库。 2009年5月,Ryan Dahl在GitHub上发布了最初版本的部分Node.js包,随后几个月里,有人开始使用Node.js开发应用。 2009年11月和2010年4月,两届JSConf大会都安排了Node.js的讲座。 2010年年底,Node.js获得云计算服务商Joyent资助,创始人Ryan Dahl加入Joyent全职负责Node.js的发展。 2011年7月,Node.js在微软的支持下发布Windows版本。 Node.js应用案例 虽然Node.js诞生刚刚两年多,但是其发展势头逐渐赶超Ruby/Rails,我们在这里列举了部分企业应用Node.js的案例,听听来自客户的声音。 在社交网站LinkedIn最新发布的移动应用中,NodeJS是该移动应用的后台基础。LinkedIn移动开发主管Kiran Prasad对媒体表示,其整个移动软件平台都由NodeJS构建而成: LinkedIn内部使用了大量的技术,但是在移动服务器这一块,我们完全基于Node。 (使用它的原因)第一,是因为其灵活性。第二,如果你了解Node,就会发现它最擅长的事情是与其他服务通信。移动应用必须与我们的平台API和数据库交互。我们没有做太多数据分析。相比之前采用的Ruby on Rails技术,开发团队发现Node在性能方面提高很多。他们在每台物理机上跑了15个虚拟服务器(15个实例),其中4个实例即可处理双倍流量。容量评估基于负载测试的结果。 企业社会化服务网站Yammer则利用Node创建了针对其自身平台的跨域代理服务器,第三方的开发人员可以通过该服务器实现从自身域托管的Javascript代码与Yammer平台API的AJAX通信。Yammer平台技术主管Jim Patterson对Node的优点和缺点提出了自己的看法: (优点)因为Node是基于事件驱动和无阻塞的,所以非常适合处理并发请求,因此构建在Node上的代理服务器相比其他技术实现(如Ruby)的服务器表现要好得多。此外,与Node代理服务器交互的客户端代码是由javascript语言编写的,因此客户端和服务器端都用同一种语言编写,这是非常美妙的事情。 (缺点)Node是一个相对新的开源项目,所以不太稳定,它总是一直在变,而且缺少足够多的第三方库支持。看起来,就像是Ruby/Rails当年的样子。 知名项目托管网站GitHub也尝试了Node应用。该Node应用称为NodeLoad,是一个存档下载服务器(每当你下载某个存储分支的tarball或者zip文件时就会用到它)。GitHub之前的存档下载服务器采用Ruby编写。在旧系统中,下载存档的请求会创建一个Resque任务。该任务实际上在存档服务器上运行一个git archive命令,从某个文件服务器中取出数据。然后,初始的请求分配给你一个小型Ruby Sinatra应用等待该任务。它其实只是在检查memcache flag是否存在,然后再重定向到最终的下载地址上。旧系统运行大约3个Sinatra实例和3个Resque worker。GitHub的开发人员觉得这是Node应用的好机会。Node基于事件驱动,相比Ruby的阻塞模型,Node能够更好地处理git存档。在编写新下载服务器过程中,开发人员觉得Node非常适合该功能,此外,他们还里利用了Node库socket.io来监控下载状态。 不仅在国外,Node的优点也同样吸引了国内开发人员的注意,淘宝就实际应用了Node技术: MyFOX 是一个数据处理中间件,负责从一个MySQL集群中提取数据、计算并输出统计结果。用户提交一段SQL语句,MyFOX根据该SQL命令的语义,生成各个数据库分片所需要执行的查询语句,并发送至各个分片,再将结果进行汇总和计算。 MyFOX的特点是CPU密集,无文件IO,并只处理只读数据。起初MyFOX使用PHP编写,但遇到许多问题。例如PHP是单线程的,MySQL又需要阻塞查询,因此很难并发请求数据,后来的解决方案是使用nginx和dirzzle,并基于HTTP协议实现接口,并通过curl_multi_get命 令进行请求。不过MyFOX项目组最终还是决定使用Node.js来实现MyFOX。 选择Node.js有许多方面的原因,比如考虑了兴趣及社区发展,同时也希望可以提高并发能力,榨干CPU。例如,频繁地打开和关闭连接会让大量端口处于等待状态,当并发数量上去之后,时常会因为端口不够用(处于TIME_WAIT状态)而导致连接失败。之前往往是通过修改系统设置来减少等待时间以绕开这个错误,然而使用连接池便可以很好地解决这个问题。此外,以前MyFOX会在某些缓存失效的情况下出现十分密集的访问压力,使用 Node.js便可以共享查询状态,让某些请求“等待片刻”,以便系统重新填充缓存内容。 小结 本文简要介绍了Node.js的基本知识,包括概念、特点、历史、案例等等。作为一个仅仅2岁的平台,Node.js的发展势头有目共睹,越来越多的企业开始关注并尝试Node.js,前后端开发人员应该了解相关的内容。 参考文献 [1] http://nodejs.org/ [2] http://beakkon.com/geek/node.js/why-node.js-single-thread-event-loop-javascript [3] http://www.tbdata.org/archives/1285 [4] http://www.infoq.com/interviews/node-ryan-dahl [5] http://www.infoq.com/cn/news/2011/08/enterprise-nodejs [6] http://www.infoq.com/cn/news/2010/11/nodejs-joyent [7] http://www.infoq.com/cn/news/2011/06/node-exe […]
View Detailswin下node-mongodb-native操作mongodb
nodejs win下没有npm功能,应用各种库文件相当杯具,这里用了node-mongodb-native在win下操作mongodb,小D很菜,对nodejs各种不懂,勉强简单封装了个对mongodb的curd操作,,,, 首先去下载https://github.com/christkv/node-mongodb-native,把文件lib下的东西放到了如下目录 如何操作看他的文档吧,e文不行的估计看例子也能看个大概….下面是封装代码mghp.js
1 |
<span style="line-height:1.5;color:#0000ff;">var</span><span style="line-height:1.5;color:#000000;"> mongodb </span><span style="line-height:1.5;color:#000000;">=</span><span style="line-height:1.5;color:#000000;"> require(</span><span style="line-height:1.5;color:#000000;">'</span><span style="line-height:1.5;color:#000000;">./refLib/mongodb</span><span style="line-height:1.5;color:#000000;">'</span><span style="line-height:1.5;color:#000000;">);<br /><br /></span><span style="line-height:1.5;color:#0000ff;">var</span><span style="line-height:1.5;color:#000000;"> _server </span><span style="line-height:1.5;color:#000000;">=</span><span style="line-height:1.5;color:#000000;"></span><span style="line-height:1.5;color:#0000ff;">function</span><span style="line-height:1.5;color:#000000;">(mongoCollection, mongoDB) {<br /></span><span style="line-height:1.5;color:#0000ff;">var</span><span style="line-height:1.5;color:#000000;"> sv </span><span style="line-height:1.5;color:#000000;">=</span><span style="line-height:1.5;color:#000000;"></span><span style="line-height:1.5;color:#0000ff;">new</span><span style="line-height:1.5;color:#000000;"> mongodb.Server(</span><span style="line-height:1.5;color:#000000;">'</span><span style="line-height:1.5;color:#000000;">127.0.0.1</span><span style="line-height:1.5;color:#000000;">'</span><span style="line-height:1.5;color:#000000;">, </span><span style="line-height:1.5;color:#000000;">27017</span><span style="line-height:1.5;color:#000000;">, {});<br /></span><span style="line-height:1.5;color:#0000ff;">this</span><span style="line-height:1.5;color:#000000;">.mongoCollection </span><span style="line-height:1.5;color:#000000;">=</span><span style="line-height:1.5;color:#000000;"> mongoCollection </span><span style="line-height:1.5;color:#000000;">||</span><span style="line-height:1.5;color:#000000;"></span><span style="line-height:1.5;color:#000000;">'</span><span style="line-height:1.5;color:#000000;">nodeCollection</span><span style="line-height:1.5;color:#000000;">'</span><span style="line-height:1.5;color:#000000;">;<br /></span><span style="line-height:1.5;color:#0000ff;">this</span><span style="line-height:1.5;color:#000000;">.mongoDB </span><span style="line-height:1.5;color:#000000;">=</span><span style="line-height:1.5;color:#000000;"> mongoDB </span><span style="line-height:1.5;color:#000000;">||</span><span style="line-height:1.5;color:#000000;"></span><span style="line-height:1.5;color:#000000;">'</span><span style="line-height:1.5;color:#000000;">nodeDB</span><span style="line-height:1.5;color:#000000;">'</span><span style="line-height:1.5;color:#000000;">;<br /></span><span style="line-height:1.5;color:#0000ff;">this</span><span style="line-height:1.5;color:#000000;">.db </span><span style="line-height:1.5;color:#000000;">=</span><span style="line-height:1.5;color:#000000;"></span><span style="line-height:1.5;color:#0000ff;">new</span><span style="line-height:1.5;color:#000000;"> mongodb.Db(</span><span style="line-height:1.5;color:#0000ff;">this</span><span style="line-height:1.5;color:#000000;">.mongoDB, sv, {});<br />}<br /><br />_server.prototype.Open </span><span style="line-height:1.5;color:#000000;">=</span><span style="line-height:1.5;color:#000000;"></span><span style="line-height:1.5;color:#0000ff;">function</span><span style="line-height:1.5;color:#000000;">(callback) {<br /></span><span style="line-height:1.5;color:#0000ff;">var</span><span style="line-height:1.5;color:#000000;"> that </span><span style="line-height:1.5;color:#000000;">=</span><span style="line-height:1.5;color:#000000;"></span><span style="line-height:1.5;color:#0000ff;">this</span><span style="line-height:1.5;color:#000000;">;<br /></span><span style="line-height:1.5;color:#0000ff;">if</span><span style="line-height:1.5;color:#000000;"> (that.db </span><span style="line-height:1.5;color:#000000;">&&</span><span style="line-height:1.5;color:#000000;"> that.db.state </span><span style="line-height:1.5;color:#000000;">==</span><span style="line-height:1.5;color:#000000;"></span><span style="line-height:1.5;color:#000000;">'</span><span style="line-height:1.5;color:#000000;">connected</span><span style="line-height:1.5;color:#000000;">'</span><span style="line-height:1.5;color:#000000;">) {<br /> callback </span><span style="line-height:1.5;color:#000000;">&&</span><span style="line-height:1.5;color:#000000;"> callback();<br /> } </span><span style="line-height:1.5;color:#0000ff;">else</span><span style="line-height:1.5;color:#000000;"> {<br /> that.db.open(</span><span style="line-height:1.5;color:#0000ff;">function</span><span style="line-height:1.5;color:#000000;">(error, client) {<br /></span><span style="line-height:1.5;color:#0000ff;">if</span><span style="line-height:1.5;color:#000000;"> (error) </span><span style="line-height:1.5;color:#0000ff;">throw</span><span style="line-height:1.5;color:#000000;"> error;<br /> that.collection </span><span style="line-height:1.5;color:#000000;">=</span><span style="line-height:1.5;color:#000000;"></span><span style="line-height:1.5;color:#0000ff;">new</span><span style="line-height:1.5;color:#000000;"> mongodb.Collection(client, that.mongoCollection);<br /> callback </span><span style="line-height:1.5;color:#000000;">&&</span><span style="line-height:1.5;color:#000000;"> callback();<br /> });<br /> }<br />}<br /><br />_server.prototype.Close </span><span style="line-height:1.5;color:#000000;">=</span><span style="line-height:1.5;color:#000000;"></span><span style="line-height:1.5;color:#0000ff;">function</span><span style="line-height:1.5;color:#000000;">(isClose) {<br /></span><span style="line-height:1.5;color:#0000ff;">if</span><span style="line-height:1.5;color:#000000;"> (</span><span style="line-height:1.5;color:#000000;">!!</span><span style="line-height:1.5;color:#000000;">isClose </span><span style="line-height:1.5;color:#000000;">&&</span><span style="line-height:1.5;color:#000000;"></span><span style="line-height:1.5;color:#0000ff;">this</span><span style="line-height:1.5;color:#000000;">.db) {<br /></span><span style="line-height:1.5;color:#0000ff;">this</span><span style="line-height:1.5;color:#000000;">.db.close();<br /></span><span style="line-height:1.5;color:#0000ff;">this</span><span style="line-height:1.5;color:#000000;">.collection </span><span style="line-height:1.5;color:#000000;">=</span><span style="line-height:1.5;color:#000000;"></span><span style="line-height:1.5;color:#0000ff;">null</span><span style="line-height:1.5;color:#000000;">;<br /></span><span style="line-height:1.5;color:#0000ff;">this</span><span style="line-height:1.5;color:#000000;">.db </span><span style="line-height:1.5;color:#000000;">=</span><span style="line-height:1.5;color:#000000;"></span><span style="line-height:1.5;color:#0000ff;">null</span><span style="line-height:1.5;color:#000000;">;<br /> }<br />}<br /><br />_server.prototype.Insert </span><span style="line-height:1.5;color:#000000;">=</span><span style="line-height:1.5;color:#000000;"></span><span style="line-height:1.5;color:#0000ff;">function</span><span style="line-height:1.5;color:#000000;">(obj, func, isClose) {<br /></span><span style="line-height:1.5;color:#0000ff;">var</span><span style="line-height:1.5;color:#000000;"> that </span><span style="line-height:1.5;color:#000000;">=</span><span style="line-height:1.5;color:#000000;"></span><span style="line-height:1.5;color:#0000ff;">this</span><span style="line-height:1.5;color:#000000;">;<br /> that.Open(</span><span style="line-height:1.5;color:#0000ff;">function</span><span style="line-height:1.5;color:#000000;">() {<br /> that.collection.insert(obj, { safe: </span><span style="line-height:1.5;color:#0000ff;">true</span><span style="line-height:1.5;color:#000000;"> }, </span><span style="line-height:1.5;color:#0000ff;">function</span><span style="line-height:1.5;color:#000000;">(err) {<br /> that.Close(isClose);<br /></span><span style="line-height:1.5;color:#0000ff;">if</span><span style="line-height:1.5;color:#000000;"> (err) {<br /> console.warn(err.message);<br /> }<br /></span><span style="line-height:1.5;color:#0000ff;">if</span><span style="line-height:1.5;color:#000000;"> (err </span><span style="line-height:1.5;color:#000000;">&&</span><span style="line-height:1.5;color:#000000;"> err.message.indexOf(</span><span style="line-height:1.5;color:#000000;">'</span><span style="line-height:1.5;color:#000000;">E11000</span><span style="line-height:1.5;color:#000000;">'</span><span style="line-height:1.5;color:#000000;">) </span><span style="line-height:1.5;color:#000000;">!==</span><span style="line-height:1.5;color:#000000;"></span><span style="line-height:1.5;color:#000000;">-</span><span style="line-height:1.5;color:#000000;">1</span><span style="line-height:1.5;color:#000000;">) {<br /></span><span style="line-height:1.5;color:#008000;">//</span><span style="line-height:1.5;color:#008000;">this _id was already inserted in the database</span><span style="line-height:1.5;color:#008000;"><br /></span><span style="line-height:1.5;color:#000000;"> }<br /> func </span><span style="line-height:1.5;color:#000000;">&&</span><span style="line-height:1.5;color:#000000;"> func();<br /> });<br /> });<br />}<br /><br /><br />_server.prototype.Find </span><span style="line-height:1.5;color:#000000;">=</span><span style="line-height:1.5;color:#000000;"></span><span style="line-height:1.5;color:#0000ff;">function</span><span style="line-height:1.5;color:#000000;">(obj, func, isClose) {<br /></span><span style="line-height:1.5;color:#0000ff;">var</span><span style="line-height:1.5;color:#000000;"> that </span><span style="line-height:1.5;color:#000000;">=</span><span style="line-height:1.5;color:#000000;"></span><span style="line-height:1.5;color:#0000ff;">this</span><span style="line-height:1.5;color:#000000;">;<br /> that.Open(</span><span style="line-height:1.5;color:#0000ff;">function</span><span style="line-height:1.5;color:#000000;">() {<br /> that.collection.find(obj, </span><span style="line-height:1.5;color:#0000ff;">function</span><span style="line-height:1.5;color:#000000;">(err, cursor) {<br /> that.Close(isClose);<br /></span><span style="line-height:1.5;color:#0000ff;">if</span><span style="line-height:1.5;color:#000000;"> (err) {<br /> console.warn(err.message);<br /> }<br /> cursor.toArray(</span><span style="line-height:1.5;color:#0000ff;">function</span><span style="line-height:1.5;color:#000000;">(err, items) {<br /> func </span><span style="line-height:1.5;color:#000000;">&&</span><span style="line-height:1.5;color:#000000;"> func(items);<br /> });<br /><br /> });<br /> });<br />}<br /><br />_server.prototype.Update </span><span style="line-height:1.5;color:#000000;">=</span><span style="line-height:1.5;color:#000000;"></span><span style="line-height:1.5;color:#0000ff;">function</span><span style="line-height:1.5;color:#000000;">(obj, objN, func, isClose) {<br /></span><span style="line-height:1.5;color:#0000ff;">var</span><span style="line-height:1.5;color:#000000;"> that </span><span style="line-height:1.5;color:#000000;">=</span><span style="line-height:1.5;color:#000000;"></span><span style="line-height:1.5;color:#0000ff;">this</span><span style="line-height:1.5;color:#000000;">;<br /> that.Open(</span><span style="line-height:1.5;color:#0000ff;">function</span><span style="line-height:1.5;color:#000000;">() {<br /> that.collection.update(obj, { $set: objN }, { safe: </span><span style="line-height:1.5;color:#0000ff;">true</span><span style="line-height:1.5;color:#000000;"> }, </span><span style="line-height:1.5;color:#0000ff;">function</span><span style="line-height:1.5;color:#000000;">(err) {<br /> that.Close(isClose);<br /></span><span style="line-height:1.5;color:#0000ff;">if</span><span style="line-height:1.5;color:#000000;"> (err) {<br /> console.warn(err.message);<br /> }<br /> func </span><span style="line-height:1.5;color:#000000;">&&</span><span style="line-height:1.5;color:#000000;"> func();<br /> });<br /> });<br />}<br /><br />_server.prototype.Remove </span><span style="line-height:1.5;color:#000000;">=</span><span style="line-height:1.5;color:#000000;"></span><span style="line-height:1.5;color:#0000ff;">function</span><span style="line-height:1.5;color:#000000;">(obj, func, isClose) {<br /></span><span style="line-height:1.5;color:#0000ff;">var</span><span style="line-height:1.5;color:#000000;"> that </span><span style="line-height:1.5;color:#000000;">=</span><span style="line-height:1.5;color:#000000;"></span><span style="line-height:1.5;color:#0000ff;">this</span><span style="line-height:1.5;color:#000000;">;<br /> that.Open(</span><span style="line-height:1.5;color:#0000ff;">function</span><span style="line-height:1.5;color:#000000;">() {<br /> that.collection.remove(obj, </span><span style="line-height:1.5;color:#0000ff;">function</span><span style="line-height:1.5;color:#000000;">(err, result) {<br /> that.Close(isClose);<br /></span><span style="line-height:1.5;color:#0000ff;">if</span><span style="line-height:1.5;color:#000000;"> (err) {<br /> console.warn(err.message);<br /> }<br /> func </span><span style="line-height:1.5;color:#000000;">&&</span><span style="line-height:1.5;color:#000000;"> func(result);<br /> });<br /> });<br />}<br /><br />exports.Server </span><span style="line-height:1.5;color:#000000;">=</span><span style="line-height:1.5;color:#000000;"> _server;</span> |
因为是异步的所以连续操作的话要放在回调函数中,方法还有第三个参数,如果是true则关闭链接 s.Insert({ "ID": "10001", "name": "ygm" }, function() { …. },true); 查看操作结果 转自:http://www.cnblogs.com/ygm125/archive/2011/09/02/2163532.html
View Details50+ 个在网页中使用内容滑块的惊艳案例
少废话,直接上图! Ludlow Kingsley – MORE INFO Sven Prim – MORE INFO Kyan – MORE INFO Team Viget – MORE INFO Mathieu Clauss – MORE INFO Marco Rotoli – MORE INFO Fitz FitzPatrick – MORE INFO Jackson and Kent – MORE INFO Jérôme Détraz – MORE INFO Filtered – MORE INFO TravelBuzz – MORE INFO Studio Breakfast – MORE INFO Purple, Rock, Scissors – MORE INFO Gen 3 Creative – MORE INFO Mark Dearman – MORE INFO LQDI – MORE INFO Dux Design – MORE INFO Guns N Roses […]
View Details强大的绘图类库d3js
今天发现一个强大的绘图js类库:d3 官网:http://d3js.org/ 示例:https://github.com/mbostock/d3/wiki/Gallery 文档:https://github.com/mbostock/d3/wiki/CN-Home
View Details10 个你必须掌握的超酷 VI 命令技巧
摘要:大部分Linux开发者对vi命相当熟悉,可是遗憾的是,大部分开发者都只能掌握一些最常用的Linux vi命令,下面介绍的10个vi命令虽然很多不为人知,但是在实际应用中又能让你大大提高效率。 在使用vi 编辑器时—无论是初次使用的用户,还是有经验的用户—大多数人往往只掌握核心命令集,这些命令可以执行最常用的功能:导航或保存文件;插入、更新、删除或搜索数据;退出但不保存修改。 但是,vi 编辑器极其强大,特性和功能非常丰富。即使在多年使用vi 之后,您仍然可能会发现有不知道的新命令。本文讨论的命令就属于不太为人所知的命令,但是它们可以简化您目前采用的操作方法,让您的工作方式更高效,或者 让您能够完成原来不知道可以用vi 完成的操作。 打开和关闭行号 vi 编辑器的许多选项可以控制编辑会话的外观和感觉。使用 :set 命令修改vi 中的会话设置。按 Escape 键进入命令模式之后,可以使用 :set all 命令显示选项和设置的列表。可以设置的选项之一是 number,它的作用是打开和关闭行号 # # Internet host table # ::1 localhost 127.0.0.1 localhost loghost 192.168.0.6 centos5 192.168.0.10 appserv 192.168.0.11 webserv 192.168.0.12 test 192.168.0.5 solaris10 # Added by DHCP ~ ~ ~ :set number 这个vi命令 在当前编辑的文件中的每个记录上显示行号。让vi 进入命令模式之后,可以输入 :set number 并按回车来打开行号 # # Internet host table # ::1 localhost […]
View Details25 个 Linux 服务器安全小贴士
大家都认为 Linux 默认是安全的,我大体是认可的 (这是个有争议的话题)。Linux默认确实有内置的安全模型。你需要打开它并且对其进行定制,这样才能得到更安全的系统。Linux更难管理,不过相应也更灵活,有更多的配置选项。 对于系统管理员,让产品的系统更安全,免于骇客和黑客的攻击,一直是一项挑战。这是我们关于“如何让Linux系统更安全” 或者 “加固Linux系统“之类话题的第一篇文章。本文将介绍 25个有用的技巧和窍门 ,帮助你让Linux系统更加安全。希望下面的这些技巧和窍门可以帮助你加强你的系统的安全。 1. 物理系统的安全性 配置BIOS,禁用从CD/DVD、外部设备、软驱启动。下一步,启用BIOS密码,同时启用GRUB的密码保护,这样可以限制对系统的物理访问。 通过设置GRUB密码来保护Linux服务器 2. 磁盘分区 使用不同的分区很重要,对于可能得灾难,这可以保证更高的数据安全性。通过划分不同的分区,数据可以进行分组并隔离开来。当意外发生时,只有出问题的分区的数据才会被破坏,其他分区的数据可以保留下来。你最好有以下的分区,并且第三方程序最好安装在单独的文件系统/opt下。
1 2 3 4 5 6 7 |
/ /boot /usr /var /home /tmp /opt |
3. 最小包安装,最少漏洞 你真的需要安装所有的服务么?建议不要安装无用的包,避免由这些包带来的漏洞。这将最小化风险,因为一个服务的漏洞可能会危害到其他的服务。找到并去除或者停止不用的服务,把系统漏洞减少到最小。使用‘chkconfig‘命令列出运行级别3的运行所有服务。
1 |
# /sbin/chkconfig --list |grep '3:on' |
当你发现一个不需要的服务在运行时,使用下面的命令停止这个服务。
1 |
# chkconfig serviceName off |
使用RPM包管理器,例如YUM或者apt-get 工具来列出所有安装的包,并且利用下的命令来卸载他们。
1 |
# yum -y remove package-name |
1 |
# sudo apt-get remove package-name |
5 chkconfig Command Examples 20 Practical Examples of RPM Commands 20 Linux YUM Commands for Linux Package Management 25 APT-GET and APT-CACHE Commands to Manage Package Management 4. 检查网络监听端口 在网络命令 ‘netstat‘ 的帮助下,你将能够看到所有开启的端口,以及相关的程序。使用我上面提到的 ‘chkconfig‘ 命令关闭系统中不想要的网络服务。
1 |
# netstat -tulpn |
Linux 网络管理中的 20 条 Netstat 命令 5. 使用 SSH(Secure Shell) Telnet 和 rlogin 协议只能用于纯文本,不能使用加密的格式,这或将导致安全漏洞的产生。SSH 是一种在客户端与服务器端通讯时使用加密技术的安全协议。 除非必要,永远都不要直接登录 root 账户。使用 “sudo” 执行命令。sudo 由 /etc/sudoers 文件制定,同时也可以使用 “visudo” 工具编辑,它将通过 VI 编辑器打开配置文件。 […]
View Details声明式编程和命令式编程的比较
先统一一下概念,我们有两种编程方式:命令式和声明式。 我们可以像下面这样定义它们之间的不同: 命令式编程:命令“机器”如何去做事情(how),这样不管你想要的是什么(what),它都会按照你的命令实现。 声明式编程:告诉“机器”你想要的是什么(what),让机器想出如何去做(how)。 声明式编程和命令式编程的代码例子 举个简单的例子,假设我们想让一个数组里的数值翻倍。 我们用命令式编程风格实现,像下面这样:
1 |
<span class="kd lazy " style="margin:0px;padding:0px;">var</span><span class="nx lazy " style="margin:0px;padding:0px;">numbers</span><span class="o lazy " style="margin:0px;padding:0px;">=</span><span class="p lazy " style="margin:0px;padding:0px;">[</span><span class="mi lazy " style="margin:0px;padding:0px;">1</span><span class="p lazy " style="margin:0px;padding:0px;">,</span><span class="mi lazy " style="margin:0px;padding:0px;">2</span><span class="p lazy " style="margin:0px;padding:0px;">,</span><span class="mi lazy " style="margin:0px;padding:0px;">3</span><span class="p lazy " style="margin:0px;padding:0px;">,</span><span class="mi lazy " style="margin:0px;padding:0px;">4</span><span class="p lazy " style="margin:0px;padding:0px;">,</span><span class="mi lazy " style="margin:0px;padding:0px;">5</span><span class="p lazy " style="margin:0px;padding:0px;">]</span><span class="kd lazy " style="margin:0px;padding:0px;">var</span><span class="nx lazy " style="margin:0px;padding:0px;">doubled</span><span class="o lazy " style="margin:0px;padding:0px;">=</span><span class="p lazy " style="margin:0px;padding:0px;">[]</span><span class="k lazy " style="margin:0px;padding:0px;">for</span><span class="p lazy " style="margin:0px;padding:0px;">(</span><span class="kd lazy " style="margin:0px;padding:0px;">var</span><span class="nx lazy " style="margin:0px;padding:0px;">i</span><span class="o lazy " style="margin:0px;padding:0px;">=</span><span class="mi lazy " style="margin:0px;padding:0px;">0</span><span class="p lazy " style="margin:0px;padding:0px;">;</span><span class="nx lazy " style="margin:0px;padding:0px;">i</span><span class="o lazy " style="margin:0px;padding:0px;"><</span><span class="nx lazy " style="margin:0px;padding:0px;">numbers</span><span class="p lazy " style="margin:0px;padding:0px;">.</span><span class="nx lazy " style="margin:0px;padding:0px;">length</span><span class="p lazy " style="margin:0px;padding:0px;">;</span><span class="nx lazy " style="margin:0px;padding:0px;">i</span><span class="o lazy " style="margin:0px;padding:0px;">++</span><span class="p lazy " style="margin:0px;padding:0px;">)</span><span class="p lazy " style="margin:0px;padding:0px;">{</span><span class="kd lazy " style="margin:0px;padding:0px;">var</span><span class="nx lazy " style="margin:0px;padding:0px;">newNumber</span><span class="o lazy " style="margin:0px;padding:0px;">=</span><span class="nx lazy " style="margin:0px;padding:0px;">numbers</span><span class="p lazy " style="margin:0px;padding:0px;">[</span><span class="nx lazy " style="margin:0px;padding:0px;">i</span><span class="p lazy " style="margin:0px;padding:0px;">]</span><span class="o lazy " style="margin:0px;padding:0px;">*</span><span class="mi lazy " style="margin:0px;padding:0px;">2</span><span class="nx lazy " style="margin:0px;padding:0px;">doubled</span><span class="p lazy " style="margin:0px;padding:0px;">.</span><span class="nx lazy " style="margin:0px;padding:0px;">push</span><span class="p lazy " style="margin:0px;padding:0px;">(</span><span class="nx lazy " style="margin:0px;padding:0px;">newNumber</span><span class="p lazy " style="margin:0px;padding:0px;">)</span><span class="p lazy " style="margin:0px;padding:0px;">}</span><span class="nx lazy " style="margin:0px;padding:0px;">console</span><span class="p lazy " style="margin:0px;padding:0px;">.</span><span class="nx lazy " style="margin:0px;padding:0px;">log</span><span class="p lazy " style="margin:0px;padding:0px;">(</span><span class="nx lazy " style="margin:0px;padding:0px;">doubled</span><span class="p lazy " style="margin:0px;padding:0px;">)</span><span class="c1 lazy " style="margin:0px;padding:0px;">//=> [2,4,6,8,10]</span> |
我们直接遍历整个数组,取出每个元素,乘以二,然后把翻倍后的值放入新数组,每次都要操作这个双倍数组,直到计算完所有元素。 而使用声明式编程方法,我们可以用 Array.map 函数,像下面这样:
1 |
<span class="kd lazy " style="margin:0px;padding:0px;">var</span><span class="nx lazy " style="margin:0px;padding:0px;">numbers</span><span class="o lazy " style="margin:0px;padding:0px;">=</span><span class="p lazy " style="margin:0px;padding:0px;">[</span><span class="mi lazy " style="margin:0px;padding:0px;">1</span><span class="p lazy " style="margin:0px;padding:0px;">,</span><span class="mi lazy " style="margin:0px;padding:0px;">2</span><span class="p lazy " style="margin:0px;padding:0px;">,</span><span class="mi lazy " style="margin:0px;padding:0px;">3</span><span class="p lazy " style="margin:0px;padding:0px;">,</span><span class="mi lazy " style="margin:0px;padding:0px;">4</span><span class="p lazy " style="margin:0px;padding:0px;">,</span><span class="mi lazy " style="margin:0px;padding:0px;">5</span><span class="p lazy " style="margin:0px;padding:0px;">]</span><span class="kd lazy " style="margin:0px;padding:0px;">var</span><span class="nx lazy " style="margin:0px;padding:0px;">doubled</span><span class="o lazy " style="margin:0px;padding:0px;">=</span><span class="nx lazy " style="margin:0px;padding:0px;">numbers</span><span class="p lazy " style="margin:0px;padding:0px;">.</span><span class="nx lazy " style="margin:0px;padding:0px;">map</span><span class="p lazy " style="margin:0px;padding:0px;">(</span><span class="kd lazy " style="margin:0px;padding:0px;">function</span><span class="p lazy " style="margin:0px;padding:0px;">(</span><span class="nx lazy " style="margin:0px;padding:0px;">n</span><span class="p lazy " style="margin:0px;padding:0px;">)</span><span class="p lazy " style="margin:0px;padding:0px;">{</span><span class="k lazy " style="margin:0px;padding:0px;">return</span><span class="nx lazy " style="margin:0px;padding:0px;">n</span><span class="o lazy " style="margin:0px;padding:0px;">*</span><span class="mi lazy " style="margin:0px;padding:0px;">2</span><span class="p lazy " style="margin:0px;padding:0px;">})</span><span class="nx lazy " style="margin:0px;padding:0px;">console</span><span class="p lazy " style="margin:0px;padding:0px;">.</span><span class="nx lazy " style="margin:0px;padding:0px;">log</span><span class="p lazy " style="margin:0px;padding:0px;">(</span><span class="nx lazy " style="margin:0px;padding:0px;">doubled</span><span class="p lazy " style="margin:0px;padding:0px;">)</span><span class="c1 lazy " style="margin:0px;padding:0px;">//=> [2,4,6,8,10]</span> |
map 利用当前的数组创建了一个新数组,新数组里的每个元素都是经过了传入map的函数(这里是function(n) { return n*2 })的处理。 map函数所作的事情是将直接遍历整个数组的过程归纳抽离出来,让我们专注于描述我们想要的是什么(what)。注意,我们传入map的是一个纯函数;它不具有任何副作用(不会改变外部状态),它只是接收一个数字,返回乘以二后的值。 在一些具有函数式编程特征的语言里,对于list数据类型的操作,还有一些其他常用的声明式的函数方法。例如,求一个list里所有值的和,命令式编程会这样做:
1 |
<span class="kd lazy " style="margin:0px;padding:0px;">var</span><span class="nx lazy " style="margin:0px;padding:0px;">numbers</span><span class="o lazy " style="margin:0px;padding:0px;">=</span><span class="p lazy " style="margin:0px;padding:0px;">[</span><span class="mi lazy " style="margin:0px;padding:0px;">1</span><span class="p lazy " style="margin:0px;padding:0px;">,</span><span class="mi lazy " style="margin:0px;padding:0px;">2</span><span class="p lazy " style="margin:0px;padding:0px;">,</span><span class="mi lazy " style="margin:0px;padding:0px;">3</span><span class="p lazy " style="margin:0px;padding:0px;">,</span><span class="mi lazy " style="margin:0px;padding:0px;">4</span><span class="p lazy " style="margin:0px;padding:0px;">,</span><span class="mi lazy " style="margin:0px;padding:0px;">5</span><span class="p lazy " style="margin:0px;padding:0px;">]</span><span class="kd lazy " style="margin:0px;padding:0px;">var</span><span class="nx lazy " style="margin:0px;padding:0px;">total</span><span class="o lazy " style="margin:0px;padding:0px;">=</span><span class="mi lazy " style="margin:0px;padding:0px;">0</span><span class="k lazy " style="margin:0px;padding:0px;">for</span><span class="p lazy " style="margin:0px;padding:0px;">(</span><span class="kd lazy " style="margin:0px;padding:0px;">var</span><span class="nx lazy " style="margin:0px;padding:0px;">i</span><span class="o lazy " style="margin:0px;padding:0px;">=</span><span class="mi lazy " style="margin:0px;padding:0px;">0</span><span class="p lazy " style="margin:0px;padding:0px;">;</span><span class="nx lazy " style="margin:0px;padding:0px;">i</span><span class="o lazy " style="margin:0px;padding:0px;"><</span><span class="nx lazy " style="margin:0px;padding:0px;">numbers</span><span class="p lazy " style="margin:0px;padding:0px;">.</span><span class="nx lazy " style="margin:0px;padding:0px;">length</span><span class="p lazy " style="margin:0px;padding:0px;">;</span><span class="nx lazy " style="margin:0px;padding:0px;">i</span><span class="o lazy " style="margin:0px;padding:0px;">++</span><span class="p lazy " style="margin:0px;padding:0px;">)</span><span class="p lazy " style="margin:0px;padding:0px;">{</span><span class="nx lazy " style="margin:0px;padding:0px;">total</span><span class="o lazy " style="margin:0px;padding:0px;">+=</span><span class="nx lazy " style="margin:0px;padding:0px;">numbers</span><span class="p lazy " style="margin:0px;padding:0px;">[</span><span class="nx lazy " style="margin:0px;padding:0px;">i</span><span class="p lazy " style="margin:0px;padding:0px;">]</span><span class="p lazy " style="margin:0px;padding:0px;">}</span><span class="nx lazy " style="margin:0px;padding:0px;">console</span><span class="p lazy " style="margin:0px;padding:0px;">.</span><span class="nx lazy " style="margin:0px;padding:0px;">log</span><span class="p lazy " style="margin:0px;padding:0px;">(</span><span class="nx lazy " style="margin:0px;padding:0px;">total</span><span class="p lazy " style="margin:0px;padding:0px;">)</span><span class="c1 lazy " style="margin:0px;padding:0px;">//=> 15</span> |
而在声明式编程方式里,我们使用 reduce 函数:
1 |
<span class="kd lazy " style="margin:0px;padding:0px;">var</span><span class="nx lazy " style="margin:0px;padding:0px;">numbers</span><span class="o lazy " style="margin:0px;padding:0px;">=</span><span class="p lazy " style="margin:0px;padding:0px;">[</span><span class="mi lazy " style="margin:0px;padding:0px;">1</span><span class="p lazy " style="margin:0px;padding:0px;">,</span><span class="mi lazy " style="margin:0px;padding:0px;">2</span><span class="p lazy " style="margin:0px;padding:0px;">,</span><span class="mi lazy " style="margin:0px;padding:0px;">3</span><span class="p lazy " style="margin:0px;padding:0px;">,</span><span class="mi lazy " style="margin:0px;padding:0px;">4</span><span class="p lazy " style="margin:0px;padding:0px;">,</span><span class="mi lazy " style="margin:0px;padding:0px;">5</span><span class="p lazy " style="margin:0px;padding:0px;">]</span><span class="kd lazy " style="margin:0px;padding:0px;">var</span><span class="nx lazy " style="margin:0px;padding:0px;">total</span><span class="o lazy " style="margin:0px;padding:0px;">=</span><span class="nx lazy " style="margin:0px;padding:0px;">numbers</span><span class="p lazy " style="margin:0px;padding:0px;">.</span><span class="nx lazy " style="margin:0px;padding:0px;">reduce</span><span class="p lazy " style="margin:0px;padding:0px;">(</span><span class="kd lazy " style="margin:0px;padding:0px;">function</span><span class="p lazy " style="margin:0px;padding:0px;">(</span><span class="nx lazy " style="margin:0px;padding:0px;">sum</span><span class="p lazy " style="margin:0px;padding:0px;">,</span><span class="nx lazy " style="margin:0px;padding:0px;">n</span><span class="p lazy " style="margin:0px;padding:0px;">)</span><span class="p lazy " style="margin:0px;padding:0px;">{</span><span class="k lazy " style="margin:0px;padding:0px;">return</span><span class="nx lazy " style="margin:0px;padding:0px;">sum</span><span class="o lazy " style="margin:0px;padding:0px;">+</span><span class="nx lazy " style="margin:0px;padding:0px;">n</span><span class="p lazy " style="margin:0px;padding:0px;">});</span><span class="nx lazy " style="margin:0px;padding:0px;">console</span><span class="p lazy " style="margin:0px;padding:0px;">.</span><span class="nx lazy " style="margin:0px;padding:0px;">log</span><span class="p lazy " style="margin:0px;padding:0px;">(</span><span class="nx lazy " style="margin:0px;padding:0px;">total</span><span class="p lazy " style="margin:0px;padding:0px;">)</span><span class="c1 lazy " style="margin:0px;padding:0px;">//=> 15</span> |
reduce 函数利用传入的函数把一个 list 运算成一个值。它以这个函数为参数,数组里的每个元素都要经过它的处理。每一次调用,第一个参数(这里是sum)都是这个函数处理前一个值时返回的结果,而第二个参数(n)就是当前元素。这样下来,每此处理的新元素都会合计到sum中,最终我们得到的是整个数组的和。 同样,reduce 函数归纳抽离了我们如何遍历数组和状态管理部分的实现,提供给我们一个通用的方式来把一个 list 合并成一个值。我们需要做的只是指明我们想要的是什么? 声明式编程很奇怪吗? 如果你之前没有听说过map 和 reduce 函数,你的第一感觉,我相信,就会是这样。作为程序员,我们非常习惯去指出事情应该如何运行。“去遍历这个list”,“if 这种情况 then 那样做”,“把这个新值赋给这个变量”。当我们已经知道了如何告诉机器该如何做事时,为什么我们需要去学习这种看起来有些怪异的归纳抽离出来的函数工具? 在很多情况中,命令式编程很好用。当我们写业务逻辑,我们通常必须要写命令式代码,没有可能在我们的专项业务里也存在一个可以归纳抽离的实现。 但是,如果我们花时间去学习(或发现)声明式的可以归纳抽离的部分,它们能为我们的编程带来巨大的便捷。首先,我可以少写代码,这就是通往成功的捷径。而且它们能让我们站在更高的层面是思考,站在云端思考我们想要的是什么,而不是站在泥里思考事情该如何去做。 声明式编程语言:SQL 也许你还不能明白,但有一个地方,你也许已经用到了声明式编程,那就是SQL。 你可以把SQL当做一个处理数据的声明式查询语言。完全用SQL写一个应用程序?这不可能。但如果是处理相互关联的数据集,它就显的无比强大了。 像下面这样的查询语句:
1 |
<span class="nx lazy " style="margin:0px;padding:0px;">SELECT</span><span class="o lazy " style="margin:0px;padding:0px;">*</span><span class="nx lazy " style="margin:0px;padding:0px;">from</span><span class="nx lazy " style="margin:0px;padding:0px;">dogs</span><span class="nx lazy " style="margin:0px;padding:0px;">INNER</span><span class="nx lazy " style="margin:0px;padding:0px;">JOIN</span><span class="nx lazy " style="margin:0px;padding:0px;">owners</span><span class="nx lazy " style="margin:0px;padding:0px;">WHERE</span><span class="nx lazy " style="margin:0px;padding:0px;">dogs</span><span class="p lazy " style="margin:0px;padding:0px;">.</span><span class="nx lazy " style="margin:0px;padding:0px;">owner_id</span><span class="o lazy " style="margin:0px;padding:0px;">=</span><span class="nx lazy " style="margin:0px;padding:0px;">owners</span><span class="p lazy " style="margin:0px;padding:0px;">.</span><span class="nx lazy " style="margin:0px;padding:0px;">id</span> |
如果我们用命令式编程方式实现这段逻辑:
1 |
<span class="c1 lazy " style="margin:0px;padding:0px;">//dogs = [{name: 'Fido', owner_id: 1}, {...}, ... ]</span><span class="c1 lazy " style="margin:0px;padding:0px;">//owners = [{id: 1, name: 'Bob'}, {...}, ...]</span><span class="kd lazy " style="margin:0px;padding:0px;">var</span><span class="nx lazy " style="margin:0px;padding:0px;">dogsWithOwners</span><span class="o lazy " style="margin:0px;padding:0px;">=</span><span class="p lazy " style="margin:0px;padding:0px;">[]</span><span class="kd lazy " style="margin:0px;padding:0px;">var</span><span class="nx lazy " style="margin:0px;padding:0px;">dog</span><span class="p lazy " style="margin:0px;padding:0px;">,</span><span class="nx lazy " style="margin:0px;padding:0px;">owner</span><span class="k lazy " style="margin:0px;padding:0px;">for</span><span class="p lazy " style="margin:0px;padding:0px;">(</span><span class="kd lazy " style="margin:0px;padding:0px;">var</span><span class="nx lazy " style="margin:0px;padding:0px;">di</span><span class="o lazy " style="margin:0px;padding:0px;">=</span><span class="mi lazy " style="margin:0px;padding:0px;">0</span><span class="p lazy " style="margin:0px;padding:0px;">;</span><span class="nx lazy " style="margin:0px;padding:0px;">di</span><span class="o lazy " style="margin:0px;padding:0px;"><</span><span class="nx lazy " style="margin:0px;padding:0px;">dogs</span><span class="p lazy " style="margin:0px;padding:0px;">.</span><span class="nx lazy " style="margin:0px;padding:0px;">length</span><span class="p lazy " style="margin:0px;padding:0px;">;</span><span class="nx lazy " style="margin:0px;padding:0px;">di</span><span class="o lazy " style="margin:0px;padding:0px;">++</span><span class="p lazy " style="margin:0px;padding:0px;">)</span><span class="p lazy " style="margin:0px;padding:0px;">{</span><span class="nx lazy " style="margin:0px;padding:0px;">dog</span><span class="o lazy " style="margin:0px;padding:0px;">=</span><span class="nx lazy " style="margin:0px;padding:0px;">dogs</span><span class="p lazy " style="margin:0px;padding:0px;">[</span><span class="nx lazy " style="margin:0px;padding:0px;">di</span><span class="p lazy " style="margin:0px;padding:0px;">]</span><span class="k lazy " style="margin:0px;padding:0px;">for</span><span class="p lazy " style="margin:0px;padding:0px;">(</span><span class="kd lazy " style="margin:0px;padding:0px;">var</span><span class="nx lazy " style="margin:0px;padding:0px;">oi</span><span class="o lazy " style="margin:0px;padding:0px;">=</span><span class="mi lazy " style="margin:0px;padding:0px;">0</span><span class="p lazy " style="margin:0px;padding:0px;">;</span><span class="nx lazy " style="margin:0px;padding:0px;">oi</span><span class="o lazy " style="margin:0px;padding:0px;"><</span><span class="nx lazy " style="margin:0px;padding:0px;">owners</span><span class="p lazy " style="margin:0px;padding:0px;">.</span><span class="nx lazy " style="margin:0px;padding:0px;">length</span><span class="p lazy " style="margin:0px;padding:0px;">;</span><span class="nx lazy " style="margin:0px;padding:0px;">oi</span><span class="o lazy " style="margin:0px;padding:0px;">++</span><span class="p lazy " style="margin:0px;padding:0px;">)</span><span class="p lazy " style="margin:0px;padding:0px;">{</span><span class="nx lazy " style="margin:0px;padding:0px;">owner</span><span class="o lazy " style="margin:0px;padding:0px;">=</span><span class="nx lazy " style="margin:0px;padding:0px;">owners</span><span class="p lazy " style="margin:0px;padding:0px;">[</span><span class="nx lazy " style="margin:0px;padding:0px;">oi</span><span class="p lazy " style="margin:0px;padding:0px;">]</span><span class="k lazy " style="margin:0px;padding:0px;">if</span><span class="p lazy " style="margin:0px;padding:0px;">(</span><span class="nx lazy " style="margin:0px;padding:0px;">owner</span><span class="o lazy " style="margin:0px;padding:0px;">&&</span><span class="nx lazy " style="margin:0px;padding:0px;">dog</span><span class="p lazy " style="margin:0px;padding:0px;">.</span><span class="nx lazy " style="margin:0px;padding:0px;">owner_id</span><span class="o lazy " style="margin:0px;padding:0px;">==</span><span class="nx lazy " style="margin:0px;padding:0px;">owner</span><span class="p lazy " style="margin:0px;padding:0px;">.</span><span class="nx lazy " style="margin:0px;padding:0px;">id</span><span class="p lazy " style="margin:0px;padding:0px;">)</span><span class="p lazy " style="margin:0px;padding:0px;">{</span><span class="nx lazy " style="margin:0px;padding:0px;">dogsWithOwners</span><span class="p lazy " style="margin:0px;padding:0px;">.</span><span class="nx lazy " style="margin:0px;padding:0px;">push</span><span class="p lazy " style="margin:0px;padding:0px;">({</span><span class="nx lazy " style="margin:0px;padding:0px;">dog</span><span class="o lazy " style="margin:0px;padding:0px;">:</span><span class="nx lazy " style="margin:0px;padding:0px;">dog</span><span class="p lazy " style="margin:0px;padding:0px;">,</span><span class="nx lazy " style="margin:0px;padding:0px;">owner</span><span class="o lazy " style="margin:0px;padding:0px;">:</span><span class="nx lazy " style="margin:0px;padding:0px;">owner</span><span class="p lazy " style="margin:0px;padding:0px;">})</span><span class="p lazy " style="margin:0px;padding:0px;">}</span><span class="p lazy " style="margin:0px;padding:0px;">}}</span><span class="p lazy " style="margin:0px;padding:0px;">}</span> |
我可没说SQL是一种很容易懂的语言,也没说一眼就能把它们看明白,但基本上还是很整洁的。 SQL代码不仅很短,不不仅容易读懂,它还有更大的优势。因为我们归纳抽离了how,我们就可以专注于what,让数据库来帮我们优化how. 我们的命令式编程代码会运行的很慢,因为需要遍历所有 list 里的每个狗的主人。 而SQL例子里我们可以让数据库来处理how,来替我们去找我们想要的数据。如果需要用到索引(假设我们建了索引),数据库知道如何使用索引,这样性能又有了大的提升。如果在此不久之前它执行过相同的查询,它也许会从缓存里立即找到。通过放手how,让机器来做这些有难度的事,我们不需要掌握数据库原理就能轻松的完成任务。 声明式编程:d3.js 另外一个能体现出声明式编程的真正强大之处地方是用户界面、图形、动画编程。 开发用户界面是有难度的事。因为有用户交互,我们希望能创建漂亮的动态用户交互方式,通常我们会用到大量的状态声明和很多相同作用的代码,这些代码实际上是可以归纳提炼出来的。 d3.js 里面一个非常好的声明时归纳提炼的例子就是它的一个工具包,能够帮助我们使用JavaScript和SVG来开发交互的和动画的数据可视化模型。 第一次(或第5次,甚至第10次)你开发d3程序时可能会头大。跟SQL一样,d3是一种可视化数据操作的强大通用工具,它能提供你所有how方法,让你只需要说出你想要什么。 下面是一个例子(我建议你看一下这个演示)。这是一个d3可视化实现,它为data数组里的每个对象画一个圆。为了演示这个过程,我们每秒增加一个圆。 里面最有趣的一段代码是:
1 |
<span class="c1 lazy " style="margin:0px;padding:0px;">//var data = [{x: 5, y: 10}, {x: 20, y: 5}]</span><span class="kd lazy " style="margin:0px;padding:0px;">var</span><span class="nx lazy " style="margin:0px;padding:0px;">circles</span><span class="o lazy " style="margin:0px;padding:0px;">=</span><span class="nx lazy " style="margin:0px;padding:0px;">svg</span><span class="p lazy " style="margin:0px;padding:0px;">.</span><span class="nx lazy " style="margin:0px;padding:0px;">selectAll</span><span class="p lazy " style="margin:0px;padding:0px;">(</span><span class="s1 lazy " style="margin:0px;padding:0px;">'circle'</span><span class="p lazy " style="margin:0px;padding:0px;">)</span><span class="p lazy " style="margin:0px;padding:0px;">.</span><span class="nx lazy " style="margin:0px;padding:0px;">data</span><span class="p lazy " style="margin:0px;padding:0px;">(</span><span class="nx lazy " style="margin:0px;padding:0px;">data</span><span class="p lazy " style="margin:0px;padding:0px;">)</span><span class="nx lazy " style="margin:0px;padding:0px;">circles</span><span class="p lazy " style="margin:0px;padding:0px;">.</span><span class="nx lazy " style="margin:0px;padding:0px;">enter</span><span class="p lazy " style="margin:0px;padding:0px;">().</span><span class="nx lazy " style="margin:0px;padding:0px;">append</span><span class="p lazy " style="margin:0px;padding:0px;">(</span><span class="s1 lazy " style="margin:0px;padding:0px;">'circle'</span><span class="p lazy " style="margin:0px;padding:0px;">)</span><span class="p lazy " style="margin:0px;padding:0px;">.</span><span class="nx lazy " style="margin:0px;padding:0px;">attr</span><span class="p lazy " style="margin:0px;padding:0px;">(</span><span class="s1 lazy " style="margin:0px;padding:0px;">'cx'</span><span class="p lazy " style="margin:0px;padding:0px;">,</span><span class="kd lazy " style="margin:0px;padding:0px;">function</span><span class="p lazy " style="margin:0px;padding:0px;">(</span><span class="nx lazy " style="margin:0px;padding:0px;">d</span><span class="p lazy " style="margin:0px;padding:0px;">)</span><span class="p lazy " style="margin:0px;padding:0px;">{</span><span class="k lazy " style="margin:0px;padding:0px;">return</span><span class="nx lazy " style="margin:0px;padding:0px;">d</span><span class="p lazy " style="margin:0px;padding:0px;">.</span><span class="nx lazy " style="margin:0px;padding:0px;">x</span><span class="p lazy " style="margin:0px;padding:0px;">})</span><span class="p lazy " style="margin:0px;padding:0px;">.</span><span class="nx lazy " style="margin:0px;padding:0px;">attr</span><span class="p lazy " style="margin:0px;padding:0px;">(</span><span class="s1 lazy " style="margin:0px;padding:0px;">'cy'</span><span class="p lazy " style="margin:0px;padding:0px;">,</span><span class="kd lazy " style="margin:0px;padding:0px;">function</span><span class="p lazy " style="margin:0px;padding:0px;">(</span><span class="nx lazy " style="margin:0px;padding:0px;">d</span><span class="p lazy " style="margin:0px;padding:0px;">)</span><span class="p lazy " style="margin:0px;padding:0px;">{</span><span class="k lazy " style="margin:0px;padding:0px;">return</span><span class="nx lazy " style="margin:0px;padding:0px;">d</span><span class="p lazy " style="margin:0px;padding:0px;">.</span><span class="nx lazy " style="margin:0px;padding:0px;">y</span><span class="p lazy " style="margin:0px;padding:0px;">})</span><span class="p lazy " style="margin:0px;padding:0px;">.</span><span class="nx lazy " style="margin:0px;padding:0px;">attr</span><span class="p lazy " style="margin:0px;padding:0px;">(</span><span class="s1 lazy " style="margin:0px;padding:0px;">'r'</span><span class="p lazy " style="margin:0px;padding:0px;">,</span><span class="mi lazy " style="margin:0px;padding:0px;">0</span><span class="p lazy " style="margin:0px;padding:0px;">)</span><span class="p lazy " style="margin:0px;padding:0px;">.</span><span class="nx lazy " style="margin:0px;padding:0px;">transition</span><span class="p lazy " style="margin:0px;padding:0px;">().</span><span class="nx lazy " style="margin:0px;padding:0px;">duration</span><span class="p lazy " style="margin:0px;padding:0px;">(</span><span class="mi lazy " style="margin:0px;padding:0px;">500</span><span class="p lazy " style="margin:0px;padding:0px;">)</span><span class="p lazy " style="margin:0px;padding:0px;">.</span><span class="nx lazy " style="margin:0px;padding:0px;">attr</span><span class="p lazy " style="margin:0px;padding:0px;">(</span><span class="s1 lazy " style="margin:0px;padding:0px;">'r'</span><span class="p lazy " style="margin:0px;padding:0px;">,</span><span class="mi lazy " style="margin:0px;padding:0px;">5</span><span class="p lazy " style="margin:0px;padding:0px;">)</span> |
没有必要完全理解这段代码都干了什么(你需要一段时间去领会),但关键点是: 首先我们收集了svg里所有的圆,然后把data数组数据绑定到对象里。 D3 对每个圆都绑定了那些点数据有一个关系表。最初我们只有两个点,没有圆,我们使用.enter()方法获取数据点。这里,我们的意图是画一个圆,中心是x 和 y,初始值是 0 ,半秒后变换成半径为5。 为什么我说这很有意思? 从头再看一遍代码,想一想,我们是在声明我们想要的图案是什么样子,还是在说如何作图。你会发现这里根本没有关于how的代码。我们只是在一个相当高的层面描述我们想要的是什么: 我要画圆,圆心在data数据里,当增加新圆时,用动画表示半径的增加。 这太神奇了,我们没有写任何循环,这里没有状态管理。画图操作通常是很难写,很麻烦,很让人讨厌,但这里,d3归纳提取了一些常用的操作,让我们专注于描述我们想要的是什么。 现在再看,d3.js 很容易理解吗?不是,它绝对需要你花一段时间去学习。而学习的过程基本上需要你放弃去指明如何做事的习惯,而去学会如何描述我想要的是什么。 最初,这可能是很困难的事,但经过一些时间的学习后,一些神奇的事情发生了——你变得非常非常有效率了。通过归纳提取how,d3.js 能让你真正的专注说明你想要看到的是什么,让你在一个个更高的层面解决问题,解放你的创作力。 声明式编程的总结 声明式编程让我们去描述我们想要的是什么,让底层的软件/计算机/等去解决如何去实现它们。 在很多情况中,就像我们看到的一样,声明式编程能给我们的编程带来真正的提升,通过站在更高层面写代码,我们可以更多的专注于what,而这正是我们开发软件真正的目标。 问题是,程序员习惯了去描述how,这让我们感觉很好很舒服——强力——能够控制事情的发生发展,不放走任何我们不能看见不能理解的处理过程。 有时候这种紧盯着how不放的做法是没问题的。如果我需要对代码进行更高性能的优化,我需要对what进行更深一步的描述来指导how。有时候对于某个业务逻辑没有任何可以归纳提取的通用实现,我们只能写命令式编程代码。 但大多数时候,我们可以、而且应该寻求声明式的写代码方式,如果没有发现现成的归纳提取好的实现,我们应该自己去创建。起初这会很难,必定的,但就像我们使用SQL和D3.js, […]
View Details防SQL注入-初级方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
//防注入函数 function inject_check($sql_str){ $check = eregi('select|insert|update|delete|\*|\/\*|\'|\.\.\/|\.\/|UNION|into|load_file|outfile',$sql_str); if($check){ page_href("http://".$_SERVER['HTTP_HOST']."/home/sitemap.php"); exit(); }else{ return $sql_str; } } //防跨站攻击 function inject_check2($sql_str){ $check = eregi('javascript|vbscript|expression|applet|meta|xml|blink|link|style|script|embed|object|iframe|frame|frameset|ilayer|layer|bgsound|title|base|onabort|onactivate|onafterprint|onafterupdate|onbeforeactivate|onbeforecopy|onbeforecut|onbeforedeactivate|onbeforeeditfocus|onbeforepaste|onbeforeprint|onbeforeunload|onbeforeupdate|onblur|onbounce|oncellchange|onchange|onclick|oncontextmenu|oncontrolselect|oncopy|oncut|ondataavailable|ondatasetchanged|ondatasetcomplete|ondblclick|ondeactivate|ondrag|ondragend|ondragenter|ondragleave|ondragover|ondragstart|ondrop|onerror|onerrorupdate|onfilterchange|onfinish|onfocus|onfocusin|onfocusout|onhelp|onkeydown|onkeypress|onkeyup|onlayoutcomplete|onload|onlosecapture|onmousedown|onmouseenter|onmouseleave|onmousemove|onmouseout|onmouseover|onmouseup|onmousewheel|onmove|onmoveend|onmovestart|onpaste|onpropertychange|onreadystatechange|onreset|onresize|onresizeend|onresizestart|onrowenter|onrowexit|onrowsdelete|onrowsinserted|onscroll|onselect|onselectionchange|onselectstart|onstart|onstop|onsubmit|onunload',$sql_str); if($check){ page_href("http://".$_SERVER['HTTP_HOST']."/home/sitemap.php"); exit(); }else{ //return $sql_str; } } |
转自:http://www.oschina.net/code/snippet_875961_22517
View Details