前端工程化4:从0到1构建完整的前端监控平台

前言

一套完整的前端监控系统的主要部分:

  • 数据上报方式
  • 数据上送时机
  • 性能数据采集
  • 错误数据采集
  • 用户行为采集
  • 定制化指标
  • 监控sdk
    在这里插入图片描述
    监控的目的:
    在这里插入图片描述

一、数据上报方式

本文的方案是,优先navigator.sendBeacon,降级使用1x1像素gif图片,根据实际情况需要采用xhr/fetch。

1、图片打点

图片打点上报的优势:
1)支持跨域,一般而言,上报域名都不是当前域名,上报的接口请求会构成跨域
2)体积小且不需要插入dom中(相比之下,script、link要挂载到页面上才可以请求)
3)不需要等待服务器返回数据

图片打点缺点是:

1)url受浏览器长度限制

2)只能发送GET请求,无法获取相应结果

通过创建一个Image对象,将要上报的数据作为URL参数拼接到一个1x1像素的透明图片URL中,发送一个GET请求来触发上报。

const data = { event: 'click', element: 'button' };
const url = ` https://example.com/track?data= ${encodeURIComponent(JSON.stringify(data))}`;
const img = new Image();
img.src = url;

2、fetch请求上报

这类方法用于业务数据确实,模块未展示,需要紧急上报的情况

优点:可以灵活地设置请求头属性,post请求可以发送大体量数据,满足特定场景的埋点需求。

缺点:数据量大的请求占用带宽资源多,增加服务器压力。页面销毁时的监控埋点大概率上报失败。

3、sendBeacon

navigator.sendBeacon是一个用于发送少量数据到服务器的浏览器API。它有以下几个优点

  • 异步和非阻塞

    navigator.sendBeacon 是异步的,它不会阻塞浏览器的其他操作。这对于性能监控来说非常重要,因为都不希望监控的过程影响到页面的性能。

  • 在页面卸载时仍然可以发送数据

    当用户离开页面(例如关闭页面或者导航到其他页面)时,navigator.sendBeacon仍然可以发送数据。这对于捕获和上报页面卸载前的最后一些性能数据来说非常有用。

  • 低优先级

    navigator.sendBeacon 发送的请求是低优先级的,它不会影响到页面的其他网络请求。

  • 简单易用

    navigator.sendBeacon 的API非常简单,只需要提供上报的URL和数据,就可以发送请求。

与此同时,navigator.sendBeacon 也有一些限制。例如,它只能发送POST请求,不能发送GET请求。而且,它发送的请求没有返回值,不能接收服务器的响应。

最后,一些旧的浏览器可能不支持 navigator.sendBeacon。因此,在使用 navigator.sendBeacon 时,需要根据实际情况进行兼容性处理。

navigator.sendBeacon()可以在用户离开页面(包括关闭浏览器窗口、标签页,或者在移动设备上切换应用等情况)时进行数据上报。

navigator.sendBeacon()的设计目的是在页面卸载时异步发送数据,尽可能确保数据能够被发送出去而不影响页面的卸载流程。然而,当应用被强制终止时,操作系统可能会立即终止应用的所有进程,不给应用任何机会执行数据上报操作。

以下是一个使用navigator.sendBeacon()的示例代码:

function onPageUnload() {const data = { /* 要上报的数据 */ };const url = '/data-collection-endpoint';navigator.sendBeacon(url, JSON.stringify(data));
}window.addEventListener('unload', onPageUnload);

由于兼容问题,一些旧的浏览器可能不支持 navigator.sendBeacon。因此,在使用 navigator.sendBeacon 时,需要根据实际情况进行兼容性处理:

import {isSupportSendBeacon} from './util'// 如果浏览器不支持 sendBeacon,就使用图片打点
const sendBeacon = (function(){if(isSupportSendBeacon()){return window.navigator.sendBeacon.bind(window.navigator)}const reportImageBeacon = function(url, data){reportImage(url, data)}return reportImageBeacon
})()export function reportImage(url, data) {const img = new Image();img.src = url + '?reportData=' + encodeURIComponent(JSON.stringify(data));
}

