利用webrtc和sw拦截实现仿cdn资源加速设计

246 阅读3分钟

我正在参加「掘金·启航计划」

我们访问网站时,文件并不是直接请求的源服务器,而是请求的CDN分配的离你最近的服务器,达到最快加载的目的。

但是,假设一种情况:局域网内A和B用户都在访问我们网站,B访问我们网站需要某张图片,如果直接从A这边直接能拿到这张图片,那不是更快吗,而且还节省了CDN流量,这不就是企业要的降本增效吗

项目源码

实现方案:

1、信令服务器为用户A匹配最优用户B,基于网络和地理(注意:如果双方p2p通信要依赖turn转发,那放弃,以免流量走到turn服务器

2、webrct 打通数据通道,实现双方通信

3、service worker 拦截fetch请求静态资源,缓存已有资源,同步远端自身已有资源列表

4、fetch 拦截先判断远端用户是否有该资源,有就请求并组装分片,没有就正常CDN

5、远端数据通道收到资源请求后,分片发送资源

先回顾一下webrtc音视频聊天过程,service worker过程

音视频聊天过程

webrtc 基础例子请看WebRTC的p2p音视频小例子源码 ,我们简单说下过程:

1、部署 coturn 服务,用做NAT穿透

2、编写信令服务器:用nodejs socket.io编写业务,主要是房间管理、人员进出等功能

3、重点是关注类 RTCPeerConnection

-> 新建pc对象,pc = new RTCPeerConnection(iceConfig)

-> 注册pc.onnegotiationneeded回调,用于本地媒体协商信息就绪后,向远端发送offer

-> 注册pc.onicecandidate回调,用于本地经过 coturn 服务器网络协商信息后,向远端发送candidate

-> 注册pc.ontrack回调,用于接收远端音视频流,绑定到video标签:remoteVideo.srcObject = mediaStream

4、结合pc和音视频流

-> 开启本地音视频设备:mediaStream = await navigator.mediaDevices.getUserMedia 获取音视频流

-> 绑定pc和音视频流:mediaStream.getTracks().forEach((track) => { pc.addTrack(track, mediaStream);});

5、创建datachannel做聊天和数据发送

-> 发送方创建数据通道:sendChannel = pc.createDataChannel("sendChannel");

-> 接收方监听数据通道:pc.ondatachannel = onsenddatachannel

-> 接收方在ondatachannel回调中监听接收数据:evt.channel.onmessage = onReceiveMessageCallback;

6、【连接发起方】创建本地offer

-> 创建本地offer:offer = pc.createOffer()

-> 设置本地offer:pc.setLocalDescription(offer) 

-> 将offer通过信令服务器转发给远端接收方

7、【连接接收方】接收offer,创建本地answer

-> 接收offer,创建发起方offer对象:des = new RTCSessionDescription(offer)

-> 新建webtc对象:createPeerConnection 

-> 设置发起方offer:pc.setRemoteDescription(des)

-> 创建answer对象:answer = pc.createAnswer()

-> 设置本地answer:pc.setLocalDescription(answer)

-> 将answer通过信令服务器回复给发起方

8、【连接发起方】接收并设置远程answer

-> 接受answer,创建接收方answer对象:des = new RTCSessionDescription(answer)

-> 设置接收方answer:pc.setRemoteDescription(des)

9、完成可音视频、聊天、发送数据

service worker过程

文章查看 我所知道的service worker,我们简单说下过程:

1、html注册sw.js

   window.addEventListener("load", function () {
    if ("serviceWorker" in navigator) {
      navigator.serviceWorker.register("/sw.js");
    }
  });

2、sw.js初始化事件install、activate、fetch、message

self.addEventListener("install", async (event) => {
  console.log("开始安装", event);
  event.waitUntil(self.skipWaiting())
});
self.addEventListener("activate", async (event) => {
  console.log("安装完成,开始启动", event);
  event.waitUntil(self.clients.claim())
});
self.addEventListener("fetch", async (event) => {
  console.log("运行中,拦截请求", event.request);
  event.respondWith(fetch(event.request));
});
self.addEventListener("message", function (event) {
  // 收到消息
  console.log("收到消息html->sw", event.data);
  // 发送消息
  self.clients.matchAll().then(function (clients) {
    clients.forEach((client) => {
      client.postMessage({
        action: "response message",
      });
    });
  });
});

3、html js中注册监听 与 发送消息

// 收到消息
navigator.serviceWorker.addEventListener("message",function (event) {
    console.log("收到消息sw->html", event.data);
  }
);
// 发送消息
navigator.serviceWorker.controller.postMessage({
    action: "the action event",
});

正题1: