zhaobinglong / myBlog

https://zhaobinglong.github.io/myBlog/
MIT License
7 stars 0 forks source link

nodejs常用模块 #80

Closed zhaobinglong closed 2 years ago

zhaobinglong commented 3 years ago

自定义过滤器filter

在src根目录下新建filters文件夹,新建index.js编辑项目所有的过滤

import Vue from 'vue'

//格式化时间
Vue.filter('filterTime', v => {
  var time = new Date(parseInt(v) * 1000);
  var y = time.getFullYear();
  var m = time.getMonth()+1 < 10 ? '0' + (time.getMonth()+1) : time.getMonth()+1 
  var d = time.getDate() < 10 ? '0' + time.getDate() : time.getDate()
  var h = time.getHours() < 10 ? '0' + time.getHours() : time.getHours()
  var mm = time.getMinutes() < 10 ? '0' + time.getMinutes() : time.getMinutes()
  var s = time.getSeconds() < 10 ? '0' + time.getSeconds() : time.getSeconds()
  return y+'-'+m+'-'+d+' '+h+':'+mm+':'+s;
})

在main中全局自动注册filter

// 首先在filter文件夹中定义过滤器函数
export function filterGender(gender) {
  const genderMap = ['女', '男', '', '未知']
  return genderMap[gender]
}

// 导入过滤器 register global utility filters
import * as filters from './filter'
Object.keys(filters).forEach(k => Vue.filter(k, filters[k]));
zhaobinglong commented 3 years ago

数据库链接

var mysql      = require('mysql');
var connection = mysql.createConnection({
  host     : 'ip',
  user     : 'xxxx',
  password : 'xxxx',
  database : 'xxxxx'
});

connection.connect();

connection.query('SELECT * from user', function (error, results, fields) {
  if (error) throw error;
  console.log('The solution is: ', results[0].solution);
});
zhaobinglong commented 3 years ago

事件模块

const EventEmitter = require('events');

class Player extends EventEmitter {}

var player = new Player()

player.on('play', (track) => {
    console.log(`正在播放《${track}》`)
})
player.emit('play', '黑凤凰')
player.emit('play', '复仇者联盟4')

EventEmitter引发的内存泄漏

默认情况下,如果一个事件的监听函数超过10个,EventEmitters对象将会打印一个警告信息.这是一个发现内存溢出很有用的方法.很明显不是所有的Emitters对象都应该限制10个监听器.这个函数将会允许增加限制个数,如果不想限制个数,可以设置为0.

MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 6 pipe listeners added to [Request]. Use emitter.setMaxListeners() to increase limit
zhaobinglong commented 3 years ago

stream模块

fs 本身提供了 readFile 和 writeFile,它们好用的代价就是性能有问题,会将内容一次全部载入内存。但是对于几 GB 的大文件,显然会有问题。

那么针对大文件的解决方案自然是:一点点读出来。这就需要用到 stream 了。以 readStream 为例

var http = require('http');
var fs = require('fs');

//  利用stream,读取data.txt文件到内存,然后返回
var server = http.createServer(function (req, res) {
    let stream = fs.createReadStream(__dirname + '/data.txt');//创造可读流
    stream.pipe(res);//将可读流写入response, pipe方法如同stream和response之间的一个管道,将data.txt文件一小段一小段地发送到客户端,减小了服务器的内存压力。
});
server.listen(8000);

四种流类型

