本篇介绍后台开发的常用技术与概念,以Node.js的Express框架为例,Express是Node.js上最流行的开发框架,十分轻量,提供了一套方便开发的工具方法。当前使用最多的是Express4.0版本。

Express太轻,叫他框架都有点不合适。个人认为框架的特点是我们编写的代码被框架调用,而Express只是提供了一套便捷的API与相关中间件。

学习资料:

知识图谱:nodejs进阶与Express知识图谱,尽量在其他技术上也可以通用

基本使用

生成项目--express-generator

该工具会自动生成Express工程目录,安装mvc的结构新建一些目录

 npm install express-generator -g
 express myapp
 
 .
├── app.js // 主文件,设置中间件间,被www调用
├── bin
│   └── www // 启动脚本,建立了一个server,监听了端口,不用这个启动就自己写在app.js中,更清晰。
├── package.json
├── public // 静态资源
│   ├── images
│   ├── javascripts
│   └── stylesheets
│       └── style.css
├── routes // 路由,设置了一堆get post方法的路由
│   ├── index.js
│   └── users.js
└── views // html或者模板文件,这里用的jade
    ├── error.jade
    ├── index.jade
    └── layout.jade

启动服务

var express = require('express');
var app = express();
// 一个内置的中间件,处理静态资源
app.use(express.static(__dirname + '/public'));
// 这里设置其他中间件。。
// 这里设置路由。。
// 监听端口
app.listen(8080);

路由设置

本质也是中间件,安装路径分发请求

app.get('/', function (req, res) {
  res.send('Hello World!');
});

响应请求

这里的req和res和Nodejs原生的相同,只是在我们添加了一些中间件之后,req中会多一些变量,帮助我们处理请求。可以在不涉及 Express 的情况下调用 req.pipe()req.on('data', callback) 和要执行的其他任何函数,如res.end()等。

Express添加了一些便捷方法

一般中间件

中间件会被链式调用,处理请求

// 定义中间件
var myLogger = function (req, res, next) {
  console.log('LOGGED');
  next(); // 如果不调用,表示结束,不会启动下个中间件
};
// next 带参数表示错误
function uselessMiddleware(req, res, next) {
  next('出错了!');
}
function uselessMiddleware(req, res, next) {
  res.end("ending...");
}
// 使用中间件
app.use(someMiddleware);
app.use('/path', someMiddleware);
// 其他
app.all("*",someMiddleware);// 所有请求都会调用
app.get("*",someMiddleware);// get请求会调用,Express还提供post、put、delete方法
// 除了绝对匹配以外,Express允许模式匹配,参加文档

// 使用第三方库
var bodyParser = require('body-parser'); 
app.use(bodyParser.json({limit: '1mb'}));

错误处理

在其他 app.use() 和路由调用之后,最后定义错误处理中间件

// 与其他中间件不同,它有四个参数,第一个是error
app.use(function(err, req, res, next) {
  console.error(err.stack);
  res.status(500).send('Something broke!');
});
// 可以定义多个错误处理中间件

模板引擎的使用

// 默认这个目录下面
// app.set('views', './views')
// 设置这个之后,下面的render不用在.jade的后缀了。同理,也可以设置html
app.set('view engine', 'jade');

app.get('/', function (req, res) {
  res.render('index', { title: 'Hey', message: 'Hello there!'});
});

jade是一种格式的模板引擎,注意是服务端渲染使用,它的语法与html不同,感觉不如ejs,还是保留原始的html比较好。

核心概念

中间件

这个概念在后台十分广泛,广义上的中间件是任何在系统中通用的组件,比如在分布式系统中的消息队列,缓存系统,配置管理系统,soa治理系统,都可以称之为中间件。这里的中间件也有通用的含义,如对http的结果的预处理(body解析,路由)。

nodejs中的中间件是使用的connect库,深入学习https://github.com/alsotang/node-lessons/tree/master/lesson18

路由

