【转】Node深入浅出 章节总结(第九章 — 玩转进程) 完结篇

Node深入浅出 章节总结(第九章 — 玩转进程) 完结篇 看完本章后,应能理解 egg 多进程模型的原理与思想,了解进程守护与集群健壮性的重要性,基本理解 node 是如何做单机集群的。 至此 《Node深入浅出》系列总结完毕,后面两章 测试 与 产品工程化 就不总结了,这是根据公司团队决定的。 至此本书已经完毕啦~下本书不出意外是死月的《NodeJs:来一打C++扩展》,不过碍于我只会C,以前没写过C++,所以总结的速度会更慢,因为太多知识要学了~~~ 阅读详情

 转自:https://blog.youkuaiyun.com/yolo0927/article/details/81224942

为了看黑色背景,就转了过来,眼睛看白色屏幕实在受不鸟!

本章总结将结合个人搭建 egg 引入公司的一些实践来进行总结,希望能让大家了解到进程管理和集群分发的重要性。

阅读完本章你应该理解以下几点:

为什么要使用多进程架构启动服务;
经典的 Master-Worker(主从模式) 介绍到在 egg 中的应用;
如何使用 child_process 模块与句柄(Handle)传递实现主从模式架构,从而实现启动多个服务监听一个端口;
处理高并发场景的集群健壮性问题;
使用 Cluster 模块 (本质是对 child_process 模块实现 Master-Work 架构过程的封装) 快速实现进程集群;


一、为什么要使用多进程架构启动服务 

首先我们从两个关键性指标开始考虑

CPU使用率;
内存占用率;
首先我们知道 js 是单线程的,所以 NodeJs 也不以外,启动服务时只能运用到单核,即一个 CPU,但如今大家的服务器或pc普遍都已经是 4 cpu 了,这就导致了另外 3 个 cpu 资源的浪费,这导致 node 的计算能力不弱都不行,而且单线程最大的问题还有个健壮性问题,一旦有一个错误没被捕获到,有可能就会导致整个进程的崩溃,而服务自然就断了。

在高并发的场景下,单 cpu 的计算量是远远不够的,即使 node 全异步的概念很擅长处理密集型 io,但也是寡不敌众啊,高并发下仍会导致响应产生延迟与耽误的情况,这时我们就要引入多进程架构来解决这个问题了,利用其它 cpu 同时进行计算处理 io,提高 cpu 的使用率,将并发请求分发到不同进程独立处理响应,从而解决高并发下延迟请求队列积累过长的问题。

多个 cpu 的有没有缺点?当然有,多个 cpu 同时计算是需要内存的,这个大家都懂,我们每次计算的数据结构都会在内存中进行处理,新生代会被立刻释放,需要存储的会被移动到老生代中,前面的章节已经为大家介绍过这个过程了,我就不废话了,所以榨干 cpu 的同时,我们一定要关注内存的占用率,在其中取得一个最佳平衡。这就像是算法一样,时间换空间 或者 空间换时间 选择一个,等价交换。

二、经典的 Master-Worker (主从模式) 架构 

 

由上图我们可以很清晰的看到整个架构的过程,创建一个主进程,然后子进程通过 fork 主进程生成,此时便解决了不充分利用 cpu 的问题了,但是高并发问题仍然没有解决哦,因为请求的分发并没有做处理。

在这个架构中,主进程是不负责具体的业务处理的,而是专门负责管理(socket 的分发与进程间的通信等)和调度(请求分发、进程间的通信分发)工作,是最稳定的一个进程,而子进程则是专门用于进行业务处理的,稳定性也是比较差的,但多进程最大的好处就是健壮性,因为其中一个子进程的崩溃并不会影响到其他子进程继续接受分发,从而保证了主进程服务的健壮性,而后续子进程崩溃后,我们可通过监听进程的全局异常,继而重启 fork 出新的子进程,崩溃多少次则重启多少次,但是重启是需要至少 30ms 的启动时间的,内存也是需要预留至少 10MB,因为每个子进程都是独立的 V8 实例。所以我们不能短时间内频繁重启,而这些就是后话了,后面再详细向大家介绍。

三、如何使用 child_process 模块与句柄(Handle)传递实现实现主从模式架构,从而实现启动多个服务监听一个端口

