HTML5 Web Worker之性能优化

news2024/11/17 13:43:23

描述

由于 JavaScript 是单线程的,当执行比较耗时的任务时,就会阻塞主线程并导致页面无法响应,这就是 Web Workers 发挥作用的地方。它允许在一个单独的线程(称为工作线程)中执行耗时的任务。这使得 JavaScript 代码可以在后台执行,而不会阻塞主线程并导致页面无响应。

Web Worker 是一个作为后台线程运行的脚本,具有自己的引擎实例和事件循环。它与主执行线程并行运行,并且不会阻塞事件循环。

主线程(或工作线程本身)可以启动任意数量的工作线程。生成 worker 脚本:

  1. 主线程(或另一个工作线程)向新工作线程发送一条消息,其中包含所有必要的数据。

  2. 工作线程中的事件处理程序执行并开始处理数据。

  3. 完成(或失败)时,工作线程将一条带有计算结果的消息发送回主线程。

  4. 主线程中的事件处理程序执行、解析传入结果并运行必要的操作(例如显示值)。

重要性

Web Workers 为开发人员提供了在 Web 上实现多线程的方式,这对于构建高性能的 Web 应用至关重要。通过将耗时的任务在后台独立于主线程中执行,Web Workers 提高了网页的整体响应性,并使用户体验更加流畅。以下是 Web Workers 在 Web 多线程中的重要性和好处:

通过资源利用率

通过允许耗时任务在后台执行,Web Workers 更有效地利用系统资源,实现更快速和高效的数据处理,并提高整体性能。这对于涉及大量数据处理或图像操作的 Web 应用尤为重要,因为 Web Workers 可以在不影响用户界面的情况下执行这些任务。

增加稳定性和可靠性

通过将耗时任务隔离到单独的 worker 线程中,Web Workers 帮助防止在主线程上执行大量代码时发生崩溃和错误。这使得开发人员更容易编写稳定可靠的 Web 应用,减少用户的烦恼和数据丢失的可能性。

增强安全性

Web Workers 在与主线程分离的隔离环境中运行,这有助于提高 Web 应用的安全性。此隔离防止恶意代码访问或修改主线程或其他 Web Workers 中的数据,降低数据泄露或其他安全漏洞的风险。

更好的资源利用率

Web Workers 可以通过将耗时计算放到后台,使主线程用于处理用户输入和其他任务来帮助提高资源利用率。这有助于提高系统的整体性能并减少崩溃或错误的可能性。此外,通过利用多个 CPU 核心,Web Workers 可以更有效地利用系统资源,实现更快速和高效的数据处理。

Web Workers 还能够实现更好的负载平衡和扩展应用。通过允许任务在多个 worker 线程之间并行执行,Web Workers 可以帮助将工作负荷均匀分配到多个核心或处理器上,实现更快速和高效的数据处理。这对于经历高流量或需求的应用尤为重要,因为 Web Workers 可以帮助确保应用可以处理增加的负载而不影响性能。

Web Workers 客户端使用

使用 JavaScript 创建 Web Worker 的步骤如下:

  1. 创建一个新的 JavaScript 文件,其中包含要在工作线程中运行的代码(耗时任务)。该文件不应包含对 DOM 的引用,因为在工作线程中无法访问 DOM。

  2. 在主 JavaScript 文件中,使用 Worker 构造函数创建一个新的worker对象。此构造函数接收一个参数,即在步骤 1 中创建的 JavaScript 文件的 URL。

const worker = new Worker('worker.js');
  1. worker对象添加事件侦听器以处理主线程和工作线程之间发送的消息。onmessage 用于处理从工作线程发送来的消息,postMessage 用于向工作线程发送消息。

worker.onmessage = function(event) {
  console.log('Worker: ' + event.data);
};

worker.postMessage('Hello, worker!');
  1. 在 Web Worker 的 JavaScript 文件中,使用self对象的onmessage属性添加一个事件监听器来处理从主线程发出的消息。可以使用event.data属性访问发送的消息数据。

