Chrome 浏览器插件从 Manifest V2 升级到 V3 版本所需要修改的点

一、Manifest V2 支持时间表

Chrome 浏览器官方已经给出确定的时间来弃用 V2 版本的插件了。

最早从 2024 年 6 月Chrome 127 开始,我们将开始停用 Chrome 的不稳定版本(开发者版、Canary 版和 Beta 版)中的 Manifest V2 扩展程序。受此变化影响的用户会在浏览器中看到 Manifest V2 扩展程序自动停用,并且无法再从 Chrome 应用商店安装 Manifest V2 扩展程序。此外,Manifest V2 扩展程序在 Chrome 应用商店中将不再拥有“精选”徽章(如果目前已有该徽章)。

如果企业如果使用 ExtensionManifestV2Availability 政策确保其组织中的 Manifest V2 扩展程序能持续正常运行,则其组织中还有一年的时间(即在 2025 年 6 月之前)迁移 Manifest V2 扩展程序。在此之前,已启用此政策的浏览器不会受到弃用安排的影响。

如果扩展程序发布商目前仍在发布 Manifest V2 扩展程序,我们强烈建议您在 2024 年 6 月之前完成向 Manifest V3 的迁移。

官方时间线

1、2022 年 6 月:Chrome 应用商店 - 不再有新的专用扩展程序

Chrome 应用商店不再接受公开范围设为“不公开”的新 Manifest V2 扩展程序。

2、2024 年 6 月:在稳定发布前弃用 Chrome MV2

3、2024 年 6 月 + 1-X 个月:弃用 Chrome MV2 并稳定发布

4、2025 年 6 月:Chrome MV2 弃用(企业版)

二、Manifest V3 迁移核对列表

1、Manifest.json 文件

1. 更新 manifest_version 版本号

manifest_version 字段的值从 2 更改为 3。

{
  "manifest_version": 3
}

2. 更新 permissionshost_permissions 字段

Manifest V3 中的主机权限是一个单独的字段;

不需要在 permissionsoptional_permissions 中指定这些权限;

有单独的字段 host_permissions 来表示。

2.1. V2 版本
{
  "permissions": [
    "tabs",
    "bookmarks",
    "https://www.blogger.com/",
  ],
  "optional_permissions": [
    "unlimitedStorage",
    "*://*/*",
  ]
}
2.2. V3 版本
{
  "permissions": [
    "tabs",
    "bookmarks"
  ],
  "optional_permissions": [
    "unlimitedStorage"
  ],
  "host_permissions": [
    "https://www.blogger.com/",
  ],
  "optional_host_permissions": [
    "*://*/*",
  ]
}

3. 更新 web_accessible_resources 字段

Manifest V3 会限制哪些网站和扩展程序可以访问扩展程序中的资源,以此来限制数据公开范围;

Manifest V2 中,默认情况下,指定资源可供所有网站访问;

在下面的 Manifest V3 示例中,这些资源仅可供匹配的网站使用,而只有某些图片可供所有网站使用。

3.1. V2 版本
{
  "web_accessible_resources": [
    "images/*",
    "style/extension.css",
    "script/extension.js"
  ],
}
3.2. V3 版本
{
    "web_accessible_resources": [
    {
      "resources": [
        "images/*"
      ],
      "matches": [
        "*://*/*"
      ]
    },
    {
      "resources": [
        "style/extension.css",
        "script/extension.js"
      ],
      "matches": [
        "https://example.com/*"
      ]
    }
  ],
}

2、迁移到 Service Worker

使用 service worker 替换 backgroundevent pages,以确保后台代码远离主线程,这样可以让扩展程序仅在需要时运行,从而节省资源。

1. BackgroundService Worker 之间的区别

1.1. Service Workerbackground 之间的差异
  • 在主线程以外运行,这意味着不会干扰扩展程序内容;
  • 具有特殊功能,例如拦截扩展程序来源上的提取事件,例如拦截工具栏弹出式窗口中的提取事件;
  • 可以通过客户端界面与其他上下文进行通信和交互。
1.2. 改动点
  • 由于它们无法访问 DOMwindow 接口,因此需要将此类调用移至其他 API 或移至屏幕外文档中;
  • 不应注册事件监听器来响应返回的 promise 或在事件回调内部;
  • 不向后兼容 XMLHttpRequest(),因此需要将接口的调用替换为 fetch()
  • 由于它们在不使用时终止,因此需要保留应用状态,而不是依赖于全局变量;
  • 终止 Service Worker 还可以在计时器完成之前结束计时器。需要将其替换为 alarms