通过 node 建立过服务的童鞋都应该知道,http 服务我们直接使用 http.createServer 即可创建并直接监听某个端口,但是端口被占用的情况下,如果仍然监听就会引起 EADDRINUSE 异常,那么进程间是如何知道判断我们监听了同一端口呢,答案就是 socket 的套接字,首先大家都需要知道 http 模块本质上是 net 模块封装而来,本质上是 tcp 协议封装的 http 协议,其中仍然是 socket 套接字,当监听同一端口时,tcp 服务发现此时 socket 套接字的文件描述符是不同的,此时就异常了,所以如果我们能够使 2 个服务使用同一个 socket 套接字,那么文件描述符就相同了,这就解决了这个问题,由此我们就引申除了需要解决的 2 个问题

所有子进程如何与父进程一样都是用一个 socket 套接字?
子进程如何监听到父进程的 socket 的连接事件从而接收到父进程所分发的内容最传递给子进程启动的 http 服务呢?
A1:在进程间通信中通过 IPC 管道将句柄发送 socket 给子进程,从而使子进程也可以对这个 tcp 服务的 connection 进行监听; 
A2:node 子进程通过监听 message 事件便可接受到父进程所 send 的句柄,在接受到 socket 句柄后,便可监听到父进程 tcp 服务的 connection 事件了,当父进程的 tcp 服务被访问时,子进程监测并将本次的 socket 通过 emit 触发 http.server 类 的 connection 事件;

在解决方案中,我们引申出了几个具体名词和方法的解析,在以下会对其进行详解,并最终放上实现过程的代码;

进程间如何进行普通通信?
进程间如何发送 socket 句柄,句柄又是什么东西,是怎么发送的?
http.server 类继承与 net.server 类,理论上具有 net 所有通过 EventEmitter 类所定义事件;

问题1:进程间如何进行普通通信

// parent.js
var cp = require('child_process')
var child = cp.fork('./child.js')

child.send('hello world')

process.on('message', function (m) {
  console.log('parent 进程已接收到 child 进程的' + m)
})

// child.js
process.on('message', function (m) {
  console.log('child 进程已接收到 parent 进程的' + m)
})

从 demo 中我们可以看出父进程运行时会复制启动一个新的子进程,并且我们在父进程中向子进程发送了 hello world,当我们运行 node parent.js 时,会发现命令窗打印出了 ‘child 进程已接收到 parent 进程的hello world’ ,此时我们已经实现了父进程 => 子进程的单向通信了。

ps: emmmmmm,我就知道会有童鞋尝试在子进程再次 fork parent.js 然后发送消息,之后再 parent.js 中打印出来= =你以为这是双向通信吗,骚年,这是 fork 了一个新的进程再输出到控制台的而已,不信你自己启动 node 后查看过滤出正在运行的 node 进程列表,正确操作应是在父进程中取出 fork 的子进程再次监听该子进程的 message 事件,子进程中直接使用 process.send 即可,这样就实现了父子进程的双向通信了 父 => 子 => 父。
 

// parent.js
var cp = require('child_process')
var child = cp.fork('./child.js')

child.send('hello world')

child.on('message', function (m) {
  console.log('parent 进程已接收到 child 进程的' + m)
})

// child.js
process.on('message', function (m) {
  console.log('child 进程已接收到 parent 进程的' + m)
})

process.send('hello, I"m child process')

问题2:进程间如何发送 socket 句柄,句柄又是什么东西,是怎么发送的? 


其中涉及到 2 个点,IPC 通道与 句柄(handle) 
IPC通道:IPC全称为 Inter-Process Communication ,即进程间通信,在 node 中是利用 pipe 实现的,看到 pipe 大家应该会想到我们读取与写入文件时的流操作也利用过管道,联想是正确的,因为在进程通信的过程中也确实这么做的,在父进程 send 前,node 会自动将信息通过 JSON.stringify包装起来后才会 send ,之后写入 IPC 通道,通过 IPC 通道到达子进程前进行 JSON.parse 后 got 到这次发送的消息给子进程接收,所以我们可以将 IPC 通道理解为消息中间管道。

句柄:我们在问题 1 中进行的是普通信息的传递,但通过文档我们可以看到其实 send 是有第二个参数的,第二个参数便是用来发送句柄的,句柄是一种可以用来标识资源的引用,它的内部包含了指向对象的文件描述符,所以我们发送的句柄本质上就发送文件描述符,上面我们有提到,端口就是因为 socket 套接字的文件描述符而出现端口被占用的异常,而这里发送的句柄便刚好解决了这个问题,句柄有以下几种,当发送的不为以下几种时,子进程是无法接收到的。