self.onmessage = function(event) {
  console.log('Main: ' + event.data);
  self.postMessage('Hello, Main!');
};

接下来就运行应用并测试 Worker。可以在控制台看到以下信息,表示主线程和 Worker 线程之间发送和接收了消息。

Main:Hello worker!
Worker:Hello Main!

我们可以使用terminate()函数来终止一个工作线程,或者通过调用self上的close()函数使其自行终止。

// 从应用中终止一个工作线程
worker.terminate();
// 让一个工作线程自行终止
self.close();

可以使用importScripts()函数将库或文件导入到工作线程中,该函数可以接受多个文件。以下示例将script1.jsscript2.js加载到工作线程 worker.js 中:

importScripts('script1.js','script2');

可以使用 onerror函数来处理工作线程抛出的错误:

worker.onerror = function(err) {
    console.log("遇到错误")
}

Web Workers 服务端应用

服务器端 JavaScript 运行时也支持 Web Worker:

  • Node.js 在版本 10 中实现了类似 Web Worker 的功能,称为 Worker thread(工作进程)。

  • Deno 复制了 Web Worker API,因此语法与浏览器代码完全相同。它还提供了兼容模式,可以填充 Node.js API,以便可以使用该运行时的工作线程语法。

  • Bun 将支持浏览器和 Node.js 的 Web Worker API。

基本使用

要在 Node.js 中使用 Web Worker,主脚本必须定义一个 Worker 对象,其中包含相对于项目根目录的 Web Worker 脚本的名称。第二个参数定义了一个对象,其中包含一个workerData属性,该属性包含要发送的数据:

const worker = new Worker('./worker.js', {
  workerData: { a: 1, b: 2, c: 3 }
});

与浏览器中的 Web Worker 不同, 它在启动时无需运行worker.postMessage()。如果需要的话,可以调用该方法并稍后发送更多数据,它会触发parentPort.on('message')事件处理程序:

parentPort.on('message', e => {
  console.log(e);
});

一旦工作线程完成处理,它会使用以下方法将结果数据发送回主线程:

parentPort.postMessage(result);

这将在在主脚本中触发 message 事件,主线程接收到 worker 返回的结果:

worker.on('message', result => {
  console.log( result );
});

在发送完消息后,worker 就会终止。这也会触发一个exit事件,如果希望运行清理或其他函数,可以利用这个事件:

worker.on('exit', code => {
//...
});

除此之外,还支持其他事件处理:

  • messageerror:当 worker 收到无法反序列化的数据时触发。

  • online:当 worker 开始执行时触发。

  • error:当 worker 脚本中发生 JavaScript 错误时触发。

在服务端,一个单独的 Node.js 脚本文件可以同时包含主线程和工作线程的代码。脚本必须使用isMainThread检查自身是否在主线程上运行,然后将自身作为工作线程进行调用(可以在 ES 模块中使用import.meta.url作为文件引用,或者在 CommonJS 中使用__filename)。

import { Worker, isMainThread, workerData, parentPort } from "node:worker_threads";

if (isMainThread) {
  // 主线程
  const worker = new Worker(import.meta.url, {
    workerData: { a: 1, b: 2, c: 3 }
  });

  worker.on('message', msg => {});
  worker.on('exit', code => {});
}
else {
  // 工作线程
  const result = runSomeProcess( workerData );
  parentPort.postMessage(result);
}

这种方式更快,并且对于小型、自包含的单脚本项目来说是一个选择。如果是大型项目,将 worker 脚本文件分开会更容易维护。

数据通信

主线程和工作线程之间的通信涉及到了数据序列化。可以使用表示固定长度原始二进制数据的SharedArrayBuffer对象在线程之间共享数据。以下是一个示例,主线程定义了从 0 到 99 的 100 个数字元素,并将其发送给工作线程:

// main.js
import { Worker } from "node:worker_threads";

const
  buffer = new SharedArrayBuffer(100 * Int32Array.BYTES_PER_ELEMENT),
  value = new Int32Array(buffer);

value.forEach((v,i) => value[i] = i);