2. 更新 manifest.json 中的 background 字段

Manifest V3 中,background 页面被 Service Worker 所取代:

  • manifest.json 中的 "background.scripts" 替换为 "background.service_worker"
  • "service_worker" 字段接受字符串,而不是字符串数组;
  • manifest.json 中移除 "background.persistent"
2.1. V2 版本
{
  "background": {
    "scripts": [
      "backgroundContextMenus.js",
      "backgroundOauth.js"
    ],
    "persistent": false
  },
}
2.2. V3 版本
{
  "background": {
    "service_worker": "service_worker.js",
    "type": "module"
  }
}

"service_worker" 字段接受单个字符串。只有使用 ES module (使用 import 关键字)时,才需要 "type" 字段。其值将始终为 "module"

3. 将 DOMwindow 调用移至屏幕外文档

某些扩展程序需要访问 DOMwindow 对象,无需打开新的窗口或标签页。Offscreen API 支持这类使用情形,因为这类 API 可以打开和关闭与扩展程序打包在一起的未显示文档,而不会干扰用户体验。除了消息传递之外,屏幕外文档不会与其他扩展程序上下文共享 API,而是起到完整网页的作用,供扩展程序进行互动。

如需使用 Offscreen API,请通过 Service Worker 创建屏幕外文档。

chrome.offscreen.createDocument({
  url: chrome.runtime.getURL('offscreen.html'),
  reasons: ['CLIPBOARD'],
  justification: 'testing the offscreen API',
});

4. 将 localStorage 转换为其他类型

Web 平台的 Storage 接口(可从 window.localStorage 访问)无法在 Service Worker 中使用。

请使用 chrome.storage.local

5. 同步注册监听器

异步注册监听器(例如在 promisecallback 中)注册并不一定能在 Manifest V3 中有效。

5.1. V2 版本
chrome.storage.local.get(["badgeText"], ({ badgeText }) => {
  chrome.browserAction.setBadgeText({ text: badgeText });
  chrome.browserAction.onClicked.addListener(handleActionClick);
});

Manifest V3 中,系统会在分派事件时重新初始化 Service Worker。这意味着当事件触发时,系统不会注册监听器(因为它们是异步添加的),系统还会错过事件。

5.2. V3 版本

改为将事件监听器注册移至脚本的顶层。这样可以确保 Chrome 能够立即找到并调用操作的点击处理程序,即使扩展程序尚未执行其启动逻辑也是如此。

chrome.action.onClicked.addListener(handleActionClick);

chrome.storage.local.get(["badgeText"], ({ badgeText }) => {
  chrome.action.setBadgeText({ text: badgeText });
});

6. 将 XMLHttpRequest() 替换为全局 fetch()

无法从 Service Worker、扩展程序或其他方法调用 XMLHttpRequest()。将后台脚本对 XMLHttpRequest() 的调用替换为对fetch() 的调用。

const response = await fetch('https://www.example.com/greeting.json');
console.log(response.statusText);

7. 保存状态

Service Worker 是临时的,这意味着它们可能会在用户的浏览器会话期间反复启动、运行和终止。这也意味着,自之前的上下文销毁后,数据并非立即在全局变量中可用。如需解决此问题,请使用存储 API

对于 Manifest V3 来说,要将全局变量替换为对 Storage API 的调用。

chrome.runtime.onMessage.addListener(({ type, name }) => {
  if (type === "set-name") {
    chrome.storage.local.set({ name });
  }
});

chrome.action.onClicked.addListener(async (tab) => {
  const { name } = await chrome.storage.local.get(["name"]);
  chrome.tabs.sendMessage(tab.id, { name });
});

8. 把计时器/定时器替换为 alarms

setTimeout()setInterval()Web 开发中比较常见。不过,在 Service Worker 中可能会失败,因为每当 Service Worker 终止时,计时器就会取消。

8.1. V2 版本
// 3 minutes in milliseconds
const TIMEOUT = 3 * 60 * 1000;
setTimeout(() => {
  chrome.action.setIcon({
    path: getRandomIconPath(),
  });
}, TIMEOUT);
8.2. V3 版本
async function startAlarm(name, duration) {
  await chrome.alarms.create(name, { delayInMinutes: 3 });
}