net.Socket TCP套接字
net.Server TCP服务器,再 TCP 服务上的应用层都可以继承它,如 http.server 类
net.Native C++ 层面的 TCP 套接字或 IPC 管道
dgram.Socket UDP 套接字
dgram.Native C++ 层面的 UDP 套接字
上面我们提到过,只要子进程能接收到句柄,便可以使用父进程的 socket 监听 connection 事件从而去 emit 到 http 服务的 request 事件(注意是 request 事件,不是 http.clientRequest 类,也不是 http.IncomingMessage 类,而是触发 http.server 的请求事件,即 http.createServer 建立服务后自动监听的事件),并获取本次请求的 http.IncomingMessage 与 http.serverResponse 类所挂载的引用,下面是 node 使用 child_process 模块后一个端口同时启动多个服务的 demo
 

// master.js
var fork = require('child_process').fork
var cpus = require('os').cpus()

var server = require('net').createServer()
server.listen(1337)

var workers = {}
var createWorker = function () {
  var worker = fork(__dirname + '/worker.js')

  // 监听子进程的退出,若退出则重新创建一个新的子进程
  worker.on('exit', function () {
    console.log('Worker ' + worker.pid + ' exited.')
    delete workers[worker.pid]
    createWorker()
  })

  // 发送句柄
  worker.send('server', server)
  workers[worker.pid] = worker
  console.log('Create worker. pid: ' + worker.pid)
}
for (var i = 0; i < cpus.length; i++) {
  createWorker()
}

// 监测主进程的退出,当主进程退出时,主动退出所有子进程
process.on('exit', function () {
  for (var pid in workers) {
    workers[pid].kill()
  }
})

// worker.js
var http = require('http')
var server = http.createServer(function (req, res) {
  res.writeHead(200, { 'Content-Type': 'text/plain' })
  res.end('handle by child, pid is ' + process.pid + '\n')
})

var worker
process.on('message', function (m, tcp) {
  if (m === 'server') {
    worker = tcp
    worker.on('connection', function (socket) {
      server.emit('connection', socket)
    })
  }
})

process.on('uncaughtException', function (err) {
  // 停止接受新的连接
  worker.close(function () {
    // 所有已有连接断开后,退出进程
    process.exit(1)
  })
})
--------------------- 
作者:yolo0927 
来源:优快云 
原文:https://blog.youkuaiyun.com/yolo0927/article/details/81224942 
版权声明:本文为博主原创文章,转载请附上博文链接!

在以上 demo 中,我们除了使用 tcp 服务代理 http 服务以外,还为了多进程的健壮性做到了以下:

子进程因某些异常而导致的退出重启,可见 master.js 中 work 的 exit 事件回调;
当主进程 master 异常退出时,自动退出所有子进程,可见 master.js 中对 process exit 事件的监听;
在子进程中监听当前子进程的全局异常事件,若出现异常则手动关闭子进程传递过来句柄的 tcp 套接字,并在完成后退出子进程,触发父进程中子进程的 exit 事件达到退出现有子进程重启新的子进程的效果;
注意:windows 系统下会出现 curl "http://127.0.0.1:1337" 子进程 request 事件回调中一直打印的为一个 pid 的情况,而在 macOs 或 linux 等有 unix 内核的系统中会是创建的多个子进程中的随机一个 pid,这是 windows 下才会产生的问题,我们正常应是 unix 内核中的情况,因为 node 默认提供的机制是采用操作系统的抢占式策略,闲着的进程对到来的请求进行抢夺,谁抢到算谁的。

书中重点提到:这种抢占式策略是根据当前进程的繁忙度来进行抢占的,繁忙度较低的进程会先抢占到请求,而繁忙是由 CPU 与 I/O 两部分构成,即我们一直提到了 CPU 占用率与 内存 使用率,而本处定义影响抢占的确只是 cpu 的繁忙度,这就导致了,抢占到的进程有可能是 I/O 繁忙,而 cpu 却空闲的情况,这导致该进程内存不足而使得后续可能无法继续维持计算所需的内存(即高并发场景下容易造成延迟现象,因为没有内存供给 I/O 处理),这也引起了负载不均衡的现象,此时我们必须想一个新的抢占策略来实现 cpu 与 I/O 的均衡分配,而在node V0.11 增加的 Round-Robin(轮叫调用)便实现了这点,cluster 模块中启用它的方式为(一般我们都默认启动): 
cluster.schedulingPolicy = cluster.SCHED_RR
 

