Service Worker原理
Service Worker原理
- 1.基本概念与使用场景:
- a.什么是Service Worker?它的主要用途是什么?
- b.Service Worker和Web Worker有什么不同?
- c.预缓存和缓存的区别
- 2. 实现细节:
- a.描述Service Worker的生命周期。
- b.如何注册和注销一个Service Worker?
- c.怎样使用Service Worker实现资源缓存?
- d.如何确保Service Worker更新后的代码能够被使用?
- e.在Service Worker中如何实现离线数据同步?
- 3.与其他技术的交互:
- a.Service Worker与Push Notification是如何集成的?
- b.如何使用Service Worker与Background Sync API?
- 关键API
- c.与IndexedDB的交互中,Service Worker通常扮演什么角色?
- 4.性能与策略:
- a.描述一下缓存策略的种类,例如Cache First、Network First等。
- 如何选择
- b.使用Service Worker可能会带来哪些性能问题?如何避免?
- c.在Service Worker中如何实现策略来保存用户的数据或状态?
- 1. Cache Storage API
- 2. IndexedDB
- 3. PostMessage API
- 代码示例和关键步骤
- 使用IndexedDB保存购物车信息
- 使用Cache API保存页面状态
- 使用PostMessage API通信
- 5.安全与隐私:
- a.Service Worker有哪些安全限制?
- 对于HTTPS的要求,为什么Service Worker只在HTTPS下工作?
- 你如何确保Service Worker不会成为跨站脚本攻击(XSS)的目标?
- 1. 描述Service Worker的重要性
- 2. 识别潜在的安全风险
- 3. 实施的安全措施
- 4. 持续监控和更新
- 6.问题与挑战:
- 7.其他:
1.基本概念与使用场景:
a.什么是Service Worker?它的主要用途是什么?
ServiceWorker 是一个运行在浏览器背后的独立线程,它拥有访问网络的能力,可以实现资源缓存、消息推送、后台数据同步等功能.
- 资源缓存:它能拦截和缓存网络请求,提高加载速度和优化用户体验。
- 消息推送:即便在应用或浏览器未运行的情况下,Service Worker也能接收后台推送通知。
- 后台数据同步:使用 Background Sync API, 它可以在后台同步数据,这在断网或网络不稳定时特别有用。
b.Service Worker和Web Worker有什么不同?
-
主要目的:
- Service Worker:它主要用于拦截网络请求、管理缓存、提供接收推送通知。它是构建Progressive Web Apps (PWA)的核心技术之一。
- Web Worker:它的主要目的是允许开发者在背景线程上执行JavaScript,这样可以避免主线程的长时间计算导致的UI阻塞。
-
生命周期:
- Service Worker:安装、激活和终止。这使得Service Worker可以管理资源缓存、更新策略等。
- Web Worker:没有特定的生命周期,它基本上就是一个可以并行执行的线程,一旦它完成了任务或被主线程终止,它就会结束。
-
应用场景:
- Service Worker:它主要用于拦截网络请求、管理缓存、提供接收推送通知。它是构建Progressive Web Apps (PWA)的核心技术之一。
- Web Worker:用于执行CPU密集型任务,例如图像处理、大数据计算或复杂数学运算,而不阻塞UI。
总的来说,虽然这两者都在背景线程上执行任务,但Service Worker更注重于网络和资源的管理,而Web Worker更偏向于为主线程提供计算上的帮助。
c.预缓存和缓存的区别
预缓存(Pre-caching)和缓存(Caching)都是用于存储资源的技术,但它们在应用生命周期和使用场景上有所不同。
预缓存(Pre-caching)
-
时间点:预缓存通常在Service Worker的安装(Installation)阶段进行。指定缓存资源。
-
目的:预缓存的主要目的是加速后续访问(包括离线)。通过预缓存关键资源。
-
选择性:只针对核心和关键资源进行,如主要的HTML, CSS, JavaScript文件和主要图片等。
-
手动管理:手动指定哪些文件需要被预缓存。
缓存(Caching)
-
时间点:缓存一般在应用运行期间进行,通常是在Service Worker的
fetch
事件中处理。当用户访问过一个资源后,资源被缓存起来。 -
目的:用于提速重复访问同一资源,通过从缓存中读取资源,而不是从网络中重新获取,从而提高性能。
-
自动性:取决于缓存策略和HTTP头信息。
-
动态管理:缓存的资源通常是动态的,可能会随着用户与应用的交互而改变。例如,用户上传了一个新图片,这个图片可能就会被缓存起来。
2. 实现细节:
a.描述Service Worker的生命周期。
-
注册(Registration):在主线程的JavaScript代码中注册Service Worker。一旦注册成功,浏览器会自动进入安装阶段。
-
安装(Installation)进行资源预缓存的最佳时机。缓存静态资源。
-
等待(Waiting):一旦安装完成,新的Service Worker会处于“等待”状态。这个状态会持续到没有其他活动的Service Worker为止,之后新的Service Worker会进入“激活”状态。
-
激活(Activation):激活阶段主要用于更新和清理旧缓存。一旦Service Worker被激活,它就能控制所有打开的客户端或页面。
-
运行(Running):在运行阶段,Service Worker会拦截页面的网络请求,监听push事件等。它也能通过
postMessage
API与主线程进行通信。 -
终止(Termination):为了节省资源,浏览器会在Service Worker不再活动或需要时将其终止。但需要时,它会被自动重新唤醒。
-
更新(Update)
重点:安装(install
)、激活(activate
)和运行 (Running
)。
- 安装(install):预缓存一些资源,确保应用在离线状态下也可以访问这些资源。例如,在我之前的TodoList PWA项目中,我利用安装事件来缓存了应用的核心静态文件,如HTML、CSS、JavaScript和主要的图片资源。
self.addEventListener('install', function(event) {event.waitUntil(caches.open('app-static-v1').then(function(cache) {return cache.addAll(['/','/index.html','/styles.css','/main.js','/icon.png']);}));
});
在主线程监听install
事件
关键API:
caches.open
:打开一个缓存
cache.addAll
:添加数组中所有缓存
- 激活(activate):这个阶段主要用于管理缓存,例如删除旧的缓存资源(比如:版本更新、缓存策略改变)。在我的实际开发经历中,我使用此阶段来确保用户不会访问到过期的资源。
下面是一段激活阶段用于清理旧缓存的示例代码:
self.addEventListener('activate', event => {const cacheWhitelist = ['my-cache-v2']; // 新版本的缓存名列表event.waitUntil(caches.keys().then(cacheNames => {return Promise.all(cacheNames.map(cacheName => {if (cacheWhitelist.indexOf(cacheName) === -1) {// 如果缓存名不在新版本列表里,就删除它return caches.delete(cacheName);}}));}));
});
在这个例子中:
activate
事件监听器被定义。- 使用
caches.keys()
方法获取所有的缓存名称。 - 检查每一个缓存名称是否存在于新版本的缓存名单(
cacheWhitelist
)中。- 如果不存在,使用
caches.delete()
方法删除该缓存。
- 如果不存在,使用
- 运行 (Running):这是Service Worker运行中最频繁的阶段。每当页面请求一个资源,Service Worker都会捕获请求。**在TodoList PWA项目中,我利用这一特性实现了一个网络策略,先从缓存中尝试获取资源,如果缓存中没有,则从网络获取。**这种策略在网络不稳定的地区表现得尤为出色,它提高了应用的加载速度并提升了用户体验。
下面的代码示例说明了如何在Service Worker中捕获fetch
事件,并实现一个简单的缓存优先策略。在这个策略中,Service Worker首先会尝试从缓存中获取请求的资源。如果资源存在于缓存中,它将返回缓存的版本。如果缓存中没有该资源,则会从网络获取:
self.addEventListener('fetch', function(event) {event.respondWith(caches.match(event.request) // 尝试从缓存中获取资源.then(function(response) {if (response) {return response; // 如果缓存中有该资源,返回缓存的版本}return fetch(event.request) // 否则,从网络获取资源.then(function(response) {// 考虑将新获取的资源添加到缓存中return caches.open('dynamic-cache').then(function(cache) {cache.put(event.request, response.clone());return response;});});}).catch(function() {// 如果网络也不可用(或其他原因),可以返回一个备用页面或资源}));
});
在这个代码示例中,有几个关键的API和步骤:
关键API
self.addEventListener
: 用于给Service Worker添加事件监听器。event.respondWith
: 指定fetch事件的响应。caches.match
: 尝试在缓存中匹配一个请求。fetch
: 用于从网络获取资源。caches.open
: 打开一个特定的缓存对象。cache.put
: 将请求和响应对象添加到缓存中。
b.如何注册和注销一个Service Worker?
在我的实习项目中,我使用Service Worker来实现TodoList PWA应用的离线访问和资源缓存功能。以下是注册和注销Service Worker的方法:
注册:
- 首先,我会检查浏览器是否支持Service Worker,使用
'serviceWorker' in navigator
。 - 使用
navigator.serviceWorker.register('/service-worker.js')
方法进行注册,这里的'/service-worker.js'
是我的Service Worker脚本的路径。 register
方法返回一个Promise,所以我可以在.then()
中处理成功的注册或使用.catch()
来处理任何错误。
注销:
- 如果需要注销Service Worker,我可以首先通过
navigator.serviceWorker.getRegistrations()
获取所有的Service Worker注册。 - 这个方法也返回一个Promise,一旦得到所有的注册,我可以遍历它们并对每一个调用
registration.unregister()
方法。
c.怎样使用Service Worker实现资源缓存?
与生命周期回答相似:
d.如何确保Service Worker更新后的代码能够被使用?
关于Service Worker的更新,这确实是一个很好的问题,因为经常有开发者遇到缓存陈旧内容的问题。要确保Service Worker更新后的代码能够被使用,我们通常采用以下策略:
- 版本控制:在Service Worker的代码中加入版本信息,每次更改时都更新版本。这样,浏览器会检测到新的Service Worker并开始安装过程。
- skipWaiting:通常,新的Service Worker在安装后会等待,直到所有的Service Worker clients都关闭。但我们可以使用
self.skipWaiting()
来强制它立即开始控制新的clients。 - Clients.claim():在Service Worker激活时使用这个方法可以确保Service Worker立即开始控制任何新的页面。
- 通知用户:为用户提供一个提示,告知他们应用有更新,建议他们刷新页面或重新启动应用来获取新的内容。
在我们的项目中,我们结合了以上策略,确保了新的代码和内容能够及时地被用户使用,从而避免了潜在的错误和混淆。
e.在Service Worker中如何实现离线数据同步?
实现:
- 使用Background Sync API:当用户在离线状态下做出更改时,我们会把这些更改保存在IndexedDB中,并为这些更改注册一个背景同步标记。
- Service Worker的’sync’事件监听:当网络再次可用时,Service Worker会触发’sync’事件。我们在Service Worker中监听这个事件,当它被触发时,我们从IndexedDB中获取离线时存储的更改,并尝试将它们同步到服务器。
- 处理失败的同步:如果同步失败,例如因为服务器错误,我们会利用Service Worker的retry机制稍后再次尝试同步。
结果:通过这种方式,我们确保了用户在离线时的任何操作都不会丢失,并在他们重新联网时得到了处理。这大大增强了应用的健壮性和用户体验。
3.与其他技术的交互:
a.Service Worker与Push Notification是如何集成的?
在我之前的项目中,我使用Service Worker为我们的Uber PWA应用增加了推送通知功能。这样,我们可以在应用未运行或甚至在浏览器关闭的情况下发送通知给用户。
这里是我实现的主要步骤:
-
注册Service Worker:首先,我确保在页面的主线程中注册了Service Worker,并确保它已被成功安装和激活。
-
用户订阅:为了发送推送通知,我请求了用户的许可。一旦用户同意,浏览器会返回一个"PushSubscription"对象,这个对象包含了发送推送消息所需的所有信息。
-
保存订阅信息:为了以后能发送通知,我将这个"PushSubscription"对象发送到我们的后端并保存在数据库中。
-
发送通知:当我们需要发送推送通知时,我们的后端使用保存的"PushSubscription"信息来触发通知。这个通知请求发送到推送服务,如FCM或VAPID。
-
Service Worker监听:在Service Worker脚本中,我添加了一个
push
事件的监听器。当通知到达时,这个事件会被触发,并允许我们自定义通知的内容和行为。 -
显示通知:最后,在
push
事件的监听器中,我使用self.registration.showNotification()
方法来显示实际的通知。
在实现这个功能的过程中,我确保优化了用户体验,如在不同情况下为通知设置了不同的优先级,并处理了用户点击通知后的行为。通过结合Service Worker和Push Notification,我们为用户提供了实时的通知服务,从而增强了应用的用户体验和用户参与度。
b.如何使用Service Worker与Background Sync API?
Background Sync API 是一个允许在网络恢复后进行延迟操作的Web API。这意味着当用户处于离线状态或网络连接不稳定时,应用程序仍然可以完成某些操作(例如发送数据),然后等待网络连接恢复后再实际执行这些操作。这种技术通常与 Service Worker 一起使用,以实现更复杂的离线功能和性能优化。
关键API
-
SyncManager.register(tag)
在主线程中注册一个同步事件。当网络连接恢复时,Service Worker 中的
sync
事件将被触发。syncManager.register('mySyncEvent');
-
Service Worker 中的 ‘sync’ 事件
在 Service Worker 文件中,你可以监听
sync
事件,并在触发时执行相关代码。self.addEventListener('sync', function(event) {if (event.tag === 'mySyncEvent') {event.waitUntil(doSomething());} });
示例
假设你有一个表单,用户填写表单后点击“提交”按钮。如果用户当前离线,你可以使用 Background Sync 来延迟提交操作。
// 在页面中
if ('serviceWorker' in navigator && 'SyncManager' in window) {navigator.serviceWorker.ready.then(function(registration) {return registration.sync.register('submitForm');}).catch(function() {// 同步注册失败});
}// 在 Service Worker 中
self.addEventListener('sync', function(event) {if (event.tag === 'submitForm') {event.waitUntil(// 你的提交表单逻辑);}
});
c.与IndexedDB的交互中,Service Worker通常扮演什么角色?
4.性能与策略:
a.描述一下缓存策略的种类,例如Cache First、Network First等。
service Worker是实现几种缓存策略的关键!
缓存策略:
-
Cache First:首先检查缓存中是否有所需资源。如果有,则从缓存中取出;如果没有,则通过网络获取。我们用这种策略来缓存那些不经常改变的静态资源。
-
Network First:先尝试从网络获取资源。如果请求成功,将资源放入缓存;如果请求失败,再从缓存中检索。这种策略适用于数据或内容经常变化的情况。
-
Cache Only:只从缓存中获取资源,如果资源不在缓存中,则失败。我们用这种策略在应用的某些部分确保速度。
-
Network Only:总是从网络获取资源。适用于始终需要从服务器获取最新信息的场景。
在Uber PWA应用的开发中,我们主要采用了Cache First和Network First的策略,以确保用户能够在没有网络连接时仍能访问应用的主要部分,同时确保某些关键信息(如定位或价格更新)始终是最新的。
缓存策略的选择取决于多个因素,包括应用类型、用户需求、网络环境等。以下是一些常用的缓存策略和选择它们的理由:
- Cache First(缓存优先)
- 适用场景:静态资源、不经常变动的数据。
- 优点:快速响应,减少网络请求。
- 缺点:可能返回过期或陈旧的数据。
- Network First(网络优先)
- 适用场景:实时数据,如新闻、股票价格。
- 优点:始终尝试返回最新数据。
- 缺点:如果网络慢或不可用,会影响用户体验。
- Cache Then Network(先缓存后网络)
- 适用场景:适用于渐进式加载,先从缓存加载旧数据,然后再从网络更新。
- 优点:快速响应,也能更新数据。
- 缺点:可能会导致页面内容跳动或重新渲染。
- Network Only(仅网络)
- 适用场景:对实时性要求非常高的情况,比如实时聊天。
- 优点:始终获取最新数据。
- 缺点:完全依赖网络。
- Cache Only(仅缓存)
- 适用场景:已知资源一定在缓存中的情况。
- 优点:无需网络,快速响应。
- 缺点:如果资源不在缓存中,会失败。
如何选择
-
考虑数据新鲜度:如果数据需要是最新的,使用
Network First
或Network Only
。 -
考虑性能和响应时间:如果性能是关键因素,使用
Cache First
或Cache Only
。 -
考虑离线支持:如果应用需要在离线状态下工作,考虑使用
Cache First
、Cache Only
或。 -
考虑复杂性:更复杂的策略如
Cache Then Network
可能需要更多的逻辑来实现。 -
测试和评估:最终,不论选择哪种策略,都需要进行充分的测试来确定其是否符合你的应用需求。
b.使用Service Worker可能会带来哪些性能问题?如何避免?
-
过度缓存:初次尝试使用Service Worker时,我发现应用可能会缓存过多不常用的资源,导致用户首次访问时消耗大量的数据。为了解决这个问题,我仔细筛选了要预缓存的资源,只缓存核心资源,并在Service Worker更新时及时清理旧的缓存。(比如:多媒体内容:例如,视频和音频文件通常很大,除非它们是应用的核心部分,否则没必要预缓存)
-
缓存过期:随着应用迭代,有些缓存的资源可能已经过期或不再使用。为了避免这种情况,我在Service Worker中实现了版本管理,确保在新版本发布时,过期的资源能被及时清理。(在Service Worker脚本的顶部,定义一个版本变量。这个变量应该每次发布新版本时更新。)
-
缓存策略选择:不同的资源和请求需要不同的缓存策略。例如,对于一些频繁更新的API请求,我使用“网络优先”的策略,确保用户总是获得最新数据。而对于一些静态资源,我使用“缓存优先”策略以提高加载速度。
-
同步更新的挑战:利用Service Worker的Background Sync功能进行后台数据同步时,可能会遇到网络不稳定导致的同步失败。为了处理这种情况,我实现了一个重试机制,并确保在网络恢复时尝试再次同步。
c.在Service Worker中如何实现策略来保存用户的数据或状态?
在Service Worker中实现策略来保存用户的数据或状态,通常会用到以下几种技术:
1. Cache Storage API
Service Worker有访问Cache Storage的权限,通常用于缓存静态资源,但也可以用于保存用户状态或其他数据。
2. IndexedDB
IndexedDB是一个运行在浏览器中的非关系型数据库,Service Worker可以访问它。它适用于保存大量的结构化数据,包括文件、Blob等。
3. PostMessage API
使用postMessage
API,Service Worker可以与页面进行通信,获取或设置用户状态。
代码示例和关键步骤
假设我们要保存用户的购物车信息:
使用IndexedDB保存购物车信息
// In Service Worker
self.addEventListener('fetch', function(event) {if (event.request.url.endsWith('/add-to-cart')) {event.respondWith(// ...handle request);event.waitUntil(updateCartInIndexedDB(event.request));}
});async function updateCartInIndexedDB(request) {const cartData = await fetch(request).then(res => res.json());const db = await openIndexedDB();const tx = db.transaction(['cart'], 'readwrite');const store = tx.objectStore('cart');store.put(cartData);
}async function openIndexedDB() {return new Promise((resolve, reject) => {const request = indexedDB.open('myDatabase', 1);request.onsuccess = function() {resolve(request.result);};request.onerror = function() {reject(request.error);};request.onupgradeneeded = function(e) {const db = e.target.result;if (!db.objectStoreNames.contains('cart')) {db.createObjectStore('cart');}};});
}
使用Cache API保存页面状态
// 在Service Worker中
self.addEventListener('fetch', function(event) {if (event.request.url.endsWith('/some-page')) {event.respondWith(caches.match(event.request).then(function(response) {return response || fetch(event.request);}));event.waitUntil(updatePageCache(event.request));}
});async function updatePageCache(request) {const response = await fetch(request);const cache = await caches.open('my-cache');cache.put(request, response);
}
使用PostMessage API通信
在Service Worker中:
self.addEventListener('message', function(event) {if (event.data.type === 'SET_USER_STATE') {// Save user stateconst userState = event.data.userState;saveUserState(userState);}
});
在页面中:
if ('serviceWorker' in navigator) {navigator.serviceWorker.controller.postMessage({type: 'SET_USER_STATE',userState: {loggedIn: true,},});
}
这样,通过Cache Storage、IndexedDB和postMessage
,你可以在Service Worker中保存并管理用户数据和状态。
5.安全与隐私:
a.Service Worker有哪些安全限制?
基础回答:
Service Worker的主要安全限制包括:
-
只在HTTPS上运行:为了防止中间人攻击,Service Worker只能在HTTPS或者localhost上注册和运行。
-
同源限制:Service Worker只能拦截和处理与其相同源的请求,这是为了避免跨站请求伪造(CSRF)等安全问题。
-
Cookie和Header:Service Worker不能访问Cookie和HTTP Header,这样能减少跨站脚本攻击(XSS)的可能。
-
全局作用域隔离:Service Worker运行在与页面不同的全局作用域中,减少了它能够访问的DOM元素。
结合项目经验:
例如,在我之前的项目中,我们利用Service Worker来做缓存和离线访问,其中就涉及到了这些安全限制:
-
HTTPS限制:我们的所有服务都是部署在HTTPS上的,所以这个限制没有影响到我们。
-
同源限制:在Service Worker中,我们只处理了来自同一源的API请求和资料,没有涉及到跨域资源,因此遵循了这一限制。
-
Cookie和Header:由于不能访问Cookie和Header,我们使用了一些其他方式来处理身份验证和状态管理。
通过这样的回答方式,你不仅回答了面试官的问题,还通过实际的项目经验展示了你对Service Worker安全限制的理解和应用。这种答题方式通常更能得到面试官的认可。
对于HTTPS的要求,为什么Service Worker只在HTTPS下工作?
基础解释:
首先,解释 HTTPS 和 HTTP 的基本区别。强调 HTTPS 提供了额外的安全层,因为它使用 SSL/TLS 来加密所有传输的数据。然后,你可以指出 Service Worker 的工作原理,包括拦截网络请求和缓存资源等,都具有很高的安全风险。在非 HTTPS 的环境下,中间人攻击更容易发生,可能导致 Service Worker 的恶意注册和篡改。
结合项目经验:
如果你有与 Service Worker 和 HTTPS 相关的实际项目经验,这将是一个很好的切入点。例如,你可以说:
“在我之前的项目中,我们实现了一个用于缓存页面资源和提高加载速度的 PWA(Progressive Web App)。由于 Service Worker 具有拦截请求和缓存内容的能力,因此非常重要的一点是确保所有事务都是安全的。这就是为什么我们确保只在 HTTPS 下注册和使用 Service Worker。这不仅符合浏览器的要求,而且也确保了用户数据的完整性和隐私。”
这样的回答展示了你不仅理解这一要求的理论背景,而且还有实际操作经验。
通过这两点,你可以全面而具体地解答面试官的问题,同时展示你的专业知识和项目经验。
你如何确保Service Worker不会成为跨站脚本攻击(XSS)的目标?
在面试中,你可以通过以下几个步骤结合你的项目经验来回答这个问题:
1. 描述Service Worker的重要性
首先,你可以简要介绍Service Worker在你项目中的角色,比如用于缓存策略、离线访问、推送通知等。
例子:
“在我们的Uber打车PWA应用程序中,Service Worker主要用于优化TodoList的离线访问体验,通过缓存策略以及与IndexedDB结合,提供更快的加载速度和更好的用户体验。”
2. 识别潜在的安全风险
明确指出Service Worker由于其强大的能力也可能成为XSS攻击的目标。
例子:
“由于Service Worker可以拦截网络请求和操作缓存,如果被恶意脚本控制,它可以对网站的安全造成威胁。”
3. 实施的安全措施
描述在项目中你如何确保Service Worker不会被用于XSS攻击。
例子:
- “我们只从信任的源加载Service Worker脚本,并且使用了Subresource Integrity(SRI)来验证脚本的完整性。”
- “我们采用了内容安全策略(CSP)以限制脚本的来源和执行行为。”
- “在应用程序中,所有与Service Worker相关的操作都进行了严格的输入验证和输出编码,以防止恶意脚本注入。”
4. 持续监控和更新
提及你如何保持与安全漏洞相关的知识更新,并定期检查Service Worker的安全性。
例子:
“我们定期进行代码审查和安全扫描,以及紧跟Service Worker和Web安全的最新发展,确保我们的实践始终是最佳的。”
通过这种方式,你不仅回答了问题,还展示了你是如何在实际项目中应用这些最佳实践的,这将增加你的专业可信度。
6.问题与挑战:
- 描述一次你在使用Service Worker时遇到的问题以及你是如何解决的。
- 你认为Service Worker的哪些方面需要进一步改进?
7.其他:
- 在使用Service Worker时,哪些浏览器特性或API与之搭配使用可以获得更好的效果?
- 如何确保Service Worker与现有的服务器策略(例如ETag、Cache-Control等)协同工作?
ServiceWorker 让你的网页拥抱服务端的能力
相关文章:

Service Worker原理
Service Worker原理 1.基本概念与使用场景:a.什么是Service Worker?它的主要用途是什么?b.Service Worker和Web Worker有什么不同?c.预缓存和缓存的区别 2. 实现细节:a.描述Service Worker的生命周期。b.如何注册和注销一个Service Worker&am…...

MySQL集群高可用架构之MHA
MHA 一、MHA概述1.1 为什么要用MHA?1.2 什么是 MHA?1.3 MHA 的组成1.4 MHA 的特点1.5 故障切换备选主库的算法1.5 MHA工作原理 二、MySQL MHA高可用实例2.1 架构搭建部分1)所有节点服务器安装MySQL2)主从节点服务器添加域名映射3&…...

【算法专题突破】二分查找 - 704. 二分查找(16)
目录 1. 题目解析 2. 算法原理 3. 代码编写 写在最后: 1. 题目解析 题目链接:704. 二分查找 - 力扣(LeetCode) 题目非常简单,就是查找一个 target。 2. 算法原理 根据最基本的二分查找算法: 在一个…...

基于Docker_Nginx+LVS+Flask+MySQL的高可用Web集群
一.项目介绍 1.拓扑图 2.详细介绍 项目名称:基于Docker_NginxLVSFlaskMySQL的高可用Web集群 项目环境:centos7.9,docker24.0.5,mysql5.7.30,nginx1.25.2,mysqlrouter8.0.21,keepalived 1.3.5,…...

如何写一份出色的毕业设计任务书
title: 如何写一份出色的毕业设计任务书 date: 2023-09-20 毕业设计任务书是每个毕业生必须面对的关键文档。它不仅是你完成毕业设计的路线图,还是导师评估你工作的依据。因此,撰写一份清晰、详细且具体的任务书至关重要。本文将向你介绍如何编写一份出色…...

RedHat 服务器安装NGINX
参照官方文档:nginx: Linux packages 按顺序操作: 安装前提: sudo yum install yum-utils 设置yum仓库(执行命令的时候会自动新建文件): sudo vi /etc/yum.repos.d/nginx.repo 粘贴下面的内容保存退出…...

跨域问题解决方案(三种)
Same Origin Policy同源策略(SOP) 具有相同的Origin,也即是拥有相同的协议、主机地址以及端口。一旦这三项数据中有一项不同,那么该资源就将被认为是从不同的Origin得来的,进而不被允许访问。 Cross-origin resource…...

多轨音频编辑软件Multitrack Editor mac中文版主要功能
Multitrack Editor mac是一种音频编辑软件,它可以同时处理多个音轨。它通常用于录制、编辑和混合音乐、电影、电视和广播节目等多媒体项目。 Multitrack Editor的主要功能包括录音、编辑、混音和声音效果处理。使用该软件,用户可以同时录制和编辑多个音轨…...

工作中遇到的事务
文章目录 介绍原因分析:修改代码一波三折,再次出现问题 介绍 遇到了一个很有意思的问题。 在service层加了事务。 为了防止并发,在component层加了分布式锁。 先根据前端传入的id,在数据库中使用queryA()查到一个key然后对这个key进行加锁再…...

【论文写作】Latex 所有符号汇总参考
【论文写作】Latex 所有符号汇总参考 文章目录 【论文写作】Latex 所有符号汇总参考1. 希腊字母2. 数学构造3. 分割4. 累加累成等5. 标准函数名称6. 二进制符号(关系符号)7. 箭头8. 杂项符号(其他的符号)9. 数学模式重音、音节10.…...

pom.xml中解决“vulnerable dependency maven:org.yaml:snakeyaml:1.33“警告问题
问题 当我们引入依赖的时候,pom文件会有这样的提示,其大概的意思就是 maven:org.yaml:snakeyaml:1.30"表示通过Maven引入了一个潜在的安全漏洞依赖项"org.yaml:snakeyaml:1.30" 解决办法 其实我们就是要更改这个依赖的版本,…...

栈和队列-Java
目录 一、栈 1.1 概念 1.2 栈的使用 1.3 栈的模拟实现 1.4 栈的应用场景 1.5 概念区分 二、队列 2.1 概念 2.2 队列的使用 2.3 队列的模拟实现 2.4 循环队列 三、双端队列 四、面试题 一、栈 1.1 概念 栈:一种特殊的线性表,只允许在固定的一端进行插…...

ORA-07445: exception encountered: core dump [kdxlin()+4088]---惜分飞
abort方式关闭数据库,启动报错 Tue Sep 19 21:52:56 2023 NOTE: dependency between database orcl and diskgroup resource ora.DATA.dg is established Tue Sep 19 21:52:57 2023 Reconfiguration started (old inc 4, new inc 6) List of instances: 1 (myinst:…...

【C刷题】day3
一、选择题 1、已知函数的原型是: int fun(char b[10], int *a); ,设定义: char c[10];int d; ,正确的调用语句是( ) A: fun(c,&d); B: fun(c,d); C: fun(&c,&d); D: fun(&c,d); 【答案…...

go 线程限制数量 --chatGPT
问:runTask(names, limit), 遍历启动以names的子名称的工作线程 name测试打印,上限数量是limit, 要求打印所有names gpt: 你可以使用 Go 协程来实现 runTask 函数,该函数会遍历启动以 names 子名称的工作线程,并在达到上限数量 …...

【Linux网络编程】日志与守护进程
日志是网络服务器程序在后台以守护进程的形式运行时,处理情况的描述被打印到了日志文件里面,方便维护人员查看。 1.前台进程与后台进程 左边会话输入命令 sleep 10000 & 代表进程后台运行,右边会话输入命令 sleep 20000可以看到命令行解…...

多输入多输出 | MATLAB实现CNN-BiGRU卷积双向门控循环单元多输入多输出
多输入多输出 | MATLAB实现CNN-BiGRU卷积双向门控循环单元多输入多输出 目录 多输入多输出 | MATLAB实现CNN-BiGRU卷积双向门控循环单元多输入多输出预测效果基本介绍程序设计往期精彩参考资料 预测效果 基本介绍 MATLAB实现CNN-BiGRU卷积双向门控循环单元多输入多输出…...

Qt: 鼠标形状设置
设置全局鼠标形状 设置完毕后,整个APP的任何窗体,包括Dialog中的鼠标形状都会被修改为设定类型,某一个控件设定的鼠标形状将被替换。一般不建议使用 QCursor cursor;//创建鼠标对象 cursor.setShape(Qt::CursorShape::ClosedHandCursor);//…...

【Oracle】Oracle系列之七--表的创建与管理
文章目录 往期回顾前言1. 表的创建2. 表的修改3. 表中数据的增删改查(1)插入数据(2)删除数据(3)更新数据 4. 表的Merge5. 表的删除6. 表的重命名7. 表的索引(1)B树索引(2…...

C/C++运算符超详细讲解(系统性学习day5)
目录 前言 一、运算符的概念与分类 二、算术运算符 三、关系运算符 四、逻辑运算符 五、赋值运算符 六、运算符的优先级 总结 前言 本篇文章是对运算符的具体讲解。 一、运算符的概念与分类 概念: 运算符就是一种告诉编译器执行特定的数学或逻辑操作的符…...

Android 遍历界面所有的View
关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 ,擅长java后端、移动开发、商业变现、人工智能等,希望大家多多支持。 目录 一、导读二、概览三、实践四、 推荐阅读 一、导读 我们…...

建筑能源管理(1)——建筑能源管理的概念
1、什么是建筑能源管理 目前,主要有三种不同的类型能源管理: (1)节约型能源管理 又称“减少能耗型”能源管理。这种管理方式着眼于能耗数量上的减少,采取限制用能的措施。例如,在非人流高峰时段停开部分电梯、在室外气温特别高时…...

SpringSecurity
明文存储密码,前加{noop}...

C++ vector模拟实现
目录 一.默认成员函数 二.扩容相关函数 三.[]重载 四.修改函数 五.迭代器 继上次写完string之后,可以写一个vector练练手以及熟悉其底层。vector是一个顺序表,相比普通数组不同点在于顺序表的数据必须是连续存放的。 一.默认成员函数 string是只存放字符…...

BUUCTF:[GYCTF2020]FlaskApp
Flask的网站,这里的功能是Base64编码解码,并输出 并且是存在SSTI的 /hint 提示PIN码 既然提示PIN,那应该是开启了Debug模式的,解密栏那里随便输入点什么报错看看,直接报错了,并且该Flask开启了Debug模式&am…...

好玩的调度技术
好玩的调度技术 文章目录 好玩的调度技术前言一、乱金柝-空间剥离二、拖拽编辑三、全端兼容 前言 最近感觉自己抑郁了,生态技术实在太庞大太复杂,所以我决定先停一段时间,在停下写生态的这两天写了几个调度的小玩意换换脑子,很有…...

Android 自定义加解密播放音视频(m3u8独立加密)
文章目录 背景加密流程音视频解密音视频播放结语 背景 当涉及App内部视频的时候,我们不希望被别人以抓包的形式来爬取我们的视频大视频文件以文件方式整个加密的话需要完全下载后才能进行解密当前m3u8格式虽然支持加密,但是ts格式的小视频可以独立播放的…...

常见的文件格式
一、C:\fakepath\新建文本文档.txt [object String] 实现方式: <input onchange"test(this.value)" type"file"></input><script>function test(e){console.log(e,Object.prototype.toString.call(e))}</script> 二、…...

浏览器输入url后回车展开过程
当你在浏览器中输入一个URL并敲下回车后,浏览器会执行一系列步骤来访问并展示网页。下面是浏览器访问网页的一般流程: DNS解析:浏览器首先会提取URL中的主机名,然后向DNS服务器发送请求,将主机名解析为对应的IP地址。这…...

Docker 容器创建命令说明
使用命令如下: docker run -itd --name=cluster -v /home/ClusterApp/cluster:/home/ClusterApp/cluster --restart=always --privileged=true -p 12000:12000 python:3.8 命令说明: docker run 创建一个容器并运行 docker run --help 可以查看所有的参数: 命令中参数说明 -…...