三、数据上报时机

1、上报时机有三种:

  • 采用 requestIdleCallback/setTimeout 延时上报。
  • 在 beforeunload 回调函数里上报。
  • 缓存上报数据,达到一定数量后再上报。

将三种方式结合一起上报:

先缓存上报数据,缓存到一定数量后,利用 requestIdleCallback/setTimeout 延时上报。在页面离开时统一将未上报的数据进行上报。

2、还有一种情况:

用户杀掉app进程时

将数据暂存在localStorage中,在用户下次进入应用时检查并上报是一种可行的方法,业内也有这样做的情况。

以下是一个示例代码实现:

// 上报数据的函数
function reportData(data) {// 发送数据到服务器的逻辑,这里只是示例fetch('/reporting-endpoint', {method: 'POST',body: JSON.stringify(data),headers: {'Content-Type': 'application/json',},}).then(response => response.json()).then(result => {console.log('数据上报成功:', result);// 上报成功后可以从 localStorage 中删除数据localStorage.removeItem('pendingData');}).catch(error => {console.error('数据上报失败:', error);// 上报失败可以考虑再次尝试或者在下次进入时继续尝试});
}// 在合适的时机尝试上报 localStorage 中的数据
function checkAndReportPendingData() {const pendingData = localStorage.getItem('pendingData');if (pendingData) {reportData(JSON.parse(pendingData));}
}// 在某个事件触发时(比如页面加载)调用检查函数
window.addEventListener('load', checkAndReportPendingData);// 在需要上报数据时,先将数据存入 localStorage
function saveDataForLaterReporting(data) {localStorage.setItem('pendingData', JSON.stringify(data));
}

这种方法的优点是可以在一定程度上提高数据上报的成功率,尤其是对于一些关键数据,即使在用户意外退出应用的情况下也有机会在下次进入时上报。

然而,这种方法也有一些局限性:

  1. 如果用户长时间不使用应用或者清除了浏览器缓存(包括localStorage数据),那么数据可能无法上报。
  2. 如果数据量较大,存储在localStorage中可能会占用较多的存储空间。
  3. 需要考虑数据的安全性和隐私性,确保存储在localStorage中的数据不会被恶意获取。

四、性能数据收集上报

以Spa页面来说,页面的加载过程大致是这样的:
在这里插入图片描述

包括dns查询、建立tcp连接、发送http请求、返回html文档、html文档解析等阶段

最初,可以通过 window.performance.timing 来获取加载过程模型中各个阶段的耗时数据,后来 window.performance.timing 被废弃,通过 PerformanceObserver 来获取。旧的 api,返回的是一个 UNIX 类型的绝对时间,和用户的系统时间相关,分析的时候需要再次计算。而新的 api,返回的是一个相对时间,可以直接用来分析。
根据最初的规划,性能监控需要收集的数据指标需要有FP、FCP、LCP、DOMContentLoaded、onload、资源加载时间、接口请求时间。

收集FP、FCP、LCP、资源加载时间具体是利用浏览器Performance API。关于Performance API:

// window.performance.timing 各字段说明
{navigationStart,  // 同一个浏览器上下文中,上一个文档结束时的时间戳。如果没有上一个文档,这个值会和 fetchStart 相同。unloadEventStart,  // 上一个文档 unload 事件触发时的时间戳。如果没有上一个文档,为 0。unloadEventEnd, // 上一个文档 unload 事件结束时的时间戳。如果没有上一个文档,为 0。redirectStart, // 表示第一个 http 重定向开始时的时间戳。如果没有重定向或者有一个非同源的重定向,为 0。redirectEnd, // 表示最后一个 http 重定向结束时的时间戳。如果没有重定向或者有一个非同源的重定向,为 0。fetchStart, // 表示浏览器准备好使用 http 请求来获取文档的时间戳。这个时间点会在检查任何缓存之前。domainLookupStart, // 域名查询开始的时间戳。如果使用了持久连接或者本地有缓存,这个值会和 fetchStart 相同。domainLookupEnd, // 域名查询结束的时间戳。如果使用了持久连接或者本地有缓存,这个值会和 fetchStart 相同。connectStart, // http 请求向服务器发送连接请求时的时间戳。如果使用了持久连接,这个值会和 fetchStart 相同。connectEnd, // 浏览器和服务器之前建立连接的时间戳,所有握手和认证过程全部结束。如果使用了持久连接,这个值会和 fetchStart 相同。secureConnectionStart, // 浏览器与服务器开始安全链接的握手时的时间戳。如果当前网页不要求安全连接,返回 0。requestStart, // 浏览器向服务器发起 http 请求(或者读取本地缓存)时的时间戳,即获取 html 文档。responseStart, // 浏览器从服务器接收到第一个字节时的时间戳。responseEnd, // 浏览器从服务器接受到最后一个字节时的时间戳。domLoading, // dom 结构开始解析的时间戳,document.readyState 的值为 loading。domInteractive, // dom 结构解析结束,开始加载内嵌资源的时间戳,document.readyState 的状态为 interactive。domContentLoadedEventStart, // DOMContentLoaded 事件触发时的时间戳,所有需要执行的脚本执行完毕。domContentLoadedEventEnd,  // DOMContentLoaded 事件结束时的时间戳domComplete, // dom 文档完成解析的时间戳, document.readyState 的值为 complete。loadEventStart, // load 事件触发的时间。loadEventEnd // load 时间结束时的时间。
}

1、收集上报FP;

FP(First Paint)首次绘制,即浏览器开始绘制页面的时间点。这包括了任何用户自定义的绘制,它是渲染任何文本、图像、SVG等的开始时间

import { getPageURL, isSupportPerformanceObserver } from '../utils/util'
import { lazyReportCache } from '../utils/report'export default function observePaint() {
if (!isSupportPerformanceObserver()) returnconst entryHandler = (list) => {        for (const entry of list.getEntries()) {if (entry.name === 'first-paint') {observer.disconnect()}const json = entry.toJSON()delete json.durationconst reportData = {...json,subType: entry.name,type: 'performance',pageURL: getPageURL(),}lazyReportCache(reportData)}
}const observer = new PerformanceObserver(entryHandler)
// buffered 属性表示是否观察缓存数据,也就是说观察代码添加时机比事情触发时机晚也没关系。
observer.observe({ type: 'paint', buffered: true })
}

代码中observer.disconnect()是PerformanceObserver对象的一个方法,用于停止观察性能指标并断开与回调函数的连接。

事实上

observer.observe({ type: 'paint', buffered: true })

包含两种性能指标:first-contentful-paintfirst-paint

当调用observer.disconnect()方法时,PerformanceObserver对象将停止观察性能指标,并且不再接收任何性能指标的更新。与此同时,与回调函数的连接也会被断开,即使有新的性能指标数据产生,也不会再触发回调函数。

这个方法通常在不再需要观察性能指标时调用,以避免不必要的资源消耗。

2、收集上报FCP

FCP(First Contentful Paint):首次内容绘制,即浏览器首次绘制DOM内容的时间点,如文本、图像、SVG等。

看起来FCP和FP一致,其实还是有区别的

FCP(First Contentful Paint):FCP是指页面上首次渲染任何文本、图像、非空白的canvas或SVG的时间点。它表示了用户首次看到页面有实际内容的时间,即页面开始呈现有意义的内容的时间点。

FP(First Paint):FP是指页面上首次渲染任何内容的时间点,包括背景颜色、图片、文本等。它表示了页面开始呈现任何可视化内容的时间,但不一定是有意义的内容。

简而言之,FCP关注的是页面上首次呈现有意义内容的时间点,而FP关注的是页面上首次呈现任何可视化内容的时间点。FCP更关注用户感知的页面加载时间,因为它表示用户可以开始阅读或与页面进行交互的时间点。而FP则更关注页面开始渲染的时间点,无论内容是否有意义