chrome.alarms.onAlarm.addListener(() => {
  chrome.action.setIcon({
    path: getRandomIconPath(),
  });
});

3、API 调用

1. 将 tab.executeScript() 替换为 scripting.executeScript()

Manifest V3 中,executeScript()tabs API 移至 scripting API。这就需要在实际的代码更改的基础上更改 manifest.json 文件中的权限。

对于 executeScript() 方法:

  • "scripting" 权限;
  • host permissions 权限或 "activeTab" 权限。

scripting.executeScript() 方法与其使用 tabs.executeScript() 的方式类似。但还是有一些区别。

  • 旧方法只能接受一个文件,而新方法可以接受一组文件;
  • 还将传递 ScriptInjection 对象,而不是 InjectDetails
1.1. V2 版本
async function getCurrentTab() {/* ... */}
let tab = await getCurrentTab();

chrome.tabs.executeScript(
  tab.id,
  {
    file: 'content-script.js'
  }
);

代码在 background 脚本中

1.2. V3 版本
async function getCurrentTab()
let tab = await getCurrentTab();

chrome.scripting.executeScript({
  target: {tabId: tab.id},
  files: ['content-script.js']
});

代码在 service worker 中

2. 将 tab.insertCSS()tab.removeCSS() 替换为 scripting.insertCSS()scripting.removeCSS()

Manifest V3 中,insertCSS()removeCSS() 已从 tabs API 移至 scriptingAPI。不仅需要更改代码,还需要对 manifest.json 文件中的权限进行更改:

  • "scripting" 权限;
  • host permissions 权限或 "activeTab" 权限。

scripting API 上的函数与 tabs 上的函数类似。但还是有一些区别。

  • 调用这些方法时,需要传递 CSSInjection 对象,而不是 InjectDetails
  • tabId 作为 CSSInjection.target 的成员(而不是方法参数)进行传递。
2.1. V2 版本
chrome.tabs.insertCSS(tabId, injectDetails, () => {
  // callback code
});

代码在 background 脚本中

2.2. V3 版本
const insertPromise = await chrome.scripting.insertCSS({
  files: ["style.css"],
  target: { tabId: tab.id }
});
// Remaining code. 

代码在 service worker

3. 将 Browser ActionsPage Actions 替换为 Actions

Manifest V2 中,Browser ActionsPage Actions 是两个单独的概念。虽然一开始他们扮演的角色不同,但随着时间的推移,它们之间的差异越来越小。在 Manifest V3 中,这些概念已整合到 Action API 中。这需要更改 manifest.json 和扩展代码。

Manifest V3 中的操作与浏览器操作最为相似;不过,action API 不像 pageAction 那样提供 hide()show()。如果仍需要页面操作,可以使用声明性内容模拟这些操作,也可以使用标签页 ID 调用 enable()disable()

3.1. 将 "browser_action""page_action" 替换为 "action"

manifest.json 中,将 "browser_action""page_action" 字段替换为 "action" 字段。如需了解 "action" 字段的相关信息,请参阅参考文档。

3.1.1. V2 版本
{
  "page_action": { ... },
  "browser_action": {
    "default_popup": "popup.html"
   }

}
3.1.2. V3 版本
{
  "action": {
    "default_popup": "popup.html"
  }
}
3.2. 将 BrowserAction APIpageAction API 替换为 Action API

如果 Manifest V2 使用 browserActionpageAction API,现在应使用 action API

3.2.1. V2 版本
chrome.browserAction.onClicked.addListener(tab => { ... });
chrome.pageAction.onClicked.addListener(tab => { ... });
3.2.2. V3 版本
chrome.action.onClicked.addListener(tab => { ... });

4. 将 callback 替换为 promise

Manifest V3 中,许多扩展程序 API 方法都会返回 promise

为了实现向后兼容性,许多方法在添加 promise 支持后会继续支持回调。需要注意的是,不能在同一函数调用中同时使用这两者。如果传递回调,则函数不会返回 promise;如果希望返回 promise,则也不要传递回调。某些 API 功能(例如事件监听器)将继续需要回调。

如需从回调转换为 promise,请移除回调并处理返回的 promise