const worker = new Worker('./worker.js');

worker.postMessage({ value });

工作线程可以接收 value对象:

// worker.js
import { parentPort } from 'node:worker_threads';

parentPort.on('message', value => {
  value[0] = 100;
});

主线程或工作线程都可以更改值数组中的元素,数据将在两个线程之间保持一致。这可能会提高性能,但有一些缺点:

  • 只能共享整数数据。

  • 可能仍需要通知另一个线程更改。

  • 存在两个线程可能同时更新同一值并且失去同步的风险。

Node.js 子进程

在 Node.js 中,除了使用工作线程外,还可以使用子进程来实现类似的功能。子进程用于启动其他应用、传递数据并接收结果。它们与工作线程类似,但通常效率较低,进程开销较大。

子进程和工作线程的选择取决于具体的应用场景。如果只需要在 Node.js 中执行其他任务或命令,子进程是一种更好的选择。但如果需要在 Node.js 中进行复杂的计算或处理任务,Web Worker 可能更适合。

Web Workers 应用场景

Web Workers 在实际应用中有许多常见且有用的应用场景。

处理 CPU 密集任务

假设有一个应用需要执行大量的 CPU 密集型计算。如果在主线程中执行这些计算,用户界面可能会变得无响应,用户体验将受到影响。为了避免这种情况,可以使用 Web Worker 在后台执行这些计算。

在主线程中:

// 创建一个新的 Web Worker
const worker = new Worker('worker.js');

// 定义一个函数来处理来自Web Worker的消息
worker.onmessage = function(event) {
  const result = event.data;
  console.log(result);
};

// 向Web Worker发送一个消息,以启动计算
worker.postMessage({ num: 1000000 });

在 worker.js 中:

// 定义一个函数来执行计算
function compute(num) {
  let sum = 0;
  for (let i = 0; i < num; i++) {
    sum += i;
  }
  return sum;
}

// 定义一个函数来处理来自主线程的消息
onmessage = function(event) {
  const num = event.data.num;
  const result = compute(num);
  postMessage(result);
};

在这个例子中,创建了一个新的 Web Worker,并定义了一个函数来处理来自 Web Worker 的消息。然后,向 Web Worker 发送一条消息,并提供一个参数(num),指定要执行计算的迭代次数。Web Worker 接收到这条消息后,在后台执行计算。当计算完成后,Web Worker 向主线程发送一条包含结果的消息。主线程收到这个消息后,将结果记录到控制台中。

图片

在上面的例子中,向 Web Worker 的compute()函数传递了数字 1000000。这意味着compute函数将需要将从 0 到一百万的所有数字相加。这涉及大量的额外操作,可能需要很长时间才能完成,特别是如果代码在较慢的计算机上运行或在浏览器标签中同时处理其他任务。

通过将这个任务分配给 Web Worker,应用的主线程可以继续平稳运行,而不会被计算密集型的任务阻塞。这使得用户界面保持响应,并确保其他任务(如用户输入或动画)可以在没有延迟的情况下处理。

处理网络请求

假设有一个应用需要发起大量的网络请求。如果在主线程中执行这些请求,可能会导致用户界面无响应,用户体验差。为了避免这个问题,可以利用 Web Worker 在后台处理这些请求。通过这样做,主线程可以同时执行其他任务,而 Web Worker 负责处理网络请求,从而提高性能和改善用户体验。

在主线程中:

// 创建一个新的 Web Worker
const worker = new Worker('worker.js');

// 定义一个函数来处理来自Web Worker的消息
worker.onmessage = function(event) {
  const response = event.data;
  console.log(response);
};

// 向Web Worker发送一个消息,以启动计算
worker.postMessage({ urls: ['https://api.example.com/foo', 'https://api.example.com/bar'] });

在 worker.js 中:

// 定义一个函数来执行网络请求
function request(url) {
  return fetch(url).then(response => response.json());
}

// 定义一个函数来处理来自主线程的消息
onmessage = async function(event) {
  const urls = event.data.urls;
  const results = await Promise.all(urls.map(request));
  postMessage(results);
};