四、处理高并发场景多进程集群健壮性的问题 
关于集群的健壮性,有几点需要注意的

进程异常是否能够监控记录并作出应对;
进程崩溃时能否保证中断本次连接后重启出新的子进程继续工作,保证响应不会一直延迟,而是直接抛出错误;
子进程重启过于频繁时是是否能预警;
对于以上几点,个人总结就是小错提交日志自动重启,大错预警及时止损,在书中提供了这么几个思路。

1. 使用监控进程异常的 'uncaughtException' 事件,本事件为当前进程的全局异常监控,当监控到异常时像 master 主进程发送自杀指令并 ‘自杀’,由 master 统一管理进程的重启,分别对应的代码为:

// worker.js
process.on('uncaughtException', function (err) {
  // 错误日志记录
  // logger.error(err)
  process.send({ act: 'suicide' })
  // 停止接受新的连接
  worker.close(function () {
    // 所有已有连接断开后,退出进程
    process.exit(1)
  })
  // 为了防止 tcp 长连接断开时间过长,此处设置一个超时机制,超过一定时间如果当前 process 还存在,则手动退出
  setTimeout(() => {
    process.exit(1)
  }, 5000)
})

// master.js 在创建进程的函数 createWorker 中插入监听事件,当监听到时,同步进行新子进程的创建与旧子进程的连接中断与进程退出
worker.on('message', function (message) {
  if (message.act === 'suicide') {
    createWorker()
  }
})

 

这 2 个事件的监控已经处理完了日志监控与中断连接退出异常进程并重启的过程,原理很简单,进程内全局监控异常,有异常时直接中断连接,保证所有连接中断后退出进程,在这个过程进行的同时,通知 master 创建新进程,两个任务同步进行以保证负载均衡不会被打破,新的请求仍可以立刻分发到新的子进程。 
需注意文档已经有说明不要尝试恢复旧进程的应用了,所以书中选择直接退出旧进程,创建新进程 
正确地使用 uncaughtException

2. 如果遇到业务异常导致子进程频繁无限重启该怎么办? 
这种情况的发生比较少,一般极有可能是业务代码出错,否则不会无限频繁报异常,单纯的内存分配不足只会造成延迟等待,如果cpu 负载运转率过高,可能系统已经自动 kill 掉主进程了,子进程会全部被正常杀死(demo 中可看出非异常退出不会造成自动重启,而是直接 emit 进程的 exit 事件),这都是正常的处理情况,此时收到预警的运维童鞋重启服务即可。 
但如果是业务模块的错误导致重启频率过高,这时我们查看日志发现一直是一个异常,且因为频繁重启查过限定后导致的主进程手动触发退出,就要注意了,下面为完整的 createWorker 与判断重启频率函数 isTooFrequently
 

// master.js
var fork = require('child_process').fork
var cpus = require('os').cpus()

var server = require('net').createServer()
server.listen(1337)

var limit = 10;
var during = 60000;
var restart = [];
var isTooFrequently = function () {
  var time = Date.now()
  var length = restart.push(time)
  // restart 数组永远只取最后限定条数
  if (length > limit) {
    restart = restart.slice(-1 * limit)
  }
  // 最后的重启次数大于或等于限定次数且最后一次重启距离 limit 第一条的重启时间如果小于单位时间,则代表单位时间内重启太频繁了
  return restart.length >= limit && restart[restart.length - 1] - restart[0] < during
}

var workers = {}
var createWorker = function () {
  // 检测是否重启太过频繁
  if (isTooFrequently()) {
    process.emit('giveup', length, during)
    return ;
  }

  var worker = fork(__dirname + '/worker.js')

  worker.on('message', function (message) {
    if (message.act === 'suicide') {
      createWorker()
    }
  })

  // 监听子进程的退出,若退出则重新创建一个新的子进程
  worker.on('exit', function () {
    console.log('Worker ' + worker.pid + ' exited.')
    delete workers[worker.pid]
    createWorker()
  })

  // 发送句柄
  worker.send('server', server)
  workers[worker.pid] = worker
  console.log('Create worker. pid: ' + worker.pid)
}

for (var i = 0; i < cpus.length; i++) {
  createWorker()
}