4.1. Callback
chrome.permissions.request(newPerms, (granted) => {
  if (granted) {
    console.log('granted');
  } else {
    console.log('not granted');
  }
});
4.2. Promise
const newPerms = { permissions: ['topSites'] };
chrome.permissions.request(newPerms)
.then((granted) => {
  if (granted) {
    console.log('granted');
  } else {
    console.log('not granted');
  }
});

5. 替换需要 Manifest V2 background 上下文的函数

其他扩展程序上下文只能使用消息传递与扩展程序 Service Worker 交互。因此,需要替换需要后台上下文的调用,具体而言:

  • chrome.runtime.getBackgroundPage()
  • chrome.extension.getBackgroundPage()
  • chrome.extension.getExtensionTabs()

扩展程序脚本应使用消息传递在 Service Worker 和扩展程序的其他部分之间进行通信。目前,这需要使用 sendMessage(),并在扩展 Service Worker 中实现 chrome.runtime.onMessage。从长远来看,应计划将这些调用替换为 postMessage()Service Worker 的消息事件处理程序。

6. 替换不受支持的 API

需要在 Manifest V3 中更改下列方法和属性。

Manifest V2 方法或属性替换为 Manifest V3 方法或属性
chrome.extension.connect()chrome.runtime.connect()
chrome.extension.connectNative()chrome.runtime.connectNative()
chrome.extension.getExtensionTabs()chrome.extension.getViews()
chrome.extension.getURL() chrome.runtime.getURL()
chrome.extension.lastError 如果方法返回 promise,请使用 promise.catch()
chrome.extension.onConnect chrome.runtime.onConnect
chrome.extension.onConnectExternalchrome.runtime.onConnectExternal
chrome.extension.onMessage chrome.runtime.onMessage
chrome.extension.onRequestchrome.runtime.onRequest
chrome.extension.onRequestExternal chrome.runtime.onMessageExternal
chrome.extension.sendMessage() chrome.runtime.sendMessage()
chrome.extension.sendNativeMessage()chrome.runtime.sendNativeMessage()
chrome.extension.sendRequest() chrome.runtime.sendMessage()
chrome.runtime.onSuspendbackground 脚本)在扩展 Service Worker 中不受支持。请改用 beforeunload 文档事件。
chrome.tabs.getAllInWindow() chrome.tabs.query()
chrome.tabs.getSelected()chrome.tabs.query()
chrome.tabs.onActiveChangedchrome.tabs.onActivated
chrome.tabs.onHighlightChanged chrome.tabs.onHighlighted
chrome.tabs.onSelectionChanged chrome.tabs.onActivated
chrome.tabs.sendRequest()chrome.runtime.sendMessage()
chrome.tabs.Tab.selectedchrome.tabs.Tab.highlighted

4、替换屏蔽 Web 请求监听器

Manifest V3 更改了扩展程序处理网络请求修改的方式。扩展程序会指定规则来描述在满足一组给定条件时要执行的操作,而不是拦截网络请求并在运行时使用 chrome.webRequest 更改请求。

Web Request API 和声明式网络请求 API 有很大的区别。需要根据用例重新编写代码,而不是将一个函数调用替换为另一个函数调用。

1. 更新 permissions

manifest.json 中的 "permissions" 字段进行以下更改。

  • 如果不再需要观察网络请求,请移除 "webRequest" 权限;
  • 将匹配模式从 "permissions" 移至 "host_permissions"

需要根据使用场景添加其他权限。这些权限通过其支持的用例进行描述。

2. 创建声明性网络请求规则

如需创建声明性 net 请求规则,需要向 manifest.json 添加 "declarative_net_request" 对象。"declarative_net_request" 代码块包含指向规则文件的 "rule_resource" 对象数组。规则文件包含一组对象,用于指定操作以及调用这些操作的条件。

3. 常见使用场景

3.1. 屏蔽单个网址

Manifest V2 中的一个常见用例是在后台脚本中使用 onBeforeRequest 事件来屏蔽网络请求。

3.1.1. Background 脚本改为 V3 规则文件
3.1.1.1. 规则文件
  1. rule.json
[
  {
    "id": 1,
    "priority": 1,
    "action": { "type": "block" },
    "condition": {
      "urlFilter": "||example.com",
      "resourceTypes": ["main_frame"]
    }
  }
]
  1. 示例