在这个例子中,创建一个新的 Web Worker 并定义一个函数来处理来自该 Worker 的消息。然后,向 Worker 发送一个包含一组 URL 请求的消息。Worker 接收到这个消息后,在后台使用 fetch API 执行请求。当所有请求完成后,Worker 向主线程发送包含结果的消息。主线程接收到这个消息后,将结果记录到控制台中。

并行处理

假设应用需要执行大量独立计算。 如果在主线程中依次执行这些计算,用户界面将变得无响应,用户体验将受到影响。 为了避免这种情况,可以实例化多个 Web Worker 来并行执行计算。

在主线程中:

// 创建三个新的 Web Worker
const worker1 = new Worker('worker.js');
const worker2 = new Worker('worker.js');
const worker3 = new Worker('worker.js');


// 定义三个处理来自 worker 的消息的函数
worker1.onmessage = handleWorkerMessage;
worker2.onmessage = handleWorkerMessage;
worker3.onmessage = handleWorkerMessage;

function handleWorkerMessage(event) {
  const result = event.data;
  console.log(result);
}

// 将任务分配给不同的 worker 对象,并发送消息启动计算
worker1.postMessage({ num: 1000000 });
worker2.postMessage({ num: 2000000 });
worker3.postMessage({ num: 3000000 });

在 worker.js 中:

// 定义一个函数来执行单个计算
function compute(num) {
  let sum = 0;
  for (let i = 0; i < num; i++) {
    sum += i;
}
  return sum;
}

// 定义一个函数来处理来自主线程的消息
onmessage = function(event) {
  const result = compute(event.data.num);
  postMessage(result);
};

在这个例子中,创建三个新的 Web Worker 并定义一个函数来处理来自该 Worker 的消息。然后,向三个 Worker 分别发送一个要计算的数字消息。Worker 接收到这个消息后执行计算。当计算完成后,Worker 向主线程发送包含结果的消息。主线程接收到这个消息后,将结果记录到控制台中。

Web Workers 使用限制

Web Worker 是一个提高 Web 应用性能和响应能力的强大工具,但它们也有一些限制和注意事项。

浏览器支持

目前所有主流浏览器、Node.js、Deno 和 Bun 都支持 Web Workers。

图片

 

对 DOM 的访问受到限制

Web Worker 在单独的线程中运行,无法直接访问主线程中的 DOM 或其他全局对象。这意味着不能直接从 Web Worker 中操作 DOM,也不能访问像windowdocument这样的全局对象。

为了解决这个限制,可以使用postMessage方法与主线程进行通信,间接地更新 DOM 或访问全局对象。例如,使用postMessage将数据发送到主线程,然后根据接收到的消息来更新 DOM 或全局对象。

另外,还有一些库可以帮助解决这个问题。例如,WorkerDOM[1] 库允许在 Web Worker 中运行 DOM,从而加快页面的渲染速度并提高性能。

现代桌面浏览器支持共享工作线程,即在不同窗口、iframes 或工作线程中可被多个脚本访问的单个脚本,它们通过独立的端口进行通信。但是,大多数移动浏览器不支持共享工作线程,所以对于大多数 Web 项目来说,它们并不实用。

通信开销大

Web Worker 使用postMessage方法与主线程进行通信,这可能会引入通信开销。通信开销指的是在两个或多个计算系统之间建立和维护通信所需的时间和资源量,比如在 Web 应用中,Web Worker 与主线程之间的通信。这可能导致消息处理延迟,潜在地减慢应用程序的速度。为了最小化这种开销,应该只在线程之间发送必要的数据,避免发送大量数据或频繁发送消息。

调试工具有限

与在主线程中调试代码相比,调试 Web Worker 可能更具挑战性,因为可用的调试工具较少。为了简化调试过程,可以使用控制台 API 在 Worker 线程中记录消息,并使用浏览器开发者工具检查线程之间发送的消息。

代码复杂度