process.on('giveup', function (count, time) {
  // logger.error(`子进程重启过于频繁自动退出主进程,请紧急处理,${time}ms内重启次数为: ${count}`)
  process.close(function () {
    process.exit(1)
  })
  setTimeout(() => {
    process.exit(1)
  }, 5000)
})

// 监测主进程的退出,当主进程退出时,主动退出所有子进程
process.on('exit', function () {
  for (var pid in workers) {
    workers[pid].kill()
  }
})

每次创建新的子进程时,会使用 isTooFrequently去做判断,判断是否频率过高需要满足 2 个条件

重启的次数超过限定次数(存在重启次数早已大于限定,但时间频率未达到的情况,所以我们可以看到 restart 每次都会取最后 limit 的位长度,保证了未到达重启次数限定时不可能算 “太频繁”)
在超过限定次数的前提下,且满足 restart 数组中最后一位 - 第一位 的值小于初始设定的单位时间值 during(restart 数组每次 push 的是重启时的时间戳,时间戳相减即可得到 limit 次重启的单位时间)
如果符合我们规定的频率过高设定,则触发自定义的 giveup 事件,书中代码未监听 giveup 事件,结果上我们应该监听 giveup 事件并写入预警函数以及特别严重的错误提示,告知运维童鞋紧急处理,在本总结上面的 demo 中我已加入对 giveup 事件的监听,并设定等待连接中断的限定时间,process 是 EventEmitter 的实例,所以大家大可随意使用订阅发布模式的事件订阅与发布,别再小白的问为什么可以自定义订阅事件名了~~~。

 

五、cluster模块的应用 


在使用 child_process 模块实现主从模式的单机集群架构时,我们会感觉比较繁琐,要反复进行主从进程间的通信,并且订阅很多自定义事件去做如异常监控、退出重启等功能日志,还要自己创建 tcp 服务,监听与发送句柄的繁杂的操作,对于一个经验较少的工程师来说是很容易搞错其中一步的,所以 Node 从 V0.8 版本开始新增了 Cluster 模块来解决这个问题,egg 也是使用 Cluster 模块创建的主从模式,并在 master => worker 中间加了一层代理层用于一些公共的业务处理,以求不用让每个子进程都去运行重复代码。

以下是书中的两种方式,书中不太推崇官方文档的创建方式,认为官方的创建方式比较繁琐,需要使用者自己判断是 master 还是 worker 进程,但我个人反而觉得官方的方式比较清晰、明了。
 

// 书中朴老师比较推崇的使用 setupMaster 的方式
var cluster = require('cluster')

cluster.setupMaster({
  exec: 'worker.js'
})

var cpus = require('os').cpus()
for(var i = 0; i < cpus.length; i++) {
  cluster.fork()
}

// 官方的方式
var cluster = require('cluster')
var http = require('http')
var numCPUs = require('os').cpus().length

if (cluster.isMaster) {
  for (var i = 0; i< numCPUs; i++) {
    cluster.fork()
  }

  cluster.on('exit', function (worker, code, signal) {
    console.log(`worker ${worker.process.pid} died`)
  })
} else {
  console.log('current process pid is: ' + process.pid)
  http.createServer(function (req, res) {
    res.writeHead(200)
    console.log(process.pid)
    res.end('hello world\n')
  }).listen(8000)
}

比较两种方式的区别是,官方的方式需要我们自行判断当前 cluster 创建的进程是 master 还是 worker,然后进行不同的事件监听,个人认为官方的方式更让我觉得清晰,比较贴近原本 child_process 模块与创建 tcp 服务发送句柄的原理,会感受到整个过程是 “可控的” ,而朴老师的方式虽然非常简洁,但是却容易让新手产生摸不着门路的困惑感。

基本 egg master => agent => worker 这一套多进程架构就跟我们上面介绍的创建 tcp 服务并使用 child_process fork 子进程的思想是一样的,只是换成了 Cluster 模块来实现,免去了繁琐的创建 tcp 服务与发送句柄这个过程与监控 process exit 等过程,继而全部由 cluster 模块的 api 与事件来实现,大家看完本章再去看 egg 的这节文档就会发现文档中写的东西都非常清晰了。

egg —– 多进程模型和进程间通讯
 

跟着《深入浅出nodejs》系统学习 node 以及相关面试题一网打尽 内容持续更新中,内容较多难免疏漏,有不对的欢迎指正,有遗漏的部分会陆续补充。欢迎关注我的专栏《一网打尽所有面试题。 阅读详情

