• Node.js / JavaScript后端开发指引


    这是一篇关于后端 JavaScript 开发的指引,如果你对 JavaScript 的认识仍然停留在前端开发的话,你需要更新自己的知识体系了。



    Web 开发框架从以前流行的 LAMP/LEMP  架构逐渐转移到 Ruby on Rails 和 Python Django 架构之上。但最近有另外几种的架构变得成熟了:比如 Golang, Clojure/leiningen,  Node.js 。这三者之中我更关注 Node.js。原因是它的生态更加完善。所以最近的几个项目都是用它来实现的。Node.js 既适合做后端 API 的粘合,也适合给终端用户提供 API 。

    用 NPM 来做项目管理和包维护

    Node.js 的每个项目都应该有一个 package.json 配置文件。其中包含了这个项目或者库的信息和所依赖的库。一个相对完成的 package.json 文件像这样:

    {
      "name": "my-app",
      "version": "0.1.0",
      "private": true,
      "description": "My app and service",
      "main": "app.js",
      "scripts": {
        "start": "forever start app.js",
        "test": "mocha test"
      },
      "dependencies": {
        "coffee-script": "=1.4.0",
        "express": "=3.0.6",
        "mocha": "=1.7.4",
        "underscore": "=1.4.3",
        "forever": "=0.10.0",
        "async": "",
        "grunt-beautify": ""
      },
      "repository": "",
      "author": "Bruce Dou",
      "license": "BSD"
    }
    

    这个文件可以在你的项目目录下执行 npm init 来按指引创建。注意其中的 scripts 部分,这里可以创建很多自定义命令。比如如此配置之后,npm start 会执行 forever start app.js 命令。

    用 Grunt 来做代码格式修整、Lint 和其他自动化任务

    Grunt 是一个命令行任务自动化工具。它包含了很多有用的插件。可以实现代码美化、自动化部署、自动生成 sprit、自动创建项目模板、自动压缩前端代码、自动编译 coffescript 等等你能想到的任务。假如你找不到自己需要的功能,还可以自己开发 Grunt 插件来实现。它的配置文件是位于项目根目录的 grunt.js 。一般项目都需要的功能是代码美化和 Lint,配置文件象这样:

    module.exports = function (grunt) {
      // Project configuration.
      grunt.initConfig({
        beautify: {
          files: ['grunt.js', '*.js', 'lib/*.js']
        },
        lint: {
          files: ['grunt.js', '*.js', 'lib/*.js']
        },
        beautifier: {
          options: {
            indentSize: 2
          },
          tests: {
            options: {
              indentSize: 4
            }
          }
        },
        jshint: {
          options: {
            curly: true,
            eqeqeq: true,
            immed: true,
            latedef: true,
            newcap: true,
            noarg: true,
            sub: true,
            undef: true,
            boss: true,
            eqnull: true,
            browser: true
          },
          globals: {
            jQuery: true,
            Drupal: true,
            Backbone: true,
            _: true,
            app: true
          }
        }
      });
      grunt.loadNpmTasks('grunt-beautify');
      //grunt.loadNpmTasks('grunt-contrib-uglify');
      grunt.registerTask('default', 'beautify');
    };
    

    添加了配置文件之后可以在项目目录运行 grunt 命令自动执行任务串。grunt beautify 可以执行子任务。

    用 CoffeeScript 方言写更友好的 JavaScript

    CoffeeScript 提供了类似于 Ruby 的语法,简化了 JavaScript 的书写,简化了 JavaScript 中实现类和类的继承的实现。你不必再自己繁琐得通过 prototype 和 constructor,call,apply 实现类的继承。 无论用 VIM 还是 Sublime Text 都可以找到对应的实时转换工具,在写 CoffeeScript 的同时就能看到对应的 JavaScript。

    必不可少的几个库

    underscore

    underscore 是写 JavaScript 必不可少的库。提供了一些非常常用的方法,这些方法不仅使用方便,而且提高了代码的可读性。:

    用 _.each 代替 for 循环,比如:

    var contents = [];
    for(var i in msgs) {
      contents.push(msgs[i].content);
    }
    

    可以改写为:

    var contents = [];
    _.each(msgs, function(el) {
      contents.push(el.content);
    });
    

    其他常用的方法还有:

    _.map 用来对数组元素进行批量转换

    _.reduce 用来将数组元素合并为结果

    _.pluck 用来取对象数组中的子元素,返回包含对应子元素的新数组

    _.filter 用来有选择性的取数组中的某些值,返回包含符合条件的值的新数组

    _.mixin 用来增加自定义函数

    _.chain 用来实现函数式编程:

    _.chain([1,2,3])
     .map(function(v) {return v * 2;})
     .reduce(function(total, v) { total += v}, 0)
     .value();
    

    Async

    Async.js 对常用的流程控制模式进行了封装,比如并行处理、Pipeline等等:

    async.parallel 并行处理,整体等待最慢的函数返回,常用作多个后端请求的聚合或者并行处理:

    ...
    // construct call functions
    var callItems = [];
    function make_query_func(json) {
      return function (callback) {
        callBackService(json, callback);
      };
    }
    _.each(messages, function (el, i) {
      callItems.push(make_query_func({
        vmsg: messages[i].vmsg,
        cmsg: messages[i].cmsg
       }));
    });
    // execute the call functions parallelly
    async.parallel(callItems, function (err, results) {
      main_cb(results);
    });
    

    async.waterfall 可以用来将大量嵌套的函数顺序执行,前一个函数的结果作为下一个函数的参数。用它之后你的代码里将不会再出现过深的 callback 嵌套:

    // old way
    ...
    var result = function(uid, callback) {
      get_user_ids(uid, function(err, user_ids) {
        get_content(user_ids, function(err, content) {
          content_clean(content, function(err, clean_content) {
            callback(err, clean_content);
          });
        });
      });
    }
    // new way
    ...
    async.waterfall([get_user_ids, get_content, content_clean]);
    

    开发 C++ addons 增强和扩展 Node.js

    这意味着你不必担心某些逻辑的性能,也不必担心和其他框架的继承。因为所有语言或者开发栈都会提供 C/C++ 的接口。

    Node.js 配置管理方式

    有两种配置方式,config.js 或者 config.json 假如以 config.js 作为配置文件:

    // config.js
    exports.config = {'a':'a val', 'b': 'b val'};
    // app.js
    var config = require('./config').config;
    

    如果以 config.json 作为配置文件:

    // config.json
    {'a': 'a val', 'b': 'b val'}
    // app.js
    var config = JSON.parse(fs.readFileSync(process.cwd() + '/config.json'));
    

    开发内部 Service 推荐的部件

    • 进程统计信息,比如 uptime, memory usage, heap size, connection number, 处理的请求数量等等。这样便于和监控系统对接
    • RESTful apis,推荐以 RESTful JSON 格式作为内部通信协议,这其实对大部分应用完全足够,而且容易调试。
    • 配置文件。将可能会发生变化的变量放到配置文件里。
    • Service Level Agreement。比如超过 100ms 的请求报错而不是继续等待返回信息。

    Forever: Daemon 管理工具

    既然 Node.js 是长时间运行的后台进程,缺不了进程管理工具。Forever 就是为了实现对 Node.js 的 daemon 进程进行管理的工具。常用命令:

    forever start app.js 启动进程
    forever restart app.js 重启进程
    forever list 列出后台进程,并且列出 log 文件,可以方便的及时查看 log 文件内容。

    Node.js Cluster

    Node.js Cluster 是为了利用多核 CPU 的计算能力, 并且子进程可以共用同一个端口:

    var cluster = require('cluster');
    if (cluster.isMaster) {
        //start up workers for each cpu
        require('os').cpus().forEach(function() {
            cluster.fork();
        });
    
    } else {
        //load up your application as a worker
        require('./app.js');
    }
    

    JavaScript 开发常见问题:

    JavaScript 是传值还是传引用?

    简单说:如果参数为一个对象则为传引用,如果参数为变量或者函数则为传值。

    如何用 GDB 调试自己开发的 Node.js C++ addons?

    gdb –args nodejs script.js

    如何捕获 uncaughtException 并打印详细错误信息?

    在进程级别捕获异常:

    process.on('uncaughtException', function (e) {
      console.trace('Error: '.red + e);
      console.trace(e.stack);
      //process.exit();
    });
    

    如何优雅结束进程?

    Node.js 中对接收的 POSIX 信号做自定义操作很方便,进行进程结束前的清理工作:

    process.on('SIGINT', function () {
      // wait connections to close
      process.exit();
      console.log("gracefully shutting down from  SIGINT (Crtl-C)".yellow);
    });
    

    如何使用 Array 和 Object

    对于列表类的信息,比如用户列表推荐使用 Object 而不是 Array:

    var a = [];
    a[1000] = 1;
    //a is an Array which length is 1001
    

    如何聚合多个后端服务并提供 SLA ?

    用 Node.js 可以非常简单的实现对返回所用时间进行控制。比如在之前例子代码 async.parallel 的请求中设置超时定时器。对多个处理进程并发请求取其中时间最短的结果,这样可以保证返回时间都保持很短。

    什么时候使用 process.nextTick() ?

    1. 重量使用 CPU 的函数中释放 CPU 给其他任务
    2. 将执行放到下一个 tick ,等待初始化

    更多相关参考:

    http://nodejs.org/
    https://npmjs.org/
    http://coffeescript.org/
    http://underscorejs.org/
    http://nodejs.org/api/addons.html
    http://expressjs.com/
    https://github.com/caolan/async
    http://howtonode.org/understanding-process-next-tick
    http://book.mixu.net/ch7.html
    http://gruntjs.com/

  • 相关阅读:
    InChatter系统之服务器开发(一)
    InChatter系统开源聊天模块前奏曲
    System.Lazy<T>延迟加载
    系统的层级架构
    Android网络通信库Volley简介(转)
    switch case 忘记 break
    switch case 忘记 break
    C# Lock 解读
    JSON资料整理(转载)
    [Leetcode]253. 会议室 II(数组/堆)
  • 原文地址:https://www.cnblogs.com/meetrice/p/3000782.html
Copyright © 2020-2023  润新知