Nodejs后端架构基础知识和案例展示

入门笔记,大神请绕路!!!
简单的说 Node.js 就是运行在服务端的 JavaScript。
Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台。
Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎,V8引擎执行Javascript的速度非常快,性能非常好。

NodeJS的优缺点

  • 优点:
    1.高并发(高并发的时候,会出现数据库连接数不够用的情况,网上还有对应解决方案,还未实践)
    2.适合I/O密集型应用
    3.事件驱动、非阻塞I/O、高效、轻量

解决数据库高并发,程序解决方案:
数据库高并发时,连接数不够。平台监听sql操作,如果状态是ready,就回调执行sql操作。相当于将sql操作也做异步回调处理,每次sql操作都是在上次操作ready后,再执行,强制串行执行sql操作。这样就减轻数据库压力。但是也可能导致整个应用变慢。

var proxy = new EventProxy();
var status = "ready";
var select = function(callback){
    proxy.once("selected",callback);
    if(status == "ready"){
        status = "pending";
        db.select("SQL", function(results){
            proxy.emit("selected",results);
            status = "ready";
        });
    }
  • 缺点:
    1.不适合CPU密集型应用;
    CPU密集型应用给Node带来的挑战主要是:由于JavaScript单线程的原因,如果有长时间运行的计算(比如大循环),将会导致CPU时间片不能释放,使得后续I/O无法发起;
    解决方案:分解大型运算任务为多个小任务,使得运算能够适时释放,不阻塞I/O调用的发起;
    2.只支持单核CPU,不能充分利用CPU(可以利用多进程利用cpu的,cluster就可以让你做到nodejs将电脑多核利用起来)
    解决方案:
    (1)Nnigx反向代理,负载均衡,开多个进程,绑定多个端口;
    (2)开多个进程监听同一个端口,使用cluster模块;

应用场景

1. RESTful API
这是NodeJS最理想的应用场景,可以处理数万条连接,本身没有太多的逻辑,只需要请求API,组织数据进行返回即可。它本质上只是从某个数据库中查找 一些值并将它们组成一个响应。由于响应是少量文本,入站请求也是少量的文本,因此流量不高,一台机器甚至也可以处理最繁忙的公司的API需求。

2. 统一Web应用的UI层
目前MVC的架构,在某种意义上来说,Web开发有两个UI层,一个是在浏览器里面我们最终看到的,另一个在server端,负责生成和拼接页面。

不讨论这种架构是好是坏,但是有另外一种实践,面向服务的架构,更好的做前后端的依赖分离。如果所有的关键业务逻辑都封装成REST调用,就意味着在上层 只需要考虑如何用这些REST接口构建具体的应用。那些后端程序员们根本不操心具体数据是如何从一个页面传递到另一个页面的,他们也不用管用户数据更新是 通过Ajax异步获取的还是通过刷新页面。

3. 大量Ajax请求的应用

例如个性化应用,每个用户看到的页面都不一样,缓存失效,需要在页面加载的时候发起Ajax请求,NodeJS能响应大量的并发请求。总而言之,NodeJS适合运用在高并发、I/O密集、少量业务逻辑的场景。

演示一:Hello World!

脚本模式
以下是我们的第一个Nodejs程序:

console.log("Hello World");

保存该文件,文件名为 helloworld.js, 并通过 node命令来执行:node helloworld.js。程序执行后,正常的话,就会在终端输出 Hello World

image.png

交互模式
打开终端,键入node进入命令交互模式,可以输入一条代码语句后立即执行并显示结果,例如:

$ node
> console.log('Hello World!');
Hello World!

image

演示二:nodejs基于http插件,监听接口,返回数据

1、新建app.js文件,然后并执行执行node app.js
代码如下:

var http = require('http');
var server = http.createServer(function (req, res) {
    var body = '';
    req.on('data', function(data) {      // 接收客户端发送过来的数据, 也就是 xmlHttp.send(value);
        body += data;
    });

    req.on('end', function() {
        res.writeHeader(200, {
            'Content-Type': 'text/plain',
            'Access-Control-Allow-Origin': '*'    //解决跨域问题
        });
        res.write("hello:" + body);
        res.end();
    })
});
server.listen(3000, function () {
    console.log('server start at localhost:3000');
});

2、找个引入jquery的页面,执行post请求:

$.ajax({
url:"http://localhost:3000",
data:"我是快递小哥,请开开门",
type:"post",
success:function(d){console.log("success",d)},
error:function(d){console.log("error",d)}
})

执行请求,返回hello:我是快递小哥,请开开门

image.png

这个例子中,nodejs充当后台,app.js中实现了http://loadlhost:3000接口,返回了前端提交的内容。

演示三:nodejs基于express插件,监听接口,返回数据

1、新建service.js文件,然后执行执行node service.js
代码如下:

//demo
const express = require('express');
const app = express();

//设置允许跨域访问该服务.
app.all('*', function (req, res, next) {
  res.header('Access-Control-Allow-Origin', '*');
  //Access-Control-Allow-Headers ,可根据浏览器的F12查看,把对应的粘贴在这里就行
  res.header('Access-Control-Allow-Headers', 'Content-Type');
  res.header('Access-Control-Allow-Methods', '*');
  res.header('Content-Type', 'application/json;charset=utf-8');
  next();
});

app.post('/hello', function (req, res) {
  res.json({title:"你好"});
  
});

const server = app.listen(8082, function () {
  console.log('Express app server listening on port %d', server.address().port);
});

2、找个引入jquery的页面,执行post请求:

$.ajax({
url:"http://localhost:8082/hello",
type:"post",
success:function(d){console.log("success",d)},
error:function(d){console.log("error",d)}
})

返回数据:


image.png

演示四:nodejs+express+mysql搭建后端环境

引用:http://www.cnblogs.com/ssqqhh/p/6289690.html
1、创建数据库
创建名为nodejs的数据库,新建一张users表,采用utf8编码。表结构和数据如下:

image.png

2、安装express

npm i express -g//安装express
npm i express-generator -g//安装命令行工具
express --version//检测express是否安装成功

3、创建express项目
执行express express-demo,新建express项目,文件目录说明:

app.js//启动文件,或者说入口文件。新的页面以及页面相应的路由需要在这里配置;
package.json//存储着工程的信息及模块依赖,当在 dependencies 中添加依赖的模块时,运行 npm install,npm //会检查当前目录下的 package.json,并自动安装所有指定的模块
node_modules//存放 package.json 中安装的模块,当你在 package.json 添加依赖的模块并安装后,存放在这个文件夹下
public//存放 image、css、js 等文件
routes//存放路由文件
views//存放视图文件或者说模版文件
bin//存放可执行文件

4、运行express项目

cd express-demo//进入项目
npm i//安装依赖文件
npm start//启动项目

启动成功,空荡荡的页面中就出现下面内容

image.png

5、写项目代码
需要创建以下文件:
model\config.js:数据库配置

module.exports = {
    mysql: {
        host: 'localhost',
        user: 'root',
        password: '123123',
        database: 'nodejs',
        port: 3306
    }
};

views\login.jade:登录页面

doctype html
html
  head
    title
    link(href='/stylesheets/style.css', rel='stylesheet')
    link(href='/bootstrap-3.3.7/css/bootstrap.min.css', rel='stylesheet')
    script(src='/javascripts/jquery.min.js')
    script(src='/bootstrap-3.3.7/js/bootstrap.min.js')
  body(style='background: #dcd9da')
    // h1= title
    .container
      .row
        .col-md-4.col-md-offset-4
          .panel.panel-default(style='margin-top: 100px')
            .panel-heading(style='background: #0d6aad')
              h4(align='center', style='color: #ffffff;')
                | express-jade-bootstrap-mysql-demo
            .panel-body(style='background: #e0e1ea')
              .form-group
                .input-group
                  span.input-group-addon 账号
                  input#username.form-control(type='text', placeholder='请输入账号')
              .form-group
                .input-group
                  span.input-group-addon 密码
                  input#password.form-control(type='password', placeholder='请输入密码')
              .form-group
                .col-lg-offset-7
                  | 没有账号?
                  a(href='/register') 注册
              .form-group
                button#login.btn.btn-success.btn-block(type='button')
                  | 登录
              #popup.alert.alert-warning
                a#close.close(href='#') ×
                div(align='center')
                  strong#popup-content(style='color: #b12e30;')
    script(type='text/javascript').
      $(document).ready(function () {
        var username = $("#username");
        var password = $("#password");
        var login = $("#login");
        var popup = $("#popup");
        var popupContent = $("#popup-content");
        var close = $("#close");
        popup.hide();
        close.click(function () {
          popup.hide();
        });
        login.click(function () {
          if (username.val() == "" || password.val() == "") {
            popup.show();
            popupContent.html("账号或密码不能为空!");
          } else {
            $.ajax({
                      url: "/login/userLogin",
                      data: {
                        username: $("#username").val(),
                        password: $("#password").val()
                      },
                      type: "POST",
                      timeout: 36000,
                      dataType: "text",
                      success: function (data, textStatus) {
                        //alert(data);
                        var dataJson = eval("(" + data + ")");
                        if (dataJson.code == 200) {
                          alert("登录成功");
                          window.location.href = "/";
                        } else if (dataJson.code == 300) {
                          popup.show();
                          popupContent.html("账号不存在,请重新输入!");
                        } else if (dataJson.code == 400) {
                          popup.show();
                          popupContent.html("密码有误,请重新输入!");
                        } else {
                          popup.show();
                          popupContent.html("登录出错!");
                        }
                      },
                      error: function (XMLHttpRequest, textStatus, errorThrown) {
                        alert("error:" + textStatus);
                      }
                    }
            );
          }
        })
        ;
      })
      ;

routes\login.js:登录接口逻辑

var express = require('express');
var router = express.Router();
// 实现与MySQL交互
var mysql = require('mysql');
var config = require('../model/config');
// 使用连接池,提升性能
var pool = mysql.createPool(config.mysql);
/* GET home page. */
router.get('/', function (req, res, next) {
    res.render('login', { title: 'login' });
});
router.post('/userLogin', function (req, res, next) {
    var username = req.body.username;//获取前台请求的参数
    var password = req.body.password;
    pool.getConnection(function (err, connection) {
        //先判断该账号是否存在
        var $sql = "select * from users where username=?";
        connection.query($sql, [username], function (err, result) {
            var resultJson = result;
            console.log(resultJson.length);
            if (resultJson.length === 0) {
                result = {
                    code: 300,
                    msg: '该账号不存在'
                };
                res.json(result);
                connection.release();
            } else {  //账号存在,可以登录,进行密码判断
                var $sql1 = "select password from users where username=?";
                connection.query($sql1, [username], function (err, result) {
                    var temp = result[0].password;  //取得数据库查询字段值
                    console.log(temp);
                    if (temp == password) {
                        result = {
                            code: 200,
                            msg: '密码正确'
                        };
                    } else {
                        result = {
                            code: 400,
                            msg: '密码错误'
                        };
                    }
                    res.json(result); // 以json形式,把操作结果返回给前台页面
                    connection.release();// 释放连接
                });
            }
        });
    });
});
module.exports = router;

views\register.jade:注册页面

doctype html
html
  head
    title
    link(href='/bootstrap-3.3.7/css/bootstrap.min.css', rel='stylesheet')
    script(src='/javascripts/jquery.min.js')
    script(src='/bootstrap-3.3.7/js/bootstrap.min.js')
  body(style='background: #dcd9da')
    nav.collapse.navbar-collapse.navbar-inverse
      .navbar-header
        a.navbar-brand express-jade-bootstrap-mysql-demo
      ul.nav.navbar-nav.navbar-right
        li(style='margin-right: 20px')
          a(href='/login')
            span.glyphicon.glyphicon-log-in
            |  登录
    .container
      .row
        .col-md-4.col-md-offset-4
          .panel.panel-default(style='margin-top: 40px')
            .panel-heading(style='background: #0d6aad')
              h3(align='center', style='color: #ffffff;')
                | 账号注册
            .panel-body(style='background: #e0e1ea')
              .form-group
                .input-group
                  span.input-group-addon   账   号  
                  input#username.form-control(type='text', placeholder='请输入账号')
              .form-group
                .input-group
                  span.input-group-addon   密   码  
                  input#password.form-control(type='password', placeholder='请输入密码')
              .form-group
                .input-group
                  span.input-group-addon 确认密码
                  input#password1.form-control(type='password', placeholder='请再次输入密码')
              .form-group
                .input-group
                  span.input-group-addon   姓   名  
                  input#name.form-control(type='text', placeholder='请输入姓名')
              .form-group
                button#register.btn.btn-success.btn-block(type='button')
                  | 注册
              #popup.alert.alert-warning
                a#close.close(href='#') ×
                div(align='center')
                  strong#popup-content(style='color: #b12e30;')
    script(type='text/javascript').
      $(document).ready(function () {
        var username = $("#username");
        var password = $("#password");
        var password1 = $("#password1");
        var name = $("#name");
        var register = $("#register");
        var popup = $("#popup");
        var popupContent = $("#popup-content");
        var close = $("#close");
        popup.hide();
        close.click(function () {
          popup.hide();
        });
        register.click(function () {
          if (username.val() == "" || password.val() == "" || password1.val() == "" || name.val() == "") {
            popup.show();
            popupContent.html("注册信息不能为空!");
          } else if (password.val() !== password1.val()) {
            popup.show();
            popupContent.html("两次输入的密码不一样!");
          } else {
            //访问服务器,将注册信息写入数据库
            $.ajax({
              url: "/register/userRegister",
              data: {
                username: $("#username").val(),
                password: $("#password").val(),
                name: $("#name").val()
              },
              type: "POST",
              timeout: 36000,
              dataType: "text",
              success: function (data, textStatus) {
                var dataJson = eval("(" + data + ")");
                if (dataJson.code == 200) {
                  alert("注册成功");
                  window.location.href = "/login";
                } else if (dataJson.code == 300) {
                  popup.show();
                  popupContent.html("该账号已存在!");
                } else if (dataJson.code == 400) {
                  popup.show();
                  popupContent.html("注册失败,请重新注册!");
                } else {
                  popup.show();
                  popupContent.html("注册出错!");
                }
              },
              error: function (XMLHttpRequest, textStatus, errorThrown) {
                alert("error:" + textStatus);
              }
            });
          }
        });
      });

routes\register.js:注册接口逻辑

var express = require('express');
var router = express.Router();
// 实现与MySQL交互
var mysql = require('mysql');
var config = require('../model/config');
// 使用连接池,提升性能
var pool = mysql.createPool(config.mysql);
/* GET home page. */
router.get('/', function (req, res, next) {
    res.render('register', { title: 'register' });
});
router.post('/userRegister', function (req, res, next) {
    var username = req.body.username;
    var password = req.body.password;
    var name = req.body.name; //获取前台请求的参数
    pool.getConnection(function (err, connection) {
        //先判断该账号是否存在
        var $sql = "select * from users where username=?";
        connection.query($sql, [username], function (err, result) {
            var resultJson = result;
            console.log(resultJson.length);
            if (resultJson.length !== 0) {
                result = {
                    code: 300,
                    msg: '该账号已存在'
                };
                res.json(result);
                connection.release();
            } else {  //账号不存在,可以注册账号
                // 建立连接,向表中插入值  数据库表名为user-info会出错
                var $sql1 = "INSERT INTO users(id, username, password, name) VALUES(0,?,?,?)";
                connection.query($sql1, [username, password, name], function (err, result) {
                    console.log(result);
                    if (result) {
                        result = {
                            code: 200,
                            msg: '注册成功'
                        };
                    } else {
                        result = {
                            code: 400,
                            msg: '注册失败'
                        };
                    }
                    res.json(result); // 以json形式,把操作结果返回给前台页面
                    connection.release();// 释放连接
                });
            }
        });
    });
});
module.exports = router;

app.js:修改代码

...
var login = require("./routes/login");
var register = require("./routes/register");
...
app.use('/login', login);
app.use('/register', register);
...

jquery.min.js:引入jquery,放到public文件夹
bootstrap:引入bootstrap,放到public文件夹

准备好以上代码后,执行npm start命令启动项目,打开登录页面:http://localhost:3000/login,注册页面:http://localhost:3000/register

登陆成功

注册成功



参考:
NodeJs+Express+SqlServer简易后台API服务搭建

推荐阅读更多精彩内容