相关推荐

微信旧岛小程序章节目录

索引 第一章 申请appkey ## 标题##我是 标题 第二章 准备工作 小程序开发工具介绍和第三方开发工具介绍 第三章 小程序基础知识 与新特性 1.小程序的4种文件类型 2.小程序的组织结构 3.新建第一个小程序页面 4.消除错误提示 5.app.json配置之导航栏 6.app.json配置之background窗口 7.wxml与组件 第四章 flex 布局 1.小程序代码编写规范 2.所谓布局 3.block 、inline、与inline-block 4.flex容器将消除it

qq_37964687的博客 918

node进程cpu 100%问题排查

nodejs 内存泄漏,cpu100%问题追踪一次线上事故新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入 一次...

小喽喽 1万+

Node.js生产应用的优化与监控

本文深入探讨了如何在生产环境中优化Node.js应用程序的性能和监控。介绍了通过创建专门的Redis连接库、配置日志系统、实现Express和Socket.IO的性能优化以及如何使用Forever工具来保持系统运行的实践。

weixin_42497762的博客 332

nodeJS之进程process对象

前面的话   process对象是一个全局对象,在任何地方都能访问到它,通过这个对象提供的属性和方法,使我们可以对当前运行的程序的进程进行访问和控制。本文将详细介绍process对象   概述   process是一个全局对象,即global对象的属性,可以在任何地方直接访问到它而无需引入额外模块 console.log(process === global.process);//t...

weixin_34402090的博客 560

Node.js与Redis构建可扩展应用的指南

本书详尽介绍了使用Node.js和Redis构建可扩展应用程序的过程。从基础的Express框架开始,逐步深入到实时应用开发、用户认证、消息队列、前端依赖管理、前端框架应用、JavaScript编码最佳实践,以及部署和扩展性问题。作者分享了个人在使用Node.js进行后端开发的体验和感悟,强调了其在现代web应用开发中的重要性和便利性。

weixin_33506815的博客 338

【玩转 Postman 接口测试与开发2_010】第九章:Postman 的 Newman 工具在命令行接口测试、报表插件定制以及 GitHub Actions 工作流集成中的应用

本篇为《API Testing and Development with Postman》全新第2版第9章自学笔记,主要梳理了Postman的命令行工具 newman 的相关用法,包括 newman 的安装、启动、常用参数配置等。然后深入探讨了 newman 的报表插件配置方法,分别归纳了三类报表的用法:内置报表、第三方插件、自定义报表插件。最后还简单演示了 newman 与 GitHub Actions 工作流的集成方法。实测过程中还自行探索了 newman 在 npm 模块中的应用。强烈建议收藏!

PeacefulWinter的博客 1258

总结:Node.js+MongoDB+AngularJS Web 开发--第九章 在 Node.js 中使用多处理器扩展应用程序

第九章  在 Node.js 中使用多处理器扩展应用程序 在前面章节已经了解到了 Node.js 应用程序在单个线程上运行,而不在多线程上运行。对应用程序处理使用单个线程,使 Node.js 进程更加高效和快捷。但是大多数服务器均具有多个处理器,并且你可以利用这些处理器扩展你的 Node.js 应用程序。Node.js 允许用户从主应用程序把工作派生为随后可以彼此并行和主应用程序一起处理的各个进...

TracyWorld 218

cadence—virtuosoic617的使用技巧收集区

cadence使用

qq_33599939的博客 3万+

Node 现代前端开发(一)

本书涵盖了使你充分利用 Node.js 的力量、其概念和其生态系统所需的一切。这包括你需要了解的所有关于模块系统、包、辅助库、CLI 工具、WebAssembly 以及一系列可用的工具,如打包器(Webpack(v5)、Parcel(v2)、Vite 和 esbuild)、测试运行器(AVA、Jest 和 Mocha)、转译器(Babel 和 TypeScript)以及许多其他工具(Flow、Prettier、eslint 和 Stylelint)的内容。本书面向初级和中级前端 Web 开发者,他们希望利用

龙哥盟 1145

量化投资进阶:Fama-French三因子模型实战解析与Python实现

