解决 socket.io 频繁连接导致 cpu 升高问题

前提

如果前端频繁刷新页面或者频繁断开重连将会导致服务端 cpu 升高

解决方法

  • 使用 localStorage 缓存连接,每次通信都使用这个连接

代码

let socket;
if(localStorage.getItem('socket')) {
    socket = JSON.parse(localStorage.getItem('socket'));
    // 创建一个不自动连接的 socket 对象
    let temp = io({autoConnect: false});
    Object.setPrototypeOf(socket, Object.getPrototypeOf(temp));
    delete temp;
} else {
    socket = io('http://localhost:5000', {'timeout':5000, 'connect timeout':5000});
    localStorage.setItem('socket', JSON.stringify(JSON.decycle(socket)));
}

出现的问题

  • localStorage 只能存储字符串,所以使用 stringify 将对象转成字符串,但是转化过程中出现循环引用问题
    在这里插入图片描述
    在这里插入图片描述

    解决方法:https://juejin.cn/post/6904563374873395214#heading-2

  • 现在可以将对象存到缓存中了,但是原型丢失,导致这个对象上的方法无法使用

    解决方法:

    1. 可以将原型也缓存,直到最后 Object,但是 stringify 不可以序列化对象上的函数,所以这种方法放弃
    2. 可以创建一个 io({autoConnect: false}) 对象,表示不自动连接,需要手动调用 connect 进行连接,然后将这个对象上的原型赋给从缓存中拿到的那个对象就可以了

解决完毕!睡觉!😎


醒来了!又出 bug 了!

  • 主要想解决的问题是socket.io 频繁连接所导致的 CPU 升高问题,其实不仅是 socket.io 频繁连接,如果是 Http 频繁连接,照样会导致 CPU 升高。咱们从客户端和服务器两个角度来分析怎样做,如果是客户端,使用缓存连接的方法,可以在服务端避免大量创建和销毁套接字,这在原理上是行的通的,但是,当刷新页面的时候,这个连接会被销毁,会给服务器发送 disconnect 事件,服务端的这个连接也会销毁,导致客户端在缓存中保存的连接根本没用,所以这个方法行不通。取消一段时间重复进行请求也没用,使用闭包也没用。咱们考虑一下服务器,首先 nodejs 本身是 IO 密集型的,也就是可以抗住大量的请求事件,所以不用担心 CPU 的上升,因为只是暂时的,如果有一个人很缺德,就是要每天给服务器发请求,把它的ip加入黑名单。而且在客户端断开的时候,服务器也会及时清除这个连接上的socket,所以不会造成内存泄露的问题。所以这个问题可以变成如何优化服务器,可以使服务器处理大量并发请求,可以根据你服务器CPU的数量开启这个数值的nodejs服务线程数量,然后主线程将客户端发来的请求进行调度,平均分配到这几个nodejs线程上,就可以了

参考朴灵老师的书

  • 我来看看朴灵老师说了点啥
  • 单线程没有锁、线程同步问题,提高 CPU 利用率
  • 主从模式:通过fork()复制的进程都是一个独立的进程,这个进程中有着独立而全新的V8实例。它需要至少30毫秒的启动时间和至少10 MB的内存,好在Node通过事件驱动的方式在单线程上解决了大并发的问题,这里启动多个进程只是为了充分将CPU资源利用起来,而不是为了解决并发问题(使用 epoll 监听文件描述符的数量决定了并发数),可以提高 CPU 利用率。尽快响应。让每个进程监听不同的端口,其中主进程监听主端口(如80),主进程对外接收所有的网络请求,再将这些请求分别代理到不同的端口的进程上。
    在这里插入图片描述
  • 对了,创建的是 HTTP 服务,也就是一个连接,并不是 listen accept 那种。而且设置了端口复用。意思就是在主线程上处理IO事件,在子线程上处理和客户端的数据收发事件。不能用所有线程都执行 http 服务,应该还有干别的的,输出日志啥的,这个线程数量要设计好。而且,主线程和子线程之间通信要消耗资源,要衡量线程同步、进程间通信这些,创建线程是否有优势,也可以对请求进行分类,IO密集和CPU密集执行不同的处理方式。一旦有异常出现,主进程会创建新的工作进程来为用户服务
    在这里插入图片描述
    在这里插入图片描述
  • 在Node进程中不宜存放太多数据,因为它会加重垃圾回收的负担,进而影响性能
  • 第三方进行数据存储解决数据共享
  • cluster模块,用以解决多核CPU的利用率问题
    在这里插入图片描述
    在这里插入图片描述
  • 在进程中判断是主进程还是工作进程,主要取决于环境变量中是否有NODE_UNIQUE_ID
    在这里插入图片描述
  • 它会在内部启动TCP服务器,在cluster.fork()子进程时,将这个TCP服务器端socket的文件描述符发送给工作进程。如果进程是通过cluster.fork()复制出来的,那么它的环境变量里就存在NODE_UNIQUE_ID,如果工作进程中存在listen()侦听网络端口的调用,它将拿到该文件描述符,通过SO_REUSEADDR端口重用,从而实现多个子进程共享端口。对于普通方式启动的进程,则不存在文件描述符传递共享等事情
  • 对于自行通过child_process来操作时,则可以更灵活地控制工作进程,甚至控制多组工作进程
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值