import { getPageURL, isSupportPerformanceObserver } from '../utils/util'
import { lazyReportCache } from '../utils/report'export default function observePaint() {
if (!isSupportPerformanceObserver()) returnconst entryHandler = (list) => {        for (const entry of list.getEntries()) {if (entry.name === 'first-contentful-paint') {observer.disconnect()}const json = entry.toJSON()delete json.durationconst reportData = {...json,subType: entry.name,type: 'performance',pageURL: getPageURL(),}lazyReportCache(reportData)}}const observer = new PerformanceObserver(entryHandler)// buffered 属性表示是否观察缓存数据,也就是说观察代码添加时机比事情触发时机晚也没关系。observer.observe({ type: 'paint', buffered: true })}

3、收集上报LCP

LCP(Largest Contentful Paint):最大内容绘制,即视口中最大的图像或文本块的渲染完成的时间点

import { getPageURL, isSupportPerformanceObserver } from '../utils/util'
import { lazyReportCache } from '../utils/report'export default function observeLCP() {
if (!isSupportPerformanceObserver()) {
return
}const entryHandler = (list) => {if (observer) {observer.disconnect()}for (const entry of list.getEntries()) {const json = entry.toJSON()delete json.durationconst reportData = {...json,target: entry.element?.tagName,name: entry.entryType,subType: entry.entryType,type: 'performance',pageURL: getPageURL(),}lazyReportCache(reportData)}}const observer = new PerformanceObserver(entryHandler)observer.observe({ type: 'largest-contentful-paint', buffered: true })}

4、收集上报DOMContentLoaded

DOMContentLoaded:当HTML文档被完全加载和解析完成后,DOMContentLoaded事件被触发,无需等待样式表、图像和子框架的完成加载

import { lazyReportCache } from '../utils/report'export default function observerLoad() {
['DOMContentLoaded'].forEach(type => onEvent(type))
}function onEvent(type) {function callback() {lazyReportCache({type: 'performance',subType: type.toLocaleLowerCase(),startTime: performance.now(),})window.removeEventListener(type, callback, true)}window.addEventListener(type, callback, true)
}

5、收集上报onload数据

onload:当所有需要立即加载的资源(如图片和样式表)已加载完成时的时间点

import { lazyReportCache } from '../utils/report'export default function observerLoad() {
\['load'].forEach(type => onEvent(type))
}function onEvent(type) {
function callback() {
lazyReportCache({
type: 'performance',
subType: type.toLocaleLowerCase(),
startTime: performance.now(),
})window.removeEventListener(type, callback, true)}window.addEventListener(type, callback, true)}

6、收集上报资源加载时间

收集资源加载时间

observer.observe({ type: 'resource', buffered: true })

我在想什么是资源加载时间?应该就是下面的entry.duration的。我觉得写监控SDK很有意义,可以更加深入的学习浏览器模型。了解浏览器是怎么看待各种html文件资源的

import { executeAfterLoad, isSupportPerformanceObserver} from '../utils/util'
import { lazyReportCache } from '../utils/report'export default function observeEntries() {executeAfterLoad(() => {observeEvent('resource')})
}export function observeEvent(entryType) {
function entryHandler(list) {const data = list.getEntries()for (const entry of data) {if (observer) {observer.disconnect()}lazyReportCache({name: entry.name, // 资源名称subType: entryType,type: 'performance',sourceType: entry.initiatorType, // 资源类型duration: entry.duration, // 资源加载耗时dns: entry.domainLookupEnd - entry.domainLookupStart, // DNS 耗时tcp: entry.connectEnd - entry.connectStart, // 建立 tcp 连接耗时redirect: entry.redirectEnd - entry.redirectStart, // 重定向耗时ttfb: entry.responseStart, // 首字节时间protocol: entry.nextHopProtocol, // 请求协议responseBodySize: entry.encodedBodySize, // 响应内容大小responseHeaderSize: entry.transferSize - entry.encodedBodySize, // 响应头部大小resourceSize: entry.decodedBodySize, // 资源解压后的大小startTime: performance.now(),})}}let observerif (isSupportPerformanceObserver()) {observer = new PerformanceObserver(entryHandler)observer.observe({ type: entryType, buffered: true })}}

7、收集上报接口请求时间

这里通过覆写原生xhr对象方法,对方法做拦截实现接口时间收集以及上报

import { originalOpen, originalSend, originalProto } from '../utils/xhr'
import { lazyReportCache } from '../utils/report'function overwriteOpenAndSend() {originalProto.open = function newOpen(...args) {this.url = args\[1]this.method = args\[0]originalOpen.apply(this, args)}originalProto.send = function newSend(...args) {this.startTime = Date.now()const onLoadend = () => {this.endTime = Date.now()this.duration = this.endTime - this.startTimeconst { status, duration, startTime, endTime, url, method } = thisconst reportData = {status,duration,startTime,endTime,url,method: (method || 'GET').toUpperCase(),success: status >= 200 && status < 300,subType: 'xhr',type: 'performance',}lazyReportCache(reportData)this.removeEventListener('loadend', onLoadend, true)}this.addEventListener('loadend', onLoadend, true)originalSend.apply(this, args)}}export default function xhr() {
overwriteOpenAndSend()
}

五、错误数据收集上报

根据最初的规划需要收集资源加载错误js错误promise错误

1、收集上报资源加载错误

收集 JavaScript、CSS 和图片的加载错误,使用window.addEventListener监听错误

import { lazyReportCache } from '../utils/report'
import { getPageURL } from '../utils/util'export default function error() {// 捕获资源加载失败错误 js css img...window.addEventListener('error', e => {const target = e.targetif (!target) returnif (target.src || target.href) {const url = target.src || target.hreflazyReportCache({url,type: 'error',subType: 'resource',startTime: e.timeStamp,html: target.outerHTML,resourceType: target.tagName,paths: e.path.map(item => item.tagName).filter(Boolean),pageURL: getPageURL(),})}}, true)}

2、收集上报js错误

收集 JavaScript 错误,可以使用 window.onerror 或者 window.addEventListener('error', callback)

import { lazyReportCache } from '../utils/report'
import { getPageURL } from '../utils/util'export default function error() {// 监听 js 错误window.onerror = (msg, url, line, column, error) => {lazyReportCache({msg,line,column,error: error.stack,subType: 'js',pageURL: url,type: 'error',startTime: performance.now(),})}}

说明一下window.onerror无法捕获资源加载错误,所以这里可以单独拿来监听js错误。

3、收集上报promise错误

收集 Promise 错误,可以使用 window.addEventListener(‘unhandledrejection’, callback)

import { lazyReportCache } from '../utils/report'
import { getPageURL } from '../utils/util'export default function error() {// 监听 promise 错误 缺点是获取不到列数据window.addEventListener('unhandledrejection', e => {lazyReportCache({reason: e.reason?.stack,subType: 'promise',type: 'error',startTime: e.timeStamp,pageURL: getPageURL(),})})}

为了减少对html文件代码的干扰,错误收集可以添加一个缓存代理。

六、行为数据收集上报

根据最初的规划,行为数据收集pv、uv,页面停留时长,用户点击。

1、收集上报pv、uv

收集 pv(Page View,页面浏览量)和 uv(Unique Visitor,独立访客)数据,需要在每次页面加载时发送一个请求到服务器,然后在服务器端进行统计

import { lazyReportCache } from '../utils/report'
import getUUID from './getUUID'
import { getPageURL } from '../utils/util'export default function pv() {lazyReportCache({type: 'behavior',subType: 'pv',startTime: performance.now(),pageURL: getPageURL(),referrer: document.referrer,uuid: getUUID(),})
}

这里只能收集了pv数据,uv数据统计需要在服务端进行。

2、页面上报停留时长

收集页面停留时长,可以在页面加载时记录一个开始时间,然后在页面卸载时记录一个结束时间,两者的差就是页面的停留时长。这个计算逻辑可以放在beforeunload事件里做

import { report } from '../utils/report'
import { onBeforeunload, getPageURL } from '../utils/util'
import getUUID from './getUUID'export default function pageAccessDuration() {onBeforeunload(() => {report({type: 'behavior',subType: 'page-access-duration',startTime: performance.now(),pageURL: getPageURL(),uuid: getUUID(),}, true)})
}

3、用户点击上报

收集用户点击事件,可以使用 addEventListener 来监听 click 事件,这里借助了冒泡

import { lazyReportCache } from '../utils/report'
import { getPageURL } from '../utils/util'
import getUUID from './getUUID'export default function onClick() {
['mousedown', 'touchstart'].forEach(eventType => {let timerwindow.addEventListener(eventType, event => {clearTimeout(timer)timer = setTimeout(() => {const target = event.targetconst { top, left } = target.getBoundingClientRect()lazyReportCache({top,left,eventType,pageHeight: document.documentElement.scrollHeight || document.body.scrollHeight,scrollTop: document.documentElement.scrollTop || document.body.scrollTop,type: 'behavior',subType: 'click',target: target.tagName,paths: event.path?.map(item => item.tagName).filter(Boolean),startTime: event.timeStamp,pageURL: getPageURL(),outerHTML: target.outerHTML,innerHTML: target.innerHTML,width: target.offsetWidth,height: target.offsetHeight,viewport: {width: window.innerWidth,height: window.innerHeight,},uuid: getUUID(),})}, 500)})})}

七、改造完善四维监控类

性能数据、错误数据、行为数据入口文件的收集方法在监控类四维init方法内初始化

import performance from './performance/index'
import behavior from './behavior/index'
import error from './error/index'class FourDimension {constructor() {this.init()}// 初始化init() {performance()error()behavior()}
}new FourDimension().init()

在具体使用过程中,采用异步加载的方式引入。

八、自定义指标

1、long task

执行时间超过50ms的任务,被称为 long task 长任务

获取页面的长任务列表:

const entryHandler = list => {for (const long of list.getEntries()) {// 获取长任务详情console.log(long);}
};let observer = new PerformanceObserver(entryHandler);
observer.observe({ entryTypes: ["longtask"] });

2、memory页面内存

performance.memory 可以显示此刻内存占用情况,它是一个动态值,其中:

  • jsHeapSizeLimit 该属性代表的含义是:内存大小的限制。

  • totalJSHeapSize 表示总内存的大小。

  • usedJSHeapSize 表示可使用的内存的大小。

通常,usedJSHeapSize 不能大于 totalJSHeapSize,如果大于,有可能出现了内存泄漏

// load事件中获取此时页面的内存大小
window.addEventListener("load", () => {console.log("memory", performance.memory);
});

3、首屏加载时间

首屏加载时间和首页加载时间不一样,首屏指的是屏幕内的dom渲染完成的时间
比如首页很长需要好几屏展示,这种情况下屏幕以外的元素不考虑在内
计算首屏加载时间流程
1)利用MutationObserver监听document对象,每当dom变化时触发该事件
2)判断监听的dom是否在首屏内,如果在首屏内,将该dom放到指定的数组中,记录下当前dom变化的时间点
3)在MutationObserver的callback函数中,通过防抖函数,监听document.readyState状态的变化
4)当document.readyState === 'complete'停止定时器和 取消对document的监听
5)遍历存放dom的数组,找出最后变化节点的时间,用该时间点减去performance.timing.navigationStart 得出首屏的加载时间

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/429397.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

群晖NAS使用Docker本地部署网页版Ubuntu系统并实现无公网IP远程访问

文章目录 前言1. 下载Docker-Webtop镜像2. 运行Docker-Webtop镜像3. 本地访问网页版Linux系统4. 群晖NAS安装Cpolar工具5. 配置异地访问Linux系统6. 异地远程访问Linux系统7. 固定异地访问的公网地址 前言 本文旨在详细介绍如何在群晖NAS部署docker-webtop&#xff0c;并结合c…

《财富之眼:用经济思维看清世界》pdf电子书下载

《财富之眼&#xff1a;用经济思维看清世界》pdf电子书下载 内容简介 一切社会现象都是经济现象&#xff0c;我们只能赚到自己认知范围内的 钱。我国社会主要矛盾已经转化为人民日益增长的美好生活需要和不 平衡不充分的发展之间的矛盾&#xff0c;其中“不平衡不充分”很大程…

【网络】高级IO——Reactor版TCP服务器

目录 1.什么是Reactor 1.1.餐厅里的Reactor模式 2.Reactor的由来 2.1.单 Reactor 单进程 / 线程 2.2.单 Reactor 多线程 / 多进程 2.3.多 Reactor 多进程 / 线程 3.实现单 Reactor 单进程版本的TCP服务器 3.1.Connection类 3.2.TcpServer类 3.3.Connection的真正用处 …

C++—vector的常见接口与用法(正式进入STL)

目录 0.提醒 1.介绍 2.构造 1.正常构造 2.默认值构造 3.调用默认构造函数构造 3.遍历 1.迭代器 2.范围for 3.下标访问 4.容量 1.capacity&#xff1a;返回当前容器的容量 2.reserve&#xff1a;如果传的k比当前容量大&#xff0c;则扩容到比k大或者等于k的数&…

Windows10安装cuda11.3.0+cudnn8.5.0,以及创建conda虚拟环境(pytorch)

1、检查电脑驱动版本为561.09&#xff0c;选择cuda版本&#xff0c;下图可知cuda版本<12.6。 nvidia-smi #查看驱动版本&#xff0c;以及最大可以安装的cuda版本 2、Anaconda3-2024.06-1-Windows-x86_64.exe下载&#xff1a; 官网&#xff1a;https://www.baidu.com/link?…

STL之vector

vector简单介绍 vector是一个STL的一个容器&#xff0c;行为类似于变成数组&#xff0c;也就是存储空间是连续的&#xff0c;同时其空间大小又是动态可变的。 vector访问元素的效率很高可以通过下标直接访问&#xff0c;但是其占用的空间很多&#xff0c;插入删除元素的效率很…

PostMan使用变量

环境变量 使用场景 当测试过程中&#xff0c;我们需要对开发环境、测试环境、生产环境进行测试 不同的环境对应着不同的服务器&#xff0c;那么这个时候我们就可以使用环境变量来区分它们 避免切换测试环境后&#xff0c;需要大量的更改接口的url地址 全局变量 使用场景 当…

无人机集群路径规划:麻雀搜索算法(Sparrow Search Algorithm, SSA)​求解无人机集群路径规划,提供MATLAB代码

一、单个无人机路径规划模型介绍 无人机三维路径规划是指在三维空间中为无人机规划一条合理的飞行路径&#xff0c;使其能够安全、高效地完成任务。路径规划是无人机自主飞行的关键技术之一&#xff0c;它可以通过算法和模型来确定无人机的航迹&#xff0c;以避开障碍物、优化…

Linux shell编程学习笔记81:zcat命令——快速查看压缩文件内容

0 引言 在 Linux shell编程学习笔记80&#xff1a;gzip命令——让文件瘦身-CSDN博客https://blog.csdn.net/Purpleendurer/article/details/141862213?spm1001.2014.3001.5501中&#xff0c;我们使用gzip命令可以创建压缩文件。那么&#xff0c;我们可以使用zcat命令来查看压…

Apache CVE-2021-41773 漏洞攻略

1.环境搭建 docker pull blueteamsteve/cve-2021-41773:no-cgid docker run -d -p 8080:80 97308de4753d 2.使用poc curl http://192.16.10.190:8080/cgi-bin/.%2e/.%2e/.%2e/.%2e/etc/passwd 3.工具验证

双击热备 Electron网页客户端

安装流程&#xff1a; 1.下载node.js安装包进行安装 2.点击Next; 3.勾选&#xff0c;点击Next; 4.选择安装目录 5.选择Online 模式 6.下一步执行安装 。 7.运行cmd,执行命令 path 和 node --version&#xff0c;查看配置路径和版本 8.Goland安装插件node.js 9.配置运行…

【数据结构与算法 | 灵神题单 | 自底向上DFS篇】力扣508, 1026, 951

1. 力扣508&#xff1a;出现次数最多的子树元素和 1.1 题目&#xff1a; 给你一个二叉树的根结点 root &#xff0c;请返回出现次数最多的子树元素和。如果有多个元素出现的次数相同&#xff0c;返回所有出现次数最多的子树元素和&#xff08;不限顺序&#xff09;。 一个结…

JVM 调优篇7 调优案例4- 线程溢出

一 线程溢出 1.1 报错信息 每个 Java 线程都需要占用一定的内存空间&#xff0c;当 JVM 向底层操作系统请求创建一个新的 native 线程时&#xff0c;如果没有足够的资源分配就会报此类错误。报错信息&#xff1a;java.lang.outofmemoryError:unable to create new Native Thr…

【leetcode】树形结构习题

二叉树的前序遍历 返回结果&#xff1a;[‘1’, ‘2’, ‘4’, ‘5’, ‘3’, ‘6’, ‘7’] 144.二叉树的前序遍历 - 迭代算法 给你二叉树的根节点 root &#xff0c;返回它节点值的 前序 遍历。 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&#xff1a;[1,…

AI时代,服务器厂商能否打破薄利的命运?

文&#xff5c;刘俊宏 编&#xff5c;王一粟 AI大模型正在引发新一轮的“算力焦渴”。 近日&#xff0c;OpenAI刚发布的o1大模型再次刷新了大模型能力的上限。对比上一次迭代的版本&#xff0c;o1的推理能力全方位“吊打”了GPT-4o。更优秀的能力&#xff0c;来自与o1将思维…

大学生必看!60万人在用的GPT4o大学数学智能体有多牛

❤️作者主页&#xff1a;小虚竹 ❤️作者简介&#xff1a;大家好,我是小虚竹。2022年度博客之星&#x1f3c6;&#xff0c;Java领域优质创作者&#x1f3c6;&#xff0c;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;掘金年度人气作者&#x1…

利用QEMU安装一台虚拟机的三种方法

文章目录 宿主机的选择方法一&#xff1a;直接用qemu源码安装步骤1&#xff1a;下载好qemu源码&#xff0c;这里我们用qemu-5.1.0步骤2&#xff1a;编译步骤3&#xff1a;创建一个系统盘步骤4&#xff1a;用步骤2编译的qemu-system-x86_64 启动一台Linux虚拟机步骤5&#xff1a…

arm-硬件

一、ARM体系与架构 ARM芯片组成 -- arm 体系中&#xff0c;一般讲到的芯片由两大部分组成&#xff1a;arm的内核、外设 arm内核&#xff1a; -- 其内核主要由&#xff1a;寄存器、指令集、总线、存储器映射规则、中断逻辑主调试组件构成。ARM公司只设计内核&#xff0c;授权给…

用最通俗易懂的语言和例子讲解三维点云

前言&#xff1a; 我整体的学习顺序是看的按B站那“唯一”的三维点云的视频学习的&#xff08;翻了好久几乎没有第二个...&#xff09;对于深度学习部分&#xff0c;由于本人并没有进行学习&#xff0c;所以没有深究。大多数内容都进行了自己的理解并找了很多网络的资源方便理解…

客户转化预测以及关键因素识别_支持向量机与相关性分析

数据入口&#xff1a;数字营销转化数据集 - Heywhale.com 数据集记录了客户与数字营销活动的互动情况。它涵盖了人口统计数据、营销特定指标、客户参与度指标以及历史购买数据&#xff0c;为数字营销领域的预测建模和分析提供了丰富的信息。 数据说明&#xff1a; 字段说明Cu…