image.png

  1. Manifest.json 文件引入
{
  "name": "URL Blocker",
  "version": "0.1",
  "manifest_version": 3,
  "description": "Uses the chrome.declarativeNetRequest API to block requests.",
  "background": {
    "service_worker": "service_worker.js"
  },
  "declarative_net_request": {
    "rule_resources": [
      {
        "id": "ruleset_1",
        "enabled": true,
        "path": "rules_1.json"
      }
    ]
  },
  "permissions": ["declarativeNetRequest", "declarativeNetRequestFeedback"]
}
3.1.1.2. V2 版本
chrome.webRequest.onBeforeRequest.addListener((e) => {
    return { cancel: true };
}, { urls: ["https://www.example.com/*"] }, ["blocking"]);
3.1.1.3. V3 版本

对于 Manifest V3,请使用 "block" 操作类型创建新的 declarativeNetRequest 规则。请注意示例规则中的 "condition" 对象。其 "urlFilter" 取代了传递给 webRequest 监听器的 urls 选项。"resourceTypes" 数组指定要屏蔽的资源的类别。

[
  {
    "id" : 1,
    "priority": 1,
    "action" : { "type" : "block" },
    "condition" : {
      "urlFilter" : "||example.com",
      "resourceTypes" : ["main_frame"]
    }
  }
]
3.1.2. 需要更新该扩展程序的权限。

manifest.json 中,将 "webRequestBlocking" 权限替换为 "declarativeNetRequest" 权限。请注意,由于屏蔽内容不需要主机权限,因此该网址已从 "permissions" 字段中移除。

3.1.2.1. V2 版本
  "permissions": [
    "webRequestBlocking",
    "https://*.example.com/*"
  ]
3.1.2.2. V3 版本
"permissions": [
  "declarativeNetRequest",
]
3.2. 重定向多个网址

Manifest V2 中的另一个常见用例是使用 BeforeRequest 事件重定向网络请求。

3.2.1. Background 脚本改为 V3 规则文件
3.2.1.1. V2 版本
chrome.webRequest.onBeforeRequest.addListener((e) => {
    console.log(e);
    return { redirectUrl: "https://developer.chrome.com/docs/extensions/mv3/intro/" };
  }, { 
    urls: [
      "https://developer.chrome.com/docs/extensions/mv2/"
    ]
  }, 
  ["blocking"]
);
3.2.1.2. V3 版本

对于 Manifest V3,请使用 "redirect" 操作类型。与之前一样,"urlFilter" 会替换传递给 webRequest 监听器的 url 选项。请注意,在此示例中,规则文件的 "action" 对象包含一个 "redirect" 字段,其中包含要返回的网址,而不是要过滤的网址。