使用 Web Worker 可能会增加代码的复杂性,因为需要管理线程之间的通信,并确保数据正确传递。这可能会使编写、调试和维护代码更加困难,因此应该仔细考虑是否有必要在应用中使用 Web Worker。

Web Workers 实践

上面提到了在使用 Web Workers 时,可能会出现的一些潜在问题。下面就来看看如何缓解这些问题。

worker.on('message', result => {
  console.log( result );
});

消息批处理

消息批处理涉及将多个消息组合成一个批处理消息,这比单独发送个别消息更有效。这种方法减少了主线程和 Web Worker 之间往返的数量,它有助于最小化通信开销并提高应用的整体性能。

为了实现消息批量处理,可以使用队列来累积消息,并在队列达到一定阈值或经过设定时间后将消息批量发送。下面来在 Web Worker 中简单实现消息的批处理:

// 创建一个消息队列累积消息
const messageQueue = [];

// 创建一个将消息添加到队列的函数
function addToQueue(message) {
  messageQueue.push(message);

  // 检查队列是否达到阈值大小
  if (messageQueue.length >= 10) {
    // 如果是,请将批处理消息发送到主线程
    postMessage(messageQueue);

    // 清除消息队列
    messageQueue.length = 0;
  }
}

// 将消息添加到队列中
addToQueue({type: 'log', message: 'Hello, world!'});

// 再添加另一条消息到队列中
addToQueue({type: 'error', message: 'An error occurred.'});

在这个例子中, 创建了一个消息队列,用于累积需要发送到主线程的消息。每当使用addToQueue函数将消息添加到队列时,检查队列是否已达到阈值大小(10)。如果是,则使用postMessage方法将批处理消息发送到主线程。然后,清除消息队列,以准备进行下一次批处理。

通过以这种方式批处理消息,可以减少主线程和 Web Worker 之间发送的消息总数,从而提高应用性能。

避免同步方法

同步方法是阻塞其他代码执行的 JavaScript 函数或操作。同步方法可以阻塞主线程,导致应变得无响应。为了避免这种情况,应尽量避免在 Web Worker 中使用同步方法。而应该使用setTimeout()setInterval()等异步方法来执行长时间运行的计算。

// 在Web Worker中
self.addEventListener('message', (event) => {
  if (event.data.action === 'start') {
    // 使用setTimeout来异步执行计算
    setTimeout(() => {
      const result = doSomeComputation(event.data.data);

      // 将结果发送回主线程
      self.postMessage({ action: 'result', data: result });
    }, 0);
  }
});

注意内存使用情况

Web Workers 有自己的内存空间,这个空间根据用户的设备和浏览器设置可能是有限的。为了避免内存问题,应该注意你的 Web Worker 代码使用的内存量,并避免不必要地创建大对象。例如:

// 在Web Worker中
self.addEventListener('message', (event) => {
  if (event.data.action === 'start') {
    // 使用for循环处理一个数据数组
    const data = event.data.data;
    const result = [];

    for (let i = 0; i < data.length; i++) {
      // 处理数组中的每个项,并将结果添加到结果数组中
      const itemResult = processItem(data[i]);
      result.push(itemResult);
    }

    // 将结果发送回主线程
    self.postMessage({ action: 'result', data: result });
  }
});

在这段代码中,Web Worker 处理一个数据数组,并使用postMessage方法将结果发送回主线程。然而,用于处理数据的 for 循环可能耗时较长。

导致这个问题的原因是代码一次性处理了整个数据数组,这意味着所有的数据都必须同时加载到内存中。如果数据集非常大,这可能导致 Web Worker 消耗大量的内存,甚至超过浏览器为 Web Worker 分配的内存限制。

为了缓解这个问题,可以考虑使用内置的 JavaScript 方法,如forEachreduce,它们可以逐项处理数据,避免一次性加载整个数组到内存中。

浏览器兼容性

大多数现代浏览器都支持 Web Worker,但某些较旧的浏览器可能不支持它们。 为了确保与各种浏览器的兼容性,应该在不同的浏览器和版本中测试 Web Worker 代码。 还可以使用功能检测来检查 Web Worker 是否受支持,然后再在代码中使用它们,如下所示:

if (typeof Worker !== 'undefined') {
  const worker = new Worker('worker.js');
} else {
  console.log('Web Workers are not supported in this browser.');
}

这段代码会检查当前浏览器是否支持 Web Workers,并在支持时创建一个新的 Web Worker。如果 Web Workers 不受支持,则该代码记录一条消息到控制台,表示该浏览器不支持 Web Workers。

总结

Web Workers 是现代 Web 开发的一个基本特性,它允许开发人员将 CPU 密集型任务放到单独的线程中执行,从而提高应用的性能和响应能力。然而,在处理 Web Workers 时需要记住一些重要的限制和注意事项,例如无法访问 DOM 和数据类型之间传递的限制等。为了避免这些潜在问题,可以采用上面提到的策略,如使用异步方法并注意卸载的任务的复杂性。在未来,使用 Web Workers 进行多线程似乎仍然是提高 Web 应用程序性能和响应能力的重要技术。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

openssl3.2 - exp - 选择最好的内建椭圆曲线

文章目录 openssl3.2 - exp - 选择最好的内建椭圆曲线概述笔记将 openssl ecparam -list_curves 实现迁移到自己的demo工程备注END openssl3.2 - exp - 选择最好的内建椭圆曲线 概述 在openssl中使用椭圆曲线, 只允许选择椭圆曲线的名字, 无法给定椭圆曲线的位数. 估计每种椭…

硬件工程师面试题梳理-百度硬件面试题

硬件工程师基本职责 在公司里面&#xff0c;硬件工程师的主要职责包括设计、开发和测试硬件系统&#xff0c;以满足产品需求和性能要求。他们负责确保硬件系统的可靠性、稳定性和可维护性&#xff0c;并与软件工程师和其他团队成员合作&#xff0c;以确保硬件和软件的协同工作…

22.网络游戏逆向分析与漏洞攻防-网络通信数据包分析工具-加载配置文件到分析工具界面

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果 内容参考于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;21.配置数据保存…

Django工具

一、分页器介绍 1.1、介绍 分页,就是当我们在页面中显示一些信息列表,内容过多,一个页面显示不完,需要分成多个页面进行显示时,使用的技术就是分页技术 在django项目中,一般是使用3种分页的技术: 自定义分页功能,所有的分页功能都是自己实现django的插件 django-pagin…

CSS常见用法 以及JS基础语法

CSS简介 首先我们要明白css对网页的页面效果就类似于化妆的效果,使得页面更好看 我们需要明白的就是CSS怎么使用即可 首先CSS的基本语法是<style></style>标签来修改 基本语法规范是选择器n条选择规范 例如 <style>p{color : red;} </style> 这里就是将…

ABAP接口-RFC连接(ABAP TO ABAP)

目录 ABAP接口-RFC连接&#xff08;ABAP TO ABAP&#xff09;创建ABAP连接RFC函数的调用 ABAP接口-RFC连接&#xff08;ABAP TO ABAP&#xff09; 创建ABAP连接 事务代码&#xff1a;SM59 点击创建&#xff0c;填写目标名称&#xff0c;选择连接类型&#xff1a; 填写主机名…

哈希表|242.有效的字母异位词