本文深入解析了Fama-French三因子模型,并提供了完整的Python实战指南。文章阐述了该模型如何通过引入市值因子(SMB)和价值因子(HML),对传统CAPM模型进行重要扩展,以更精准地解释股票收益来源。详细演示了使用Python获取数据、执行回归分析并解读结果(包括关键的阿尔法α)的全过程,并探讨了该模型在量化策略构建、风格诊断和业绩归因中的核心应用。

yog99的博客 1127

企业老板管理者,如何监控员工的电脑屏幕?

老板监控员工电脑,需要知道员工开机后的上网行为来及时了解到员工工作情况,及时发现上班不良行为员工,及时淘汰低价值员工。 文章目录iMonitor EAM一、计算机系统日志二、具体功能介绍1.屏幕记录2.键盘记录总结 iMonitor EAM 怎么监控员工电脑,我们需要用到第三方软件,2021年最新版本软件-iMonitor EAM,软件分管理端和客户端,管理端在管理者电脑登录,客户端在员工电脑双击安装就可以,安装中不用任何设置的,客户端安装到员工电脑后也是隐藏的,不会有任何弹窗和警告信息,是无法发现和删

iMonitorSoft的博客 2180

《Node.js 实战(双色)》试读

         Node.js,在读这本书之前,我对Node.js的了解甚少,唯一有关联的是我们公司有个nodejs的服务器程序,用于消息服务的发送支持,并且下载的试读章节也是从第九章《Web安全实战》开始介绍的,所以我去问了问度娘,原来Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台, 用来方便地搭建快速的, 易于扩展的网络应用。它在某种程度上弥补了Java...

lyx_50802的博客 240

AR1020触摸控制器驱动开发:嵌入式I²C/SPI底层集成指南

电容式触摸控制器是现代嵌入式人机交互(HMI)系统的核心感知单元,其工作原理基于寄生电容变化检测与数字信号处理。Microchip AR1020 作为典型单芯片方案,内置硬件滤波、多点跟踪与手势识别引擎,通过I²C或SPI接口输出结构化事件数据,显著降低主控MCU负载。该类器件的技术价值在于平衡精度、功耗与实时性,广泛应用于工业HMI、医疗设备及车载终端等对可靠性要求严苛的场景。本文聚焦AR1020的寄存器映射、中断驱动模型、坐标解析机制及在STM32平台的HAL/LL级工程实现,涵盖硬件时序约束、固件配置

weixin_29323977的博客 600

React TypeScript Node 全栈开发(三)

在本章中,我们学习了 Node 和 Express。Node 是驱动网络服务器的核心服务器端技术,Express 是构建 Web 应用程序的最流行和经常使用的基于 Node 的框架。我们现在对前端和后端技术如何共同创建网站有了完整的了解。在下一章中,我们将学习 GraphQL,这是一种非常流行且相对较新的标准,用于创建基于 Web 的 API 服务。一旦我们掌握了这个知识,我们就可以开始构建我们的项目了。在本章中,我们将学习 GraphQL,这是目前最热门的 web 技术之一。

龙哥盟 994

LLVM实战:如何用Clang+LLVM打造你的第一个C++编译器(附完整配置步骤)

本文详细介绍了如何使用Clang+LLVM构建C++编译器的完整配置步骤,从环境准备到编译流程剖析,再到调试与优化技巧。通过实战指南,帮助开发者深入理解LLVM编译器框架的工作原理,并掌握交叉编译与性能分析等高级功能。

sprite的博客 748

从东南大学906被刷到调剂过程(内含个人感想)

介绍了自己在看到初试成绩,到后期复习以及调剂的全过程,当中也包含了自己的复试中的感想,希望大家理性看待。

Philo的博客 2857

# Node.js与内置模块 基础知识点复盘

Node.js是一个基于Chrome V8引擎的JavaScript运行环境浏览器是JavaScript的前端运行环境node.js是JavaScript的后端运行环境node.js 中无法调用BOW,DOW 等浏览器内置APIfs模块是官方提供用来操作文件的模块fs.readFile()方法 读取文件内容fs.writeFile()方法 写入文件内容使用需要导入fs模块4.11定义。

m0_74619623的博客 1456

强化学习算法案例——一个经典的走迷宫任务。

作者:禅与计算机程序设计艺术 1.简介 在强化学习(Reinforcement Learning, RL)中,有一个经典的任务——走迷宫,其原理简单易懂,但它的实际意义却深远,它让计算机学习如何通过策略网络(policy network),以最佳的方式解决复杂的任务。本文将介绍如何用强化学习解决这个经典