通俗的说就是对请求地址的分发,按照地址路由给某个方法来处理。因此需要一些正则表达式的知识。

多个函数处理一个路由

app.get('/example/b', function (req, res, next) {
  console.log('the response will be sent by the next function ...');
  next();
}, function (req, res) {
  res.send('Hello from B!');
});

链式处理的便捷方法 app.route('/book')

app.route('/book')
  .get(function(req, res) { // 对book的get方法处理
    res.send('Get a random book');
  })
  .post(function(req, res) { // 对book的post方法处理
    res.send('Add a book');
  })
  .put(function(req, res) { // 对book的put方法处理
    res.send('Update the book');
  });

Express的Router方法可以模块化的处理路由

var express = require('express');
var router = express.Router();
// middleware that is specific to this router
router.use(function timeLog(req, res, next) {
  console.log('Time: ', Date.now());
  next();
});
// define the home page route
router.get('/', function(req, res) {
  res.send('Birds home page');
});
// define the about route
router.get('/about', function(req, res) {
  res.send('About birds');
});
module.exports = router;

//////////////////////其他文件///////////////////////
var birds = require('./birds');
app.use('/birds', birds); // 对/birds/xxx的路由

模板引擎与render

渲染一个常用的概念,主要用来对前端界面(html)的填值处理。渲染分为服务端渲染客户端渲染

模板引擎是渲染填值的工具,如jade主要用于服务端,ejs两端通用,可以深入学习ejs的使用方法,和它的一些概念如layout模板等。

异步控制方法与回调地狱

实践

下面的总结有些乱,可以参考知识图谱梳理。

登录

授权登录oAuth2.0(第三方登录)

思考

MemCache Redis

memcache、redis为什么是必须的
由于应用程序实例作为单独的进程运行,因此它们不会共享同一内存空间。也就是说,对象位于应用程序每个实例的本地。因此,无法在应用程序代码中保存状态。然而,可以使用 Redis 之类的内存中数据存储器来存储与会话相关的数据和状态。此警告适用于几乎所有形式的水平扩展(无论是多个进程的集群还是多个物理服务器的集群)
对于负载均衡功能,可能必须确保与特定会话标识关联的请求连接到产生请求的进程。这称为会话亲缘关系或者粘性会话,可通过以上的建议来做到这一点:将 Redis 之类的数据存储器用于会话数据(取决于您的应用程序)。

数据库

mongodb hbase mysql 常见的数据库和应用场景
mongodb:不支持表 join,事务,schema-less(应用场景:log)
mongodb 和 mysql 要我选的话,无关紧要的应用我会选择 mongodb,就当个简单的存 json 数据的数据库来用;如果是线上应用,肯定还是会选择 mysql。毕竟 sql 比较成熟,而且各种常用场景的最佳实践都有先例了。

多进程与进程管理

nodejs是单线程的,一个进程中只有一个主线程,因此无法发挥多核CPU的优势。解决方法是启动多个nodejs实例,即一个程序启动多个相同的进程。这就需要一个中介来分发请求管理进程的生命周期。常用的管理方式有

多进程带来的问题,如何进程间通讯,共享内存变量(如session),这就引入了redis或者memcache这些分布式缓存。

一个典型的场景:登录请求第一次被分配到A实例,缓存了一些值(如sessionId和对应的值,如登录成功),第二次请求被分配到B实例,由于它没有缓存的值,sessionId找不到,又被判定为未登录

测试

部署

HTTP 是一个无状态协议,所以客户端每次发出请求时,Server下一次请求无法得知上一次请求所包含的状态数据。两种方案解决这个问题。(重点:Server需要这些信息,来逻辑处理

这两种技术都是在服务端解决http无状态的问题的方案,且主要用在浏览器中(有cookies实现),其思想可以通用,

另外一种是Token授权,主要是客户端的授权。这个技术往往用在由客户端维持状态,服务端仅仅提供无状态的服务的场景。(客户端凭token获取服务)

安全