当前位置:   article > 正文

node实战入门总结_session pm2

session pm2

搭建流程(按新建顺序)层层嵌套引入

系统架构设计的四层抽象:

  • 第一层: www.js [开启 Server]
  • 第二层:app.js [通信设置层]
  • 第三层:router.js [业务逻辑层]
  • 第四层:controller.js [数据层]

总体结构:

  • bin/www.js(第一层):node构建服务的基本流程配置,和业务无关。package里面配置好的入口文件
  • app.js(第二层
  • src
    • router(第三层:根据不同路由返回不同数据)
      • blog.js:博客crud接口
      • user.js:用户相关的接口
    • control(第四层:返回数据给到路由这边,只关心数据及其处理,一般是和数据库这边交互)
    • model(数据模型:成功、失败返回的数据格式等)
    • conf:配置
      • db.js:获取环境参数对数据库进行配置,根据开发环境下的和线上环境使用不同的数据库
    • db
      • mysql.js:对数据库的基本操作(连接创建关闭,不涉及业务逻辑)
      • redis.js: 封装redis的set、get操作
    • utils:工具
      • log.js:专门用来写日志的工具
  • logs:日志
    • access.log:存储访问日志
    • event.log:存储一些自定义内容
    • error.log:存储错误内容

记录

session和Redis

一、session不足:

  • session是存储在nodeJS进程的内存中的
  • 进程内存有限,session过大,会把进程挤爆
  • 进程与进程之间内存相互独立,无法共享,因此session也无法在多进程之间共享

解决:将session存储在Redis中

  • Redis 是另外一个服务,和web server的nodeJS 进程没有关系了
  • Redis类似于数据库,也是一个数据仓库,用来存储数据的(可以叫做内存数据库吧)
  • 但是普通数据库的数据是存储在硬盘上的,访问速度慢
  • Redis上的数据是存储在内存中的,访问速度快,但昂贵

二、为何session适合用Redis存储

  • session访问频繁,对性能要求极高
  • session可不考虑断点丢失数据问题(内存的硬伤),因为大不了重新登录
  • session数据量不会太大,一般只是存储用户的个人信息(相比于 MySQL 中 存储的数据)

三、开启redis服务:

打开一个 cmd 窗口 使用 cd 命令切换目录到redis目录下 运行

redis-server.exe redis.windows.conf
  • 1
接口和前端联调
  • 登陆功能依赖cookie,必须用浏览器来联调(postman无法做到)
  • cookie跨域不共享,前端和server端必须同域
  • 因此需要用到Nginx 做代理,让前后端同域
Nginx介绍
  • 高性能的 web 服务器,开源免费
  • 一般用于做静态服务(CDN)、负载均衡
  • 反向代理

如何配置反向代理:

  1. 要同时开启对应的服务(占用不同端口):nodeJS server服务8000端口(后端接口)、http-server服务8001端口(前端页面)
  2. 监听8080端口
  3. 反向代理配置

nginx.conf配置文件:

location / {
    # 如果是根目录(即 http://localhost:8080)则代理到 8001 端口
	proxy_pass http://localhost:8001;
}

location /api/ {
    # 如果是访问接口,则代理到 8000 端口
	proxy_pass http://localhost:8000;
    proxy_set_header Host $host;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  1. 通过访问http://localhost:8080/index.html即可

ps:

  • 开启nginx 服务:start nginx
  • 开启http-server服务:http-server -p8001
  • 开启nodeJs server服务:npm run dev

Nginx命令

  • start nginx:启动nginx服务
  • nginx -t:测试配置文件nginx.conf 语法是否正确

express搭建

构建流程

  1. npm install -g express -generator全局安装express命令安装工具
  2. express 项目名
  3. npm install 安装组件
  4. npm start启动项目(服务器)
  5. npm i nodemon cross-env

写自己的业务逻辑:

  1. 新建路由文件,针对不同路由进行业务逻辑处理
  2. 将新建的路由文件(比如blog.js)引入app.js文件中,并使用app.use注册我们的路由

相对于原生nodejs:

  • 直接通过req.query获取get传过来的参数,通过req.body获取 post传过来的参数
  • 通过res.json直接返回json数据给客户端
  • 使用express-sessionconnect-redis、登陆中间件
  • 使用morgan来记录日志,根据配置决定将日志输出到控制台还是文件中

koa2

构建流程

  1. npm install koa-generator -g全局安装express命令安装工具
  2. Koa2 项目名
  3. npm install 安装组件
  4. npm i cross-env
  5. npm run dev启动项目(服务器)

相对于express和原生nodejs:

  • 直接通过ctx.query获取get传过来的参数,通过ctx.request.body获取 post传过来的参数
  • 通过ctx.body直接返回json数据给客户端
  • 通过async/await来实现中间件
  • 通过await next()来执行下一个中间件
  • 相比express,koa2在记录日志时要手动安装koa-morgan插件

中间件

一、express中间件
  • 其实中间件就是一个这样格式的函数,三个参数req, res, next
function loginCheck(req, res, next) {
    console.log('模拟登陆成功')
    next()
}
  • 1
  • 2
  • 3
  • 4
  • 可以通过app.use()app.get()app.post()注册中间件
  • 可以注册多个中间件,依次执行
  • 通过next()的执行一个一个的往下串联下一个中间件

实现原理思路:

  1. app.use用来注册中间件,先收集起来
  2. 遇到http请求,根据pathmethod判断触发哪些中间件
  3. 实现next()机制,即上一个通过next()触发下一个
// 实现类似 express 的中间件
const http = require('http')
const slice = Array.prototype.slice

class LikeExpress {
    constructor() {
        // 收集存放中间件的列表
        this.routes = {
            all: [],   // app.use(...)
            get: [],   // app.get(...)
            post: []   // app.post(...)
        }
    }

    register(path) {
        const info = {}
        if (typeof path === 'string') {
            info.path = path
            // 从第二个参数开始,转换为数组,存入 stack
            info.stack = slice.call(arguments, 1)
        } else {
            info.path = '/'
            // 从第一个参数开始,转换为数组,存入 stack
            info.stack = slice.call(arguments, 0)
        }
        return info
    }

    // 中间件注册和收集
    use() {
        const info = this.register.apply(this, arguments)
        this.routes.all.push(info)
    }

    get() {
        const info = this.register.apply(this, arguments)
        this.routes.get.push(info)
    }

    post() {
        const info = this.register.apply(this, arguments)
        this.routes.post.push(info)
    }

    // 通过当前 method 和 url 来匹配当前路由可执行的中间件
    match(method, url) {
        let stack = []
        if (url === '/favicon.ico') {
            return stack
        }

        // 获取 routes
        let curRoutes = []
        curRoutes = curRoutes.concat(this.routes.all)
        curRoutes = curRoutes.concat(this.routes[method])

        curRoutes.forEach(routeInfo => {
            if (url.indexOf(routeInfo.path) === 0) {
                // url === '/api/get-cookie' 且 routeInfo.path === '/'
                // url === '/api/get-cookie' 且 routeInfo.path === '/api'
                // url === '/api/get-cookie' 且 routeInfo.path === '/api/get-cookie'
                stack = stack.concat(routeInfo.stack)
            }
        })
        return stack
    }

    // 核心的 next 机制
    handle(req, res, stack) {
        const next = () => {
            // 拿到第一个匹配的中间件
            const middleware = stack.shift()
            if (middleware) {
                // 执行中间件函数
                middleware(req, res, next)
            }
        }
        next()
    }

    callback() {
        return (req, res) => {
            res.json = (data) => {
                res.setHeader('Content-type', 'application/json')
                res.end(
                    JSON.stringify(data)
                )
            }
            const url = req.url
            const method = req.method.toLowerCase()

            const resultList = this.match(method, url)
            this.handle(req, res, resultList)
        }
    }

    listen(...args) {
        const server = http.createServer(this.callback())
        server.listen(...args)
    }
}

// 工厂函数
module.exports = () => {
    return new LikeExpress()
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
二、koa2中间件
  • koa2中间件其实就是一个async函数,参数为(ctx, next)
app.use(async (ctx, next) => {
    await next();
    ctx.body = 'Hello World';
});
  • 1
  • 2
  • 3
  • 4

实现思路:

  • 也是使用app.use来注册中间件,先收集起来
  • 实现next机制,即上一个通过await next()触发下一个中间件
  • 不涉及methodpath的判断
<!--实现类似 靠中间件-->
const http = require('http')

// 组合中间件
function compose(middlewareList) {
    return function (ctx) {
        function dispatch(i) {
            const fn = middlewareList[i]
            try {
                return Promise.resolve(
                    fn(ctx, dispatch.bind(null, i + 1))  // promise
                )
            } catch (err) {
                return Promise.reject(err)
            }
        }
        return dispatch(0)
    }
}

class LikeKoa2 {
    constructor() {
        this.middlewareList = []
    }

    // 收集中间件列表
    use(fn) {
        this.middlewareList.push(fn)
        return this
    }

    createContext(req, res) {
        const ctx = {
            req,
            res
        }
        ctx.query = req.query
        return ctx
    }

    handleRequest(ctx, fn) {
        return fn(ctx)
    }

    callback() {
        const fn = compose(this.middlewareList)

        return (req, res) => {
            const ctx = this.createContext(req, res)
            return this.handleRequest(ctx, fn)
        }
    }

    listen(...args) {
        const server = http.createServer(this.callback())
        server.listen(...args)
    }
}

module.exports = LikeKoa2

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61

node线上环境

PM2
  • 进程守护,系统奔溃自动重启restart,而不是说系统出错之后其他用户就无法使用了
  • 启动多进程,充分利用cpu 和内存
  • 自带日志记录功能

下载安装

cnpm i pm2 -g
  • 1

配置命令

"prd": "cross-env NODE_ENV=production pm2 start app.js"
  • 1

启动

npm run prd
  • 1

常用命令

  • pm2 start … 启动进程
  • pm2 list: 可以查看pm2进程列表
  • pm2 restart appNme/id : 重启进程
  • pm2 stop appName/id : 停止
  • pm2 delete appName/id : 删除
  • pm2 info appName/id :查看基本信息
  • pm2 log appName/id :查看进程日志
  • pm2 monit appName/id:监控进程的cpu和内存信息

pm2配置文件

  1. 包括进程数量、日志文件目录等
  2. 修改pm2 启动命令,重启
  3. 访问 server,检查日志文件的内容(日志记录是否生效)
{
    "apps": {
        "name": "pm2-test-server", // 进程名
        "script": "app.js", //用框架就是'bin/www'
        "watch": true,  // 监听文件变化,是否自动重启
        "ignore_watch": [ // 哪些文件不需要重启
            "node_modules",
            "logs"
        ],
        "instances": 4, // 进程个数,这里
        "error_file": "logs/err.log", // 错误日志存放位置
        "out_file": "logs/out.log", // 本来打印在控制台的console自定义存放在文件里
        "log_date_format": "YYYY-MM-DD HH:mm:ss" // 日志的时间戳
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
多进程

为何使用多进程

  • 操作系统会限制一个进程的最大可用内存。因为按照软件设计拆分这种模式来说,如果一个进程做很多很多的事情,该进程万一奔溃,这可是天灾后果。因此需要分为多个进程,进程之间相互独立,提高了服务器稳定性
  • 因此多进程可以充分利用机器的内存,充分发挥多核CPU的优势(同时处理多个进程)

多进程和redis

  • 多进程之间的session无法共享
  • 采用共享redis 来解决

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/118902
推荐阅读
相关标签
  

闽ICP备14008679号