AI天才研究院 3217

Node 和 Redis 可扩展应用构建指南(三)

发现可能的极限的唯一方法就是超越它们进入不可能的领域。” —— 亚瑟·C·克拉克在整本书中,我们学习了利用 Node.Js 和 Express.Js 为项目管理软件所需 API 的重要概念和方法。通过这个单一示例,我们为用户、项目、任务等设计了必要的 API。如果你遵循了章节并完成了自己的 API 版本,你应该有一个功能齐全的项目管理软件的后端。有很多更多的事情可以做,但这些都无法仅凭一本书来涵盖。本章将尝试涵盖可以进一步做的事情,以及可以遵循的最佳实践。在执行所有这些时,你应该手头有相当不错的软件。

龙哥盟 1482

2026年全国青少年信息素养大赛算法应用主题赛C++样题及答案解析

2026年全国青少年信息素养大赛算法应用主题赛C++样题

王老师青少年编程 3724

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

第九章 安全认证

1. 访问控制概述 | 2. 认证管理 | 3. 授权管理 | 4. 准入控制 |

yirenyuan的博客 868

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Vue.js实战第九章笔记

Vue.js2.x使用了VirtualDom(虚拟DOM)来更新DOM节点,提升渲染性能。虽然前面章节我们的组件模板都是写在template选项里的,但是在Vue.js编译是,都会解析为VirtualDom 9.1什么是Virtual Dom Virtual Dom并不是真正意义上的DOM,而是一个轻量级的JavaScript对象,在状态发生变化时,Virtual Dom会进行Diff运算,来更新...

ss_veine的博客 585

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

Node.js 事件循环阻塞 故障排查手册:现象、定位与修复

围绕「Node.js 事件循环阻塞」,本文提供可落地的技术指南,并在关键节点说明如何用多节点测速验收上线效果。 本文是一份围绕「Node.js 事件循环阻塞 故障排查手册」的可执行长文手册(建议阅读 15–20 分钟,全文约 1.5 万–2 万字)。 不同于只列步骤的短文,我们会把原理、术语、架构、实操、案例、误区讲透—— 让你不仅知道「怎么做」,还知道「为什么这么做」。 全文以免费工具 SpeedCE 为网络层验收示例。 你学到的排查思路适用于任何多节点测速场景。建议收藏,故障或变更时按章节对照操作。

IT技术测评员 463

第九章二叉搜索树(Binary Search Tree, BST)

二叉搜索树(BST)是一种树形数据结构,广泛用于动态数据的查找、插入和删除。它的有序性使其在特定场景下比哈希表(第十五章)更适合,如需要范围查询或排序输出的场景。本章将介绍BST的概念、操作及实现,并与哈希表进行对比。

专注于前沿技术的探索与实践,热衷于从代码逻辑、架构设计到行业应用等多维度深挖技术本质。期待在技术之路上与你共同成长~ 748

容器集群k8s从入门到精通之安全认证(第九章)

第九章 安全认证 本章节主要介绍Kubernetes的安全认证机制。 访问控制概述 ​ Kubernetes作为一个分布式集群的管理工具,保证集群的安全性是其一个重要的任务。所谓的安全性其实就是保证对Kubernetes的各种客户端进行认证和鉴权操作。 客户端 在Kubernetes集群中,客户端通常有两类: User Account:一般是独立于kubernetes之外的其他服务管理的用户账号。 Service Account:kubernetes管理的账号,用于为Pod中的服务进程在访问K

梦溪笔谈的博客 505

NodeJS 开发学习手册(一)

欢迎阅读《学习 Node.js 开发》。本书充满了大量的内容、项目、挑战和真实世界的例子,所有这些都旨在通过实践教授 Node。这意味着在接下来的章节中,您将很早就开始动手写一些代码,并且您将为每个项目编写代码。您将编写支持我们应用程序的每一行代码。现在,我们需要一个文本编辑器。我们有各种文本编辑器选项可供选择。我始终建议使用 Atom,您可以在atom.io找到它。它是免费的、开源的,并且适用于所有操作系统,即 Linux、macOS 和 Windows。它是由 GitHub 背后的人员创建的。

龙哥盟 1013
上一篇: Javascript中的Date.now() 方法与Date.UTC() 方法 ,Date.parse() 方法
下一篇: JSON.parse()与JSON.stringify()的区别
acoolgiser
博客等级 码龄11年 179粉丝 146原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值