Readable - 可读的流 (例如 fs.createReadStream()). Writable - 可写的流 (例如 fs.createWriteStream()). Duplex - 可读写的流 (例如 net.Socket). Transform - 在读写过程中可以修改和变换数据的 Duplex 流 (例如 zlib.createDeflate()

可读流实例

let fs = require('fs');
//通过创建一个可读流
let rs = fs.createReadStream('./1.txt',{
    flags:'r',//我们要对文件进行何种操作
    mode:0o666,//权限位
    encoding:'utf8',//不传默认为buffer,显示为字符串
    start:3,//从索引为3的位置开始读
    //这是我的见过唯一一个包括结束索引的
    end:8,//读到索引为8结束
    highWaterMark:3//缓冲区大小
});
rs.on('open',function () {
    console.log('文件打开');
});
rs.setEncoding('utf8');//显示为字符串
//希望流有一个暂停和恢复触发的机制
rs.on('data',function (data) {
    console.log(data);
    rs.pause();//暂停读取和发射data事件
    setTimeout(function(){
        rs.resume();//恢复读取并触发data事件
    },2000);
});
//如果读取文件出错了,会触发error事件
rs.on('error',function () {
    console.log("error");
});
//如果文件的内容读完了,会触发end事件
rs.on('end',function () {
    console.log('读完了');
});
rs.on('close',function () {
    console.log('文件关闭');
});

/**
文件打开
334
455
读完了
文件关闭
**/
zhaobinglong commented 3 years ago

cluster模块(内建模块)

HTTP服务器用于响应来自客户端的请求,当客户端请求数逐渐增大时服务端的处理机制有多种,如tomcat的多线程、nginx的事件循环等。而对于node而言,由于其也采用事件循环异步I/O机制,因此在并发的场景下性能非常好,但是由于单个node程序仅仅利用单核cpu,因此为了更好利用系统资源需要fork多个node进程执行HTTP服务器逻辑,所以node内建模块提供了child_process和cluster模块。

demo

const cluster = require('cluster');
const http = require('http');

// 计算当前的服务器CPU数量
const numCPUs = require('os').cpus().length;

// 如果是主线程,就去fork子线程,有多少个CPU就fokr多少个
if (cluster.isMaster) {
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('exit', (worker, code, signal) => {
    console.log(`工作进程 ${worker.process.pid} 已退出`);
  });
} else {
  http.createServer((req, res) => {
    res.writeHead(200);
    res.end('hello world\n');
  }).listen(8000);
}

原理

这里的三行代码就是cluster.js的全部内容,可以看出,子进程和主进程的区分是通过‘NODE_UNIQUE_ID’来判断的。我们分析cluster.fork方法可以发现,在createworkprocess中都会对NODE_UNIQUE_ID进行赋值,而master进程中是没有NODE_UNIQUE_ID的。所以再demo程序中可以分别在主进程和子进程中执行不同的内容。因此主进程执行完后,就仅仅fork出了子进程。

'use strict';
const childOrMaster = 'NODE_UNIQUE_ID' in process.env ? 'child' : 'master';
module.exports = require(`internal/cluster/${childOrMaster}`);

子进程不监听端口

子进程服务没有创建对底层服务端socket的进行监听,所以自然不会出现子进程端口复用的情况。最后,调用cb函数,将fake后的handle传递给上层net.Server,设置net.Server对底层的socket的引用。此后,子进程利用fake后的handle做端口侦听(其实压根啥都没有做),执行成功后返回。

function rr(message, indexesKey, cb) {
  if (message.errno)
    return cb(message.errno, null);

  var key = message.key;

  function listen(backlog) {
    // TODO(bnoordhuis) Send a message to the master that tells it to
    // update the backlog size. The actual backlog should probably be
    // the largest requested size by any worker.
    return 0;
  }  //...  const handle = { close, listen, ref: noop, unref: noop };   handles[key] = handle;  cb(0, handle);
}

主进程分发请求给子进程

子进程TCP服务器没有创建底层socket,它主要依赖IPC通道与主进程通信,既然主进程负责接受客户端请求,那么理所应当由主进程分发客户端请求给某个子进程,由子进程处理请求。具体分配给哪个子进程处理,是由round-robbine分发策略(本质上就是轮询算法)来决定的。由于子进程在server中设置了对底层的socket的引用,所以子进程接收到任务后,触发connection事件开始执行业务逻辑。

对于该部分还需要持续关注,因为涉及底层libuv,需要结合C++代码一起理解。比如:IPC通信方式有多种,node.js是如何决定使用哪种方式来通信?

参考

https://www.cnblogs.com/novak12/p/9304617.html https://segmentfault.com/a/1190000021230376 https://blog.csdn.net/xtx1990/article/details/8437622

zhaobinglong commented 3 years ago

global模块

常见错误1

// 服务器端return global提示如下错误
0|www      | TypeError: Converting circular structure to JSON
0|www      |     --> starting at object with constructor 'global'
0|www      |     --- property 'global' closes the circle
0|www      |     at JSON.stringify (<anonymous>)
0|www      |     at stringify (/Users/zhaobinglong/Desktop/github/nurse-helper/api/node_modules/_express@4.16.4@express/lib/response.js:1119:12)
0|www      |     at ServerResponse.json (/Users/zhaobinglong/Desktop/github/nurse-helper/api/node_modules/_express@4.16.4@express/lib/response.js:260:14)
0|www      |     at Request._callback (/Users/zhaobinglong/Desktop/github/nurse-helper/api/routes/wechat.js:26:13)
0|www      |     at Request.self.callback (/Users/zhaobinglong/Desktop/github/nurse-helper/api/node_modules/_request@2.88.2@request/request.js:185:22)
0|www      |     at Request.emit (events.js:311:20)
0|www      |     at Request.<anonymous> (/Users/zhaobinglong/Desktop/github/nurse-helper/api/node_modules/_request@2.88.2@request/request.js:1154:10)
0|www      |     at Request.emit (events.js:311:20)
0|www      |     at IncomingMessage.<anonymous> (/Users/zhaobinglong/Desktop/github/nurse-helper/api/node_modules/_request@2.88.2@request/request.js:1076:12)
0|www      |     at Object.onceWrapper (events.js:417:28)
0|www      | WARNING: NODE_APP_INSTANCE value of '0' did not match any instance config file names.
0|www      | WARNING: See https://github.com/lorenwest/node-config/wiki/Strict-Mode

global不能直接被ruturn,因为其中包含了很多服务器端特有的函数或者方法,可以return其中的某个对象

        global.poquanyundong = {
            token: body.access_token,
            expires_time: new Date().getTime() + 7200
        }
zhaobinglong commented 3 years ago

request模块

zhaobinglong commented 3 years ago

nodejs发送邮件

参考

https://blog.csdn.net/weixin_44251396/article/details/103921082 https://www.cnblogs.com/jackson-yqj/p/10154296.html

zhaobinglong commented 3 years ago

JWT(JSON WEB TOKEN)

JSON Web Token(缩写 JWT)是一种跨域认证解决方案。服务器认证以后,生成一个字符串,发回给用户,为了防止用户篡改数据,服务器在生成这个字符串的时候,会加上签名。以后,用户与服务端通信的时候,都要带上这个字符串。服务器完全只靠这个字符串认定用户身份。字符串的形式如下这样:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

这个字符串我们可以称之为JWT。JWT字符串可以在官网上jwt.io/去解析出来。解析出来后,发现有三个部分。仔细看JWT字符串,有两个点(.)把字符串分隔开了,其实就是和解析后的结构一一对应。 这三个部分就是JWT的数据结构。分别是Header(头部),Payload(负载),Signature(签名)。

参考

https://www.jianshu.com/p/576dbf44b2ae https://zhuanlan.zhihu.com/p/86937325