[
  {
    "id" : 1,
    "priority": 1,
    "action": {
      "type": "redirect",
      "redirect": { "url": "https://developer.chrome.com/docs/extensions/mv3/intro/" }
    },
    "condition": {
      "urlFilter": "https://developer.chrome.com/docs/extensions/mv2/",
      "resourceTypes": ["main_frame"]
    }
  }
3.2.2. 需要更改扩展程序的权限

"webRequestBlocking" 权限替换为 "declarativeNetRequest" 权限。系统再次将这些网址从 manifest.json 移到了规则文件中。请注意,除了主机权限之外,重定向还需要 "declarativeNetRequestWithHostAccess" 权限。

3.2.2.1. V2 版本
  "permissions": [
    "webRequestBlocking",
    "https://developer.chrome.com/docs/extensions/*",
    "https://developer.chrome.com/docs/extensions/reference"
  ]
3.2.2.2. V3 版本
  "permissions": [
    "declarativeNetRequestWithHostAccess"
  ],
  "host_permissions": [
    "https://developer.chrome.com/*"
  ]
3.3. 屏蔽 Cookie

Manifest V2 中,要屏蔽 Cookie,需要先拦截网络请求标头,然后再发送这些标头并移除特定的 Cookie

3.3.1. Background 脚本改为 V3 规则文件
3.3.1.1. V2 版本
chrome.webRequest.onBeforeSendHeaders.addListener(
  function(details) {
    removeHeader(details.requestHeaders, 'cookie');
    return {requestHeaders: details.requestHeaders};
  },
  // filters
  {urls: ['https://*/*', 'http://*/*']},
  // extraInfoSpec
  ['blocking', 'requestHeaders', 'extraHeaders']);
3.3.1.2. V3 版本

Manifest V3 也通过规则文件中的规则实现这一点。这次的操作类型为 "modifyHeaders"。该文件接受 "requestHeaders" 对象数组,用于指定要修改的标头以及如何修改这些标头。请注意,"condition" 对象仅包含 "resourceTypes" 数组。它支持的值与前面的示例相同。

[
  {
    "id": 1,
    "priority": 1,
    "action": {
      "type": "modifyHeaders",
      "requestHeaders": [
        { "header": "cookie", "operation": "remove" }
      ]
    },
    "condition": {
      "urlFilter": "|*?no-cookies=1",
      "resourceTypes": ["main_frame"]
    }
  }
]
3.3.2. 需要更新该扩展程序的权限。

"webRequestBlocking" 权限替换为 "declarativeNetRequest" 权限。

3.3.2.1. V2 版本
  "permissions": [
    "webRequestBlocking",
    "https://developer.chrome.com/docs/extensions/*",
    "https://developer.chrome.com/docs/extensions/reference"
  ]
3.3.2.2. V3 版本
  "permissions": [
    "declarativeNetRequestWithHostAccess"
  ],
  "host_permissions": [
    "https://developer.chrome.com/*"
  ]

引用

  • 【mv2-deprecation-timeline】
  • 【transition-to-mv3】
  • 【Update the manifest】
  • 【Migrate to a service worker】
  • 【Update your code】
  • 【Replace blocking web request listeners】

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MySQL入门:DCL数据控制语言(管理用户,权限控制),MySQL函数(字符串,数值,日期,流程)

目录 1.DCL(数据控制语言)1.管理用户2.权限控制 2.函数1.字符串函数2.数值函数3.日期函数4.流程函数 1.DCL(数据控制语言) DCL英文全称是Data ControlLanguage(数据控制语言),用来管理数据库用户、控制数据库的访问权限…

vivado 使用项目摘要、配置项目设置、仿真设置

使用项目摘要 Vivado IDE包括一个交互式项目摘要,可根据设计动态更新命令被运行,并且随着设计在设计流程中的进展。项目摘要包括概览选项卡和用户可配置的仪表板,如下图所示。有关信息,请参阅《Vivado Design Suite用户指南&…

轻松上手Linux文件操作:五种方法教你创建文件

轻松上手Linux文件操作:五种方法教你创建文件 一、引言二、使用touch命令创建文件三、使用文本编辑器创建文件四、使用echo命令创建文件五、使用cat命令创建文件六、使用重定向符号创建文件七、总结 一、引言 本文介绍五种在Linux系统中创建文件的方法,…

PaaS服务的零代码开发平台——JNPF

目前市场上低代码平台鱼龙混杂,真正能满足企业复杂业务(ERP、MES等)的平台不多,这里推荐一款好用、靠谱、性价比较高的低代码平台:JNPF开发平台。 JNPF开发平台是一款PaaS服务为核心的零代码开发平台,集成了…

Go 如何处理死锁以提供哪些工具来检测或防死锁?

并发是 Go 的核心特性,它使程序能够同时处理多个任务。它是现代编程的一个强大组件,如果使用正确,可以产生高效、高性能的应用程序。然而,并发性也带来了顺序编程中不存在的某些类型错误的可能性,其中最臭名昭著的是死…

双指针问题——求只包含两个元素的最长连续子序列(子数组)

一,题目描述 你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 fruits 表示,其中 fruits[i] 是第 i 棵树上的水果 种类 。 你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必…

Istio安装和基础原理

1、Istio简介 Istio 是一个开源服务网格,它透明地分层到现有的分布式应用程序上。 Istio 强大的特性提供了一种统一和更有效的方式来保护、连接和监视服务。 Istio 是实现负载平衡、服务到服务身份验证和监视的路径——只需要很少或不需要更改服务代码。它强大的控…

LeetCode刷题.14(不用算法解决1557. 可以到达所有点的最少点数目)

给你一个 有向无环图 , n 个节点编号为 0 到 n-1 ,以及一个边数组 edges ,其中 edges[i] [fromi, toi] 表示一条从点 fromi 到点 toi 的有向边。 找到最小的点集使得从这些点出发能到达图中所有点。题目保证解存在且唯一。 你可以以任意顺…

Python跑pytorch程序抢占公共GPU自动运行脚本

问题描述 当我们有一个服务器,服务器上面有4-5个GPU,那么我们需要时刻看哪个GPU空着,当发现服务器空闲了,我们就可以跑自己的深度学习了。 然而,人盯着总是费时费力的,所以可以让Python看到哪个GPU空闲就…

Windows使用(版本8.11)ElasticSearch、elasticsearch-head、kibana

下载安装引用这篇文章 目录 1、ES基本知识核心术语核心概念倒排索引ES字典树ES怎么保证读写一致 2、Window启动ES步骤elasticsearch-8.11.3elasticsearch-head-masterkibana-8.11.3 3、Kibana 调用ES API示例 1、ES基本知识 核心术语 ● 索引:index (相…

centos 7.6 忘记root密码 怎么重置root密码

centos 7.6 忘记root密码 怎么重置root密码 1、 问题描述2、解决方法 1、 问题描述 centos 7.6 忘记root密码,登录不了root用户 2、解决方法 启动系统进入grub界面,按e进入编辑模式,找到含有quiet的这行。在这行最后 添加 rw init/bin/ba…

基础数据结构之堆栈

堆栈的定义、入栈、出栈、查询栈顶 #include <stdio.h> #include <stdlib.h>typedef int DataType;// 定义栈节点结构体 struct StackNode;struct StackNode {DataType data; // 节点数据struct StackNode* next; // 指向下一个节点的指针 };// 定…

Python基础知识:整理12 JSON数据格式的转换

首先导入python中的内置包json import json 1 准备一个列表&#xff0c;列表内每个元素都是字典&#xff0c;将其转换为JSON 使用json.dumps()方法 data [{"name": "John", "age": 30}, {"name": "Jane", "age":…

NAND系统性能提升常见方案

随着NAND的发展&#xff0c;针对NAND系统性能提升&#xff0c;业内目前主要的做法有以下几种方案&#xff1a; 1.提升总线频率和优化AC时序&#xff1a; 提高NAND闪存接口的工作频率可以显著加快数据传输速度。通过不断改进工艺和技术&#xff0c;缩短了信号稳定时间、降低了延…

逆向分析爬取网页动态

本例子以爬取人民邮电出版社网页新书的信息为例 由于页面是动态的&#xff0c;信息会不停地更新&#xff0c;所以不同时间的爬取结果会不同。

Selenium+Remote WebDriver+python脚本访问示例

一、环境要求&#xff1a; 1、selenium-server安装&#xff0c;下载地址&#xff1a;Release Selenium 4.16 SeleniumHQ/selenium GitHub 2、python3及pycharm 二、启动selenium-server 下载selenium-server之后&#xff0c;解压到D:\selenium-server目录&#xff0c;然后…

error: undefined reference to ‘cv::imread(std::__ndk1::basic_string<char

使用android studio编译项目时&#xff0c;由于用到了 cv::imread&#xff08;&#xff09;函数&#xff0c;编译时却报错找不到该函数的定义。 cv::imread一般是在highgui.hpp中定义&#xff0c;因此我加上了该头文件&#xff1a; #include “opencv2/highgui/highgui.hpp” 但…

设计模式之六大设计原则

学习的最大理由是想摆脱平庸&#xff0c;早一天就多一份人生的精彩&#xff1b;迟一天就多一天平庸的困扰。各位小伙伴&#xff0c;如果您&#xff1a; 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持&#xff0c;想组团高效学习… 想写博客但无从下手&#xff0c;急需…

Hive 数据同步

一、需求 同步集团的数据到断直连环境。 二、思路 三、同步数据&#xff08;方案&#xff09; 1、环境&#xff1a;断直连模拟环境 2、操作机器&#xff1a;ETL 机器 XX.14.36.216 3、工作路径&#xff1a;cd /usr/local/fqlhadoop/hadoop/bin 4、执行命令&#xff1a; 命令…

GIS真的是天坑专业吗?

是&#xff0c;也不是。 首先说是&#xff0c;GIS到底坑在哪&#xff1f; 1、专业定位不清晰&#xff0c;具有很强的误导性 听过很多学生抱怨&#xff0c;关于GIS专业&#xff0c;大家觉得最坑的地方&#xff0c;在于一开始在选专业的时候&#xff0c;以为这个专业跟计算机专…