力扣题目链接 bool isAnagram(char* s, char* t) {int len_s strlen(s), len_t strlen(t);if(len_s ! len_t) {return false;}int table[26];memset(table, 0, sizeof(table));for(int i 0; i < len_s; i) {table[s[i] - a];}for(int i 0; i < len_t; i) {table[t[i…

Vue3中Vue Router的使用区别

在 Vue 3 中&#xff0c;useRouter 和 useRoute 是两个用于 Vue Router 的 Composition API 函数&#xff0c;它们的用途和返回的对象不同&#xff0c;接下来详细了解一下它们的区别以及如何正确使用它们。 useRouter useRouter 用于获取 router 实例&#xff0c;这个实例提供…

蓝桥杯每日一题(kmp)

//141 周期 求一个字符串的所有前缀的循环节出现的最大次数。也就是最小循环节 kmp算法求循环节&#xff1b; 将原串移动&#xff0c;移动后我们得知&#xff0c;四个黑色大括号完全相同。在下图所示的事例中&#xff0c;原串只有两个循环节&#xff0c;加一个红括号。k3加一…

线程的魔法:揭开现代操作系统并发执行的面纱

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

WebMagic框架

1.webmagic框架 webmagic框架是一个Java实现的爬虫框架&#xff0c;底层依然是HttpClient和jsoup 组件&#xff1a; downloader&#xff1a;下载器组件PageProcessor&#xff1a;页面解析组件&#xff08;必须自定义&#xff09;scheculer&#xff1a;访问队列组件pipeline&am…

跨境电商三大趋势

跨境电商有着不断发展的三大趋势&#xff1a; 个性化定制&#xff1a;随着消费者需求的不断变化和个性化定制的潮流&#xff0c;跨境电商平台开始提供更多的定制化服务。消费者可以根据自己的需求选择产品的款式、材料和设计&#xff0c;从而获得更加个性化的产品体验。 无界销…

JDBC的学习记录

JDBC就是使用java语言操作关系型数据库的一套API。 JDBC&#xff08;Java Database Connectivity&#xff09;是Java语言中用于连接和操作数据库的一种标准接口。它提供了一组方法和类&#xff0c;使Java程序能够与各种不同类型的关系型数据库进行交互。 JDBC的主要功能包括建…

【notepad++工具使用之】批量加逗号

背景 在使用sql语句in关键字查询时&#xff0c;我们需要把数据用逗号进行隔开&#xff0c;在数据量非常少的时候&#xff08;十几二十个这样&#xff09;&#xff0c;可以手动的去加逗号分隔符&#xff1b; 但是遇到1000个怎么弄呢&#xff1f; 强大的Notepad 批量处理数据时…

讲解Python3内置模块之json编码解码方法

简介 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式&#xff0c;它基于ECMAScript的一个子集。 JSON采用完全独立于语言的文本格式&#xff0c;这些特性使JSON成为理想的数据交换格式&#xff0c;易于人阅读和编写&#xff0c;同时也易于机器解析和生成&#…

学习笔记docker——容器通过网络通信

inspect-a&#xff1a;查看Linux中的网络细节。 docker inspect 容器id/容器名&#xff1a;查看容器的细节。 注意&#xff1a;上面有误&#xff0c;应该是接在docker0网桥上的容器只能通过容器的ip(eth0)之间进行通信&#xff0c;不能通过容器名称通信&#xff0c;要用容器名…

git搜索历史上曾经的文本内容

文章目录 问题在命令行搜索历史内容参考 问题 我们知道&#xff0c;github有文本搜索功能&#xff1a; 比如想搜哪些文件内容包括 aaa &#xff0c;在搜索框中输入 aaa &#xff1a; 但是&#xff0c;如果是历史上曾经有过的文本&#xff0c;这个办法貌似不行。 比如文件 tes…

Babel:现代JavaScript的桥梁

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

实时工业控制系统的创新整合:PLC4X与CnosDB的高效数据采集与存储

在当代工业自动化系统中&#xff0c;实时监测和数据分析变得至关重要。本文将介绍如何通过集成Apache PLC4X与CnosDB&#xff0c;实现对工业控制系统中的PLC设备进行高效数据采集和存储&#xff0c;为工程师们提供更强大的数据分析和监测工具。 PLC的定义 PLC是可编程逻辑控制…

C++_lambda表达式

目录 前言-lambda表达式的介绍&#xff1a; 1、lambda表达式的作用 2、lambda表达式的优势 2.1 用lambda构建lambda变量 3、lambda表达式的类型 4、捕捉列表说明 4.1 传值捕捉 4.2 mutable 4.3 传引用捕捉 4.4 混合捕捉 5、lambda的大小 结语 前言-lambda表达…