<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="/rss.xsl" type="text/xsl"?>
<rss version="2.0">
  <channel>
    <title>IT瘾工具推荐</title>
    <link>https://itindex.net/categories/工具</link>
    <description>IT社区推荐资讯 - ITIndex.net</description>
    <language>zh</language>
    <copyright>https://itindex.net/</copyright>
    <generator>https://itindex.net/</generator>
    <docs>http://backend.userland.com/rss</docs>
    <image>
      <url>https://itindex.net/images/logo.gif</url>
      <title>IT社区推荐资讯 - ITIndex.net</title>
      <link>https://itindex.net/categories/工具</link>
    </image>
    <item>
      <title>亚马逊因 AI 编码工具引发多起故障</title>
      <link>https://itindex.net/detail/63176-%E4%BA%9A%E9%A9%AC%E9%80%8A-ai-%E7%BC%96%E7%A0%81</link>
      <description>&lt;div&gt;亚马逊因 AI 编码工具引发多起故障，紧急召开工程师大会&lt;/div&gt; &lt;div&gt;  &lt;br /&gt;&lt;/div&gt; &lt;div&gt;亚马逊电商部门本周二召集大批工程师开会，对近期一连串系统故障进行&amp;quot;深度复盘&amp;quot;——其中多起事故与 AI 编码工具直接相关。&lt;/div&gt; &lt;div&gt;  &lt;br /&gt;&lt;/div&gt; &lt;div&gt;据英国《金融时报》看到的内部备忘录，亚马逊近几个月出现了&amp;quot;事故趋势&amp;quot;，特征包括&amp;quot;高爆炸半径&amp;quot;（即波及面广）以及&amp;quot;生成式 AI 辅助的代码变更&amp;quot;。备忘录明确将&amp;quot;尚未建立完善最佳实践和安全防护的新型 GenAI 用法&amp;quot;列为事故的诱因之一。&lt;/div&gt; &lt;div&gt;  &lt;br /&gt;&lt;/div&gt; &lt;div&gt;具体来说，亚马逊网站和购物 App 本月曾宕机近 6 小时，用户无法完成交易、查看账户和价格。另外，AWS 旗下的 Kiro AI 编码工具在去年 12 月造成了一次长达 13 小时的服务中断——起因是工程师让 AI 工具执行某些变更，结果 AI 自行决定&amp;quot;删除并重建整个环境&amp;quot;。&lt;/div&gt; &lt;div&gt;  &lt;br /&gt;&lt;/div&gt; &lt;div&gt;作为应对，亚马逊已要求初级和中级工程师在提交任何 AI 辅助的代码变更前，必须获得资深工程师的签字批准。&lt;/div&gt; &lt;div&gt;  &lt;br /&gt;&lt;/div&gt; &lt;div&gt;值得注意的是，这些故障发生的背景是亚马逊近年来的多轮裁员——今年 1 月刚裁掉 1.6 万个企业岗位。此前已有多名工程师向《金融时报》反映，裁员导致每天需要紧急处理的高优先级事故（内部称&amp;quot;Sev2&amp;quot;）数量明显增加，但亚马逊否认裁员与故障频发有关。&lt;/div&gt; &lt;div&gt;  &lt;br /&gt;&lt;/div&gt; &lt;div&gt;这可能是整个行业的一个预警信号：当企业大规模推广 AI 编码工具、同时又在削减人手时，代码质量和系统稳定性的风险正在累积。&lt;/div&gt; &lt;div&gt;  &lt;br /&gt;&lt;/div&gt; &lt;div&gt;来源：www.ft.com/content/7cab4ec7-4712-4137-b602-119a44f771de&lt;/div&gt;
     
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/63176-%E4%BA%9A%E9%A9%AC%E9%80%8A-ai-%E7%BC%96%E7%A0%81</guid>
      <pubDate>Wed, 11 Mar 2026 08:03:54 CST</pubDate>
    </item>
    <item>
      <title>NotebookLM：我目前最常用、也最愿意推荐的 AI 学习与内容组织工具</title>
      <link>https://itindex.net/detail/63089-notebooklm-%E7%9B%AE%E5%89%8D-ai</link>
      <description>&lt;blockquote&gt;
  &lt;p&gt;NotebookLM 是我迄今用过最贴合知识工作者需求的 AI 工具，它真正帮我把庞杂信息结构化，极大提升了学习和内容创作效率。&lt;/p&gt;&lt;/blockquote&gt;
 &lt;p&gt;作为一个长期学习主义者、读技术规范、研究开源项目的人，我一直在寻找一种工具，能在我面对海量资料时替我“抄近道”、减少机械性阅读、帮我快速建立全局理解。
  &lt;a href="https://notebooklm.google.com" rel="noopener" target="_blank"&gt;
NotebookLM
&lt;/a&gt; 是过去一年里我用下来体验最顺滑、也最稳定可靠的一个。&lt;/p&gt;
 &lt;p&gt;它不是传统意义上的“聊天式 AI 工具”，更像是一个能把你的资料吃进去、组织出来、再以各种结构化方式呈现给你的   &lt;strong&gt;AI 原生学习与内容组织系统&lt;/strong&gt;。越用越觉得，它对我学习新技术、理解陌生领域、整理大项目文档、构建教学材料的帮助，是其他通用大语言模型（LLM, Large Language Model）给不了的。&lt;/p&gt;
 &lt;h2&gt;NotebookLM 给我带来的核心价值&lt;/h2&gt;
 &lt;p&gt;NotebookLM 在实际使用中为我带来了多方面的提升，尤其是在学习新技术、整理文档和内容创作方面表现突出。&lt;/p&gt;
 &lt;h2&gt;快速理解陌生技术：把庞杂资料丢进去，它帮我生成“可学的版本”&lt;/h2&gt;
 &lt;p&gt;我最常用、也是最离不开的场景，就是  &lt;strong&gt;学习一个我完全不熟悉的技术或开发框架&lt;/strong&gt;。面对几十页甚至几百页的文档，我通常的做法是：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;把官方文档、README、设计文档、架构草图全部加入一个 Notebook&lt;/li&gt;
  &lt;li&gt;让 NotebookLM 帮我生成：
   &lt;ul&gt;
    &lt;li&gt;学习指南&lt;/li&gt;
    &lt;li&gt;简报&lt;/li&gt;
    &lt;li&gt;关键知识点&lt;/li&gt;
    &lt;li&gt;FAQ&lt;/li&gt;
    &lt;li&gt;Quiz&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;最终得到一个结构清晰的“学习入口”，而不是一场资料洪水。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;下面这张流程图展示了 NotebookLM 如何将复杂文档压缩为可学习的结构：&lt;/p&gt;

 &lt;img alt="&amp;#22270; 1: NotebookLM &amp;#25991;&amp;#26723;&amp;#32467;&amp;#26500;&amp;#21270;&amp;#27969;&amp;#31243;" height="4004" src="https://jimmysong.io/blog/notebooklm-learning-and-knowledge-organization/588d50fb52b65ad460d25d7fcd8052e8.svg" width="2400"&gt;&lt;/img&gt;
图 1: NotebookLM 文档结构化流程

 &lt;p&gt;最终我获得的是一个“整理好的知识体系”，而不是一堆等我啃的 PDF。&lt;/p&gt;
 &lt;h2&gt;生成 MindMap：大量文档瞬间变成结构化知识图谱&lt;/h2&gt;
 &lt;p&gt;我很依赖 MindMap 来构建“知识的骨架”。NotebookLM 的 MindMap 最大的优势有：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;自动识别主题间的关联&lt;/li&gt;
  &lt;li&gt;可以交互式展开或折叠节点&lt;/li&gt;
  &lt;li&gt;支持多来源文档综合生成&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;虽然目前只能导出 PNG，但逻辑结构本身已经是非常好的“知识压缩”。&lt;/p&gt;
 &lt;p&gt;下表对比了不同工具的自动生成能力和可视化效果：&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;th&gt;工具&lt;/th&gt;
   &lt;th&gt;自动生成能力&lt;/th&gt;
   &lt;th&gt;多文档整合&lt;/th&gt;
   &lt;th&gt;可视化质量&lt;/th&gt;
   &lt;th&gt;导出格式&lt;/th&gt;
&lt;/tr&gt;


  &lt;tr&gt;
   &lt;td&gt;NotebookLM&lt;/td&gt;
   &lt;td&gt;强&lt;/td&gt;
   &lt;td&gt;强&lt;/td&gt;
   &lt;td&gt;好&lt;/td&gt;
   &lt;td&gt;仅 PNG（暂不支持 SVG）&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;常见 LLM 工具&lt;/td&gt;
   &lt;td&gt;较弱&lt;/td&gt;
   &lt;td&gt;较弱&lt;/td&gt;
   &lt;td&gt;弱&lt;/td&gt;
   &lt;td&gt;视工具而定&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;思维导图软件（手工）&lt;/td&gt;
   &lt;td&gt;无&lt;/td&gt;
   &lt;td&gt;无&lt;/td&gt;
   &lt;td&gt;强&lt;/td&gt;
   &lt;td&gt;全支持&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;

表 1: 主流工具 MindMap 能力对比

 &lt;p&gt;NotebookLM 最大的优势是  &lt;strong&gt;自动性&lt;/strong&gt;。&lt;/p&gt;
 &lt;h2&gt;生成教学大纲、培训稿、图书结构：真正节约我大量时间&lt;/h2&gt;
 &lt;p&gt;NotebookLM 不只是“总结”，它能按我给的提示词帮我生成  &lt;strong&gt;正式的教学结构&lt;/strong&gt;。只要把项目文档、API 说明、架构设计、案例、视频、博客全都丢进去，让它按提示词生成：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;教学大纲&lt;/li&gt;
  &lt;li&gt;项目培训手册&lt;/li&gt;
  &lt;li&gt;课程结构&lt;/li&gt;
  &lt;li&gt;图书章节架构&lt;/li&gt;
  &lt;li&gt;幻灯片文本&lt;/li&gt;
  &lt;li&gt;培训案例说明&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;对于需要写内容、做培训、做演讲的大部分人而言，这个功能非常省心。&lt;/p&gt;
 &lt;p&gt;下面是我真实在用的典型提示词示例：&lt;/p&gt;
 &lt;div&gt;  &lt;pre&gt;   &lt;code&gt;根据提供的内容摘录，编写一份详细的培训手册，系统地阐述通过提供内容中所涉及的核心原则。手册应采用专业和指导性的语气，将复杂的概念分解为可行的步骤和课程。确保内容完全基于源材料，涵盖从所提供内容涉及的所有方面。

培训手册应包括以下内容：
1. 培训目标和预期成果
2. 培训内容和结构
3. 培训方法和工具
4. 培训评估和反馈
5. 培训总结和后续行动
6. 培训案例和实例
7. 培训资源和参考文献
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;p&gt;实际效果往往出奇地好。&lt;/p&gt;
 &lt;h2&gt;多格式输入能力：这是我见过最稳的&lt;/h2&gt;
 &lt;p&gt;NotebookLM 支持直接 ingest 各种资料类型，解析能力非常稳定。下表是我的实际体验总结：&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;th&gt;输入类型&lt;/th&gt;
   &lt;th&gt;我的实际使用体验&lt;/th&gt;
&lt;/tr&gt;


  &lt;tr&gt;
   &lt;td&gt;PDF&lt;/td&gt;
   &lt;td&gt;最稳，解析结构清晰&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Google Docs&lt;/td&gt;
   &lt;td&gt;更新即同步，非常顺滑&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Word / PPT&lt;/td&gt;
   &lt;td&gt;可正常识别&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;YouTube 视频&lt;/td&gt;
   &lt;td&gt;自动总结 + 提取关键内容，很好用&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;网站 URL&lt;/td&gt;
   &lt;td&gt;视网站结构，成功率高&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;纯文本&lt;/td&gt;
   &lt;td&gt;没问题&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;图片&lt;/td&gt;
   &lt;td&gt;部分成功，但足够应对截图内容&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;

表 2: NotebookLM 多格式输入体验

 &lt;p&gt;相比之下，其他工具经常出现格式解析问题、乱码、丢内容、跳段落的问题。NotebookLM 在“多格式 ingest”这一点上体验特别稳定。&lt;/p&gt;
 &lt;h2&gt;我目前最常用的 NotebookLM 工作流&lt;/h2&gt;
 &lt;p&gt;下面这张流程图展示了我每天实际使用 NotebookLM 的工作流：&lt;/p&gt;

 &lt;img alt="&amp;#22270; 2: NotebookLM &amp;#26085;&amp;#24120;&amp;#24037;&amp;#20316;&amp;#27969;" height="977" src="https://jimmysong.io/blog/notebooklm-learning-and-knowledge-organization/c07a9c742a038f6d6919d10907e42566.svg" width="2400"&gt;&lt;/img&gt;
图 2: NotebookLM 日常工作流

 &lt;p&gt;其本质就是：让 AI 先帮我抓全局 → 再帮我深入 → 再帮我输出内容。&lt;/p&gt;
 &lt;h2&gt;我遇到的小遗憾与建议&lt;/h2&gt;
 &lt;p&gt;NotebookLM 已经很好用，但我仍有一些强烈期待的改进方向：&lt;/p&gt;
 &lt;h3&gt;MindMap 的导出格式应该支持 SVG 或基于文本（Markmap）&lt;/h3&gt;
 &lt;p&gt;目前只能 PNG，放大容易糊。下表是我对未来功能的期待：&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;th&gt;期待功能&lt;/th&gt;
   &lt;th&gt;用途&lt;/th&gt;
&lt;/tr&gt;


  &lt;tr&gt;
   &lt;td&gt;SVG 导出&lt;/td&gt;
   &lt;td&gt;用于写书、做幻灯片、可放大不失真&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Markmap 输出&lt;/td&gt;
   &lt;td&gt;对写 Markdown 的开发者最友好&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;原始 JSON&lt;/td&gt;
   &lt;td&gt;允许自行做二次渲染&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;

表 3: MindMap 导出格式期待

 &lt;p&gt;我非常期待 NotebookLM 支持
  &lt;a href="https://markmap.js.org" rel="noopener" target="_blank"&gt;
Markmap 格式
&lt;/a&gt;导出，这对习惯用 Markdown 写博客和文档的用户来说极为友好。&lt;/p&gt;
 &lt;p&gt;最近 Google 还推出了类似
  &lt;a href="https://deepwiki.com" rel="noopener" target="_blank"&gt;
DeepWiki
&lt;/a&gt; 的
  &lt;a href="https://codewiki.google" rel="noopener" target="_blank"&gt;
CodeWiki
&lt;/a&gt;，可为 GitHub 项目自动生成带图片的 Wiki，但目前也未支持 Mermaid 或 Markmap。&lt;/p&gt;
 &lt;h3&gt;对话记录应该支持长期保存&lt;/h3&gt;
 &lt;p&gt;现在的体验是：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;聊天不会持续保存&lt;/li&gt;
  &lt;li&gt;只有手动“加入笔记”才能留存结果&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;这导致一些知识背景容易丢失，期待未来推出“Notebook 对话历史”功能。&lt;/p&gt;
 &lt;h3&gt;幻灯片生产能力如果能支持模板，会更适合作为创作者工具&lt;/h3&gt;
 &lt;p&gt;目前 Video Overview 的视觉风格虽然多，但无法：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;上传自己的 PPT 模板&lt;/li&gt;
  &lt;li&gt;套用企业/个人品牌模版&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;如果未来能开放 PPT 模板能力，NotebookLM 会直接成为内容创作者的“视频生成中枢”。&lt;/p&gt;
 &lt;h3&gt;Deep Research 早日上线并全面开放&lt;/h3&gt;
 &lt;p&gt;我特别期待这个功能，因为它可能会让 NotebookLM 从“知识整理工具”升级为“研究级工具”。期待它能做到：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;稳定地抓取更多公开网页&lt;/li&gt;
  &lt;li&gt;保证引用质量&lt;/li&gt;
  &lt;li&gt;能和 Notebook 原有资料结合&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;这是我个人非常关注的大升级。&lt;/p&gt;
 &lt;h3&gt;移动端希望尽快增强，而不是只提供播放内容&lt;/h3&gt;
 &lt;p&gt;当前移动端体验极简，只能：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;听音频&lt;/li&gt;
  &lt;li&gt;查看 Notebook Guide 的摘要&lt;/li&gt;
  &lt;li&gt;简单的问答&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;期待移动端早日支持：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;编辑 Notebook&lt;/li&gt;
  &lt;li&gt;深度对话&lt;/li&gt;
  &lt;li&gt;MindMap 交互&lt;/li&gt;
  &lt;li&gt;内容输出能力（生成文档、大纲等）&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;总结&lt;/h2&gt;
 &lt;p&gt;NotebookLM 是我目前真正意义上“每天都在用”的 AI 工具之一，因为它做到了一件关键的事情：&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;把信息组织好，把知识结构化，让我不用从零开始面对庞杂文档。&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;无论是：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;学习新技术&lt;/li&gt;
  &lt;li&gt;阅读长文档&lt;/li&gt;
  &lt;li&gt;做课程&lt;/li&gt;
  &lt;li&gt;做培训&lt;/li&gt;
  &lt;li&gt;写书&lt;/li&gt;
  &lt;li&gt;做演讲稿&lt;/li&gt;
  &lt;li&gt;做内容总结&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;它都能在最前期帮我节省大量时间，把注意力集中在“理解”和“创作”本身。&lt;/p&gt;
 &lt;p&gt;我会继续把 NotebookLM 作为我的重要工具之一，也会在未来继续观察它的 Deep Research、模板系统与移动端的进展。&lt;/p&gt;
 &lt;p&gt;这是一款真正贴近“知识工作者”需求的工具，也值得被更多人认识。&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/63089-notebooklm-%E7%9B%AE%E5%89%8D-ai</guid>
      <pubDate>Mon, 17 Nov 2025 16:44:45 CST</pubDate>
    </item>
    <item>
      <title>网络安全专家爱用的逆向工具 Top9</title>
      <link>https://itindex.net/detail/62960-%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8-%E4%B8%93%E5%AE%B6-%E5%B7%A5%E5%85%B7</link>
      <description>&lt;div&gt;    &lt;p&gt;逆向工程是指解构应用程序的过程，不论使用何种编程语言开发，目的是获得其源代码或其中的任何部分。逆向工程的代码有助于发现任何程序中的安全风险，也能用于解密任何恶意应用以进行干扰。      &lt;img alt="" src="https://image.3001.net/images/20241217/1734406966_6760f3360af9160622067.png!small"&gt;&lt;/img&gt;&lt;/p&gt;    &lt;p&gt;在寻找破解敏感数据或加密密钥的过程中，黑客们通常选择逆向工程作为一种选项，以找出整个系统中隐藏漏洞的所在之处。这导致了敏感数据的完全暴露，包括被硬编码到应用程序中的 API 密钥、 URL 和API 机密信息，开发人员用于测试的开发服务器 URL，非标准端口号，以及硬编码到应用程序文件及其子目录中的多个私钥等。&lt;/p&gt;    &lt;p&gt;逆向工程涉及一系列步骤，包括数据编译、记录元素和功能、评估数据、记录控制流、提取流结构、审查提取的设计、生成逆向工程文档。&lt;/p&gt;    &lt;p&gt;从实时跟踪运行的应用程序，解析二进制代码到汇编代码，管理和编辑二进制文件或嵌入式资源在 exe 文件中，逆向工具有各种各样的类型，根据其应用可以分为以下几类：&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;反汇编器&lt;/li&gt;      &lt;li&gt;调试器&lt;/li&gt;      &lt;li&gt;数据包跟踪和分析工具&lt;/li&gt;      &lt;li&gt;脚本工具&lt;/li&gt;      &lt;li&gt;文件分析工具&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;以下分享&lt;/p&gt;    &lt;h2&gt;1.十六进制编辑器&lt;/h2&gt;    &lt;p&gt;十六进制编辑器是一组用于微软 Windows 的十六进制开发工具，结合了高级的二进制编辑和字处理器的简洁易用性和多功能性。它主要用于操纵构成计算机文件的基本二进制数据。&lt;/p&gt;    &lt;p&gt;此外，十六进制编辑器还支持查找、替换、比较、计算校验和、添加智能标签、颜色映射，并在一个扇区或文件中生成字符分布。十六进制编辑器还支持拖放功能，并可与所有的 Windows 操作系统集成，无论其迭代版本如何。&lt;/p&gt;    &lt;p&gt;根据其不同的功能和应用，有各种不同类型的十六进制编辑器，有些允许它们以可视化方式显示文件的内部结构。因此，您可能需要在最常用的工作空间中进行快速简单的十六进制编辑。&lt;/p&gt;    &lt;p&gt;数据检视器非常适合解释、查看和编辑十进制和二进制值。算术、逻辑、 ASCII 过程和位操作可用于帮助操作数据集。&lt;/p&gt;    &lt;p&gt;集成结构查看器使您能够直观而充分地编辑和查看数据。结构查看器验证信息结构、对各种网络的引用，以及许多原子数据类型：char 、byte 、ubyte 、word 、uword 、long 、ulong 、longlong 、float 、double 、OLE 日期/时间、 DOSTIME 、DOSDATE 、FILETIME 和time_t 。&lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;优点：&lt;/strong&gt;&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;进行任何十六进制计算时非常方便&lt;/li&gt;      &lt;li&gt;提供多种选项&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;      &lt;strong&gt;缺点：&lt;/strong&gt;&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;用户友好度不太高&lt;/li&gt;      &lt;li&gt;有时更新问题&lt;/li&gt;&lt;/ul&gt;    &lt;h2&gt;2.OllyDbg&lt;/h2&gt;    &lt;p&gt;OllyDbg 是一个针对 Microsoft Windows 的32 位汇编调试器。任何无法获得源代码的情况下，二进制代码的计算认知使得它在许多情况下都非常适用。此外，OllyDbg 是共享软件应用程序,可以下载使用。&lt;/p&gt;    &lt;p&gt;OllyDbg 的一些关键特性如下：&lt;/p&gt;    &lt;blockquote&gt;      &lt;p&gt;1.保存补丁以在会话之间返回到可执行文件并进行修补升级&lt;/p&gt;      &lt;p&gt;2.查找对象和库模式&lt;/p&gt;      &lt;p&gt;3.代码分析——跟踪记录、查找过程切换、 API 调用、表和循环常量和字符串&lt;/p&gt;      &lt;p&gt;4.DNow 、MMX 和为 Athlon 等SSE 数据类型和扩展提供指令&lt;/p&gt;      &lt;p&gt;5.识别高级配置，如对事件的请求&lt;/p&gt;      &lt;p&gt;6.用于执行的跟踪系统，日志已知可用于调和冲突&lt;/p&gt;      &lt;p&gt;7.查找错误命令和掩盖关键字&lt;/p&gt;      &lt;p&gt;8.检查和修改内存，设置断点并在不可见的情况下暂停应用程序&lt;/p&gt;      &lt;p&gt;9.在会话之间输入标记，将它们还原到可执行文件并修复更新&lt;/p&gt;&lt;/blockquote&gt;    &lt;p&gt;      &lt;strong&gt;优点：&lt;/strong&gt;&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;共享软件，免费使用&lt;/li&gt;      &lt;li&gt;功能强大的动态调试器&lt;/li&gt;      &lt;li&gt;相对于 IDA 来说更容易操作&lt;/li&gt;      &lt;li&gt;允许直接加载和调试 DLL &lt;/li&gt;      &lt;li&gt;有脚本和插件可用&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;      &lt;strong&gt;缺点：&lt;/strong&gt;&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;仅限于 Microsoft® Windows®&lt;/li&gt;      &lt;li&gt;只适用于 x86（或 32 位）软件&lt;/li&gt;      &lt;li&gt;不是静态调试器&lt;/li&gt;&lt;/ul&gt;    &lt;h2&gt;3.APKTool&lt;/h2&gt;    &lt;p&gt;Apktool 是另一种开源选择，主要用于 Android 逆向工程，可以将资源解码为几乎其原始形式。可以进行修改，并将其转换回二进制 APK/JAR 文件。&lt;/p&gt;    &lt;p&gt;此外，Apktool 还允许逐步调试 smali 代码，并且由于项目文件的结构以及对一些重复性操作的自动化，使得处理应用程序变得更加容易。使用该程序需要 Java 7 。&lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;优点：&lt;/strong&gt;&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;在逆向 Android 应用文件方面高效&lt;/li&gt;      &lt;li&gt;可在网上免费使用&lt;/li&gt;      &lt;li&gt;社区支持良好&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;      &lt;strong&gt;缺点：&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;不如 JEB 反编译工具普遍&lt;/p&gt;    &lt;h2&gt;4.WireShark&lt;/h2&gt;    &lt;p&gt;Wireshark 是一个知名的网络和网络领域的工具。它是免费和开源的，是一个 Web 调试器，可以拦截和修改 HTTP 请求，并且可以记录 HTTPS 请求。它用于数据包分析和网络故障排除。&lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;优点：&lt;/strong&gt;&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;免费且开源的 Web 调试器&lt;/li&gt;      &lt;li&gt;支持跨平台&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;      &lt;strong&gt;缺点：&lt;/strong&gt;&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;对于初学者来说，可能会感到压力山大&lt;/li&gt;&lt;/ul&gt;    &lt;h2&gt;5.Scylla&lt;/h2&gt;    &lt;p&gt;Scylla 不是一个独立的工具，而更倾向于用于重构 Windows 的x86 和x64 文件的工具。它还具有全 Unicode 支持，并且与 Windows 7 、8 和10 完全兼容。&lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;优点：&lt;/strong&gt;&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;一个开源的产品&lt;/li&gt;      &lt;li&gt;支持 x64 和x86 &lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;      &lt;strong&gt;缺点：&lt;/strong&gt;&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;缺乏更新&lt;/li&gt;      &lt;li&gt;有时会有错误&lt;/li&gt;&lt;/ul&gt;    &lt;h2&gt;6.Dex2jar&lt;/h2&gt;    &lt;p&gt;Dex2jar 是一个 API，用于扫描 Dalvik Executable（.dex/.odex）格式。它与 Android 和Java .class 文件兼容。&lt;/p&gt;    &lt;p&gt;Dex2jar 包括以下几个组件：&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;Dex-reader 用于扫描 Dalvik Executable（.dex/.odex）格式。它具有类似 ASM 的轻量级 API &lt;/li&gt;      &lt;li&gt;Dex-translator 用于执行转换工作。它读取 dex 指令以 dex-or 格式，经过一些优化后，转换为 ASM 格式&lt;/li&gt;      &lt;li&gt;Dex-用于它使用 Dex-translator 表示 dex 指令&lt;/li&gt;      &lt;li&gt;Dex-tools 用于处理.class 文件&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;示例：修改应用程序、解混淆一个.jar 文件。&lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;优点：&lt;/strong&gt;&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;可以读取 Dalvik Executable 格式&lt;/li&gt;      &lt;li&gt;轻量级 API &lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;      &lt;strong&gt;缺点：&lt;/strong&gt;&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;只与 Android 和Java .class 文件兼容&lt;/li&gt;&lt;/ul&gt;    &lt;h2&gt;7.CCF&lt;/h2&gt;    &lt;p&gt;CCF 是一个免费的便携式可执行编辑器，支持.NET 文件结构。 CCF 支持 32 位和 64 位PE 文件。 CCF 由NTCore 开发，还可用于解压缩 UPX 打包器。&lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;优点：&lt;/strong&gt;&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;免费的 PE 编辑器&lt;/li&gt;      &lt;li&gt;也支持.NET 文件&lt;/li&gt;      &lt;li&gt;支持 PE 32 位和 64 位&lt;/li&gt;      &lt;li&gt;包含 PE 重建器&lt;/li&gt;      &lt;li&gt;可用于解压缩 UPX &lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;      &lt;strong&gt;缺点：&lt;/strong&gt;&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;免费版本自 2012 年以来未更新&lt;/li&gt;&lt;/ul&gt;    &lt;h2&gt;8.Oracle VM VirtualBox&lt;/h2&gt;    &lt;p&gt;Oracle VM VirtualBox 是一个开源的虚拟化解决方案，在 Windows 、Mac 、Linux 等不同平台上皆可使用，用于在安全环境中分析恶意软件。&lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;优点：&lt;/strong&gt;&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;免费且开源&lt;/li&gt;      &lt;li&gt;活跃的开发社区&lt;/li&gt;      &lt;li&gt;支持虚拟机操作系统&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;      &lt;strong&gt;缺点：&lt;/strong&gt;&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;与 VMware 相比，功能略差&lt;/li&gt;&lt;/ul&gt;    &lt;h2&gt;9.BinaryNinja&lt;/h2&gt;    &lt;p&gt;目前没有反编译器，但计划在将来的“高级”版本中加入。 Binary Ninja 由Vector 35 开发，以其易用性而自豪，使得自动化比其他解决方案更容易理解。&lt;/p&gt;    &lt;p&gt;尽管易于使用，但该软件在反向工程本地主机中仍然存在一些问题。&lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;优点：&lt;/strong&gt;&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;简单易用&lt;/li&gt;      &lt;li&gt;包含反汇编器&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;      &lt;strong&gt;缺点：&lt;/strong&gt;&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;不是调试器或反编译器&lt;/li&gt;      &lt;li&gt;有时无法加载用户界面&lt;/li&gt;      &lt;li&gt;免费版本有限&lt;/li&gt;&lt;/ul&gt;    &lt;blockquote&gt;      &lt;p&gt;参考来源：        &lt;a href="https://www.secureblink.com/blogs/top-9-reverse-engineering-hacking-tools-for-cyber-security-experts"&gt;https://www.secureblink.com/blogs/top-9-reverse-engineering-hacking-tools-for-cyber-security-experts&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;/div&gt;
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/62960-%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8-%E4%B8%93%E5%AE%B6-%E5%B7%A5%E5%85%B7</guid>
      <pubDate>Tue, 17 Dec 2024 15:13:47 CST</pubDate>
    </item>
    <item>
      <title>开源实时数据同步工具NiFi</title>
      <link>https://itindex.net/detail/62957-%E5%BC%80%E6%BA%90-%E5%AE%9E%E6%97%B6-%E6%95%B0%E6%8D%AE</link>
      <description>&lt;h2&gt;Apache NiFi简介&lt;/h2&gt;
 &lt;p&gt;Apache NiFi 是一个强大的数据流管理和自动化工具，旨在简化数据的采集、传输、处理和分发。它特别适合于构建和管理复杂的数据流管道，支持从各种数据源到不同目标系统的数据传输。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="245" src="https://www.biaodianfu.com/wp-content/uploads/2024/11/NiFi.png" width="525"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;Apache NiFi主要功能&lt;/h3&gt;
 &lt;p&gt;Apache NiFi 是一个用于自动化数据流的强大工具，具有广泛的功能集，旨在支持从各种数据源到不同目标的复杂数据流管道。以下是 Apache NiFi 的主要功能：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;数据采集与传输&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;支持从多种数据源采集数据，包括文件系统、数据库、HTTP 服务、消息队列（如 Kafka）、传感器设备等。&lt;/li&gt;
    &lt;li&gt;支持将数据传输到多种目标系统，如 HDFS、数据库、云存储服务、REST API 等。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;数据流可视化&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;提供直观的 Web 用户界面，用户可以通过拖拽和配置处理器来设计和管理数据流。&lt;/li&gt;
    &lt;li&gt;实时显示数据流的状态和性能指标，便于监控和调试。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;数据处理与转换&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;提供丰富的内置处理器，支持数据的解析、转换、清洗、聚合和格式化等操作。&lt;/li&gt;
    &lt;li&gt;支持复杂的数据处理逻辑，如条件路由、数据分片、合并和拆分。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;动态路由与优先级控制&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;支持根据数据内容或属性动态路由数据到不同的处理器或目标。&lt;/li&gt;
    &lt;li&gt;允许为数据流设置优先级，以控制数据处理的顺序和速度。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;实时流处理&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;支持实时数据流处理，能够在数据到达时立即执行处理操作。&lt;/li&gt;
    &lt;li&gt;事件驱动架构，处理器在接收到数据或触发条件时自动执行。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;分布式架构与扩展性&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;支持多节点集群部署，可以水平扩展以处理大规模数据流。&lt;/li&gt;
    &lt;li&gt;集群中的节点通过 Apache ZooKeeper 进行协调和管理。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;数据安全与合规&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;支持数据加密、访问控制和用户身份验证，确保数据的安全性。&lt;/li&gt;
    &lt;li&gt;提供数据审计功能，记录数据流的处理历史和用户操作。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;错误处理与重试机制&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;自动处理数据传输和处理过程中出现的错误，支持重试和故障转移。&lt;/li&gt;
    &lt;li&gt;提供数据回退和恢复功能，确保数据的可靠性和完整性。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;可扩展性与集成性&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;支持自定义处理器和控制器服务的开发，用户可以根据需要扩展 NiFi 的功能。&lt;/li&gt;
    &lt;li&gt;与其他大数据工具和框架（如 Apache Kafka、Hadoop、Spark）紧密集成，支持复杂的数据处理和分析工作流。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;监控与管理&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;提供详细的日志记录和监控功能，帮助用户了解数据流的执行状态和性能指标。&lt;/li&gt;
    &lt;li&gt;支持告警和通知机制，用户可以根据特定条件设置告警，及时响应异常情况。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;Apache NiFi 的设计目标是提供一个灵活、高效且易于管理的数据流管理平台，适用于各种数据集成和处理场景。其丰富的功能集使其成为企业数据管道构建和管理的理想选择。&lt;/p&gt;
 &lt;h3&gt;Apache NiFi的优势&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;灵活性：通过丰富的处理器和自定义开发能力，NiFi 可以适应各种复杂的数据处理需求。&lt;/li&gt;
  &lt;li&gt;可扩展性：支持多节点集群部署，可以水平扩展以处理大规模数据流。&lt;/li&gt;
  &lt;li&gt;可视化管理：提供直观的 Web UI，用户可以轻松设计和管理数据流，无需编写复杂的代码。&lt;/li&gt;
  &lt;li&gt;高可用性：通过故障转移和数据重试机制，确保数据流的高可用性和可靠性。&lt;/li&gt;
  &lt;li&gt;安全性：支持数据加密、访问控制和审计，确保数据的安全性和隐私保护。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;Apache NiFi的架构&lt;/h2&gt;
 &lt;p&gt;Apache NiFi 的架构设计旨在提供一个灵活、高效且可扩展的数据流管理平台。它采用模块化设计，支持分布式部署，能够处理各种规模和复杂度的数据流任务。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="510" src="https://www.biaodianfu.com/wp-content/uploads/2024/11/nifi-2.png" width="970"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;核心组件&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;Web UI（用户界面）：NiFi 提供了一个直观的 Web 用户界面，用于设计、监控和管理数据流。用户可以通过拖拽和配置组件来构建数据流，并实时查看数据流的状态和性能指标。&lt;/li&gt;
  &lt;li&gt;FlowFile：FlowFile 是 NiFi 中的数据单元，包含数据内容和属性。每个 FlowFile 都有唯一标识符和元数据，支持数据的高效传输和处理。&lt;/li&gt;
  &lt;li&gt;处理器（Processor）：处理器是执行特定数据处理任务的基本单元。NiFi 提供了丰富的内置处理器，用于数据采集、转换、路由和传输。用户还可以开发自定义处理器以满足特定需求。&lt;/li&gt;
  &lt;li&gt;连接（Connection）：连接用于在处理器之间传递 FlowFile。连接可以配置为使用不同的队列策略，以控制数据的流动速度和优先级。&lt;/li&gt;
  &lt;li&gt;流程组（Process Group）：流程组用于组织和管理多个处理器和连接，形成逻辑上的子流程。这有助于复杂数据流的模块化设计和维护。&lt;/li&gt;
  &lt;li&gt;控制器服务（Controller Service）：控制器服务提供共享的配置和服务，例如数据库连接池、分布式缓存等。它们可以在多个处理器之间复用，提高资源利用率。&lt;/li&gt;
  &lt;li&gt;报告任务（Reporting Task）：报告任务用于生成和发送 NiFi 系统的运行状态和指标数据，通常用于监控和告警系统。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;工作流和数据流&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;数据采集与处理&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;数据流从输入处理器开始，输入处理器从外部数据源（如文件系统、HTTP、Kafka）获取数据并生成 FlowFile。&lt;/li&gt;
    &lt;li&gt;中间处理器对 FlowFile 进行处理，包括数据解析、转换、过滤和聚合等操作。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;数据路由与分发&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;根据业务规则和条件，NiFi 可以将 FlowFile 路由到不同的处理器或流程组。&lt;/li&gt;
    &lt;li&gt;输出处理器将处理后的 FlowFile 发送到目标系统（如 HDFS、数据库、云存储）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;实时监控与管理&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;Web UI 提供实时数据流监控功能，用户可以查看处理器的性能指标、队列长度、处理速率等。&lt;/li&gt;
    &lt;li&gt;用户可以动态调整数据流的配置和参数，以优化性能和处理逻辑。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;分布式架构&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;多节点集群&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;NiFi 支持多节点集群部署，可以通过增加节点来扩展处理能力。集群中的每个节点都可以执行数据流任务。&lt;/li&gt;
    &lt;li&gt;集群节点通过 Apache ZooKeeper 进行协调和管理，以确保任务的负载均衡和高可用性。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;高可用性与故障转移&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;NiFi 采用主从架构，集群中一个节点为主节点（Primary Node），负责调度任务和管理集群配置。&lt;/li&gt;
    &lt;li&gt;在主节点故障时，集群会自动选举新的主节点，确保数据流的持续性和可靠性。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;安全性&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;用户认证与授权&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;支持多种认证机制（如 LDAP、Kerberos），确保只有授权用户才能访问和管理 NiFi 系统。&lt;/li&gt;
    &lt;li&gt;提供细粒度的权限控制，用户可以对不同的数据流组件和操作进行授权。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;数据加密&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;支持数据传输加密和存储加密，确保数据在传输和存储过程中的安全性。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;审计与日志&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;提供详细的审计日志记录，记录用户操作和数据流处理历史，便于合规性检查和故障排查。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;Apache NiFi 的架构设计使其成为一个灵活、可扩展和安全的数据流管理平台，适用于各种规模和复杂度的数据集成和处理任务。其模块化设计和丰富的功能集使得用户能够高效地构建和管理复杂的数据流管道。&lt;/p&gt;
 &lt;h2&gt;Airflow、Kafka的对比&lt;/h2&gt;
 &lt;p&gt;Apache NiFi、Apache Airflow 和 Apache Kafka 都是现代数据处理和管理生态系统中的重要工具，但它们各自的设计目的和应用场景有所不同。以下是它们的详细对比：&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;特性&lt;/td&gt;
   &lt;td&gt;Apache NiFi&lt;/td&gt;
   &lt;td&gt;Apache Airflow&lt;/td&gt;
   &lt;td&gt;Apache Kafka&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;主要用途&lt;/td&gt;
   &lt;td&gt;实时数据流管理和自动化&lt;/td&gt;
   &lt;td&gt;工作流调度和管理&lt;/td&gt;
   &lt;td&gt;消息队列和流处理&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;架构特点&lt;/td&gt;
   &lt;td&gt;可视化界面，事件驱动架构&lt;/td&gt;
   &lt;td&gt;编程接口定义工作流（DAGs），基于调度器和执行器&lt;/td&gt;
   &lt;td&gt;发布/订阅模型，分布式架构&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;数据处理&lt;/td&gt;
   &lt;td&gt;实时数据采集、转换和路由&lt;/td&gt;
   &lt;td&gt;批处理任务调度，不直接处理数据流&lt;/td&gt;
   &lt;td&gt;高吞吐量的消息传输，支持实时流处理&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;扩展性与部署&lt;/td&gt;
   &lt;td&gt;多节点集群，水平扩展&lt;/td&gt;
   &lt;td&gt;分布式调度和执行，支持多种执行器&lt;/td&gt;
   &lt;td&gt;水平扩展，通过分区和副本实现容错&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;安全性&lt;/td&gt;
   &lt;td&gt;细粒度权限控制和数据加密&lt;/td&gt;
   &lt;td&gt;用户认证和授权（RBAC）&lt;/td&gt;
   &lt;td&gt;SSL 加密、SASL 认证和 ACL 授权&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;应用场景&lt;/td&gt;
   &lt;td&gt;实时数据集成、物联网数据采集、日志管理和监控&lt;/td&gt;
   &lt;td&gt;定时数据处理任务、复杂的 ETL 管道、机器学习工作流&lt;/td&gt;
   &lt;td&gt;实时数据传输、日志收集和分析、事件驱动架构&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;  &lt;strong&gt;对比总结&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;实时 vs 批处理：
   &lt;ul&gt;
    &lt;li&gt;NiFi：适合实时数据流处理和自动化。&lt;/li&gt;
    &lt;li&gt;Airflow：适合批处理任务调度和复杂的工作流管理。&lt;/li&gt;
    &lt;li&gt;Kafka：适合高吞吐量的消息传输和实时流处理。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;用户界面 vs 编程接口：
   &lt;ul&gt;
    &lt;li&gt;NiFi：提供可视化界面，适合需要快速构建和管理数据流的场景。&lt;/li&gt;
    &lt;li&gt;Airflow：提供编程接口，适合需要灵活定义复杂工作流的场景。&lt;/li&gt;
    &lt;li&gt;Kafka：主要通过编程接口和命令行工具进行管理和配置。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;数据流管理 vs 工作流调度 vs 消息队列：
   &lt;ul&gt;
    &lt;li&gt;NiFi：专注于数据流的管理和处理。&lt;/li&gt;
    &lt;li&gt;Airflow：专注于任务调度和工作流管理。&lt;/li&gt;
    &lt;li&gt;Kafka：专注于消息队列和流处理。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;根据具体的需求和场景，企业可以选择合适的工具或组合使用这些工具来构建复杂的数据处理和管理生态系统。例如，可以使用 NiFi 进行数据采集和预处理，使用 Kafka 进行高吞吐量的消息传输，使用 Airflow 进行批处理任务的调度和管理。&lt;/p&gt;
 &lt;div&gt;

  &lt;h3&gt;相关文章:&lt;/h3&gt;  &lt;ol&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/kafka.html" rel="bookmark" title="&amp;#20998;&amp;#24067;&amp;#24335;&amp;#28040;&amp;#24687;&amp;#31995;&amp;#32479;Kafka"&gt;分布式消息系统Kafka&lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/hive-sql-guide.html" rel="bookmark" title="Hive SQL&amp;#31995;&amp;#32479;&amp;#21270;&amp;#23398;&amp;#20064;"&gt;Hive SQL系统化学习&lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/python-schedule.html" rel="bookmark" title="Python&amp;#33258;&amp;#21160;&amp;#21270;&amp;#20043;&amp;#23450;&amp;#26102;&amp;#20219;&amp;#21153;"&gt;Python自动化之定时任务&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>器→工具 开源项目 大数据</category>
      <guid isPermaLink="true">https://itindex.net/detail/62957-%E5%BC%80%E6%BA%90-%E5%AE%9E%E6%97%B6-%E6%95%B0%E6%8D%AE</guid>
      <pubDate>Sat, 09 Nov 2024 21:04:10 CST</pubDate>
    </item>
    <item>
      <title>将 Android 手机变成监听工具</title>
      <link>https://itindex.net/detail/62947-android-%E6%89%8B%E6%9C%BA-%E7%9B%91%E5%90%AC</link>
      <description>之前的实验表明，智能手机中的陀螺仪和加速计等惯性测量单元（IMU），可以通过检测声波振动监听对话。这意味着，即使是一个没有开启麦克风权限的应用程序也可以通过 IMU 获得对话内容。为了不让攻击者获得准确信息，Google 将 Android 应用从 IMU 采样数据的频率限制在每秒 200 次，使攻击者无法准确获得对话内容。根据发表在预印本平台 arXiv 上的预印本，研究人员发现了一个漏洞——通过欺骗陀螺仪和运动传感器在时间上稍微偏移地进行测量，将应用实际采样率从每秒 200 次提高到 400 次，可以突破上述保护措施。利用这种方法，攻击者能修复获得的音频量大大提升。与每秒仅采集 200 个样本相比，他们的方法在 AI 转录时单词错误率降低了 83%。这表明，目前的安全保护措施“不足以防止复杂的窃听攻击发生”，应该对其重新评估。
 &lt;p&gt;&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/62947-android-%E6%89%8B%E6%9C%BA-%E7%9B%91%E5%90%AC</guid>
      <pubDate>Tue, 15 Oct 2024 23:48:56 CST</pubDate>
    </item>
    <item>
      <title>Python地理数据分析工具MovingPandas</title>
      <link>https://itindex.net/detail/62944-python-%E5%9C%B0%E7%90%86-%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90</link>
      <description>&lt;p&gt;MovingPandas 是一个用于分析轨迹数据的 Python 库。它在处理和分析移动对象的时空数据方面非常强大，适用于地理信息系统（GIS）、时空数据分析和可视化等领域。它是在热门的地理数据处理库 GeoPandas 的基础上构建的，GeoPandas 本身是建立在Pandas数据处理库之上的。MovingPandas 旨在提供高效、易于使用的工具，以便分析和处理包含位置信息的时间序列数据。MovingPandas使得研究移动模式、路径分析、时空聚类等任务变得更加高效和直观。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="719" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/MovingPandas.png" width="977"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;核心功能：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;轨迹数据表示。MovingPandas 使用 GeoPandas GeoDataFrames 来表示轨迹数据。每条轨迹由一系列带有时间戳的点组成，形成一条时空路径。&lt;/li&gt;
  &lt;li&gt;轨迹分割。可以根据时间间隔、距离阈值等条件将轨迹分割成多个子轨迹。这对于处理长轨迹或者在某些关键事件发生前后进行分析非常有用。&lt;/li&gt;
  &lt;li&gt;轨迹特征提取。提供了多种方法来计算轨迹的特征，比如速度、加速度、方向变化等。这些特征在进行模式识别和行为分析时非常有用。&lt;/li&gt;
  &lt;li&gt;轨迹聚类。支持基于轨迹的聚类分析，可以识别出类似移动模式的轨迹群体。常用的聚类方法包括基于密度的聚类（DBSCAN）、分层聚类等。&lt;/li&gt;
  &lt;li&gt;轨迹可视化。通过与 Matplotlib 和 Folium 等库的集成，MovingPandas 能够提供强大的轨迹数据可视化功能，包括静态和交互式地图。&lt;/li&gt;
  &lt;li&gt;时空聚合。支持时空聚合分析，比如计算某个区域在特定时间段内的平均速度、轨迹数量等。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;MovingPandas的使用&lt;/h2&gt;
 &lt;h3&gt;MovingPandas的安装&lt;/h3&gt;
 &lt;p&gt;MovingPandas作者推荐在Python 3.7及以上环境下安装。请确保你的Python版本符合这一要求。如果你已经安装了Anaconda，可以使用conda命令来安装MovingPandas及其依赖包。&lt;/p&gt;
 &lt;pre&gt;conda install -c conda-forge movingpandas&lt;/pre&gt;
 &lt;p&gt;MovingPandas同样可以使用pip进行安装，但是不推荐，主要原因是其依赖环境较为复杂，使用pip安装可能会出现依赖项缺失或版本冲突的问题。因此，推荐使用conda进行安装。&lt;/p&gt;
 &lt;h3&gt;MovingPandas接口详解&lt;/h3&gt;
 &lt;h4&gt;MovingPandas.Trajectory对象&lt;/h4&gt;
 &lt;p&gt;在 MovingPandas 中，Trajectory 类是核心组件之一，主要用于表示和处理单个轨迹。Trajectory 对象是一个时间序列的集合，其中每个数据点代表轨迹上的一个位置，包含了位置信息（经纬度或其他地理空间参考）、时间戳和其他可能的属性（如速度、方向等）。因此，一个 Trajectory 对象是连续移动的点组成的线，这些点按照时间顺序排列。&lt;/p&gt;
 &lt;p&gt;Trajectory 对象的主要特性：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;时间索引：Trajectory 对象的索引通常是时间戳，这使得基于时间的查询和操作变得直观和高效。&lt;/li&gt;
  &lt;li&gt;空间位置：每个时间点对应一个空间位置，这通常是通过经纬度坐标表示的。&lt;/li&gt;
  &lt;li&gt;其他属性：除了时间和位置，还可以包含其他相关的数据列，如速度、加速度、方向等，这些信息对于分析移动行为至关重要。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;创建 Trajectory 对象通常涉及几个步骤，首先你可能需要有一个包含时空数据的pandas DataFrame。这个DataFrame应该至少包含三列：表示时间戳的列（通常会被设置为索引）、表示X坐标的列（如经度）、表示Y坐标的列（如纬度）。然后，你可以使用 MovingPandas 提供的函数或方法（如TrajectoryCollection.from_geodataframe()）来创建一个或多个 Trajectory 对象。&lt;/p&gt;
 &lt;p&gt;class movingpandas.Trajectory(df, traj_id, traj_id_col=None, obj_id=None, t=None, x=None, y=None, crs=’epsg:4326′, parent=None)&lt;/p&gt;
 &lt;p&gt;参数说明：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;df：具有GeoPandas的geometry坐标列和时间戳索引的GeoDataFrame，或Pandas的DataFrame。必填参数。&lt;/li&gt;
  &lt;li&gt;traj_id：任意类型，表示轨迹的唯一标识符。必填参数。&lt;/li&gt;
  &lt;li&gt;obj_id：任意类型，表示移动物体的唯一标识符。默认为 None。&lt;/li&gt;
  &lt;li&gt;t：表示包含时间戳的列名，默认为 None。&lt;/li&gt;
  &lt;li&gt;x：表示包含x坐标的列名，使用Pandas的DataFrame需指定。默认为 None。&lt;/li&gt;
  &lt;li&gt;y：表示包含y坐标的列名，使用Pandas的DataFrame需指定。默认为 None。&lt;/li&gt;
  &lt;li&gt;crs：表示 x/y 坐标的坐标参考系统。默认为 “epsg:4326″，即 WGS84。&lt;/li&gt;
  &lt;li&gt;parent：一个Trajectory 对象，表示父轨迹。默认为 None。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;基本信息与操作&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;copy(): 返回轨迹对象的一个副本。&lt;/li&gt;
  &lt;li&gt;drop(**kwargs) 方法用于从数据集中删除满足特定条件的行或列。&lt;/li&gt;
  &lt;li&gt;plot(self, *args, **kwargs): 绘制轨迹。&lt;/li&gt;
  &lt;li&gt;explore(*args, **kwargs) 方法用于以交互方式可视化和分析数据，支持多种参数和选项以定制显示。&lt;/li&gt;
  &lt;li&gt;is_latlon() 方法用于判断轨迹数据是否采用经纬度坐标系。&lt;/li&gt;
  &lt;li&gt;is_valid() 方法用于检查轨迹数据是否有效，例如是否包含必要的字段和合理的坐标。&lt;/li&gt;
  &lt;li&gt;size() 方法用于返回轨迹中包含的定位点数量。&lt;/li&gt;
  &lt;li&gt;get_crs() 方法用于获取当前地理数据集的坐标参考系统（CRS），返回一个描述该坐标系的对象或信息。&lt;/li&gt;
  &lt;li&gt;to_crs(self, crs): 转换轨迹的坐标参考系统。&lt;/li&gt;
  &lt;li&gt;get_column_names() 方法用于获取数据集中的所有列名，返回一个包含列名的列表。这个方法通常用于快速查看数据集的结构或在进行数据处理时动态获取列名。&lt;/li&gt;
  &lt;li&gt;get_direction_col() 方法用于获取表示方向数据的列，这些数据通常以角度或方位形式存储。&lt;/li&gt;
  &lt;li&gt;get_distance_col() 方法用于获取表示距离数据的列，这些数据通常用于计算或分析两点之间的距离。&lt;/li&gt;
  &lt;li&gt;get_speed_col() 方法用于获取表示对象速度的列名。&lt;/li&gt;
  &lt;li&gt;get_timedelta_col() 方法用于获取表示时间增量的列名。&lt;/li&gt;
  &lt;li&gt;get_traj_id_col() 方法用于获取表示轨迹标识符的列名。&lt;/li&gt;
  &lt;li&gt;get_geom_col() 方法用于获取表示几何数据的列，该列通常包含地理空间信息，如点、线或多边形。&lt;/li&gt;
  &lt;li&gt;get_angular_difference_col() 方法用于获取包含角度差异的列，这些差异通常用于分析方向或角度变化。&lt;/li&gt;
  &lt;li&gt;to_point_gdf(self): 返回包含轨迹点的GeoDataFrame。&lt;/li&gt;
  &lt;li&gt;to_line_gdf(columns=None) 方法用于将轨迹数据转换为 GeoDataFrame 格式的线条几何数据，可以选择包含特定的列。&lt;/li&gt;
  &lt;li&gt;to_linestring() 方法用于将轨迹数据转换为 LineString 对象，表示轨迹的线条几何形状。&lt;/li&gt;
  &lt;li&gt;to_linestringm_wkt() 方法用于将轨迹数据转换为包含 ZM（高程和度量）信息的 WKT（Well-Known Text）格式的 LineStringM 字符串。&lt;/li&gt;
  &lt;li&gt;to_mf_json(datetime_to_str=True, temporal_columns=None) 方法用于将轨迹数据转换为 Moving Features JSON 格式，可以选择将日期时间转换为字符串，并指定时间相关的列。&lt;/li&gt;
  &lt;li&gt;to_point_gdf(return_orig_tz=False) 方法将轨迹数据转换为 GeoDataFrame 格式的点几何数据，可以选择返回原始时区的时间。&lt;/li&gt;
  &lt;li&gt;to_traj_gdf(wkt=False, agg=False) 方法将轨迹数据转换为 GeoDataFrame 格式，可以选择生成 WKT 格式的几何数据或进行聚合处理。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;轨迹分析与聚合统计&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;get_bbox(self): 返回轨迹的范围 (bounding box)。&lt;/li&gt;
  &lt;li&gt;get_start_location(self): 返回轨迹的起始位置。&lt;/li&gt;
  &lt;li&gt;get_end_location(self): 返回轨迹的结束位置。&lt;/li&gt;
  &lt;li&gt;get_start_time() 方法用于获取时间序列数据或对象轨迹的起始时间。&lt;/li&gt;
  &lt;li&gt;get_end_time() 方法用于获取某个事件或过程的结束时间，通常返回一个时间戳或日期时间对象。&lt;/li&gt;
  &lt;li&gt;get_max(column) 方法用于获取指定列 column 中的最大值。&lt;/li&gt;
  &lt;li&gt;get_min(column) 方法用于获取指定列 column 中的最小值。&lt;/li&gt;
  &lt;li&gt;get_position_at(t, method=’interpolated’) 方法用于获取在时间点 t 处的对象位置，默认使用插值方法来计算位置。&lt;/li&gt;
  &lt;li&gt;get_row_at(t, method=’nearest’) 方法用于获取在时间点 t 附近的对象所在的行，默认使用最近邻方法来选择行。&lt;/li&gt;
  &lt;li&gt;get_length(units=(None, None, None, None)) 方法用于计算并获取几何对象的长度，可以接受多个单位参数来指定长度的测量单位。&lt;/li&gt;
  &lt;li&gt;get_mcp() 方法用于获取某个对象的最小凸包 (Minimum Convex Polygon, MCP)，通常用于地理空间分析中确定一组点的最小包围区域。&lt;/li&gt;
  &lt;li&gt;add_direction(self, overwrite=False): 计算并添加方向信息到轨迹数据中。&lt;/li&gt;
  &lt;li&gt;get_direction() 方法用于计算和获取两个地理点之间的方向或方位角，通常以度数表示。&lt;/li&gt;
  &lt;li&gt;get_duration(self): 返回轨迹的总时长。&lt;/li&gt;
  &lt;li&gt;add_distance(overwrite=False, name=’distance’, units=None)：计算并添加轨迹数据中相邻点之间的距离信息。&lt;/li&gt;
  &lt;li&gt;add_acceleration(self, overwrite=False, name=’acceleration’): 计算并添加加速度信息到轨迹数据中。&lt;/li&gt;
  &lt;li&gt;add_speed(self, overwrite=False): 计算并添加速度信息到轨迹数据中。&lt;/li&gt;
  &lt;li&gt;add_angular_difference(overwrite=False, name=’angular_difference’)：计算并添加轨迹中相邻点之间的角度差异信息到轨迹数据中。&lt;/li&gt;
  &lt;li&gt;add_timedelta(overwrite=False, name=’timedelta’) ：计算并添加轨迹数据中相邻点之间的时间差信息。&lt;/li&gt;
  &lt;li&gt;add_traj_id(overwrite=False) 方法用于为轨迹数据添加或覆盖轨迹ID列，以标识相同轨迹中的所有点。&lt;/li&gt;
  &lt;li&gt;get_segment_between(t1, t2) 方法用于获取在时间点 t1 和 t2 之间的对象轨迹或数据段。&lt;/li&gt;
  &lt;li&gt;get_linestring_between(t1, t2, method=’interpolated’) 方法用于生成并获取在时间点 t1 和 t2 之间的一条线串，默认使用插值方法。&lt;/li&gt;
  &lt;li&gt;get_sampling_interval() 方法用于获取时间序列数据中的采样时间间隔。&lt;/li&gt;
  &lt;li&gt;hausdorff_distance(other, units=(None, None, None, None)) 方法用于计算当前轨迹与另一个轨迹之间的Hausdorff距离，并允许指定单位。&lt;/li&gt;
  &lt;li&gt;hvplot(*args, **kwargs) 方法用于使用hvPlot库创建高度可定制的图形和可视化。&lt;/li&gt;
  &lt;li&gt;hvplot_pts(*args, **kwargs) 方法用于使用hvPlot库对地理点数据进行可视化并创建交互式图形。&lt;/li&gt;
  &lt;li&gt;interpolate_position_at(t) 方法用于在给定时间 t 处插值并返回轨迹的位置。&lt;/li&gt;
  &lt;li&gt;intersection(feature, point_based=False) 方法用于计算轨迹与给定地理特征的交集，并可以选择基于点的方式进行计算。&lt;/li&gt;
  &lt;li&gt;intersects(polygon) 方法用于判断轨迹是否与指定的多边形区域相交。&lt;/li&gt;
  &lt;li&gt;clip(self, polygon): 按多边形裁剪轨迹。&lt;/li&gt;
  &lt;li&gt;apply_offset_minutes(column, offset) 方法用于将指定列的时间值按给定的分钟数进行偏移调整。&lt;/li&gt;
  &lt;li&gt;apply_offset_seconds(column, offset) 方法用于将指定列的时间值按给定的秒数进行偏移调整。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;MovingPandas.TrajectoryCollection对象&lt;/h4&gt;
 &lt;p&gt;TrajectoryCollection 类是 MovingPandas 中用于表示多条轨迹的集合。它允许用户以集合的形式操作多条轨迹，支持对这些轨迹的批量处理和分析。&lt;/p&gt;
 &lt;p&gt;可以通过传递一系列 Trajectory 对象来创建一个 TrajectoryCollection。每个 Trajectory 对象代表一条轨迹，包含了时间和位置的信息。&lt;/p&gt;
 &lt;p&gt;class movingpandas.TrajectoryCollection(data, traj_id_col=None, obj_id_col=None, t=None, x=None, y=None, crs=’epsg:4326′, min_length=0, min_duration=None)&lt;/p&gt;
 &lt;p&gt;参数说明：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;data (list[Trajectory] 或 GeoDataFrame 或 DataFrame) – 包含 Trajectory 对象的列表，或一个包含轨迹 ID、点几何列和时间戳索引的 GeoDataFrame。&lt;/li&gt;
  &lt;li&gt;traj_id_col (string) – 包含轨迹 ID 的 GeoDataFrame 列名。&lt;/li&gt;
  &lt;li&gt;obj_id_col (string) – 包含移动对象 ID 的 GeoDataFrame 列名。&lt;/li&gt;
  &lt;li&gt;t (string) – 包含时间戳的 DataFrame 列名。&lt;/li&gt;
  &lt;li&gt;x (string) – 包含 x 坐标的 DataFrame 列名。&lt;/li&gt;
  &lt;li&gt;y (string) – 包含 y 坐标的 DataFrame 列名。&lt;/li&gt;
  &lt;li&gt;crs (string) – x/y 坐标的坐标参考系 (CRS)。&lt;/li&gt;
  &lt;li&gt;min_length (numeric) – 期望的轨迹最小长度。长度使用 CRS 单位计算，若 CRS 是地理坐标系（例如 EPSG:4326 WGS84），则长度以米为单位计算。（较短的轨迹将被丢弃。）&lt;/li&gt;
  &lt;li&gt;min_duration (timedelta) – 期望的轨迹最短持续时间。（较短的轨迹将被丢弃。）&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;相比MovingPandas.Trajectory多了一些方法：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;filter(predicate): 根据给定条件过滤轨迹集合。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;MovingPandas.TrajectoryCollectionAggregator对象&lt;/h4&gt;
 &lt;p&gt;MovingPandas.TrajectoryCollectionAggregator 是 MovingPandas 库中的一个类，主要用于对轨迹集合进行聚合操作。通过对轨迹数据进行空间和时间上的聚合，可以帮助用户有效地分析和总结移动模式。&lt;/p&gt;
 &lt;p&gt;class movingpandas.TrajectoryCollectionAggregator(traj_collection, max_distance, min_distance, min_stop_duration, min_angle=45)&lt;/p&gt;
 &lt;p&gt;参数说明&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;traj_collection (TrajectoryCollection) – 要进行聚合的 TrajectoryCollection 对象。&lt;/li&gt;
  &lt;li&gt;max_distance (float) – 重要点之间的最大距离（距离使用 CRS 单位计算，若 CRS 是地理坐标系，例如 EPSG:4326 WGS84，则距离以米为单位计算）。&lt;/li&gt;
  &lt;li&gt;min_distance (float) – 重要点之间的最小距离。&lt;/li&gt;
  &lt;li&gt;min_stop_duration (datetime.timedelta) – 停止检测所需的最短持续时间。&lt;/li&gt;
  &lt;li&gt;min_angle (float) – 提取重要点的最小角度。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;相关方法：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;get_clusters_gdf() 方法返回一个 GeoDataFrame，其中包含聚合后的轨迹数据的簇（clusters）。&lt;/li&gt;
  &lt;li&gt;get_flows_gdf() 方法返回一个 GeoDataFrame，其中包含聚合后的轨迹数据的流动（flows）信息。&lt;/li&gt;
  &lt;li&gt;get_significant_points_gdf() 方法返回一个 GeoDataFrame，其中包含从轨迹数据中提取的显著点（significant points）。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;MovingPandas.TrajectoryCleaner对象&lt;/h4&gt;
 &lt;p&gt;MovingPandas.TrajectoryCleaner 是 MovingPandas 库中的一个类，专门用于清理轨迹数据。清理操作可以帮助去除数据中的噪声、填补缺失值以及进行其他预处理步骤，确保轨迹数据的质量和一致性。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;IqrCleaner(traj) 是一个类，用于基于四分位数范围 (IQR) 方法来清理轨迹数据中的异常值。&lt;/li&gt;
  &lt;li&gt;OutlierCleaner(traj) 是一个类，用于通过多种方法识别和清理轨迹数据中的离群点（异常值）。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;MovingPandas.TrajectoryGeneralizer对象&lt;/h4&gt;
 &lt;p&gt;MovingPandas.TrajectoryGeneralizer 是 MovingPandas 库中的一个类，用于对轨迹数据进行简化和概括。通过轨迹数据的概括，可以减少数据量，提高处理效率，并且在某些应用场景下有助于更清晰地展示轨迹特征。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;TrajectoryGeneralizer(traj) 是一个类，用于通过多种算法对轨迹数据进行简化和概括，以减少数据量并提高处理效率。&lt;/li&gt;
  &lt;li&gt;DouglasPeuckerGeneralizer(traj) 是一个类，专门使用 Douglas-Peucker 算法对轨迹数据进行简化，保留主要特征点以减少数据量。&lt;/li&gt;
  &lt;li&gt;MinDistanceGeneralizer(traj) 是一个类，用于根据最小距离间隔对轨迹数据进行简化，移除距离变化小于指定阈值的点。&lt;/li&gt;
  &lt;li&gt;MinTimeDeltaGeneralizer(traj) 是一个类，用于根据最小时间间隔对轨迹数据进行简化，移除时间间隔小于指定阈值的点。&lt;/li&gt;
  &lt;li&gt;TopDownTimeRatioGeneralizer(traj) 是一个类，用于通过时间比率算法对轨迹数据进行简化，保留关键时间点以减少数据量。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;MovingPandas.TrajectorySmoother对象&lt;/h4&gt;
 &lt;p&gt;MovingPandas.TrajectorySmoother 是一个类，用于对轨迹数据进行平滑处理。轨迹平滑通常是为了减少由于数据采集误差和噪声导致的轨迹抖动和异常点，从而得到更加平滑和准确的轨迹线条。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;KalmanSmootherCV(traj) 是一个类，用于使用常速模型（Constant Velocity Model）的卡尔曼滤波算法对轨迹数据进行平滑处理，以减少噪声和抖动。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;MovingPandas.TrajectorySplitter对象&lt;/h4&gt;
 &lt;p&gt;MovingPandas.TrajectorySplitter 是一个类，用于将轨迹数据根据特定条件进行分割。这在处理长时间、多段的轨迹数据时特别有用，比如在分析车辆行驶路径、运动员运动轨迹或动物迁徙路径时，可以根据特定的规则将连续的轨迹分割成多个部分，以便进行更细致的分析。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;TrajectorySplitter(traj) 是一个类，用于根据指定的条件（如距离、时间或速度）对轨迹数据进行分割，生成多个段以便更细致的分析。&lt;/li&gt;
  &lt;li&gt;TemporalSplitter(traj) 是一个类，用于根据时间间隔对轨迹数据进行分割，将轨迹分成多个时间段以便更细致的时间序列分析。&lt;/li&gt;
  &lt;li&gt;ObservationGapSplitter(traj) 是一个类，用于根据观测数据中的时间间隙对轨迹进行分割，当连续观测点之间的时间间隔超过指定阈值时，将轨迹分割成多个部分。&lt;/li&gt;
  &lt;li&gt;SpeedSplitter(traj) 是一个类，用于根据速度阈值对轨迹数据进行分割，当轨迹点的速度超过指定阈值时，将轨迹分割成多个部分。&lt;/li&gt;
  &lt;li&gt;StopSplitter(traj) 是一个类，用于根据停留点（长时间停留的点）对轨迹数据进行分割，将轨迹分成移动段和停留段以便更细致的分析。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;MovingPandas.TrajectoryStopDetector对象&lt;/h4&gt;
 &lt;p&gt;TrajectoryStopDetector 通过分析轨迹点的时空属性来识别停留点。它会检查一个轨迹对象中的每个点，并根据设定的阈值参数（如最小速度、最小停留时间和最小停留距离等）来鉴定轨迹中是否存在停留段。&lt;/p&gt;
 &lt;p&gt;class movingpandas.TrajectoryStopDetector(traj, n_threads=1)&lt;/p&gt;
 &lt;p&gt;方法介绍：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;get_stop_points(max_diameter, min_duration) 是 TrajectoryStopDetector 类中的方法，用于根据最大停留直径和最小持续时间来识别和提取轨迹数据中的停留点，并返回包含这些停留点的 GeoDataFrame。&lt;/li&gt;
  &lt;li&gt;get_stop_segments(max_diameter, min_duration) 是 TrajectoryStopDetector 类中的方法，用于根据最大停留直径和最小持续时间来识别和提取轨迹中的停留段，并返回包含这些停留段的列表。&lt;/li&gt;
  &lt;li&gt;get_stop_time_ranges(max_diameter, min_duration) 是 TrajectoryStopDetector 类中的方法，用于根据最大停留直径和最小持续时间来识别停留时间范围，并返回停留时间段的列表。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;MovingPandas使用实例&lt;/h2&gt;
 &lt;h3&gt;准备工作&lt;/h3&gt;
 &lt;p&gt;  &lt;strong&gt;加载需要的库&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;import pandas as pd
import geopandas as gpd
import movingpandas as mpd
from datetime import datetime, timedelta
import matplotlib.pyplot as plt
import folium
import bokeh.io
bokeh.io.output_notebook()
from holoviews import opts
opts.defaults(opts.Overlay(active_tools=[&amp;quot;wheel_zoom&amp;quot;], frame_width=500, frame_height=400))
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;加载数据&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;df = pd.read_excel(&amp;quot;driver_log.xlsx&amp;quot;)

# 将DataFrame 转换为 GeoDataFrame
gdf = gpd.GeoDataFrame(df, geometry=gpd.points_from_xy(df.lon, df.lat), crs=&amp;apos;EPSG:4326&amp;apos;)

# 将GeoDataFrame转化为TrajectoryCollection对象
tc = mpd.TrajectoryCollection(gdf, traj_id_col=&amp;apos;session_id&amp;apos;, obj_id_col = &amp;apos;driver_no&amp;apos;, t=&amp;apos;log_time&amp;apos;)
# 过滤某个司机的轨迹
df[&amp;apos;driver_no&amp;apos;].value_counts()
df[&amp;apos;driver_no&amp;apos;].value_counts().plot(kind=&amp;apos;bar&amp;apos;, figsize=(15,3))
driver_tc = tc.filter(&amp;apos;driver_no&amp;apos;, &amp;apos;DR202407021504081000000&amp;apos;)

# 展示司机轨迹
driver_tc.plot()
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="413" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/driver_traj.png" width="450"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;# 获取单个轨迹
my_traj = driver_tc.trajectories[0]

# 展示单个轨迹
traj_plot = my_traj.hvplot(title=&amp;quot;Trajectory {}&amp;quot;.format(my_traj.id),line_width=7.0, tiles=&amp;quot;CartoLight&amp;quot;, color=&amp;quot;slategray&amp;quot;)
traj_plot
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="636" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/my_traj.png" width="815"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;停留点检测&lt;/h3&gt;
 &lt;p&gt;  &lt;strong&gt;针对单轨迹停留点检测&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;detector = mpd.TrajectoryStopDetector(my_traj)
## 检测停留的时间（这里检测5分钟位移100米以内）
stop_time_ranges = detector.get_stop_time_ranges(min_duration=timedelta(seconds=300), max_diameter=100)
## 检测停留的时间
for stop_time in stop_time_ranges:
    print(stop_time)
## 检测停留点
stop_points = detector.get_stop_points(min_duration=timedelta(seconds=300), max_diameter=100)
stop_points
## 展示停留点
stop_point_plot = traj_plot * stop_points.hvplot(geo=True, size=&amp;quot;duration_s&amp;quot;, color=&amp;quot;deeppink&amp;quot;)
stop_point_plot
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="631" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/stop-point.png" width="817"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;## 停留点信息
stop_points_gdf = gpd.GeoDataFrame(stop_points, geometry=&amp;quot;geometry&amp;quot;, crs=&amp;quot;EPSG:4326&amp;quot;)
stop_points_gdf
## 使用folium展示停留点
# m = my_traj.explore(color=&amp;quot;blue&amp;quot;,style_kwds={&amp;quot;weight&amp;quot;: 4},name=&amp;quot;Trajectory&amp;quot;)
# stop_points_gdf.explore(m=m,color=&amp;quot;red&amp;quot;,style_kwds={&amp;quot;style_function&amp;quot;: lambda x: {&amp;quot;radius&amp;quot;: x[&amp;quot;properties&amp;quot;][&amp;quot;duration_s&amp;quot;] / 10 }},name=&amp;quot;Stop points&amp;quot;)
# folium.TileLayer(&amp;quot;OpenStreetMap&amp;quot;).add_to(m)
# folium.LayerControl().add_to(m)
# m
## 停留轨迹
stop_segments = detector.get_stop_segments(min_duration=timedelta(seconds=60), max_diameter=100)
stop_segments.to_traj_gdf()
## 停留轨迹
stop_segment_plot = stop_point_plot * stop_segments.hvplot(line_width=7.0, tiles=None, color=&amp;quot;orange&amp;quot;)
stop_segment_plot
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="629" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/stop_segment.png" width="811"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;## 使用folium绘图
# m = my_traj.explore(
#     color=&amp;quot;blue&amp;quot;,
#     popup=True,
#     style_kwds={&amp;quot;weight&amp;quot;: 4},
#     name=&amp;quot;Trajectory&amp;quot;,
# )

# stop_segments.explore(
#     m=m,
#     color=&amp;quot;orange&amp;quot;,
#     popup=True,
#     style_kwds={&amp;quot;weight&amp;quot;: 4},
#     name=&amp;quot;Stop segments&amp;quot;,
# )

# stop_points_gdf.explore(
#     m=m,
#     color=&amp;quot;red&amp;quot;,
#     tooltip=&amp;quot;stop_id&amp;quot;,
#     popup=True,
#     marker_kwds={&amp;quot;radius&amp;quot;: 3},
#     name=&amp;quot;Stop points&amp;quot;,
# )

# folium.TileLayer(&amp;quot;CartoDB positron&amp;quot;).add_to(m)
# folium.LayerControl().add_to(m)

# m

## 行驶线路
split = mpd.StopSplitter(my_traj).split(min_duration=timedelta(seconds=300), max_diameter=100)
split.to_traj_gdf()
## 可视化行驶线路
split.explore(column=&amp;quot;session_id&amp;quot;, tiles=&amp;quot;CartoDB positron&amp;quot;, style_kwds={&amp;quot;weight&amp;quot;: 4})

## 整体可视化
stop_segment_plot + split.hvplot(title=&amp;quot;Trajectory {} split at stops&amp;quot;.format(my_traj.id),line_width=7.0,tiles=&amp;quot;CartoLight&amp;quot;)
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="457" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/stop_segment_plot.png" width="1058"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;轨迹合集的经停点检测&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;## 停留点检测
detector = mpd.TrajectoryStopDetector(driver_tc)
stop_points = detector.get_stop_points(min_duration=timedelta(seconds=300), max_diameter=100)
stop_points
## 停留点可视化
ax = driver_tc.plot(figsize=(7, 7))
stop_points.plot(ax=ax, color=&amp;quot;red&amp;quot;)
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="486" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/TrajectoryStopDetector.png" width="603"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;## 使用folium可视化
## 使用方folium可视化
# m = driver_tc.explore(
#     column=&amp;quot;session_id&amp;quot;,
#     popup=True,
#     style_kwds={&amp;quot;weight&amp;quot;: 4},
#     name=&amp;quot;Trajectories&amp;quot;,
# )

# stop_points.explore(
#     m=m,
#     color=&amp;quot;red&amp;quot;,
#     tooltip=&amp;quot;stop_id&amp;quot;,
#     popup=True,
#     marker_kwds={&amp;quot;radius&amp;quot;: 5},
#     name=&amp;quot;Stop points&amp;quot;,
# )

# folium.TileLayer(&amp;quot;CartoDB positron&amp;quot;).add_to(m)
# folium.LayerControl().add_to(m)

# m
&lt;/pre&gt;
 &lt;h3&gt;速度计算&lt;/h3&gt;
 &lt;pre&gt;## 单轨迹增加速度
my_traj.add_speed(overwrite=True,units=(&amp;quot;km&amp;quot;, &amp;quot;h&amp;quot;))
my_traj.df.head()

## 展示速度
my_traj.plot(column=&amp;quot;speed&amp;quot;, linewidth=5, capstyle=&amp;apos;round&amp;apos;, legend=True)
# my_traj.hvplot(c=&amp;apos;speed&amp;apos;, clim=(0,20), line_width=7.0, tiles=&amp;apos;CartoLight&amp;apos;, cmap=&amp;apos;Viridis&amp;apos;, colorbar=True)
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="404" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/speed.png" width="546"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;## 添加方向
my_traj.add_direction(overwrite=True)
my_traj.df.head()

## 添加时差
my_traj.add_timedelta(overwrite=True)
my_traj.df.head()

## 添加距离
my_traj.add_distance(overwrite=True, name=&amp;quot;distance (km)&amp;quot;, units=&amp;quot;m&amp;quot;)
my_traj.df.head()

## 添加加速度
my_traj.add_acceleration(overwrite=True, name=&amp;quot;acceleration (mph/s)&amp;quot;, units=(&amp;quot;mi&amp;quot;, &amp;quot;h&amp;quot;, &amp;quot;s&amp;quot;))
my_traj.df.head()

## 轨迹集增加速度
driver_tc.add_speed(overwrite=True,units=(&amp;quot;km&amp;quot;, &amp;quot;h&amp;quot;))
driver_tc.plot(column=&amp;apos;speed&amp;apos;, linewidth=5, capstyle=&amp;apos;round&amp;apos;, legend=True, vmax=20)
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="418" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/speed2.png" width="534"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;提取位置&lt;/h3&gt;
 &lt;pre&gt;## 获取起点与终点
ax = my_traj.plot()
gpd.GeoSeries(my_traj.get_start_location()).plot(ax=ax, color=&amp;apos;blue&amp;apos;)
gpd.GeoSeries(my_traj.get_end_location()).plot(ax=ax, color=&amp;apos;red&amp;apos;)
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="417" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/location.png" width="574"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;## 获取特定时间点的位置
t = datetime(2024,7,3,9,30,0)
print(my_traj.get_position_at(t, method=&amp;quot;nearest&amp;quot;))
print(my_traj.get_position_at(t, method=&amp;quot;interpolated&amp;quot;))
print(my_traj.get_position_at(t, method=&amp;quot;ffill&amp;quot;)) # from the previous row
print(my_traj.get_position_at(t, method=&amp;quot;bfill&amp;quot;)) # from the following row

point = my_traj.get_position_at(t, method=&amp;quot;interpolated&amp;quot;)
ax = my_traj.plot()
gpd.GeoSeries(point).plot(ax=ax, color=&amp;apos;red&amp;apos;, markersize=100)
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="417" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/time-point.png" width="574"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;## 获取特定时间区间的位置
segment = my_traj.get_segment_between(datetime(2024,7,3,9,10,0), datetime(2024,7,3,9,30,0))
print(segment)
ax = my_traj.plot()
segment.plot(ax=ax, color=&amp;apos;red&amp;apos;, linewidth=5)
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="446" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/segment.png" width="542"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;## 获取特定区域内的轨迹
from shapely.geometry import Polygon

xmin, xmax, ymin, ymax = 104.135, 104.137, 30.642, 30.643
polygon = Polygon([(xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin), (xmin, ymin)])
intersections = my_traj.clip(polygon)
ax = my_traj.plot()
gpd.GeoSeries(polygon).plot(ax=ax, color=&amp;apos;lightgray&amp;apos;)
intersections.plot(ax=ax, color=&amp;apos;red&amp;apos;, linewidth=5, capstyle=&amp;apos;round&amp;apos;)
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="446" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/intersections.png" width="542"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;导出轨迹&lt;/h3&gt;
 &lt;pre&gt;## 返回 GeoDataFrame
driver_tc.to_point_gdf()
driver_tc.to_line_gdf()
driver_tc.to_traj_gdf(wkt=True) # 生成wkt格式的聚合

# 聚合数据
driver_tc.add_speed(overwrite=True,units=(&amp;quot;km&amp;quot;, &amp;quot;h&amp;quot;))
driver_tc.to_traj_gdf(agg={&amp;apos;speed&amp;apos;:[&amp;apos;min&amp;apos;, &amp;apos;max&amp;apos;,&amp;apos;mode&amp;apos;]})

# 导出数据
export_gdf = driver_tc.to_traj_gdf(agg={&amp;apos;speed&amp;apos;:[&amp;apos;min&amp;apos;, &amp;apos;max&amp;apos;,&amp;apos;mode&amp;apos;]})
export_gdf.to_file(&amp;quot;temp.gpkg&amp;quot;, layer=&amp;apos;trajectories&amp;apos;, driver=&amp;quot;GPKG&amp;quot;)
gpd.read_file(&amp;apos;temp.gpkg&amp;apos;).plot()
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="413" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/export_gdf.png" width="450"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;轨迹分割&lt;/h3&gt;
 &lt;pre&gt;## 数据准备
my_traj.add_speed(overwrite=True,units=(&amp;quot;km&amp;quot;, &amp;quot;h&amp;quot;))
my_traj.plot(column=&amp;apos;speed&amp;apos;, vmax=20, linewidth=5, capstyle=&amp;apos;round&amp;apos;, figsize=(9,3), legend=True )
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="308" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/my_traj.plot_.png" width="455"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;## 根据观测数据中的时间间隙对轨迹进行分割
split = mpd.ObservationGapSplitter(my_traj).split(gap=timedelta(minutes=1))
split.to_traj_gdf()
fig, axes = plt.subplots(nrows=1, ncols=len(split), figsize=(19,4))
for i, traj in enumerate(split):
    traj.plot(ax=axes[i], linewidth=5.0, capstyle=&amp;apos;round&amp;apos;, column=&amp;apos;speed&amp;apos;, vmax=20)
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="288" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/split.png" width="1047"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;## 根据停留点（长时间停留的点）对轨迹数据进行分割
split = mpd.StopSplitter(my_traj).split(max_diameter=10, min_duration=timedelta(minutes=1), min_length=20)
split.to_traj_gdf()
fig, axes = plt.subplots(nrows=1, ncols=len(split), figsize=(19,4))
for i, traj in enumerate(split):
    traj.plot(ax=axes[i], linewidth=5.0, capstyle=&amp;apos;round&amp;apos;, column=&amp;apos;speed&amp;apos;, vmax=20)
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="269" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/split2.png" width="1055"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;## 根据速度阈值对轨迹数据进行分割
split = mpd.SpeedSplitter(my_traj).split(speed=0, duration=timedelta(minutes=1))
split.to_traj_gdf()
fig, axes = plt.subplots(nrows=1, ncols=len(split), figsize=(19,4))
for i, traj in enumerate(split):
    traj.plot(ax=axes[i], linewidth=5.0, capstyle=&amp;apos;round&amp;apos;, column=&amp;apos;speed&amp;apos;, vmax=20)
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="284" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/split3.png" width="1031"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;轨迹抽稀&lt;/h3&gt;
 &lt;pre&gt;## 展示原始轨迹
plot_defaults = {&amp;apos;linewidth&amp;apos;:5, &amp;apos;capstyle&amp;apos;:&amp;apos;round&amp;apos;, &amp;apos;figsize&amp;apos;:(9,3), &amp;apos;legend&amp;apos;:True}
my_traj.add_speed(overwrite=True,units=(&amp;quot;km&amp;quot;, &amp;quot;h&amp;quot;))
my_traj.plot(column=&amp;apos;speed&amp;apos;, vmax=20, **plot_defaults)
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="308" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/plot_defaults.png" width="455"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;## 使用 Douglas-Peucker 算法对轨迹数据进行简化
dp_generalized  = mpd.DouglasPeuckerGeneralizer(my_traj).generalize(tolerance=0.0001)
dp_generalized.plot(column=&amp;apos;speed&amp;apos;, vmax=20, **plot_defaults)
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="308" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/dp_generalized.png" width="448"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;print(&amp;apos;Original length: %s&amp;apos;%(my_traj.get_length()))
print(&amp;apos;Generalized length: %s&amp;apos;%(dp_generalized.get_length()))

## 根据最小时间间隔对轨迹数据进行简化
time_generalized = mpd.MinTimeDeltaGeneralizer(my_traj).generalize(tolerance=timedelta(minutes=3))
time_generalized.plot(column=&amp;apos;speed&amp;apos;, vmax=20, **plot_defaults)
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="308" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/time_generalized.png" width="448"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;## 通过时间比率算法对轨迹数据进行简化
tdtr_generalized = mpd.TopDownTimeRatioGeneralizer(my_traj).generalize(tolerance=0.001)
fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(19,4))
tdtr_generalized.plot(ax=axes[0], column=&amp;apos;speed&amp;apos;, vmax=20, **plot_defaults)
dp_generalized.plot(ax=axes[1], column=&amp;apos;speed&amp;apos;, vmax=20, **plot_defaults)
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="344" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/tdtr_generalized.png" width="1198"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(19,4))
tdtr_generalized.plot(ax=axes[0], column=&amp;apos;speed&amp;apos;, vmax=20, **plot_defaults)
time_generalized.plot(ax=axes[1], column=&amp;apos;speed&amp;apos;, vmax=20, **plot_defaults)
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="344" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/tdtr_generalized.plot_.png" width="1199"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;平滑轨迹&lt;/h3&gt;
 &lt;pre&gt;split = mpd.ObservationGapSplitter(my_traj).split(gap=timedelta(minutes=1))
smooth = mpd.KalmanSmootherCV(split).smooth(process_noise_std=0.1, measurement_noise_std=10)
hvplot_defaults = {&amp;apos;tiles&amp;apos;:&amp;apos;CartoLight&amp;apos;, &amp;apos;frame_height&amp;apos;:320, &amp;apos;frame_width&amp;apos;:320, &amp;apos;cmap&amp;apos;:&amp;apos;Viridis&amp;apos;, &amp;apos;colorbar&amp;apos;:True}
kwargs = {**hvplot_defaults, &amp;apos;line_width&amp;apos;:4}
(split.hvplot(title=&amp;apos;Original Trajectories&amp;apos;, **kwargs) +  smooth.hvplot(title=&amp;apos;Smooth Trajectories&amp;apos;, **kwargs))
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="378" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/smooth.png" width="733"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;kwargs = {**hvplot_defaults, &amp;apos;c&amp;apos;:&amp;apos;speed&amp;apos;, &amp;apos;line_width&amp;apos;:7, &amp;apos;clim&amp;apos;:(0,20)}
(split.trajectories[1].hvplot(title=&amp;apos;Original Trajectory&amp;apos;, **kwargs) + smooth.trajectories[1].hvplot(title=&amp;apos;Smooth Trajectory&amp;apos;, **kwargs))
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="380" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/split.trajectories.png" width="846"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;traj = split.trajectories[1]

cleaned = traj.copy()
cleaned = mpd.OutlierCleaner(cleaned).clean(alpha=2)

smoothed = mpd.KalmanSmootherCV(cleaned).smooth(process_noise_std=0.1, measurement_noise_std=10)
    
(traj.hvplot(title=&amp;apos;Original Trajectory&amp;apos;, **kwargs) + 
 cleaned.hvplot(title=&amp;apos;Cleaned Trajectory&amp;apos;, **kwargs) + 
 smoothed.hvplot(title=&amp;apos;Cleaned &amp;amp; Smoothed Trajectory&amp;apos;, **kwargs))
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="386" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/smoothed.png" width="1301"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;轨迹聚类和分类&lt;/h3&gt;
 &lt;pre&gt;## 查看数据
driver_tc.explore(column=&amp;quot;session_id&amp;quot;, cmap=&amp;quot;plasma&amp;quot;, style_kwds={&amp;quot;weight&amp;quot;: 4})
## 根据最小距离间隔对轨迹数据进行简化
generalized = mpd.MinDistanceGeneralizer(driver_tc).generalize(tolerance=100)
generalized.to_traj_gdf()

## 对轨迹进行聚合操作
aggregator = mpd.TrajectoryCollectionAggregator(
    generalized,
    max_distance=1000,
    min_distance=100,
    min_stop_duration=timedelta(minutes=10),
)

## 提取显著点
pts = aggregator.get_significant_points_gdf()
pts.hvplot(geo=True, tiles=&amp;quot;OSM&amp;quot;)
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="634" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/pts.png" width="819"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;## 获取聚合轨迹的簇
clusters = aggregator.get_clusters_gdf()
(pts.hvplot(geo=True, tiles=&amp;quot;OSM&amp;quot;) * clusters.hvplot(geo=True, color=&amp;quot;red&amp;quot;))
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="638" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/clusters.png" width="815"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;## 使用folium绘制
# m = pts.explore(marker_kwds={&amp;quot;radius&amp;quot;: 3}, name=&amp;quot;Significant points&amp;quot;)
# clusters.explore(m=m, color=&amp;quot;red&amp;quot;, marker_kwds={&amp;quot;radius&amp;quot;: 3}, name=&amp;quot;Cluster centroids&amp;quot;)
# folium.TileLayer(&amp;quot;CartoDB positron&amp;quot;).add_to(m)
# folium.LayerControl().add_to(m)
# m

## 获取聚合后的轨迹数据的流动
flows = aggregator.get_flows_gdf()
(flows.hvplot(geo=True, hover_cols=[&amp;quot;weight&amp;quot;], line_width=dim(&amp;quot;weight&amp;quot;) * 7, color=&amp;quot;#1f77b3&amp;quot;,tiles=&amp;quot;CartoLight&amp;quot;) * clusters.hvplot(geo=True, color=&amp;quot;red&amp;quot;, size=dim(&amp;quot;n&amp;quot;)))
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="626" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/flows.png" width="829"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;## 使用Folium绘制
# m = flows.explore(style_kwds={&amp;quot;weight&amp;quot;: 5},name=&amp;quot;Flows&amp;quot;)
# clusters.explore( m=m,color=&amp;quot;red&amp;quot;,style_kwds={&amp;quot;style_function&amp;quot;: lambda x: {&amp;quot;radius&amp;quot;: x[&amp;quot;properties&amp;quot;][&amp;quot;n&amp;quot;]}}, name=&amp;quot;Clusters&amp;quot;)
# folium.TileLayer(&amp;quot;OpenStreetMap&amp;quot;).add_to(m)
# folium.LayerControl().add_to(m)
# m
&lt;/pre&gt;
 &lt;h3&gt;距离计算&lt;/h3&gt;
 &lt;pre&gt;## 选择2个轨迹
my_traj = driver_tc.trajectories[3]
toy_traj = driver_tc.trajectories[1]
## 呈现数据
ax = my_traj.plot()
toy_traj.plot(ax=ax, color=&amp;apos;red&amp;apos;)
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="343" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/toy_traj.plot_.png" width="565"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;## 计算记录
print(f&amp;apos;Distance: {toy_traj.distance(my_traj)} meters&amp;apos;) # 返回最短距离
print(f&amp;apos;Hausdorff distance: {toy_traj.hausdorff_distance(my_traj):.2f} meters&amp;apos;) # 返回Hausdorff距离
&lt;/pre&gt;
 &lt;p&gt;Hausdorff距离可以理解为：对于集合A 中的每个点，计算它到集合B的最近距离，然后在这些距离中找到最大值；反过来对于集合 B 中的每个点，计算它到集合A 的最近距离，然后在这些距离中找到最大值。Hausdorff距离是这两个最大值中的较大者。&lt;/p&gt;
 &lt;p&gt;参考链接：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;a href="https://github.com/movingpandas/movingpandas"&gt;movingpandas/movingpandas: Movement trajectory classes and functions built on top of GeoPandas (github.com)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://movingpandas.org/"&gt;MovingPandas | A Python library for movement data exploration and analysis&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://movingpandas.org/examples.html"&gt;Tutorials | MovingPandas&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://movingpandas.readthedocs.io/en/main/"&gt;MovingPandas Documentation — MovingPandas main documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;div&gt;

  &lt;h3&gt;相关文章:&lt;/h3&gt;  &lt;ol&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/seaborn.html" rel="bookmark" title="Python&amp;#25968;&amp;#25454;&amp;#21487;&amp;#35270;&amp;#21270;&amp;#20043;Seaborn"&gt;Python数据可视化之Seaborn&lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/learn-css.html" rel="bookmark" title="CSS&amp;#20307;&amp;#31995;&amp;#21270;&amp;#23398;&amp;#20064;"&gt;CSS体系化学习&lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/arima.html" rel="bookmark" title="&amp;#26102;&amp;#38388;&amp;#24207;&amp;#21015;&amp;#39044;&amp;#27979;&amp;#20043;ARIMA"&gt;时间序列预测之ARIMA&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>器→工具 工具软件 开源项目 GIS</category>
      <guid isPermaLink="true">https://itindex.net/detail/62944-python-%E5%9C%B0%E7%90%86-%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90</guid>
      <pubDate>Wed, 09 Oct 2024 19:54:20 CST</pubDate>
    </item>
    <item>
      <title>开源可视化报表工具：Superset</title>
      <link>https://itindex.net/detail/62903-%E5%BC%80%E6%BA%90-%E5%8F%AF%E8%A7%86%E5%8C%96-%E5%B7%A5%E5%85%B7</link>
      <description>&lt;h2&gt;Superset简介&lt;/h2&gt;
 &lt;p&gt;Superset 是一个开源的数据可视化和数据探索平台，最初由 Airbnb 开发，后来成为了 Apache 软件基金会的顶级项目。它支持各种类型的数据源，如数据库和 SQL 引擎，并提供了一个易于使用的界面来创建和共享仪表板和图表。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="528" src="https://www.biaodianfu.com/wp-content/uploads/2024/01/superset-1.jpg" width="780"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;主要特点包括：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;数据可视化: Superset 提供了丰富的图表库，支持从简单的折线图和条形图到更复杂的地理信息系统 (GIS) 可视化等。&lt;/li&gt;
  &lt;li&gt;数据探索: 用户可以通过 Superset 的 SQL 编辑器执行查询，探索数据，并将结果可视化。&lt;/li&gt;
  &lt;li&gt;仪表板: 可以将多个图表组合成仪表板，为数据分析提供全面视图。&lt;/li&gt;
  &lt;li&gt;安全性和权限管理: Superset 支持细粒度的访问控制，允许管理员定义用户和角色，控制对数据和功能的访问。&lt;/li&gt;
  &lt;li&gt;易于集成: 作为一个开源工具，Superset 可以与多种数据源和其他数据工具集成。&lt;/li&gt;
  &lt;li&gt;自定义和扩展: 用户可以根据需要自定义图表和界面，并且可以开发新的可视化插件。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;Superset 适用于数据分析师和开发人员，帮助他们快速有效地探索和可视化数据，从而做出更好的数据驱动决策。&lt;/p&gt;
 &lt;p&gt;看板示例：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="447" src="https://www.biaodianfu.com/wp-content/uploads/2024/01/superset-2.jpg" width="780"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;拖拽式看板编辑器：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="414" src="https://www.biaodianfu.com/wp-content/uploads/2024/01/superset-3.jpg" width="780"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;SQL编辑器：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="546" src="https://www.biaodianfu.com/wp-content/uploads/2024/01/sql-lab.jpg" width="780"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;Superset架构&lt;/h2&gt;
 &lt;p&gt;Apache Superset 是一款开源的数据可视化和数据探索平台，它的架构设计允许用户轻松地进行数据分析并创建交互式的仪表板。Superset的架构主要由以下几个核心组件构成：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="464" src="https://www.biaodianfu.com/wp-content/uploads/2024/01/superset-4-1.png" width="780"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;Web服务器&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Flask：Superset使用Flask作为其Web框架，处理HTTP请求和渲染界面。&lt;/li&gt;
  &lt;li&gt;Gunicorn：在生产环境中，通常使用Gunicorn作为WSGI HTTP服务器来运行Flask应用。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;SQL查询引擎&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;SQLAlchemy：Superset通过SQLAlchemy与数据源进行交互，它支持多种数据库。&lt;/li&gt;
  &lt;li&gt;Pandas：在某些情况下，Superset会使用Pandas库来处理数据。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;数据库&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;元数据数据库：存储Superset的配置信息、仪表板定义、数据源定义等。&lt;/li&gt;
  &lt;li&gt;缓存数据库：用于缓存数据，提高查询性能。Redis和Memcached是常用的选项。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;前端&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;React &amp;amp; JavaScript：Superset的前端主要使用React框架结合JavaScript开发，用于实现用户界面的交互和动态展示。&lt;/li&gt;
  &lt;li&gt;js：图表的渲染利用了D3.js库，提供丰富的可视化选项。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;安全性&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;认证与授权：Superset提供灵活的认证选项（如LDAP、OAuth、数据库等）和基于角色的访问控制（RBAC）。&lt;/li&gt;
  &lt;li&gt;数据安全：支持数据级别的安全控制，确保用户只能访问授权的数据。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;扩展性&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;插件系统：Superset支持自定义插件，允许用户扩展新的可视化类型或其他功能。&lt;/li&gt;
  &lt;li&gt;API：提供REST API，支持与其他系统的集成。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;任务调度器&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Celery：用于执行后台任务，如异步查询和发送报告。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;Superset功能扩展&lt;/h2&gt;
 &lt;p&gt;拓展Apache Superset主要涉及添加新的可视化类型、增强现有功能、集成更多数据源等方面。&lt;/p&gt;
 &lt;p&gt;开发自定义可视化插件&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Superset支持通过插件机制添加新的图表和可视化类型。&lt;/li&gt;
  &lt;li&gt;可以使用React和JavaScript开发新的可视化组件。&lt;/li&gt;
  &lt;li&gt;开发完成后，将插件包含在Superset的配置中，使其成为可用的可视化类型。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;集成更多数据源&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Superset通过SQLAlchemy与数据源进行交互，可以添加对新数据库的支持。&lt;/li&gt;
  &lt;li&gt;通过添加相应的数据库驱动和SQLAlchemy方言，可以实现新的数据库支持。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;增强现有功能&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;对Superset的源代码进行修改，可以增强或改变现有功能。&lt;/li&gt;
  &lt;li&gt;包括改进用户界面、增加新的数据处理功能、优化性能等。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;使用API进行集成&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Superset提供了REST API，可以用来与其他系统集成。&lt;/li&gt;
  &lt;li&gt;例如，可以通过API自动化仪表板的创建、更新数据源等。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;安全性和认证的定制&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;可以定制Superset的安全性和认证机制，如集成企业的单点登录（SSO）系统。&lt;/li&gt;
  &lt;li&gt;修改认证流程以支持LDAP、OAuth等。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;使用和配置Celery任务调度器&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;使用Celery来优化和管理后台任务，如数据刷新、报告发送等。&lt;/li&gt;
  &lt;li&gt;可以定制Celery的配置以满足特定的性能和规模需求。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;参考链接：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;a href="https://superset.apache.org/"&gt;Welcome | Superset (apache.org)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://github.com/apache/superset"&gt;GitHub – apache/superset: Apache Superset is a Data Visualization and Data Exploration Platform&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://github.com/amancevice/docker-superset"&gt;GitHub – amancevice/docker-superset: Docker image for Airbnb’s Superset&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;div&gt;

  &lt;h3&gt;相关文章:&lt;/h3&gt;  &lt;ol&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/docker-jupyterhub.html" rel="bookmark" title="Docker&amp;#23433;&amp;#35013;&amp;#22810;&amp;#29992;&amp;#25143;&amp;#29256;JupyterHub"&gt;Docker安装多用户版JupyterHub&lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/hive-udf.html" rel="bookmark" title="Hive UDF&amp;#30340;&amp;#24320;&amp;#21457;&amp;#31616;&amp;#20171;"&gt;Hive UDF的开发简介&lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/microsoft-rest-api-guidelines.html" rel="bookmark" title="Microsoft REST API Guidelines&amp;#20013;&amp;#25991;&amp;#32763;&amp;#35793;"&gt;Microsoft REST API Guidelines中文翻译&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>器→工具 工具软件 BI</category>
      <guid isPermaLink="true">https://itindex.net/detail/62903-%E5%BC%80%E6%BA%90-%E5%8F%AF%E8%A7%86%E5%8C%96-%E5%B7%A5%E5%85%B7</guid>
      <pubDate>Thu, 04 Jan 2024 12:54:55 CST</pubDate>
    </item>
    <item>
      <title>你的Spring Boot应用启动很慢？不妨试试这个工具！</title>
      <link>https://itindex.net/detail/62813-spring-boot-%E5%BA%94%E7%94%A8</link>
      <description>&lt;p&gt;睡不着闲逛，在GitHub上看到一个不错的开源项目：  &lt;strong&gt;Spring Startup Analyzer&lt;/strong&gt;。&lt;/p&gt; &lt;p&gt;从项目名称中就大概能猜到，这是一个分析Spring应用启动过程的工具。Spring Startup Analyzer通过采集Spring应用启动过程的数据，进而生成一个交互式的分析报告，帮助用户发现Spring应用启动慢的位置。同时，Spring Startup Analyzer还提供了Spring Bean异步初始化的工具，来帮助开发者加快Spring应用的启动时间。&lt;/p&gt; &lt;p&gt;下面一起来看看其提供的强大功能。&lt;/p&gt; &lt;h2&gt;  &lt;a href="https://blog.didispace.com/#&amp;#20998;&amp;#26512;&amp;#33021;&amp;#21147;" title="&amp;#20998;&amp;#26512;&amp;#33021;&amp;#21147;"&gt;&lt;/a&gt;分析能力&lt;/h2&gt; &lt;p&gt;我们可以先从该项目中给出HTML样例报告（  &lt;a href="https://linyimin-blog.oss-cn-beijing.aliyuncs.com/spring-satrtup-analyzer/hokage-20230618000928-192.168.0.101-analyzer.html" rel="external nofollow noopener noreferrer" target="_blank"&gt;点击这里查看&lt;/a&gt;）来看看它所提供的分析功能。&lt;/p&gt; &lt;p&gt;把报告内容的细节部分都收起来，可以看到如下图所示的内容：&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="https://blog.didispace.com/images2/202307/spring-satrtup-analyzer/1690049518900.png"&gt;&lt;/img&gt;  &lt;/p&gt; &lt;p&gt;主要有六个部分：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;启动的统计数据。其中包括：启动时间、Bean的数量、使用/总共的JAR包数量、未使用/总共的JAR包数量、ClassLoader数量&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;  &lt;img alt="" src="https://blog.didispace.com/images2/202307/spring-satrtup-analyzer/1690049712845.png"&gt;&lt;/img&gt;  &lt;/p&gt; &lt;ul&gt;  &lt;li&gt;Spring Bean初始化数据。这里采集了每个Spring Bean的初始化时间及其细节内容&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;  &lt;img alt="" src="https://blog.didispace.com/images2/202307/spring-satrtup-analyzer/1690049735608.png"&gt;&lt;/img&gt;  &lt;/p&gt; &lt;ul&gt;  &lt;li&gt;Bean初始化时间线。通过时间线的方式，清晰地展现了Spring应用启动时候，各个Bean的顺序关系以及时间消耗&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;  &lt;img alt="" src="https://blog.didispace.com/images2/202307/spring-satrtup-analyzer/1690049783718.png"&gt;&lt;/img&gt;  &lt;/p&gt; &lt;ul&gt;  &lt;li&gt;方法调用的详细信息。这里统计了每个方法的调用时间、总时间开销和每次调用的平均时间&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;  &lt;img alt="" src="https://blog.didispace.com/images2/202307/spring-satrtup-analyzer/1690049880585.png"&gt;&lt;/img&gt;  &lt;/p&gt; &lt;p&gt;点开之后，还能看到具体每次调用时候的时间开销和一些调用细节：&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="https://blog.didispace.com/images2/202307/spring-satrtup-analyzer/1690049910865.png"&gt;&lt;/img&gt;  &lt;/p&gt; &lt;ul&gt;  &lt;li&gt;启动后未使用的JAR。列出了所有Spring应用启动后没有使用的jar包，可以有效的帮助你清理不需要的依赖，为应用瘦身&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;  &lt;img alt="" src="https://blog.didispace.com/images2/202307/spring-satrtup-analyzer/1690050005320.png"&gt;&lt;/img&gt;  &lt;/p&gt; &lt;ul&gt;  &lt;li&gt;应用启动过程的线程火焰图&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;  &lt;img alt="" src="https://blog.didispace.com/images2/202307/spring-satrtup-analyzer/1690050108092.png"&gt;&lt;/img&gt;  &lt;/p&gt; &lt;h2&gt;  &lt;a href="https://blog.didispace.com/#&amp;#22914;&amp;#20309;&amp;#20351;&amp;#29992;" title="&amp;#22914;&amp;#20309;&amp;#20351;&amp;#29992;"&gt;&lt;/a&gt;如何使用&lt;/h2&gt; &lt;p&gt;通过上面的介绍，相信你已经了解该工具的强大之处了。接下来就可以通过下面的方法尝试分析一下自己的应用吧：&lt;/p&gt; &lt;p&gt;第一步：从里面的链接中下载最新的安装包&lt;/p&gt; &lt;p&gt;  &lt;a href="https://github.com/linyimin0812/spring-startup-analyzer/tags" rel="external nofollow noopener noreferrer" target="_blank"&gt;https://github.com/linyimin0812/spring-startup-analyzer/tags&lt;/a&gt;&lt;/p&gt; &lt;p&gt;第二步：解压下载的安装包，记住解压后的路径，下面一步要用&lt;/p&gt; &lt;p&gt;第三步：编辑Spring Boot的启动参数，包括：&lt;/p&gt; &lt;ol&gt;  &lt;li&gt;该工具采用agent的方式启动，所以要添加参数   &lt;code&gt;-javaagent:$HOME/spring-startup-analyzer/lib/spring-profiler-agent.jar&lt;/code&gt;，这里   &lt;code&gt;$HOME&lt;/code&gt;代表以前的解压路径，记得根据上面解压后的路径编辑这个参数&lt;/li&gt;  &lt;li&gt;配置分析工具的参数，这里根据自己需要添加即可，比如可以配置超时时间30分钟：   &lt;code&gt;-Dspring-startup-analyzer.app.health.check.timeout=30&lt;/code&gt;，其他可配置项如下表，你可以工具自己应用的情况去修改：&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;  &lt;img alt="" src="https://blog.didispace.com/images2/202307/spring-satrtup-analyzer/1690050542019.png"&gt;&lt;/img&gt;  &lt;/p&gt; &lt;p&gt;第四步：查看该工具的日志，可以通过  &lt;code&gt;$HOME/spring-startup-analyzer/logs&lt;/code&gt;路径，这里  &lt;code&gt;$HOME&lt;/code&gt;代表以前的解压路径，日志文件的类别为：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;startup.log: 启动过程中的日志&lt;/li&gt;  &lt;li&gt;transform.log: 被re-transform的类/方法信息&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;另外，该工具还支持自定义扩展，这里DD没试过，就不具体介绍了。感兴趣的童鞋可以根据文档去试试。&lt;/p&gt; &lt;h2&gt;  &lt;a href="https://blog.didispace.com/#&amp;#21551;&amp;#21160;&amp;#20248;&amp;#21270;" title="&amp;#21551;&amp;#21160;&amp;#20248;&amp;#21270;"&gt;&lt;/a&gt;启动优化&lt;/h2&gt; &lt;p&gt;这里提到了一个启动加速的优化思路，就是把一些耗时的Bean初始化改成异步就能实现。该项目提供了Bean的异步初始化工具，也非常好用，只需要下面几步就能完成。&lt;/p&gt; &lt;p&gt;第一步：引入依赖&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;&amp;lt;dependency&amp;gt;     &lt;br /&gt;    &amp;lt;groupId&amp;gt;io.github.linyimin0812&amp;lt;/groupId&amp;gt;     &lt;br /&gt;    &amp;lt;artifactId&amp;gt;spring-async-bean-starter&amp;lt;/artifactId&amp;gt;     &lt;br /&gt;    &amp;lt;version&amp;gt;2.0.2&amp;lt;/version&amp;gt;     &lt;br /&gt;&amp;lt;/dependency&amp;gt;     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;第二步：配置参数&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;# 异步化的Bean可能在Spring Bean初始化顺序的末尾，导致异步优化效果不佳，打开配置优先加载异步化的Bean     &lt;br /&gt;spring-startup-analyzer.boost.spring.async.bean-priority-load-enable=true     &lt;br /&gt;# 指定异步的Bean名称     &lt;br /&gt;spring-startup-analyzer.boost.spring.async.bean-names=testBean,testComponent     &lt;br /&gt;# 执行异步化Bean初始化方法线程池的核心线程数     &lt;br /&gt;spring-startup-analyzer.boost.spring.async.init-bean-thread-pool-core-size=8     &lt;br /&gt;# 执行异步化Bean初始化方法线程池的最大线程数     &lt;br /&gt;spring-startup-analyzer.boost.spring.async.init-bean-thread-pool-max-size=8     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;第三步：检查Bean是否异步初始化。查看日志$HOME/spring-startup-analyzer/logs/startup.log文件，对于异步执行初始化的方法，会按照以下格式写一条日志:&lt;/p&gt; &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;async-init-bean, beanName: ${beanName}, async init method: ${initMethodName}     &lt;br /&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt; &lt;p&gt;但是，作者在文档中也提到了，异步并不是万能的，你还需要注意以下这几点：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;应该优先从代码层面优化初始化时间长的Bean，从根本上解决Bean初始化耗时长问题&lt;/li&gt;  &lt;li&gt;对于二方包/三方包中初始化耗时长的Bean(无法进行代码优化)再考虑Bean的异步化&lt;/li&gt;  &lt;li&gt;对于不被依赖的Bean可以放心进行异步化，可以通过各个Bean加载耗时中的Root Bean判断Bean是否被其他Bean依赖&lt;/li&gt;  &lt;li&gt;对于被依赖的Bean需要小心分析，在应用启动过程中不能其他Bean被调用，否则可能会存在问题&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;好了，今天的学习就到这里！如果您学习过程中如遇困难？可以加入我们超高质量的  &lt;a href="https://blog.didispace.com/join-group-spring/index.html"&gt;Spring技术交流群&lt;/a&gt;，参与交流与讨论，更好的学习与进步！更多  &lt;a href="http://blog.didispace.com/spring-boot-learning-2x/"&gt;Spring Boot教程可以点击直达！&lt;/a&gt;，欢迎收藏与转发支持！&lt;/p&gt; &lt;p&gt;最后，奉上项目地址：  &lt;a href="https://github.com/linyimin0812/spring-startup-analyzer" rel="external nofollow noopener noreferrer" target="_blank"&gt;https://github.com/linyimin0812/spring-startup-analyzer&lt;/a&gt;&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>Spring Boot 开源 Spring Boot Spring</category>
      <guid isPermaLink="true">https://itindex.net/detail/62813-spring-boot-%E5%BA%94%E7%94%A8</guid>
      <pubDate>Sat, 22 Jul 2023 18:03:35 CST</pubDate>
    </item>
    <item>
      <title>自动生成代码工具-cursor(集成ChatGpt)</title>
      <link>https://itindex.net/detail/62716-%E4%BB%A3%E7%A0%81-%E5%B7%A5%E5%85%B7-cursor</link>
      <description>&lt;p&gt;最近体验了一把cursor，自动生成代码工具，集成了最近很火的ChatGpt，目前比较好的就是代码生成工具大概就是  &lt;a href="https://docs.github.com/zh/copilot"&gt;github copilot&lt;/a&gt;和  &lt;a href="https://www.cursor.so/"&gt;cursor&lt;/a&gt;，不过github copilot需要付费使用或者漫长的waitlist，所以目前比较好的是cursor&lt;/p&gt;
 &lt;h2&gt;配置&lt;/h2&gt;
 &lt;p&gt;配置自己经常使用的语言，比如ts、html、css等等&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image.png" src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/412812e23dca4071bdfdecfea097046a~tplv-k3u1fbpfcp-watermark.image?"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;操作&lt;/h2&gt;
 &lt;p&gt;目前来说就两个功能，如下：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image.png" src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f769e90217c64d0c8d8c7b2b97e26047~tplv-k3u1fbpfcp-watermark.image?"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;ctrl+k：生成代码&lt;/h3&gt;
 &lt;p&gt;描述需要生成的代码功能，回车后会自动帮你生成，比如生成一个斐波那契数列函数&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="cursor(ctrl).gif" src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/34418d6f60364b17810ec8eba4b87a22~tplv-k3u1fbpfcp-watermark.image?"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;如果对这段代码想做一些编辑操作，比如添加注释，只需选中代码，再次  &lt;code&gt;ctrl+k&lt;/code&gt; 回车即可，如下：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="cursor_2.gif" src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/71c52e492cc8419db996e0567f569e69~tplv-k3u1fbpfcp-watermark.image?"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;生成之后，提供了  &lt;code&gt;Accept All&lt;/code&gt;和  &lt;code&gt;Reject All&lt;/code&gt;两个功能，类似于【全选/全不选】的功能&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;code&gt;Accept All&lt;/code&gt; ：添加所有&lt;/li&gt;
  &lt;li&gt;   &lt;code&gt;Reject All&lt;/code&gt; ：删除所有&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;img alt="image.png" src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b3a41e421b6f43698e4fc6e2e7b7f8e2~tplv-k3u1fbpfcp-watermark.image?"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;ctrl+l：智能对话&lt;/h3&gt;
 &lt;p&gt;类型于gpt-4的功能，对他提出你的疑惑，他会给出解决方案，不用去百度答案，如下：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="cursor_4.gif" src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c39bd61d06dd4c8eb3d039f00ade4928~tplv-k3u1fbpfcp-watermark.image?"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;当对这段代码不理解时，也可以选中代码，问他实现逻辑或者代码结构等，如下：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="cursor_5.gif" src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3f56374575b94e4ebfc00f4f59611814~tplv-k3u1fbpfcp-watermark.image?"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;注意：从上面对话可以看出来，cursor对于语言不敏感，所以如果想要生成对话呈中文表达，最好前面加上【请用中文】类似于这类词语&lt;/p&gt;
 &lt;h2&gt;用途&lt;/h2&gt;
 &lt;p&gt;cursor的功能目前对于程序员来说，算是简而全的一个代码工具，他支持多种语言，如js、ts、python、rust、go、java等等市面上比较常见的编程语言。他可以根据你的描述自动生成代码，还可以再你接受别人代码是帮助你理解、重构代码，并且可以测试bug、校验格式等等&lt;/p&gt;
 &lt;h2&gt;参考链接&lt;/h2&gt;
 &lt;p&gt;  &lt;a href="https://cn-sec.com/archives/1614273.html"&gt;&lt;/a&gt;  &lt;a href="https://cn-sec.com/archives/1614273.html"&gt;https://cn-sec.com/archives/1614273.html&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;a href="https://cloud.tencent.com/developer/article/2242409"&gt;&lt;/a&gt;  &lt;a href="https://cloud.tencent.com/developer/article/2242409"&gt;https://cloud.tencent.com/developer/article/2242409&lt;/a&gt;&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/62716-%E4%BB%A3%E7%A0%81-%E5%B7%A5%E5%85%B7-cursor</guid>
      <pubDate>Fri, 31 Mar 2023 20:11:04 CST</pubDate>
    </item>
    <item>
      <title>[分享创造] Vesta: 一个 Docker 和 Kubernetes 配置安全的快速检查工具</title>
      <link>https://itindex.net/detail/62548-%E5%88%86%E4%BA%AB-%E5%88%9B%E9%80%A0-vesta</link>
      <description>&lt;h2&gt;Vesta 可以做什么&lt;/h2&gt;
 &lt;p&gt;伴随着容器技术的快速发展，容器安全问题也逐渐成为企业所关注的话题，越来越多的公司以及个人开发着选择将他们的服务迁移到云上。目前市面上的容器扫描或容器配置检查的产品大部门都需要进行繁琐的环境配置，同时对机器性能也有着比较高的要求，而开发者或安全测试者或许只是需要扫描少数的镜像或者配置，繁琐的配置和高昂的机器费用对他们来说难以承担，导致安全检查的效率不佳。Vesta 是一款集容器扫描，Docker 和 Kubernetes 配置基线检查于一身的工具。检查内容包括镜像或容器中包含漏洞版本的组件，Docker 以及 Kubernetes 的危险配置。同时也是一个灵活，快速的工具，能够在各种系统上运行，包括但不限于 Windows ，Linux 以及 MacOS&lt;/p&gt;
 &lt;p&gt;Vesta 为开发者和安全测试者提供了方便、快速的解决方案。整个程序由 golang 编写，只需要使用  &lt;code&gt;go build&lt;/code&gt;或者从 releases 下载，无需配置任何环境和数据库，并且在 1 vCPU, 2G Memory 的机器上就可运行，最大化方便使用者们。&lt;/p&gt;
 &lt;p&gt;项目地址&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;a href="https://github.com/kvesta/vesta" rel="nofollow"&gt;https://github.com/kvesta/vesta&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;功能介绍&lt;/h2&gt;
 &lt;p&gt;Vesta 包含两大模块&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;scan: 扫描容器和镜像的组件信息，检测是否包含 CVE 漏洞版本&lt;/li&gt;
  &lt;li&gt;analyze: 检查 Docker 和 Kubernetes 配置，是否包含危险配置。后续考虑附加攻击方法&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;检查列表&lt;/h2&gt;
 &lt;blockquote&gt;
  &lt;p&gt;Docker 检查&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;th&gt;Supported&lt;/th&gt;
   &lt;th&gt;Check Item&lt;/th&gt;
   &lt;th&gt;Description&lt;/th&gt;
   &lt;th&gt;Severity&lt;/th&gt;
&lt;/tr&gt;


  &lt;tr&gt;
   &lt;td&gt;✔&lt;/td&gt;
   &lt;td&gt;PrivilegeAllowed&lt;/td&gt;
   &lt;td&gt;危险的特权模式&lt;/td&gt;
   &lt;td&gt;critical&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;✔&lt;/td&gt;
   &lt;td&gt;Capabilities&lt;/td&gt;
   &lt;td&gt;危险 capabilities 被设置&lt;/td&gt;
   &lt;td&gt;critical&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;✔&lt;/td&gt;
   &lt;td&gt;Volume Mount&lt;/td&gt;
   &lt;td&gt;敏感或危险目录被挂载&lt;/td&gt;
   &lt;td&gt;critical&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;✔&lt;/td&gt;
   &lt;td&gt;Docker Unauthorized&lt;/td&gt;
   &lt;td&gt;2375 端口打开并且未授权&lt;/td&gt;
   &lt;td&gt;critical&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;✔&lt;/td&gt;
   &lt;td&gt;Kernel version&lt;/td&gt;
   &lt;td&gt;当前内核版本存在逃逸漏洞&lt;/td&gt;
   &lt;td&gt;critical&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;✔&lt;/td&gt;
   &lt;td&gt;Network Module&lt;/td&gt;
   &lt;td&gt;Net 模式为    &lt;code&gt;host&lt;/code&gt;模式并且在特定 containerd 版本下&lt;/td&gt;
   &lt;td&gt;critical&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;✔&lt;/td&gt;
   &lt;td&gt;Docker Server version&lt;/td&gt;
   &lt;td&gt;Docker Server 版本存在漏洞&lt;/td&gt;
   &lt;td&gt;critical/high/medium/low&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;✔&lt;/td&gt;
   &lt;td&gt;Docker env password check&lt;/td&gt;
   &lt;td&gt;Docker env 是否存在弱密码&lt;/td&gt;
   &lt;td&gt;high/medium&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;✔&lt;/td&gt;
   &lt;td&gt;Image tag check&lt;/td&gt;
   &lt;td&gt;Image 没有被打 tag 或为默认 latest&lt;/td&gt;
   &lt;td&gt;low&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;待定&lt;/td&gt;
   &lt;td&gt;Container env&lt;/td&gt;
   &lt;td&gt;检查数据库是否未设置密码, 包括但不限于    &lt;code&gt;MySQL&lt;/code&gt;,     &lt;code&gt;Redis&lt;/code&gt;,     &lt;code&gt;Memcache&lt;/code&gt;&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;待定&lt;/td&gt;
   &lt;td&gt;IaC scan&lt;/td&gt;
   &lt;td&gt;IaC 扫描&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt; &lt;hr&gt;&lt;/hr&gt;
 &lt;blockquote&gt;
  &lt;p&gt;Kubernetes 检查&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;th&gt;Supported&lt;/th&gt;
   &lt;th&gt;Check Item&lt;/th&gt;
   &lt;th&gt;Description&lt;/th&gt;
   &lt;th&gt;Severity&lt;/th&gt;
&lt;/tr&gt;


  &lt;tr&gt;
   &lt;td&gt;✔&lt;/td&gt;
   &lt;td&gt;PrivilegeAllowed&lt;/td&gt;
   &lt;td&gt;危险的特权模式&lt;/td&gt;
   &lt;td&gt;critical&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;✔&lt;/td&gt;
   &lt;td&gt;Capabilities&lt;/td&gt;
   &lt;td&gt;危险 capabilities 被设置&lt;/td&gt;
   &lt;td&gt;critical&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;✔&lt;/td&gt;
   &lt;td&gt;PV and PVC&lt;/td&gt;
   &lt;td&gt;PV 被挂载到敏感目录并且状态为 active&lt;/td&gt;
   &lt;td&gt;critical/medium&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;✔&lt;/td&gt;
   &lt;td&gt;ClusterRoleBinding&lt;/td&gt;
   &lt;td&gt;默认账户被赋予了权限&lt;/td&gt;
   &lt;td&gt;high/medium&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;✔&lt;/td&gt;
   &lt;td&gt;Kubernetes-dashborad&lt;/td&gt;
   &lt;td&gt;检查     &lt;code&gt;-enable-skip-login&lt;/code&gt;以及 dashborad 的账户权限&lt;/td&gt;
   &lt;td&gt;critical/high/low&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;✔&lt;/td&gt;
   &lt;td&gt;Kernel version (k8s versions is less than v1.24)&lt;/td&gt;
   &lt;td&gt;当前内核版本存在逃逸漏洞&lt;/td&gt;
   &lt;td&gt;critical&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;✔&lt;/td&gt;
   &lt;td&gt;Docker Server version  (k8s versions is less than v1.24)&lt;/td&gt;
   &lt;td&gt;Docker Server 版本存在漏洞&lt;/td&gt;
   &lt;td&gt;critical/high/medium/low&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;✔&lt;/td&gt;
   &lt;td&gt;Kubernetes certification expiration&lt;/td&gt;
   &lt;td&gt;证书到期时间小于 30 天&lt;/td&gt;
   &lt;td&gt;medium&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;✔&lt;/td&gt;
   &lt;td&gt;ConfigMap and Secret check&lt;/td&gt;
   &lt;td&gt;ConfigMap 或者 Secret 是否存在弱密码&lt;/td&gt;
   &lt;td&gt;high/medium&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;✔&lt;/td&gt;
   &lt;td&gt;Auto Mount ServiceAccount Token&lt;/td&gt;
   &lt;td&gt;Pod 默认挂载了     &lt;code&gt;/var/run/secrets/     &lt;a href="http://kubernetes.io/serviceaccount/token" rel="nofollow"&gt;kubernetes.io/serviceaccount/token&lt;/a&gt;&lt;/code&gt;.&lt;/td&gt;
   &lt;td&gt;low&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;✔&lt;/td&gt;
   &lt;td&gt;NoResourceLimits&lt;/td&gt;
   &lt;td&gt;没有限制资源的使用，例如 CPU,Memory, 存储&lt;/td&gt;
   &lt;td&gt;low&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;✔&lt;/td&gt;
   &lt;td&gt;Job and Cronjob&lt;/td&gt;
   &lt;td&gt;Job 或 CronJob 没有设置 seccomp 或 seLinux 安全策略&lt;/td&gt;
   &lt;td&gt;low&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;待定&lt;/td&gt;
   &lt;td&gt;CVE-2022-29179&lt;/td&gt;
   &lt;td&gt;检测 CVE-2022-29179 是否存在&lt;/td&gt;
   &lt;td&gt;critical&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;待定&lt;/td&gt;
   &lt;td&gt;Envoy admin&lt;/td&gt;
   &lt;td&gt;Envoy admin 被配置以及监听    &lt;code&gt;0.0.0.0&lt;/code&gt;.&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;待定&lt;/td&gt;
   &lt;td&gt;Kubelet 10255 and Kubectl proxy&lt;/td&gt;
   &lt;td&gt;10255 port 打开或 Kubectl proxy 开启&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;待定&lt;/td&gt;
   &lt;td&gt;Trampoline attack&lt;/td&gt;
   &lt;td&gt;RBAC 权限不安全，容易遭受 Trampoline 攻击&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;待定&lt;/td&gt;
   &lt;td&gt;IaC scan&lt;/td&gt;
   &lt;td&gt;Iac 扫描&lt;/td&gt;
   &lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt; &lt;h2&gt;使用样例&lt;/h2&gt;
 &lt;p&gt;检查 k8s 的基础配置&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;$./vesta analyze k8s

2022/11/29 23:15:59 Start analysing
2022/11/29 23:15:59 Geting docker server version
2022/11/29 23:15:59 Geting kernel version

Detected 4 vulnerabilities

Pods:
+----+--------------------+------------------------------+-------------------+-----------------------+----------+--------------------------------+
| ID |     POD DETAIL     |            PARAM             |       VALUE       |         TYPE          | SEVERITY |          DESCRIPTION           |
+----+--------------------+------------------------------+-------------------+-----------------------+----------+--------------------------------+
|  1 | Name: vulntest     | test-volume                  | /etc              | Directory             | critical | Mounting &amp;apos;/etc&amp;apos; is suffer      |
|    | Namespace: default |                              |                   |                       |          | vulnerable of container        |
|    |                    |                              |                   |                       |          | escape.                        |
+    +                    +------------------------------+-------------------+-----------------------+----------+--------------------------------+
|    |                    | Privileged                   | true              | Pod                   | critical | There has a potential          |
|    |                    |                              |                   |                       |          | container escape in privileged |
|    |                    |                              |                   |                       |          | module.                        |
+    +                    +------------------------------+-------------------+-----------------------+----------+--------------------------------+
|    |                    | AllowPrivilegeEscalation     | true              | Pod                   | critical | There has a potential          |
|    |                    |                              |                   |                       |          | container escape in privileged |
|    |                    |                              |                   |                       |          | module.                        |
+    +                    +------------------------------+-------------------+-----------------------+----------+--------------------------------+
|    |                    | Resource                     | memory, cpu,      | Pod                   | low      | None of resources is be        |
|    |                    |                              | ephemeral-storage |                       |          | limited.                       |
+----+--------------------+------------------------------+-------------------+-----------------------+----------+--------------------------------+

Configures:
+----+-----------------------------+--------------------------------+--------------------------------------------------------+----------+--------------------------------+
| ID |            TYPEL            |             PARAM              |                         VALUE                          | SEVERITY |          DESCRIPTION           |
+----+-----------------------------+--------------------------------+--------------------------------------------------------+----------+--------------------------------+
|  1 | K8s version less than v1.24 | kernel version                 | 5.10.104-linuxkit                                      | critical | Kernel version is suffering    |
|    |                             |                                |                                                        |          | the CVE-2022-0185 with         |
|    |                             |                                |                                                        |          | CAP_SYS_ADMIN vulnerablility,  |
|    |                             |                                |                                                        |          | has a potential container      |
|    |                             |                                |                                                        |          | escape.                        |
+----+-----------------------------+--------------------------------+--------------------------------------------------------+----------+--------------------------------+
|  2 | ConfigMap                   | data: db.string                | db.string:mysql+pymysql://dbapp:Password123@db:3306/db | high     | ConfigMap has found weak       |
|    |                             |                                |                                                        |          | password: &amp;apos;Password123&amp;apos;.       |
+----+-----------------------------+--------------------------------+--------------------------------------------------------+----------+--------------------------------+
|  3 | Secret                      | data: password                 | password:Password123                                   | high     | Secret has found weak          |
|    |                             |                                |                                                        |          | password: &amp;apos;Password123&amp;apos;.       |
+----+-----------------------------+--------------------------------+--------------------------------------------------------+----------+--------------------------------+
|  4 | ClusterRoleBinding          | binding name:                  | verbs:                                                 | high     | Key permission are given to    |
|    |                             | vuln-clusterrolebinding |      | get,watch,list,create,update |                         |          | the default service account    |
|    |                             | rolename: vuln-clusterrole |   | resources: pods,services                               |          | which will cause a potential   |
|    |                             | namespace: default             |                                                        |          | container escape.              |
+----+-----------------------------+--------------------------------+--------------------------------------------------------+----------+--------------------------------+
&lt;/code&gt;&lt;/pre&gt;
 &lt;h2&gt;展望&lt;/h2&gt;
 &lt;p&gt;Vesta 希望能够最大化方便开发者们排查日常配置中的一些基线安全问题，并且也希望云上环境更加安全&lt;/p&gt;

	&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/62548-%E5%88%86%E4%BA%AB-%E5%88%9B%E9%80%A0-vesta</guid>
      <pubDate>Sat, 17 Dec 2022 00:48:36 CST</pubDate>
    </item>
    <item>
      <title>黑客或者从事安全领域工作的人用metasploit、Nessus这些工具用的多吗？ - 知乎</title>
      <link>https://itindex.net/detail/62544-%E9%BB%91%E5%AE%A2-%E4%BB%8E%E4%BA%8B-%E5%AE%89%E5%85%A8</link>
      <description>&lt;div&gt;    &lt;p&gt;可耻地匿了。&lt;/p&gt;    &lt;p&gt;作为一个世界500强的金融企业，我们对于漏洞自查这块，完全依赖于商业版Findstone，在买这个以前，自查都用的免费版Nessus。上面反映的威胁，中、高、高危一律限期整改，结束。&lt;/p&gt;    &lt;p&gt;我们会自己写脚本来加固标装OS，会聘请军工来对自己进行透渗试测，会优化基线并推行，但是不会自己写工具实现自己的需求。因为领导相信：员工只需要做我安排的任务就可以了，这种专业的事情还是买来的好。(╯‵□′)╯︵┻━┻不止一个项目，我们私底下都说这大几百万你给我一半就行我保证给你办好绝对实现你的需求，但是领导不干啊，即使只考虑法律保障道德风险也就决定了领导还是只会去买厂商的整体解决方案不会交给我们耍的。&lt;/p&gt;    &lt;p&gt;各位不管你们搞攻击的还是搞安全的机会都大大的啊。。。除了漏洞这块至少还有十几个领域，任一个领域做到了与世界水平相近，你们的机会就很大了，因为很多企事业单位不让用国外产品，或者在条件相同的情况下喜欢国内的二次开发，你看我都不提价格优势。。。&lt;/p&gt;&lt;/div&gt;
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/62544-%E9%BB%91%E5%AE%A2-%E4%BB%8E%E4%BA%8B-%E5%AE%89%E5%85%A8</guid>
      <pubDate>Thu, 15 Dec 2022 09:48:57 CST</pubDate>
    </item>
    <item>
      <title>市面上有哪些安全漏洞扫描工具？ - 知乎</title>
      <link>https://itindex.net/detail/62543-%E5%AE%89%E5%85%A8%E6%BC%8F%E6%B4%9E-%E5%B7%A5%E5%85%B7-%E7%9F%A5%E4%B9%8E</link>
      <description>&lt;div&gt;    &lt;p&gt;AWVS、Nessus、AppScan、Goby、NetSparker、Xray等等一系列&lt;/p&gt;    &lt;p&gt;如果细分的话应当区分web漏扫和主机漏扫。&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;web漏扫：通常需要爬虫爬取网页，先爬取再进行漏洞挖掘。&lt;/li&gt;      &lt;li&gt;主机漏扫：则通常使用POC脚本扫描服务端口。&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;web漏扫一般建议使用AWVS配合Xray被动扫描&lt;/p&gt;    &lt;p&gt;主机漏扫的话Goby就很不错了&lt;/p&gt;    &lt;p&gt;      &lt;br /&gt;&lt;/p&gt;    &lt;p&gt;下面分享一下上述软件以及使用的小技巧&lt;/p&gt;    &lt;h3&gt;AWVS配合Xray被动扫描&lt;/h3&gt;    &lt;p&gt;      &lt;a href="https://link.zhihu.com/?target=https%3A//www.iculture.cc/software/pig%3D5708" rel="nofollow noreferrer" target="_blank"&gt;最新Acunetix14.6.211215172 支持Log4j检测&lt;/a&gt;&lt;/p&gt;    &lt;p&gt;      &lt;a href="https://link.zhihu.com/?target=https%3A//www.iculture.cc/software/pig%3D3738" rel="nofollow noreferrer" target="_blank"&gt;xray1.7.1 pro下载 使用方法&lt;/a&gt;&lt;/p&gt;    &lt;p&gt;上面两个软件可以配合使用，这里分享一个实用的批量扫描的脚本&lt;/p&gt;    &lt;p&gt;      &lt;br /&gt;&lt;/p&gt;    &lt;ul&gt;      &lt;li&gt;批量扫描脚本分享&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;链接：      &lt;a href="https://link.zhihu.com/?target=https%3A//pan.baidu.com/s/1HFSYtpygYVYo6acqPI0JHw" rel="nofollow noreferrer" target="_blank"&gt;https://pan.baidu.com/s/1HFSYtpygYVYo6acqPI0JHw&lt;/a&gt;&lt;/p&gt;    &lt;p&gt;提取码：mhxv&lt;/p&gt;    &lt;p&gt;需要先将批量扫描的网址填写到      &lt;strong&gt;url.txt&lt;/strong&gt;中，按行分开&lt;/p&gt;    &lt;p&gt;并设置好      &lt;strong&gt;awvs_config.ini&lt;/strong&gt;中的api_key&lt;/p&gt;    &lt;img src="data:image/svg+xml;utf8,&lt;svg xmlns='http://www.w3.org/2000/svg' width='937' height='518'&gt;&lt;/svg&gt;" width="937"&gt;&lt;/img&gt;    &lt;p&gt;需要和awvs中的密钥相同（AWVS后台中右上角的配置文件，打开找到API密钥，点击复制）&lt;/p&gt;    &lt;img src="data:image/svg+xml;utf8,&lt;svg xmlns='http://www.w3.org/2000/svg' width='1234' height='832'&gt;&lt;/svg&gt;" width="1234"&gt;&lt;/img&gt;    &lt;p&gt;都配置好之后，方可打开脚本，你可以选择直接让AWVS单独扫描，但这样的效果并不是很好。由于最新版本的AWVS与Xray被动扫描不太兼容，因此我们需要指定爬虫模式，然后配合Xray进行被动扫描&lt;/p&gt;    &lt;img src="data:image/svg+xml;utf8,&lt;svg xmlns='http://www.w3.org/2000/svg' width='622' height='470'&gt;&lt;/svg&gt;" width="622"&gt;&lt;/img&gt;    &lt;p&gt;记住还要设置好代理的端口&lt;/p&gt;    &lt;p&gt;然后运行xray，进行被动扫描并输出结果&lt;/p&gt;    &lt;div&gt;      &lt;pre&gt;        &lt;code&gt;xray_windows_amd64.exe  webscan --listen 0.0.0.0:1111 --html-output scan-output.html&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;p&gt;      &lt;br /&gt;&lt;/p&gt;    &lt;h3&gt;AppScan扫描&lt;/h3&gt;    &lt;p&gt;如果你不太习惯于使用面板类的应用，你可以选择使用HCL AppScan Standard&lt;/p&gt;    &lt;p&gt;下载地址：      &lt;a href="https://link.zhihu.com/?target=https%3A//www.iculture.cc/software/pig%3D5752" rel="nofollow noreferrer" target="_blank"&gt;HCL AppScan Standard v10.0.6.28111最新版&lt;/a&gt;&lt;/p&gt;    &lt;p&gt;运行起来，输入好域名就可以扫描了。&lt;/p&gt;    &lt;p&gt;新版的GUI界面，属实感觉颜值爆表（中间的框估计还没改，后面应该会优化）&lt;/p&gt;    &lt;p&gt;测试版GUI界面进入方式：&lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;C:\Program Files (x86)\HCL\AppScan Standard&lt;/strong&gt;下运行      &lt;code&gt;AppScanGui.exe&lt;/code&gt;&lt;/p&gt;    &lt;img src="data:image/svg+xml;utf8,&lt;svg xmlns='http://www.w3.org/2000/svg' width='1569' height='834'&gt;&lt;/svg&gt;" width="1569"&gt;&lt;/img&gt;    &lt;p&gt;      &lt;br /&gt;&lt;/p&gt;    &lt;p&gt;NetSparker&lt;/p&gt;    &lt;p&gt;与之类似的还有NetSparker，这里也是提供软件下载地址：&lt;/p&gt;    &lt;p&gt;      &lt;a href="https://link.zhihu.com/?target=https%3A//www.iculture.cc/software/pig%3D4135" rel="nofollow noreferrer" target="_blank"&gt;最新Netsparker6.2.0.33156-下载&lt;/a&gt;&lt;/p&gt;    &lt;img src="data:image/svg+xml;utf8,&lt;svg xmlns='http://www.w3.org/2000/svg' width='1289' height='810'&gt;&lt;/svg&gt;" width="1289"&gt;&lt;/img&gt;    &lt;img src="data:image/svg+xml;utf8,&lt;svg xmlns='http://www.w3.org/2000/svg' width='1548' height='831'&gt;&lt;/svg&gt;" width="1548"&gt;&lt;/img&gt;    &lt;p&gt;      &lt;br /&gt;&lt;/p&gt;    &lt;p&gt;Goby&lt;/p&gt;    &lt;p&gt;Goby这里直接去官网下载就好了&lt;/p&gt;    &lt;p&gt;      &lt;a href="https://link.zhihu.com/?target=https%3A//cn.gobies.org/" rel="nofollow noreferrer" target="_blank"&gt;Goby - 帮企业梳理资产暴露攻击面&lt;/a&gt;&lt;/p&gt;    &lt;p&gt;如果你是做红队的，可以申请红队版本，里面POC还是蛮多的。&lt;/p&gt;    &lt;p&gt;      &lt;br /&gt;&lt;/p&gt;    &lt;p&gt;Nessus&lt;/p&gt;    &lt;p&gt;最后是分享Nessus，其实很多公司的二次开发都是基于Nessus里的nasl脚本插件，这里我们分享了最新版本的插件以及Nessus8.15.2软件&lt;/p&gt;    &lt;p&gt;      &lt;a href="https://link.zhihu.com/?target=https%3A//www.iculture.cc/software/pig%3D5719" rel="nofollow noreferrer" target="_blank"&gt;Nessus最新插件202112140332更新教程 解决Nessus重启插件清空问题&lt;/a&gt;&lt;/p&gt;    &lt;p&gt;      &lt;a href="https://link.zhihu.com/?target=https%3A//www.iculture.cc/software/pig%3D3744" rel="nofollow noreferrer" target="_blank"&gt;Nessus pro8.15.2专业版 10月最新版 pro插件 附使用说明&lt;/a&gt;&lt;/p&gt;    &lt;p&gt;这里简单说一下，如果Nessus占用CPU过高，如何解决？&lt;/p&gt;    &lt;h3&gt;Nessus插件CPU占用过高问题&lt;/h3&gt;    &lt;ul&gt;      &lt;li&gt;如何解决Nessus插件占用CPU过高的问题？&lt;/li&gt;&lt;/ul&gt;    &lt;p&gt;这里推荐使用      &lt;code&gt;CPULimit&lt;/code&gt;模块进行限制，当然，如果你在安全公司内部有比较好的服务器，当我没说，跑满它！&lt;/p&gt;    &lt;h3&gt;CentOS安装CPULimit&lt;/h3&gt;    &lt;div&gt;      &lt;pre&gt;        &lt;code&gt;sudo yum install cpulimit&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;h3&gt;Ubuntu/Debian安装CPULimit&lt;/h3&gt;    &lt;div&gt;      &lt;pre&gt;        &lt;code&gt;sudo apt-get install cpulimit&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;p&gt;安装好之后，我们可以进行测试，我们先找到nessus进程的pid&lt;/p&gt;    &lt;div&gt;      &lt;pre&gt;        &lt;code&gt;ps aux | grep nessusd&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;img src="data:image/svg+xml;utf8,&lt;svg xmlns='http://www.w3.org/2000/svg' width='658' height='47'&gt;&lt;/svg&gt;" width="658"&gt;&lt;/img&gt;    &lt;p&gt;这里看到PID是2544113，我们对其进行限制CPU占用率即可，我们这里设置的是60%&lt;/p&gt;    &lt;div&gt;      &lt;pre&gt;        &lt;code&gt;cpulimit --pid 2544113 --limit 60&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;p&gt;当然，你按ctrl+C退出便解除限制了，如果你想要后台运行可以使用      &lt;code&gt;Nohup&lt;/code&gt;&lt;/p&gt;    &lt;div&gt;      &lt;pre&gt;        &lt;code&gt;nohup cpulimit --pid 2544113 --limit 60&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;p&gt;然后Nessus就老实了，不过这里也仅对于个人CPU不太高的用户，如果是企业，服务器够好，当时是最好的了。&lt;/p&gt;    &lt;p&gt;      &lt;br /&gt;&lt;/p&gt;    &lt;p&gt;其他的内容后续持续更新，欢迎评论给我留言&lt;/p&gt;&lt;/div&gt;
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/62543-%E5%AE%89%E5%85%A8%E6%BC%8F%E6%B4%9E-%E5%B7%A5%E5%85%B7-%E7%9F%A5%E4%B9%8E</guid>
      <pubDate>Thu, 15 Dec 2022 09:44:55 CST</pubDate>
    </item>
    <item>
      <title>并发模拟的四种方式+工具，超级实用！</title>
      <link>https://itindex.net/detail/62519-%E5%B9%B6%E5%8F%91-%E6%A8%A1%E6%8B%9F-%E5%B7%A5%E5%85%B7</link>
      <description>&lt;div&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;   &lt;strong&gt;长按 关注 此公众号，技术干货，及时送达！&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;   &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;h2&gt;一、Postman&lt;/h2&gt;  &lt;p&gt;Postman是一个款http请求模拟工具&lt;/p&gt;  &lt;p&gt;   &lt;a href="http://mp.weixin.qq.com/s?__biz=MzAwMTE3MDY4MQ==&amp;mid=2652467179&amp;idx=1&amp;sn=5cdd9b9d81e33165e9610e2a7acf553b&amp;chksm=8130027db6478b6bdff854d30e30f5285a037da9cdd5d8720a16e81237b6965d918eb0cf14dc&amp;scene=21#wechat_redirect" target="_blank"&gt;    &lt;img&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;首先演示一下postman最基本的使用   &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;创建一个Springboot项目，测试的代码如下：&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;import org.springframework.web.bind.annotation.GetMapping;    &lt;br /&gt;import org.springframework.web.bind.annotation.RequestMapping;    &lt;br /&gt;import org.springframework.web.bind.annotation.RestController;    &lt;br /&gt;    &lt;br /&gt;@RestController    &lt;br /&gt;@RequestMapping(&amp;quot;test&amp;quot;)    &lt;br /&gt;public class TestConrtoller {    &lt;br /&gt;    &lt;br /&gt;    @GetMapping(&amp;quot;demo&amp;quot;)    &lt;br /&gt;    public String testDemo() {    &lt;br /&gt;        return &amp;quot;result~&amp;quot;;    &lt;br /&gt;    }    &lt;br /&gt;}    &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;为了便于操作，一般会将   &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;   &lt;code&gt;http://127.0.0.1:8080&lt;/code&gt; 是经常使用的地址+端口号，可以设置为环境&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;    &lt;a href="http://mp.weixin.qq.com/s?__biz=MzAwMTE3MDY4MQ==&amp;mid=2652447636&amp;idx=1&amp;sn=31465516f883ea9f9399c71f146ebec4&amp;chksm=81304e02b647c714d29504911e6edace624c6f259eeefb23715879fbac196635203328d4ddd6&amp;scene=21#wechat_redirect" target="_blank"&gt;推荐下自己做的 Spring Boot 的实战项目：&lt;/a&gt;&lt;/p&gt;   &lt;p&gt;    &lt;a href="http://mp.weixin.qq.com/s?__biz=MzAwMTE3MDY4MQ==&amp;mid=2652447636&amp;idx=1&amp;sn=31465516f883ea9f9399c71f146ebec4&amp;chksm=81304e02b647c714d29504911e6edace624c6f259eeefb23715879fbac196635203328d4ddd6&amp;scene=21#wechat_redirect" target="_blank"&gt;https://gitee.com/yoodb/jing-xuan&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;  &lt;p&gt;点击右上角的设置图标&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;选择global   &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;   &lt;a href="https://itindex.net/relian"&gt;    &lt;img&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;输入信息   &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;以后再进行测试就能这样搞简写了   &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;知道基本使用之后,我们来看一下如何模拟并发测试&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;填写基本信息后，创建   &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;这个时候会创建出Concurrency的文件夹，我们可以把刚才测试的demo的例子放进这个文件夹下   &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;这个时候就可以在Concurrency下看到这个接口测试了&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;选择并发测试：   &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;这个时候弹出我们想要的框了   &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;点击Run Concurrency   &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;你可以立马感觉到CPU在“燃烧”，因为要记录并打印日志，显示的话是一条一条来的，其实测试的速度，要比你看到的打印的日志的速度快，绿色表示正常&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;h2&gt;二、Apache Bench（AB）&lt;/h2&gt;  &lt;p&gt;ApacheBench 是 Apache 服务器自带的一个web压力测试工具，简称ab。&lt;/p&gt;  &lt;p&gt;ab又是一个命令行工具，对发起负载的本机要求很低，根据ab命令可以创建很多的并发访问线程，模拟多个访问者同时对某一URL地址进行访问，因此可以用来测试目标服务器的负载压力。总的来说ab工具小巧简单，上手学习较快，可以提供需要的基本性能指标，但是没有图形化结果，不能监控。&lt;/p&gt;  &lt;p&gt;使用的话，首先需要安装Apache服务器&lt;/p&gt;  &lt;p&gt;网站：http://httpd.apache.org/download.cgi&lt;/p&gt;  &lt;p&gt;因为我的操作系统是windows10， 这里选择File for Microsoft Windows&lt;/p&gt;  &lt;p&gt;Linux下的安装是非常简单的，这里不再演示&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;选择 ApacheHaus   &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;进入下载页面 选择适合自己电脑的版本   &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;文件解压到本地文件夹下，如果不是解压在c盘，需要设置参数，注意文件路径最好都是英文   &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;关于需要设置参数，conf-&amp;gt;httpd.conf 使用文本编辑器打开，&lt;/p&gt;  &lt;p&gt;需要修改的有三个地方：&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;运行根目录，修改成自己解压到本地的路径&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;监听端口，默认监听端口是80，如果已被使用会报错需要修改，如果80端口未被使用，可不修改；如果修改了监听端口，则需要把ServerName localhost也相应改成同样的端 口号   &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;DocumentRoot 测试文件存放位置，且该目录必须存在&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;配置完成后，命令行cmd进入D:\softUtil\Apache24\bin目录下   &lt;br /&gt;&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;httpd.exe  -k  install    &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;启动：   &lt;br /&gt;&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;httpd.exe -k start    &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;测试:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;    &lt;code&gt;-n&lt;/code&gt; :请求数&lt;/li&gt;   &lt;li&gt;    &lt;code&gt;-c&lt;/code&gt;: 并发数&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;h2&gt;三、并发模拟工具JMeter&lt;/h2&gt;  &lt;p&gt;JMeter也是一款性能测试工具，是图形化的。&lt;/p&gt;  &lt;p&gt;下载地址：传送门 http://jmeter.apache.org/&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;需要Java8+的环境   &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;解压到你觉得合适的目录下（注意最好是英文路径）   &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;进入它的bin目录下 启动jmeter.bat即可&lt;/p&gt;  &lt;p&gt;   &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;   &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;使用很简单，首先在测试计划部分新建一个线程组   &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;设置好基础信息后添加HTTP请求（基本信息设置好没有OK哈，直接添加HTTP请求）   &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;填写HTTP请求相关的内容   &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;之后还要添加监听器，这里选择是图形结果   &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;再添加一个查看结果树吧   &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;在运行之前打开log Viewer&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;下面开始运行：   &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;   &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;   &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;执行成功，来感受一下结果：   &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;点进去   &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;查看结果树   &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;h2&gt;四、代码模拟&lt;/h2&gt;  &lt;p&gt;这里需要用到一个类，就是CountDownLatch。&lt;/p&gt;  &lt;p&gt;CountDownLatch是一个计数器闭锁，通过它可以完成类似于阻塞当前线程的功能，即：一个线程或多个线程一直等待，直到其他线程执行的操作完成。&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;    &lt;a href="http://mp.weixin.qq.com/s?__biz=MzAwMTE3MDY4MQ==&amp;mid=2652446071&amp;idx=2&amp;sn=6928e9486e00d24363787415a5ed8bb7&amp;chksm=813070e1b647f9f78cf57ff1b627d12d002d53e4317e8d5afe3630dbc7244ec28ac3ea0ac975&amp;scene=21#wechat_redirect" target="_blank"&gt;推荐下自己做的 Spring Cloud 的实战项目：&lt;/a&gt;&lt;/p&gt;   &lt;p&gt;    &lt;a href="http://mp.weixin.qq.com/s?__biz=MzAwMTE3MDY4MQ==&amp;mid=2652446071&amp;idx=2&amp;sn=6928e9486e00d24363787415a5ed8bb7&amp;chksm=813070e1b647f9f78cf57ff1b627d12d002d53e4317e8d5afe3630dbc7244ec28ac3ea0ac975&amp;scene=21#wechat_redirect" target="_blank"&gt;https://gitee.com/yoodb/jingxuan-springcloud&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;  &lt;p&gt;CountDownLatch用一个给定的计数器来初始化，该计数器的操作是原子操作，即同时只能有一个线程去操作该计数器。调用该类await方法的线程会一直处于阻塞状态，直到其他线程调用countDown方法使当前计数器的值变为零，每次调用countDown计数器的值减1。当计数器值减至零时，所有因调用await()方法而处于等待状态的线程就会继续往下执行。这种现象只会出现一次，因为计数器不能被重置。&lt;/p&gt;  &lt;p&gt;下图和它的方法可以体现出来：&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;CountDownLatch类只提供了一个构造器：   &lt;br /&gt;&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;public CountDownLatch(int count) {  };  //参数count为计数值    &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;然后下面这3个方法是CountDownLatch类中最重要的方法(上图能够反映出来）&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;public void await() throws InterruptedException { };   //调用await()方法的线程会被挂起，它会等待直到count值为0才继续执行    &lt;br /&gt;public boolean await(long timeout, TimeUnit unit) throws InterruptedException { };  //和await()类似，只不过等待一定的时间后count值还没变为0的话就会继续执行    &lt;br /&gt;public void countDown() { };  //将count值减1    &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;下面还需要看一个类Semaphore&lt;/p&gt;  &lt;p&gt;Semaphore与CountDownLatch相似，不同的地方在于Semaphore的值被获取到后是可以释放的，并不像CountDownLatch那样一直减到底。另外，推荐公众  号Java精选，回复java面试，获取面试资料，支持在线刷题。&lt;/p&gt;  &lt;p&gt;它也被更多地用来限制流量，类似阀门的 功能。如果限定某些资源最多有N个线程可以访问，那么超过N个主不允许再有线程来访问，同时当现有线程结束后，就会释放，然后允许新的线程进来。有点类似于锁的lock与 unlock过程。相对来说他也有两个主要的方法：&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;用于获取权限的acquire(),其底层实现与CountDownLatch.countdown()类似;&lt;/li&gt;   &lt;li&gt;用于释放权限的release()，其底层实现与acquire()是一个互逆的过程。&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;通过这两个类可以进行并发的模拟：&lt;/p&gt;  &lt;p&gt;测试一下：&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;import lombok.extern.slf4j.Slf4j;    &lt;br /&gt;    &lt;br /&gt;import java.util.concurrent.*;    &lt;br /&gt;    &lt;br /&gt;@Slf4j    &lt;br /&gt;public class CuncurrencyTest {    &lt;br /&gt;    &lt;br /&gt;    // 请求总数    &lt;br /&gt;    public static int clientTotal = 5000;    &lt;br /&gt;    &lt;br /&gt;    // 同时并发执行的线程总数    &lt;br /&gt;    public static int threadTotal = 200;    &lt;br /&gt;    &lt;br /&gt;    public static int count = 0;    &lt;br /&gt;    &lt;br /&gt;    public static void main(String[] args) throws InterruptedException {    &lt;br /&gt;        // 定义线程池    &lt;br /&gt;        ExecutorService executorService = Executors.newCachedThreadPool();    &lt;br /&gt;        // 定义信号量 最大的线程数量    &lt;br /&gt;        final Semaphore semaphore = new Semaphore(threadTotal);    &lt;br /&gt;        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);    &lt;br /&gt;    &lt;br /&gt;        for (int i = 0; i &amp;lt; clientTotal; i++) {    &lt;br /&gt;            executorService.execute(() -&amp;gt; {    &lt;br /&gt;                try {    &lt;br /&gt;                    semaphore.acquire();    &lt;br /&gt;                    add();    &lt;br /&gt;                    semaphore.release();    &lt;br /&gt;                } catch (InterruptedException e) {    &lt;br /&gt;                    e.printStackTrace();    &lt;br /&gt;                    log.error(&amp;quot;exception&amp;quot;,e);    &lt;br /&gt;                }    &lt;br /&gt;                countDownLatch.countDown();    &lt;br /&gt;            });    &lt;br /&gt;        }    &lt;br /&gt;        countDownLatch.await();    &lt;br /&gt;        executorService.shutdown();    &lt;br /&gt;        log.info(&amp;quot;count:{}&amp;quot;,count);    &lt;br /&gt;    &lt;br /&gt;    }    &lt;br /&gt;    &lt;br /&gt;    private static void  add() {    &lt;br /&gt;        count++;    &lt;br /&gt;    }    &lt;br /&gt;}    &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;因为count不是线程安全的，且没有做防护措施，结果是错的&lt;/p&gt;  &lt;p&gt;   &lt;img&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;上面是对代码的并发模拟的简单形式，值得注意的是，这里提到的两个类不是专门做并发模拟，它们的用途很广泛，大家可以了解一下。&lt;/p&gt;  &lt;blockquote&gt;来源：blog.csdn.net/qq_42322103/article/details/102736170&lt;/blockquote&gt;  &lt;blockquote&gt;版权声明：此内容来源网络，版权归原作者所有。我们转载的内容，都会注明来源，除非无法确认。若有侵权，烦请告知，我们会立即删除并表示歉意。谢谢！&lt;/blockquote&gt;PS：文章若对您有用，请帮助  &lt;strong&gt;点赞、在看、转发&lt;/strong&gt;吧！- END -点击卡片关注我们，更多技术干货，及时为您送达！  &lt;p&gt;往期推荐&lt;/p&gt;  &lt;br /&gt;  &lt;br /&gt;  &lt;a href="http://mp.weixin.qq.com/s?__biz=MzU0ODk2MzE3MA==&amp;mid=2247485613&amp;idx=1&amp;sn=9988c45f9176be1f23dd5aa3b17579cd&amp;chksm=fbb65ea0ccc1d7b6243e29354acafe8e2559722cc507698fb8dbade25b0fe3f7c0735f5a18a0&amp;scene=21#wechat_redirect" target="_blank"&gt;Spring Framework 6.0 正式GA，新一代框架的开始&lt;/a&gt;  &lt;br /&gt;  &lt;a href="http://mp.weixin.qq.com/s?__biz=MzU0ODk2MzE3MA==&amp;mid=2247485600&amp;idx=1&amp;sn=769b88444f15e35c2271db039cc960eb&amp;chksm=fbb65eadccc1d7bb35f0ebbec3b061e7c9c725af68131babcf6c99ff6d52cd7c13669f762707&amp;scene=21#wechat_redirect" target="_blank"&gt;网关系统就该这么设计（万能通用），稳的一批！&lt;/a&gt;  &lt;br /&gt;  &lt;a href="http://mp.weixin.qq.com/s?__biz=MzU0ODk2MzE3MA==&amp;mid=2247485572&amp;idx=1&amp;sn=34d9fd123bff27d7c1ecc035261da358&amp;chksm=fbb65e89ccc1d79f7726a47c29596d68de1bbad16e43b9547463360351c6fbd54faf4caee4e7&amp;scene=21#wechat_redirect" target="_blank"&gt;被裁员！从无赔偿拿到N+1的故事&lt;/a&gt;  &lt;br /&gt;  &lt;a href="http://mp.weixin.qq.com/s?__biz=MzU0ODk2MzE3MA==&amp;mid=2247485246&amp;idx=1&amp;sn=4deca9c75a6aa9de9883e388916496f4&amp;chksm=fbb65133ccc1d8257ae41dd8fe710d7cea93a312e6aa0f2079f4a140416dbf17f9a03866c895&amp;scene=21#wechat_redirect" target="_blank"&gt;如何设计一个短链接系统？&lt;/a&gt;  &lt;br /&gt;  &lt;p&gt;   &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;   &lt;br /&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>dev</category>
      <guid isPermaLink="true">https://itindex.net/detail/62519-%E5%B9%B6%E5%8F%91-%E6%A8%A1%E6%8B%9F-%E5%B7%A5%E5%85%B7</guid>
      <pubDate>Mon, 05 Dec 2022 00:00:00 CST</pubDate>
    </item>
    <item>
      <title>Meta开源JavaScript内存泄漏监测工具MemLab</title>
      <link>https://itindex.net/detail/62431-meta-%E5%BC%80%E6%BA%90-javascript</link>
      <description>&lt;h1&gt;一、MemLab简介&lt;/h1&gt;
 &lt;p&gt;上周，Facebook母公司Meta 宣布了开源 MemLab，一个基于 Chromium 的浏览器的 JavaScript 应用程序内存泄漏监测工具。同时，Facebook 技术团队指出：“应用程序的性能和功能正确性问题通常会被用户立即留意到。然而内存泄漏却不一样，它不容易被立即察觉，但它每次都会吃掉一大块内存，使得整个网络会话的响应变得非常慢。”&lt;/p&gt;
 &lt;p&gt;为了帮助开发人员解决这个问题，Meta 构建了MemLab，它可以自动进行内存泄漏检测并更容易找到泄漏的根本原因。据官方公告称，Meta 内部使用它成功地控制了不可持续的内存增长，并识别了产品和基础设施中的内存泄漏和内存优化机会。目前，Meta 已经在 GitHub 上开源了 MemLab。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image.png" src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/313eb4b7b54646729f524cba974bba76~tplv-k3u1fbpfcp-watermark.image?"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;Facebook在 2020 年被重新设计为单页应用程序 (SPA)，该应用程序的大部分渲染和导航使用客户端 JavaScript。而 Meta 的大多数其他流行网络应用程序都使用了类似的架构来构建，包括 Instagram 和 Workplace。&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;虽然这种架构使其能够提供更快的用户交互、更好的开发人员体验和更像应用程序的感觉，但在客户端维护 Web 应用程序状态会使有效管理客户端内存变得更加复杂。且内存泄漏的后果在单页应用程序（SPA）中更为严重，因为用户可能会在较长时间内持续与页面交互，而 MemLab 就是专为这种场景设计的。&lt;/p&gt;
 &lt;p&gt;在许多情况下，JavaScript 可能会泄漏内存。比如，Facebook 工程师 Liang Gong 和 Glenn Conner 就在公告中谈到，当你向 Chrome 控制台发送一个对象时，Chrome 会对其进行隐藏引用，以防止它被收集。另外，auth0 工程师 Sebastian Peyrott 也曾谈到，其他可能出现泄漏或未绑定内存增长的情况则与意外使用全局变量、忘记计时器或回调以及 DOM 外引用有关。&lt;/p&gt;
 &lt;p&gt;虽然 Chrome 开发者工具提供了检查 JavaScript 代码的内存行为的基本手段，比如时间线视图和配置文件视图，但这并不直接，也不能自动化。相反，MemLab 则可以很容易地集成到 CI/CD 管道中，Gong 和 Conner 介绍道。&lt;/p&gt;
 &lt;h1&gt;二、工作原理&lt;/h1&gt;
 &lt;p&gt;MemLab 的工作原理是通过预定义的测试场景运行 headless 浏览器并对 JavaScript heap snapshots 进行差异分析来发现内存泄漏。要达到这一目的，需要经过如下几步：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;导航到页面并返回；&lt;/li&gt;
  &lt;li&gt;查找未释放的对象；&lt;/li&gt;
  &lt;li&gt;显示泄露追踪结果。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;据悉，MemLab 使用了一个名为“Puppeteer”的 Node.js 库。它可以控制 Google Chrome 或其它基于 Chromium 内核打造的浏览器，且默认情况下以 headless 模式运行（方便命令行交互）。&lt;/p&gt;
 &lt;p&gt;Facebook 工程师解释称，MemLab 的工作方式就是导航到一个页面、然后离开。正常情况下，可预计该页面分配的大部分内存也将被释放。但若没有被释放，则意味其存在极高的内存泄露可能性。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image.png" src="https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ac9e8c69dfaa46159bbd708057629eeb~tplv-k3u1fbpfcp-watermark.image?"&gt;&lt;/img&gt;
 
我们知道，React 使用存储在树结构中、被称作 Fibers 的对象，来表示内存中的浏览器文档对象模型（DOM）。据该团队所述，这可能是存在“巨大内存泄露”的一个主要原因。拥有强连接图的缺点很是显著，若有任何外部引用指向图的任何部分，就无法对整个图开展垃圾回收。&lt;/p&gt;
 &lt;p&gt;对于浏览器内存泄漏检测，MemLab 需要开发人员提供的唯一输入是一个测试场景文件，该文件定义了如何通过 overriding Puppeteer API 和 CSS 选择器的三个回调来与网页进行交互。MemLab 会自动对 JavaScript heap 进行差异化处理，完善内存泄漏，并对结果进行汇总。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="MemLab-figure-2-FINAL.gif" src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/23c3fb7b966f4962a8b714443a1009a4~tplv-k3u1fbpfcp-watermark.image?"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;MemLab 的另一特性，就是提供了 JavaScript 堆的图形视图、启用了用于检查堆快照的 API 。这意味着开发者能够编写开展内存断言的测试，例如声明某个对象将不再存在于内存中。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image.png" src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d36a6ba054a54b2d89063576a4fe1a87~tplv-k3u1fbpfcp-watermark.image?"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;此外还有一个用于查找重复字符串实例的工具，在某个案例中，团队发现字符串占用了 70% 的堆、且其中半数至少有一个重复的实例。包括 Chrome、Edge、Firefox 在内的浏览器，都有附带内存检查工具。但正如以为开发者在 Hacker News 上吐槽的那样，这些开发工具难以在调试过程中揪出内存泄露的问题。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image.png" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2964251ed4bf4e80ac212fd7e8f41a06~tplv-k3u1fbpfcp-watermark.image?"&gt;&lt;/img&gt;
最后，MemLab 的另一项强大功能，就是可以在测试期间作为命令过程的一部分而运行。这意味着如果代码中引入了严重的泄露，开发者们也能够在投入生产环境前加以捕获。&lt;/p&gt;
 &lt;p&gt;除了内存泄漏检测之外，MemLab还包括一组用于查找内存优化机会的内置CLI命令和api，并提供如下的功能：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;堆内容分解&lt;/li&gt;
  &lt;li&gt;监测单个对象的内存使用情况&lt;/li&gt;
  &lt;li&gt;查找重复的字符串实例&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;比如，监测浏览内存泄漏部分UI。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image.png" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c4384bd59c304abb96b1fcd27f92c7e3~tplv-k3u1fbpfcp-watermark.image?"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;跟踪UI内存泄漏的整个链路。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image.png" src="https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/df39db4945bf47109ddffc4c6e03e8f2~tplv-k3u1fbpfcp-watermark.image?"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h1&gt;三、基本使用&lt;/h1&gt;
 &lt;h2&gt;3.1 安装与使用&lt;/h2&gt;
 &lt;p&gt;首先，需要全局安装MemLab插件，安装的命令如下：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;npm install -g memlab
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;例如下面是找到谷歌Maps中的内存泄漏的例子，我妈可以创建一个场景文件来定义如何与谷歌Maps进行交互，比如将其命名为test-google-maps.js。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;function url() {
  return &amp;apos;https://www.google.com/maps/@37.386427,-122.0428214,11z&amp;apos;;
}


async function action(page) {
  await page.click(&amp;apos;button[aria-label=&amp;quot;Hotels&amp;quot;]&amp;apos;);
}


async function back(page) {
  await page.click(&amp;apos;[aria-label=&amp;quot;Clear search&amp;quot;]&amp;apos;);
}


module.exports = {action, back, url};
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;现在使用下面的命令运行上面的js代码, 当memlab与web页面进行交互时就会运行内置的泄漏检测器检测内存泄漏。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;memlab run --scenario test-google-maps.js
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;执行结束之后，Memlab就会打印内存泄漏结果，显示每个泄漏对象集群的一个代表性保留跟踪。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;MemLab found 46 leak(s)
--Similar leaks in this run: 4--
--Retained size of leaked objects: 8.3MB--
[Window] (native) @35847 [8.3MB]
  --20 (element)---&amp;gt;  [InternalNode] (native) @130981728 [8.3MB]
  --8 (element)---&amp;gt;  [InternalNode] (native) @130980288 [8.3MB]
  --1 (element)---&amp;gt;  [EventListener] (native) @131009888 [8.3MB]
  --1 (element)---&amp;gt;  [V8EventListener] (native) @224808192 [8.3MB]
  --1 (element)---&amp;gt;  [eventHandler] (closure) @168079 [8.3MB]
  --context (internal)---&amp;gt;  [&amp;lt;function scope&amp;gt;] (object) @181905 [8.3MB]
  --bigArray (variable)---&amp;gt;  [Array] (object) @182925 [8.3MB]
  --elements (internal)---&amp;gt;  [(object elements)] (array) @182929 [8.3MB]
...
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;接着，我们就可以通过这些捕获的跟踪信息定位到里面的方法。&lt;/p&gt;
 &lt;p&gt;当然，我没也可以使用Memlab查看基于从Chromium、Hermes、memlab或任何node.js或electronic .js程序中获取的单个JavaScript堆快照检测到的内存问题。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;memlab view-heap --snapshot &amp;lt;PATH TO .heapsnapshot FILE&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;然后，我没可以使用对象的id，比如node-id @28173来精确定位特定的堆对象。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image.png" src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e795fdc3752e4984b70dc9cd7e0bbd67~tplv-k3u1fbpfcp-watermark.image?"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;当然，Memlab也支持自定义的检漏器，自定义检漏器时需要在场景文件中添加一个  &lt;a href="https://facebookincubator.github.io/memlab/docs/api/interfaces/core_src.IScenario/#-optional-beforeleakfilter-initleakfiltercallback"&gt;filterLeak文档&lt;/a&gt;。对于目标交互分配的每个未释放的堆对象(节点)将调用filterLeak。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;function filterLeak(node, heap) {
  // ... your leak detector logic
  // return true to mark the node as a memory leak
};
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;heap是最终JavaScript堆快照的图形表示。&lt;/p&gt;
 &lt;h2&gt;3.2 堆分析与研究&lt;/h2&gt;
 &lt;p&gt;除了检测内存泄露意外，Memlab还提供了很多其他有用的命令，比如查看某个对象在运行的交互过程中的整个链路。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;memlab analyze unbound-object
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;获取V8/hermes .heapsnapshot文件。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;memlab analyze unbound-object --snapshot-dir &amp;lt;DIR_OF_SNAPSHOT_FILES&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;使用memlab analyze查看所有内置内存分析。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;memlab trace --node-id &amp;lt;HEAP_OBJECT_ID&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
 &lt;h2&gt;3.3 Memlab API&lt;/h2&gt;
 &lt;p&gt;Memlab的npm包支持在浏览器中启动端到端运行并检测内存泄漏。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;const memlab = require(&amp;apos;memlab&amp;apos;);


const scenario = {
    url: () =&amp;gt; &amp;apos;https://www.google.com/maps/@37.386427,-122.0428214,11z&amp;apos;,
    action: async (page) =&amp;gt; await page.click(&amp;apos;button[aria-label=&amp;quot;Hotels&amp;quot;]&amp;apos;),
    back: async (page) =&amp;gt; await page.click(&amp;apos;[aria-label=&amp;quot;Clear search&amp;quot;]&amp;apos;),
}
memlab.run({scenario});
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;h2&gt;3.4 内存断言&lt;/h2&gt;
 &lt;p&gt;Memlab支持在Node.js程序中进行Jest测试，也可以使用图视图API来获得其自身状态的堆图视图，执行自内存检查，并编写各种内存断言。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;import type {IHeapSnapshot} from &amp;apos;@memlab/core&amp;apos;;
import {config, takeNodeMinimalHeap, tagObject} from &amp;apos;@memlab/core&amp;apos;;
test(&amp;apos;memory test&amp;apos;, async () =&amp;gt; {
  config.muteConsole = true;
  const o1 = {};
  let o2 = {};
  tagObject(o1, &amp;apos;memlab-mark-1&amp;apos;);
  tagObject(o2, &amp;apos;memlab-mark-2&amp;apos;);
  o2 = null;
  const heap: IHeapSnapshot = await takeNodeMinimalHeap();
   //断言函数
  expect(heap.hasObjectWithTag(&amp;apos;memlab-mark-1&amp;apos;)).toBe(true);
  //断言函数
  expect(heap.hasObjectWithTag(&amp;apos;memlab-mark-2&amp;apos;)).toBe(false);
}, 30000);
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;附件：  &lt;a href="https://github.com/facebookincubator/memlab"&gt;https://github.com/facebookincubator/memlab&lt;/a&gt;&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/62431-meta-%E5%BC%80%E6%BA%90-javascript</guid>
      <pubDate>Thu, 22 Sep 2022 11:26:54 CST</pubDate>
    </item>
    <item>
      <title>开发一个在线代码对比工具</title>
      <link>https://itindex.net/detail/62342-%E5%BC%80%E5%8F%91-%E5%9C%A8%E7%BA%BF-%E4%BB%A3%E7%A0%81</link>
      <description>&lt;hr&gt;&lt;/hr&gt;
 &lt;h2&gt;highlight: monokai&lt;/h2&gt;
 &lt;p&gt;我正在参加「创意开发 投稿大赛」详情请看：  &lt;a href="https://juejin.cn/post/7120441631530549284" title="https://juejin.cn/post/7120441631530549284"&gt;掘金创意开发大赛来了！&lt;/a&gt;&lt;/p&gt;
 &lt;h2&gt;前言&lt;/h2&gt;
 &lt;p&gt;在开发过程中，我们经常需要用到代码对比，对比下代码是否一致，有哪些改动，方便我们可以查看问题，今天我们就来说实现下，其实很简单，不需要后端，纯前端就可以实现。&lt;/p&gt;
 &lt;h2&gt;Monaco Editor&lt;/h2&gt;
 &lt;p&gt;  &lt;a href="https://microsoft.github.io/monaco-editor/" title="Monaco Editor"&gt;Monaco Editor&lt;/a&gt; 是 VS Code 中使用的开源代码编辑器， 拥有代码高亮和代码自动补全的功能，并且内置了一个 Diff Editor。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="&amp;#23448;&amp;#32593; Diff editor" src="https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fdf6eca3d39e4e8e9f4d8f1f3d0bcbad~tplv-k3u1fbpfcp-watermark.image?"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;官网就有一个 Diff Editor 的演示，我们要开发的就是在这个基础之上，加上语言切换的功能，让这个 Diff Editor 拥有内置云语言的语法高亮。&lt;/p&gt;
 &lt;p&gt;  &lt;code&gt;TypeScript, JavaScript, CSS, LESS, SCSS, JSON, HTML、XML, PHP, C#, C++, Razor, Markdown, Diff, Java, VB, CoffeeScript, Handlebars, Batch, Pug, F#, Lua, Powershell, Python, Ruby, SASS, R, Objective-C&lt;/code&gt;&lt;/p&gt;
 &lt;p&gt;官网罗列了这些语言，但远不止于此。&lt;/p&gt;
 &lt;h2&gt;马上掘金&lt;/h2&gt;
 &lt;p&gt;  &lt;a href="https://code.juejin.cn/pen/7123357709495173151" title="Monaco Sample Editor"&gt;代码片段&lt;/a&gt;
使用 monaco-editor 创建一个简单的代码编辑器&lt;/p&gt;
 &lt;p&gt;  &lt;a href="https://code.juejin.cn/pen/7123359492221173768" title="Monaco Diff Editor"&gt;代码片段&lt;/a&gt;
使用 monaco-editor 创建一个简单的 Diff 编辑器&lt;/p&gt;
 &lt;p&gt;Monaco Editor 有 2 种加载方式，分别是 amd 和 esm，也就是   &lt;code&gt;Requirejs&lt;/code&gt; 和   &lt;code&gt;ES Modules&lt;/code&gt;。马上掘金中使用的是   &lt;code&gt;requirejs&lt;/code&gt;。&lt;/p&gt;
 &lt;h2&gt;技术栈选择&lt;/h2&gt;
 &lt;p&gt;我准备把常用的工具做成一个工具网站，所以我选择使用   &lt;a href="https://nextjs.org/" title="next.js"&gt;next.js&lt;/a&gt;，并且可以使用   &lt;a href="https://vercel.com/"&gt;vercel&lt;/a&gt; 免费持续部署。&lt;/p&gt;
 &lt;p&gt;关于 Monaco Editor 在 next.js 中的配置，之前有介绍过，大家可以看这篇文章   &lt;a href="https://juejin.cn/post/7091177467498463239"&gt;《在 Next.js 中使用 Monaco Editor》&lt;/a&gt;。&lt;/p&gt;
 &lt;h2&gt;实现 Diff Editor&lt;/h2&gt;
 &lt;pre&gt;  &lt;code&gt;import type { editor as MonacoEditor } from &amp;quot;monaco-editor&amp;quot;;
import { useEffect, useRef, useState } from &amp;quot;react&amp;quot;;
import * as monaco from &amp;quot;monaco-editor&amp;quot;;

export default function TextDiffPage() {
 const editorContainer = useRef&amp;lt;HTMLDivElement | null&amp;gt;(null);
 const [language, setLanguage] = useState(&amp;quot;text&amp;quot;);
 const [inlineView, setInlineView] = useState(false);

 const [diffEditor, setDiffEditor] =
   useState&amp;lt;MonacoEditor.IStandaloneDiffEditor | null&amp;gt;(null);

 const createModel = (
   value: string,
   language: string,
   type: &amp;quot;original&amp;quot; | &amp;quot;modified&amp;quot;
 ) =&amp;gt; {
   return monaco.editor.createModel(value, language);
 };

 const initEditor = async () =&amp;gt; {
   const originalModel = createModel(`Hello World`, language, &amp;quot;original&amp;quot;);
   const modifiedModel = createModel(`Goodbye World`, language, &amp;quot;modified&amp;quot;);
   const editor = monaco.editor.createDiffEditor(editorContainer.current, {
     minimap: { enabled: false },
     theme: &amp;quot;vs-dark&amp;quot;,
     renderSideBySide: !inlineView,
     originalEditable: true,
   });
   editor.setModel({
     original: originalModel,
     modified: modifiedModel,
   });

   setDiffEditor(editor);
 };

 useEffect(() =&amp;gt; {
   initEditor();
   return () =&amp;gt; {
     if (diffEditor) diffEditor.dispose();
   };
 }, []);

 useEffect(() =&amp;gt; {
   if (diffEditor) {
     diffEditor.updateOptions({
       renderSideBySide: !inlineView,
     });
   }
 }, [inlineView]);

 return (
   &amp;lt;div className=&amp;quot;h-screen flex flex-col&amp;quot;&amp;gt;
     &amp;lt;header className=&amp;quot;h-16 border-b dark:border-neutral-800 flex-shrink-0 flex items-center px-3 space-x-5&amp;quot;&amp;gt;
       &amp;lt;label className=&amp;quot;space-x-1 flex items-center&amp;quot;&amp;gt;
         &amp;lt;input
           type=&amp;quot;checkbox&amp;quot;
           checked={inlineView}
           onChange={(e) =&amp;gt; setInlineView(e.target.checked)}
         /&amp;gt;
         &amp;lt;span&amp;gt;Inline diff&amp;lt;/span&amp;gt;
       &amp;lt;/label&amp;gt;
     &amp;lt;/header&amp;gt;
     &amp;lt;div ref={editorContainer} className=&amp;quot;h-full&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
   &amp;lt;/div&amp;gt;
 );
}
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;上述代码很简单，可能有同学对   &lt;code&gt;createModel&lt;/code&gt; 方法比较疑惑，为什么是   &lt;code&gt;Model&lt;/code&gt; ？好比 Monaco Editor 是一个容器，容器可以设置 Model、切换 Model，比如 vscode 中，每打开一个文件就是一个 Model，文件切换就是切换 model，每个文件都有状态，比如光标位置，历史记录等，这些状态都存在 model 中，这样就不会因为文件切换而状态混淆。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;// typescript 禁用类型检查
monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({
  noSemanticValidation: true,
  noSyntaxValidation: false,
});

// typescript jsx 格式使用 React 语法解析
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
  jsx: monaco.languages.typescript.JsxEmit.React,
});
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;对与一些 typescript 的语法校验我们可以选择关闭，jsx 不支持，可以设置为 react 语法支持。&lt;/p&gt;
 &lt;h2&gt;最后&lt;/h2&gt;
 &lt;p&gt;最后我的工具网站也开源了，包含一些前端常用工具，还可以在线刷面试题。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;a href="https://www.runjs.cool/text-diff"&gt;代码对比编辑器&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://github.com/maqi1520/runjs.cool"&gt;GitHub 代码&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;如果对你有帮助，可以随手点个赞，这对我真的很重要。&lt;/p&gt;
 &lt;p&gt;以上就是本文全部内容，希望这篇文章对大家有所帮助，也可以参考我往期的文章或者在评论区交流你的想法和心得，欢迎一起探索前端。&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/62342-%E5%BC%80%E5%8F%91-%E5%9C%A8%E7%BA%BF-%E4%BB%A3%E7%A0%81</guid>
      <pubDate>Sat, 23 Jul 2022 09:32:39 CST</pubDate>
    </item>
    <item>
      <title>简述前端包管理工具机制和相关实践</title>
      <link>https://itindex.net/detail/62288-%E5%89%8D%E7%AB%AF-%E7%AE%A1%E7%90%86-%E5%B7%A5%E5%85%B7%E6%9C%BA</link>
      <description>&lt;h1&gt;简述前端包管理工具机制和相关实践&lt;/h1&gt; &lt;h3&gt;npm 依赖管理机制&lt;/h3&gt; &lt;p&gt;区别于 Python 的包管理工具 pip 的全局安装，npm 会安装依赖包到当前项目目录，使不同项目的依赖更成体系，这样做的好处是减轻了包作者的 API 兼容性压力；但是缺陷是如果两个项目依赖了一个相同的库，一般这个库会在这两个项目中各安装一次，即相同的依赖包会被多次安装。  &lt;br /&gt;我们先通过一张流程图(源自掘金)来了解下 npm install 的整体流程  &lt;br /&gt;  &lt;img&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;可以看到执行 npm install 后依次会进行以下流程&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;检查 package-lock.json&lt;/li&gt;  &lt;li&gt;通过和 package.json 对比确定是否远程获取包信息&lt;/li&gt;  &lt;li&gt;扁平化构建依赖树&lt;/li&gt;  &lt;li&gt;添加缓存&lt;/li&gt;  &lt;li&gt;下载包并解压到 node_modules&lt;/li&gt;  &lt;li&gt;生成新的 lock 文件
值得注意的是，早期 npm 版本(v5.0 - v5.4)发现 package.json 和 package-lock.json 不一致时，对依赖的安装方式是不一样的。   &lt;strong&gt;所以对于团队而言，最佳实践应该是保持 npm 版本的一致性！&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt; &lt;h4&gt;缓存机制&lt;/h4&gt; &lt;p&gt;我们可以从流程图中看到，npm install 的流程中会查找和使用缓存，以及下载包后会添加缓存的环节。由于依赖嵌套机制，项目中 node_moudles 占用的磁盘空间无疑是最大的，如果安装时每次都通过网络下载获取，那么时间成本是巨大的。常见的优化方式是“空间换时间”，npm 也通过缓存机制来解决这个问题。  &lt;br /&gt;简单了解下缓存的目录的和清除机制。  &lt;br /&gt;通过  &lt;code&gt;npm config get cache&lt;/code&gt;命令可以查询到缓存目录：默认是用户主目录下的  &lt;code&gt;.npm/_cacache 目录。&lt;/code&gt;  &lt;br /&gt;  &lt;code&gt;npm cache clean --force&lt;/code&gt;即可强制清除缓存。&lt;/p&gt; &lt;h3&gt;yarn 带来了什么？&lt;/h3&gt; &lt;p&gt;yarn 是于 2016 年诞生的，它的出现解决了历史上 npm 的很多问题，比如缺乏对于依赖的完整性和一致性保障(npm v3 版本还没有 package-lock.json)，以及 npm 安装速度过慢的问题等。npm 目前已经迭代到 v8 版本，在很多方面已经借鉴了 yarn 的优点，但是我们不妨了解下 yarn 诞生时带来的理念。&lt;/p&gt; &lt;ol&gt;  &lt;li&gt;确定性。通过 yarn.lock 等机制，保证了确定性，这里的确定性包括但不限于明确的依赖版本、明确的依赖安装结构等。即在任何机器和环境下，都可以以相同的方式被安装。&lt;/li&gt;  &lt;li&gt;模块扁平化安装。将依赖包的不同版本，按照一定策略，归结为单个版本，以避免创建多个副本造成冗余。&lt;/li&gt;  &lt;li&gt;更快的速度。yarn 采取并行安装的机制进行包的安装任务，提高了性能；yarn 引入的缓存机制使二次安装的速度更快。&lt;/li&gt;  &lt;li&gt;更好的语义化。yarn 的命令更加简洁。   &lt;strong&gt;解决早期 npm 的依赖管理问题&lt;/strong&gt;&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;文章的开始提到 npm 是将依赖放到项目的 node_modules 中，同时如果 node_modules 中的依赖 A 还依赖了其他依赖 B，那么 B 也会被安装到 A 的 node_modules 文件夹，依次递归最终形成非常复杂和庞大的依赖树。  &lt;br /&gt;这种依赖管理方式会随着项目的迭代，node_moudles 会变得越来越复杂，从而造成：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;非常深的项目依赖层级，难以排查问题&lt;/li&gt;  &lt;li&gt;依赖被重复安装，浪费磁盘，网络等资源，安装速度慢
那么 yarn 是如何解决这个问题的呢？那就是模块扁平化安装机制。假如我们有这样一个文件依赖结构。&lt;/li&gt;&lt;/ul&gt; &lt;pre&gt;  &lt;code&gt;App   &lt;br /&gt; -a@2.0   &lt;br /&gt;   -b@2.0   &lt;br /&gt; -b@2.0   &lt;br /&gt; -c@1.0   &lt;br /&gt;   -b@2.0   &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;yarn 在安装依赖时会打平依赖，并对重复依赖进行提升，最终形成的依赖结构如下：&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;App   &lt;br /&gt; -a@2.0   &lt;br /&gt; -b@2.0   &lt;br /&gt; -c@1.0   &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;但是需要注意的是：  &lt;strong&gt;模块的安装顺序可能影响 node_modules 内的文件结构。&lt;/strong&gt;在 npm v3 版本中，假如 项目一开始依赖了 a@1.0，此时 a@1.0 会被安装在顶层目录；随着迭代，又引入了模块 b@1.0，而 b@1.0 又依赖了 a@2.0，此时 a@2.0 会被安装在 b@1.0 下，因为顶层已经有一个 a@1.0 了。&lt;/p&gt; &lt;h3&gt;pnpm: 最先进的包管理工具？&lt;/h3&gt; &lt;img&gt;&lt;/img&gt; &lt;p&gt;在各个场景下，pnpm 相比较于 npm(v8)和 yarn(v3)在性能上都有不错的提升。  &lt;br /&gt;pnpm 之所以有如此大的性能提升，简单来说 pnpm 是通过全局 store（目录 ${os.homedir}/.pnpm-store）来存储 node_modules 依赖的 hard-links，当在项目文件中引用依赖的时候则是通过 symlink 去找到对应虚拟磁盘目录下(.pnpm 目录)的依赖地址。相比于 npm 和 yarn 会在每个项目中都安装一份 node_moudles, pnpm 的全局 store 则实现了“安装一次，所有项目复用”，这样避免了二次安装带来的时间消耗。  &lt;br /&gt;除此之外，pnpm 本身的设计机制解决了 monorepo 的很多痛点，比如  &lt;strong&gt;”幽灵依赖“&lt;/strong&gt;和  &lt;strong&gt;”依赖重复安装“&lt;/strong&gt;的问题。如图：  &lt;img&gt;&lt;/img&gt;下面两小节内容源自：pnpm: 最先进的包管理工具  &lt;sup&gt;[1]&lt;/sup&gt;&lt;/p&gt; &lt;h4&gt;幽灵依赖&lt;/h4&gt; &lt;p&gt;Phantom dependencies 被称之为幽灵依赖，解释起来很简单，即某个包没有被安装(package.json 中并没有，但是用户却能够引用到这个包)。  &lt;br /&gt;引发这个现象的原因一般是因为 node_modules 结构所导致的，例如使用 yarn 对项目安装依赖，依赖里面有个依赖叫做 foo，foo 这个依赖同时依赖了 bar，yarn 会对安装的 node_modules 做一个扁平化结构的处理(npm v3 之后也是这么做的)，会把依赖在 node_modules 下打平，这样相当于 foo 和 bar 出现在同一层级下面。那么根据 nodejs 的寻径原理，用户能 require 到 foo，同样也能 require 到 bar。&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;package.json -&amp;gt; foo(bar 为 foo 依赖)   &lt;br /&gt;node_modules   &lt;br /&gt;  /foo   &lt;br /&gt;  /bar -&amp;gt; 依赖   &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;那么这里这个 bar 就成了一个幽灵依赖，如果某天某个版本的 foo 依赖不再依赖 bar 或者 foo 的版本发生了变化，那么 require bar 的模块部分就会抛错。&lt;/p&gt; &lt;h4&gt;依赖重复安装&lt;/h4&gt; &lt;p&gt;这个问题其实也可以说是 hoist 导致的，这个问题可能会导致有大量的依赖的被重复安装，举个例子:  &lt;br /&gt;例如有个 package，下面依赖有 lib_a、lib_b、lib_c、lib_d，其中 a 和 b 依赖 util_e@1.0.0，而 c 和 d 依赖 util_e@2.0.0。  &lt;br /&gt;那么早期 npm 的依赖结构应该是这样的:&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;- package   &lt;br /&gt;  - package.json   &lt;br /&gt;  - node_modules   &lt;br /&gt;     - lib_a   &lt;br /&gt;       - node_modules &amp;lt;- util_e@1.0.0   &lt;br /&gt;     - lib_b   &lt;br /&gt;       - node_modules &amp;lt;- util_e@1.0.0   &lt;br /&gt;     _ lib_c   &lt;br /&gt;       - node_modules &amp;lt;- util_e@2.0.0   &lt;br /&gt;     - lib_d   &lt;br /&gt;       - node_modules &amp;lt;- util_e@2.0.0   &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;这样必然会导致很多依赖被重复安装，于是就有了 hoist 和打平依赖的操作:&lt;/p&gt; &lt;pre&gt;  &lt;code&gt;- package   &lt;br /&gt;  - package.json   &lt;br /&gt;  - node_modules   &lt;br /&gt;     - util_e@1.0.0   &lt;br /&gt;     - lib_a   &lt;br /&gt;     - lib_b   &lt;br /&gt;     _ lib_c   &lt;br /&gt;       - node_modules &amp;lt;- util_e@2.0.0   &lt;br /&gt;     - lib_d   &lt;br /&gt;       - node_modules &amp;lt;- util_e@2.0.0   &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt; &lt;p&gt;但是这样也只能提升一个依赖，如果两个依赖都提升了会导致冲突，这样同样会导致一些不同版本的依赖被重复安装多次，这里就会导致使用 npm 和 yarn 的性能损失。  &lt;br /&gt;如果是 pnpm 的话，这里因为依赖始终都是存在 store 目录下的 hard links ，一份不同的依赖始终都只会被安装一次，因此这个是能够被彻彻底底的消除的。&lt;/p&gt; &lt;h3&gt;项目中的相关场景实践和常见问题&lt;/h3&gt; &lt;h4&gt;npm link&lt;/h4&gt; &lt;p&gt;适用场景：本地调试 npm 模块，将模块链接到对应的业务项目中运行
使用方法：假如我们需要把模块 pkg-a 链接到主项目 App 中，首先在 pkg-a 根目录中执行 npm link，然后在 App 根目录中执行 npm link pkg-a 即可。调试完可以使用 npm unlink 取消关联。原理：npm link 通过软连接将 pkg-a 链接到 node 模块的全局目录和可执行文件中，实现 npm 包命令的全局可执行。&lt;/p&gt; &lt;h4&gt;npx&lt;/h4&gt; &lt;p&gt;适用场景：在 npm 5.2.0 版本之后，npm 内置了 npx 的包。npx 是一个简单的 cli 工具，可以帮助我们快速的调试，还可以让我们在不通过 npm 安装包的前提下执行一些 npm 包。&lt;/p&gt; &lt;p&gt;使用方法：  &lt;br /&gt;  &lt;strong&gt;Before:&lt;/strong&gt;一般情况下，如果我们想使用 es-lint, 会先通过 npm install es-lint, 然后在项目根目录执行
./node_modules/.bin/es-lint your_file.js 或者 通过 package.json 的 npm scripts 调用 eslint。  &lt;br /&gt;  &lt;strong&gt;After:&lt;/strong&gt;npx es-lint your_file.js  &lt;br /&gt;  &lt;strong&gt;原理&lt;/strong&gt;：npx 在运行时会自动去 ./node_moudles/.bin 和 环境变量 寻找命令&lt;/p&gt; &lt;h4&gt;是否提交 lock.json 到代码仓库&lt;/h4&gt; &lt;p&gt;前面我们提到 yarn 带来了 .lock 文件的机制，使得在任何环境下执行 install，都能得到一致的 node_modules 安装结果。但是是否需要提交 lockfiles(package-lock.json/yarn.lock) 到代码仓库呢？  &lt;br /&gt;npm 官方文档  &lt;sup&gt;[2]&lt;/sup&gt;是建议把 package-lock.json 文件提交到代码仓库的。在多人协作的项目中，这样做确实没有问题。但是如果开发的是库，在 npm publish 的时候最好忽略 lockfiles。因为库一般是被其他项目依赖的，在不使用 lockfiles 的情况下，由于新版 npm 和 yarn 的 hoist 机制，可以复用住项目已经加载过的包，减少依赖重复和体积。  &lt;br /&gt;但是存在这样一种现象：即使在一些发布时忽略 lockfiles 的库中，在主项目顶层存在相关依赖包的前提下，最终生成的 lockfile 仍然没复用主项目的包。这是为什么呢？原因是库的依赖包版本和主项目存在的依赖包版本不一致。具体看下图：主项目的 yarn.lock 中显示 browser 这个包依赖了 @babel/runtime@7.0.0&lt;/p&gt; &lt;img&gt;&lt;/img&gt; &lt;p&gt;主项目 node_modules 顶层的 @babel/runtime 版本为 7.10.1&lt;/p&gt; &lt;img&gt;&lt;/img&gt; &lt;p&gt;知道了原因，那么如何减少库项目的依赖项呢。到这里，解决方案也就呼之欲出了：&lt;/p&gt; &lt;ol&gt;  &lt;li&gt;库项目尽量使用和主项目版本一致的依赖包&lt;/li&gt;  &lt;li&gt;在库项目 package.json 的 “peerDevpendencies” 字段中声明主项目已有的依赖包&lt;/li&gt;&lt;/ol&gt; &lt;h4&gt;合入其他分支代码后编译报错&lt;/h4&gt; &lt;p&gt;相信很多同学都遇到过和我一样的问题：当自己的 feat 分支代码合入 master 或者业务班车分支的代码时，重新 yarn 时，有时候会编译失败，报大量 &amp;quot;can&amp;apos;t resolve module xxx&amp;quot;的错误。这种错误有很多情况是依赖版本不一致的问题，但是又极其难以定位，令人头痛。那么此时有另外一个思路，那就是从 master 拉一个最新的分支再进行合入。  &lt;br /&gt;但更好的解决方式是：建议在日常开发过程中，定时合入 master 代码，一方面可以合入最新的 feat，另一方面可以避免长时间不合入，最后在上线阶段合入代码，可能出现大量冲突，解决不当或遗漏而造成的编译问题。同时也可以考虑将工具升级为 pnpm，以解决潜在的“幽灵依赖”和“依赖嵌套”问题，同时带来性能上的提升。&lt;/p&gt; &lt;h3&gt;参考资料&lt;/h3&gt;[1] &lt;p&gt;pnpm: 最先进的包管理工具:  &lt;em&gt;https://bytedance.feishu.cn/docs/doccngSUrvF0qPVmBE1rq1iPZQf&lt;/em&gt;&lt;/p&gt;[2] &lt;p&gt;npm官方文档:  &lt;em&gt;https://docs.npmjs.com/cli/v7/configuring-npm/package-lock-json&lt;/em&gt;&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>dev</category>
      <guid isPermaLink="true">https://itindex.net/detail/62288-%E5%89%8D%E7%AB%AF-%E7%AE%A1%E7%90%86-%E5%B7%A5%E5%85%B7%E6%9C%BA</guid>
      <pubDate>Wed, 01 Jun 2022 00:00:00 CST</pubDate>
    </item>
    <item>
      <title>中台工具产品方法论</title>
      <link>https://itindex.net/detail/62223-%E4%B8%AD%E5%8F%B0-%E5%B7%A5%E5%85%B7-%E4%BA%A7%E5%93%81</link>
      <description>&lt;h2&gt;背景&lt;/h2&gt;



 &lt;p&gt;做中台工具产品不是一件容易的事情，需要对接上层所有业务方，做的慢业务方不满意，做的快业务方未必会给好的评价。&lt;/p&gt;



 &lt;p&gt;属于容易背锅，细节极其多，用户反馈建议多，但又难以出成绩和证明自己做的好。&lt;/p&gt;



 &lt;h2&gt;场景&lt;/h2&gt;



 &lt;p&gt;虚构一些场景，大家肯定都遇到过。&lt;/p&gt;



 &lt;h3&gt;场景1&lt;/h3&gt;



 &lt;p&gt;老板：XX功能我觉得不错，做了吗？&lt;/p&gt;



 &lt;p&gt;产品经理：收到，我研究下。&lt;/p&gt;



 &lt;p&gt;几天之后……&lt;/p&gt;



 &lt;p&gt;老板：XX和XX等N个功能都不错，马上排期做下。&lt;/p&gt;



 &lt;p&gt;产品经理：收到。&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;评价：&lt;/strong&gt;产品经理是初级的工具人，执行命令，最终老板大概率不会满意，因为产品功能都是老板自己提的，上线未必效果好。&lt;/p&gt;



 &lt;p&gt;老板会认为自己是个有想法的人，同时认为产品经理没有想法，产品经理挺冤枉，干的比黄牛累。出了成绩是老板高瞻远瞩，没有成绩是没执行好。&lt;/p&gt;



 &lt;p&gt;对话通常以被动接受信息，缺乏有效的反馈和互动。收集大量需求导致执行慢，可能会被技术同事认为是战斗力等于5的渣渣。&lt;/p&gt;



 &lt;h3&gt;场景2&lt;/h3&gt;



 &lt;p&gt;老板：XX功能我觉得不错，做了吗？&lt;/p&gt;



 &lt;p&gt;产品经理：收到，我研究下…（几小时之后）这个功能挺好，和技术同事工作量比较大，会在下一个版本中增加。&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;评价：&lt;/strong&gt;高级工具人，收到信息之后，能够给予执行的时间点，对工作量和项目节奏有一定把控能力。&lt;/p&gt;



 &lt;h3&gt;场景3&lt;/h3&gt;



 &lt;p&gt;老板：XX功能我觉得不错，做了吗？&lt;/p&gt;



 &lt;p&gt;产品经理：研究过了，用户给我们反馈过，XX竞品也有这个功能。我们已经在当前规划中了，计划在下一个版本中增加。&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;评价：&lt;/strong&gt;有一定自主工作意识，对产品有一定思考，老板只是信息源之一，能从用户反馈和竞品等渠道提炼产品需求，有主动找信息和需求的能力。但很少反驳别人的想法，对事情的优先级把控能力还不够。&lt;/p&gt;



 &lt;h3&gt;场景4&lt;/h3&gt;



 &lt;p&gt;老板：XX功能我觉得不错，做了吗？&lt;/p&gt;



 &lt;p&gt;产品经理：研究过了，会放在下一个版本中增加。我们当前版本是主要提升另外一个模块的易用性，用户意见比较大，是影响产品NPS的主要因素，上线之后预期会影响xx%比例的用户。而你提的XX功能属于体验优化，我们会在下一个版本中迭代。&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;评价：&lt;/strong&gt;有优先级掌控意识，在和老板的对话中体现了自身的专业性，体现了用户反馈、NPS和数据来驱动产品迭代，在产品标准上和老板拉齐认知。产品的目的在于提升NPS等，不局限于讨论某个功能点。即使拒绝了老板需求，老板也会认为产品经理有自己的思考。&lt;/p&gt;



 &lt;p&gt;但是缺陷在于，产品经理的眼光只能看到未来1-2个月的规划，对长期规划还缺乏把控。&lt;/p&gt;



 &lt;h3&gt;场景5&lt;/h3&gt;



 &lt;p&gt;老板：XX功能我觉得不错，做了吗？&lt;/p&gt;



 &lt;p&gt;产品经理：已经在规划当中了，房产中介对这个需求有很强需求，而其他类型的用户需求不高。我们今年的目标是满足电商等前5的用户诉求，这些行业对公司贡献大，我们最近做了调研发现电商等用户对XX功能有很强的诉求，提炼了N个需求来，比如XX……&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;评价：&lt;/strong&gt;对全局有掌控，对公司战略目标有理解，基于战略目标拆解出了自己的目标，能够主导项目迭代。&lt;/p&gt;



 &lt;p&gt;老板满意，合作方满意，技术同事跟着干活能自己做事情的价值，对产品经理认可度很高，不会带着项目组做无用功。&lt;/p&gt;



 &lt;h3&gt;场景6&lt;/h3&gt;



 &lt;p&gt;老板：XX功能我觉得不错，做了吗？&lt;/p&gt;



 &lt;p&gt;产品经理：需求可以做，功能产品侧都可以做，细节是永远做不完的，但是缺乏方向性。我认为SaaS产品的核心目标是收入，当前提升收入的抓手我们还没有明确。通过数据分析、用户访谈和专家访谈，我觉得有xx场景是可以深挖的，这些行业还有50%+增量收入，期望与其他团队能联动在未来半年中达成这项目标。而你提的XX功能，不是典型的应用场景。&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;评价：&lt;/strong&gt;这样的产品经理已经可以做业务负责人了，有完善的方向判断能力和产品方法论，有能力带领项目组走向成功。&lt;/p&gt;



 &lt;p&gt;而老板是资源，说服老板投入更多精力在某个方向，老板会对产品经理更为信任，提功能细节的次数会很少，更多是讨论项目方向，输入外界信息来帮助产品经理做判断。&lt;/p&gt;



 &lt;p&gt;做产品，超出用户预期，他们才会满意。做产品经理也是一样，认知超越周围的同事，大家才会认可你。&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>晓生</category>
      <guid isPermaLink="true">https://itindex.net/detail/62223-%E4%B8%AD%E5%8F%B0-%E5%B7%A5%E5%85%B7-%E4%BA%A7%E5%93%81</guid>
      <pubDate>Thu, 28 Apr 2022 17:50:15 CST</pubDate>
    </item>
    <item>
      <title>解放双手！推荐一款阿里开源的低代码工具，YYDS！</title>
      <link>https://itindex.net/detail/62221-%E8%A7%A3%E6%94%BE-%E5%8F%8C%E6%89%8B-%E9%98%BF%E9%87%8C</link>
      <description>&lt;blockquote&gt;
  &lt;p&gt;之前分享过一些低代码相关的文章，发现大家还是比较感兴趣的。之前在我印象中低代码就是通过图形化界面来生成代码而已，其实真正的低代码不仅要负责生成代码，还要负责代码的维护，把它当做一站式开发平台也不为过！最近体验了一把阿里开源的低代码工具   &lt;code&gt;LowCodeEngine&lt;/code&gt;，确实是一款面向企业级的低代码解决方案，推荐给大家！&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;p&gt;SpringBoot实战电商项目mall（50k+star）地址：  &lt;a href="https://github.com/macrozheng/mall"&gt;https://github.com/macrozheng/mall&lt;/a&gt;&lt;/p&gt;
 &lt;h2&gt;LowCodeEngine简介&lt;/h2&gt;
 &lt;p&gt;LowCodeEngine是阿里开源的一套面向扩展设计的企业级低代码技术体系，目前在在Github上已有  &lt;code&gt;4.7K+Star&lt;/code&gt;。这个项目大概是今年2月中旬开源的，两个月不到收获这么多Star，确实非常厉害！&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a4507e7c0e5341618f46bfb3247ca81e~tplv-k3u1fbpfcp-zoom-1.image"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;LowCodeEngine主要具有如下特性：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;提炼自企业级低代码平台的低代码引擎，奉行高度可扩展、最小内核、最强生态的设计理念；&lt;/li&gt;
  &lt;li&gt;部署简单，基本上就是开箱即用，拥有完善的物料体系、功能强大的设置器、丰富的插件等；&lt;/li&gt;
  &lt;li&gt;可视化编辑器具有完善的工具链，支持物料体系、设置器、插件等生态元素；&lt;/li&gt;
  &lt;li&gt;强大的扩展能力，已支撑近 100 个各种垂直类低代码平台；&lt;/li&gt;
  &lt;li&gt;使用 TypeScript 开发，能生成基于React的前端代码。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;下面是LowCodeEngine使用过程中的一张效果图，功能还是很强大的！&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/395487c1d87d46a585f571809cffbb0c~tplv-k3u1fbpfcp-zoom-1.image"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;搭建低代码平台&lt;/h2&gt;
 &lt;blockquote&gt;
  &lt;p&gt;接下来我们将使用LowCodeEngine搭建一个低代码开发平台，仅需5分钟，可以说是开箱即用！&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;ul&gt;
  &lt;li&gt;首先我们需要想下载LowCodeEngine编辑器的示例代码，下载地址：https://github.com/alibaba/lowcode-demo&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;img alt="" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/726b28ca9ac44bba8b705177d8fdaae5~tplv-k3u1fbpfcp-zoom-1.image"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;下载成功后解压到指定目录，安装此项目需要使用   &lt;code&gt;Node.js&lt;/code&gt;和   &lt;code&gt;npm&lt;/code&gt;，确保已经安装完毕，由于依赖中有些   &lt;code&gt;npm源&lt;/code&gt;无法访问，这里推荐使用   &lt;code&gt;cnpm&lt;/code&gt;来安装，先使用如下命令安装   &lt;code&gt;cnpm&lt;/code&gt;；&lt;/li&gt;
&lt;/ul&gt;
 &lt;pre&gt;  &lt;code&gt;npm install -g cnpm --registry=https://registry.npmmirror.com
&lt;/code&gt;&lt;/pre&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;code&gt;cnpm&lt;/code&gt;安装成功后，进入解压目录使用如下命令安装依赖；&lt;/li&gt;
&lt;/ul&gt;
 &lt;pre&gt;  &lt;code&gt;cnpm install
&lt;/code&gt;&lt;/pre&gt;
 &lt;ul&gt;
  &lt;li&gt;依赖安装完成后，使用   &lt;code&gt;npm start&lt;/code&gt;命令启动项目；&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;img alt="" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2280aa7b3c3f4edcaf2233685247dd97~tplv-k3u1fbpfcp-zoom-1.image"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;项目运行成功后将运行在   &lt;code&gt;5556&lt;/code&gt;端口上，访问地址：http://localhost:5556&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;img alt="" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/0798142be6c04aed915639e9fcb47dac~tplv-k3u1fbpfcp-zoom-1.image"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;使用低代码平台&lt;/h2&gt;
 &lt;blockquote&gt;
  &lt;p&gt;之前在我的开源项目   &lt;a href="https://github.com/macrozheng/mall"&gt;mall&lt;/a&gt;中有个品牌管理功能，接下来我们将使用LowCodeEngine来实现下它，看看低代码开发有何神奇之处！&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;h3&gt;目标效果&lt;/h3&gt;
 &lt;p&gt;  &lt;a href="https://github.com/macrozheng/mall"&gt;mall&lt;/a&gt;项目中的品牌管理功能效果如下，这里使用低代码简单实现下品牌列表功能。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/9658706f0687495b8b792fcf89e3d479~tplv-k3u1fbpfcp-zoom-1.image"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;组件库&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;首先我们从   &lt;code&gt;组件库&lt;/code&gt;中选择   &lt;code&gt;查询筛选&lt;/code&gt;组件，通过拖拽的形式插入编辑区中；&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;img alt="" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cc7cf823122347c2afb98ad157587969~tplv-k3u1fbpfcp-zoom-1.image"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;然后选中   &lt;code&gt;查询筛选&lt;/code&gt;组件，通过右侧的   &lt;code&gt;设置器&lt;/code&gt;进行设置；&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;img alt="" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ecb607544b734ddc84768fa8e5aa7554~tplv-k3u1fbpfcp-zoom-1.image"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;可以点击组件左侧的   &lt;code&gt;编辑&lt;/code&gt;按钮对组件进行详细设置，比如说组件外观和输入提示等；&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;img alt="" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5b7c3b2076f6432a857be74f4cc6e80c~tplv-k3u1fbpfcp-zoom-1.image"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;接下来再拖拽一个   &lt;code&gt;高级表格&lt;/code&gt;组件到编辑器中去；&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;img alt="" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8b2d76505b9e422f832ac67e975a38bf~tplv-k3u1fbpfcp-zoom-1.image"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;同样选中   &lt;code&gt;高级表格&lt;/code&gt;组件可以对表格进行设置，我们可以通过   &lt;code&gt;数据列&lt;/code&gt;来设置需要显示的数据。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;img alt="" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/aa154000d7774627afe1f425c8327460~tplv-k3u1fbpfcp-zoom-1.image"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;数据源&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;由于表格中的数据需要访问接口来获取，这里我们可以通过   &lt;code&gt;数据源&lt;/code&gt;功能来实现，这里我们调用演示环境的API，填入请求参数即可，值得注意的是由于数据列表在   &lt;code&gt;data.list&lt;/code&gt;属性中，我们需要定制下请求成功的处理函数；&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;img alt="" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ab57510b8425473ba2acf35fad3c0f6d~tplv-k3u1fbpfcp-zoom-1.image"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;接下来选中   &lt;code&gt;高级表格&lt;/code&gt;组件，修改   &lt;code&gt;表格数据源&lt;/code&gt;，选择   &lt;code&gt;表达式输入&lt;/code&gt;，填入我们之前设置的   &lt;code&gt;数据源ID&lt;/code&gt;即可；&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;img alt="" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c5296f7705cc416a89d0e3fcf3a98619~tplv-k3u1fbpfcp-zoom-1.image"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;然后修改   &lt;code&gt;数据列&lt;/code&gt;信息，将每个数据列   &lt;code&gt;数据字段&lt;/code&gt;修改为JSON数据中对应的属性即可。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;img alt="" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/86930bccc6a841f29f7913402f3375eb~tplv-k3u1fbpfcp-zoom-1.image"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;预览及出码&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;如果想查看搭建的页面效果的话，点击右上角的   &lt;code&gt;预览&lt;/code&gt;按钮即可；&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;img alt="" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ae9f4798adf44b4e87a9ee79e1b78ca6~tplv-k3u1fbpfcp-zoom-1.image"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;下面是由低代码生成的页面预览效果；&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;img alt="" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/42036e947d08407eaadcb42d27bd7223~tplv-k3u1fbpfcp-zoom-1.image"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;如果你想获取工具生成的代码的话，点击右上角的   &lt;code&gt;出码&lt;/code&gt;按钮即可，支持直接下载。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;img alt="" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/77bba50e86ef407689b0d9d370309744~tplv-k3u1fbpfcp-zoom-1.image"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;其他功能&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;如果你想自定义一些函数的话，可以通过   &lt;code&gt;源码面板&lt;/code&gt;进行自定义；&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;img alt="" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/eb2dcb6bbcb646a2962391fd40e558a1~tplv-k3u1fbpfcp-zoom-1.image"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;通过   &lt;code&gt;大纲视图&lt;/code&gt;我们可以查看整个界面的结构。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;img alt="" src="https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4814e3fae13140c098a429cba71b04a9~tplv-k3u1fbpfcp-zoom-1.image"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;总结&lt;/h2&gt;
 &lt;p&gt;今天体验了一把阿里开源的低代码开发工具，功能确实很强大。但是低代码并不意味着可以不写代码了，想用好低代码工具还得熟悉工具生成的代码。LowCodeEngine目前仅支持生成React的前端代码，所以想要实现更为复杂的业务系统，还得熟悉React。如果有小伙伴想更深入了解低代码的概念，推荐看下这篇文章  &lt;a href="https://mp.weixin.qq.com/s/MI6MrUKKydtnSdO4xq6jwA"&gt;《阿里低代码引擎和生态建设实战及思考》&lt;/a&gt; 。&lt;/p&gt;
 &lt;h2&gt;参考资料&lt;/h2&gt;
 &lt;ul&gt;
  &lt;li&gt;项目地址：https://github.com/alibaba/lowcode-engine&lt;/li&gt;
  &lt;li&gt;项目官网：https://lowcode-engine.cn/&lt;/li&gt;
  &lt;li&gt;操作指南：https://www.yuque.com/lce/usage&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/62221-%E8%A7%A3%E6%94%BE-%E5%8F%8C%E6%89%8B-%E9%98%BF%E9%87%8C</guid>
      <pubDate>Tue, 19 Apr 2022 01:31:26 CST</pubDate>
    </item>
    <item>
      <title>Calibre 5.36 发布，功能强大的开源电子书工具</title>
      <link>https://itindex.net/detail/62086-calibre-%E5%8A%9F%E8%83%BD-%E5%BC%80%E6%BA%90</link>
      <description>&lt;div&gt;
                                                                                              &lt;p&gt;Calibre 开源项目是 Calibre 官方出的电子书管理工具。它可以查看，转换，编辑和分类所有主流格式的电子书。Calibre 是个跨平台软件，可以在 Linux、Windows 和 macOS 上运行。&lt;/p&gt; 
  &lt;p&gt;Calibre 5.36.0 正式发布，此次更新内容如下：&lt;/p&gt; 
  &lt;h3&gt;新功能&lt;/h3&gt; 
  &lt;ul&gt; 
    &lt;li&gt;编辑元数据对话框：允许通过「首选项-&amp;gt;外观-&amp;gt;编辑」元数据来控制哪些自定义列出现在这个对话框中。&lt;/li&gt; 
    &lt;li&gt;编辑元数据对话框：允许在 &amp;quot;All on 1 tab&amp;quot; 模式下手动调整对话框的各个部分的大小。&lt;/li&gt; 
    &lt;li&gt;编辑书籍：拼写检查，更新捆绑的英语和西班牙语词典&lt;/li&gt; 
    &lt;li&gt;BibTeX 目录：支持自定义列等标签&lt;/li&gt; 
&lt;/ul&gt; 
  &lt;h3&gt;错误修正&lt;/h3&gt; 
  &lt;ul&gt; 
    &lt;li&gt;Amazon 元数据下载：修复评论中的段落被合并的问题&lt;/li&gt; 
    &lt;li&gt;Amazon.de 元数据下载：修复某些书籍的出版日期和系列信息没有被获取的问题&lt;/li&gt; 
    &lt;li&gt;邮件发送：修复通过 Hotmail 发送电子邮件从本周开始无法工作的问题，因为微软改变了 SMTP 服务器的名称&lt;/li&gt; 
    &lt;li&gt;不要删除波兰语标题的文章&lt;/li&gt; 
    &lt;li&gt;电子书查看器：当使用朗读时，在暂停或停止朗读前不会自动查找高亮显示的单词&lt;/li&gt; 
    &lt;li&gt;电子书阅览器：修复打印时 Ctrl+p 快捷键不起作用的问题&lt;/li&gt; 
    &lt;li&gt;查看特定格式且文件丢失时显示错误&lt;/li&gt; 
    &lt;li&gt;编辑书籍：修复以非字开头/结尾的类的重命名不工作的问题&lt;/li&gt; 
    &lt;li&gt;编辑书籍：预览面板：修复在 macOS 上行末的连字符被呈现为方框的问题&lt;/li&gt; 
    &lt;li&gt;修复在图像缩小到适合大小时，修剪图像对话框中显示的不正确的选择尺寸&lt;/li&gt; 
    &lt;li&gt;编辑书籍：修复从另一个编辑器实例粘贴文件时，如果已存在相同名称的文件，则导致失败的问题。&lt;/li&gt; 
&lt;/ul&gt; 
  &lt;p&gt;更多详情可查看：   &lt;a href="https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fcalibre-ebook.com%2Fwhats-new" target="_blank"&gt;https://calibre-ebook.com/whats-new&lt;/a&gt;&lt;/p&gt;
                                        &lt;/div&gt;
                                    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/62086-calibre-%E5%8A%9F%E8%83%BD-%E5%BC%80%E6%BA%90</guid>
      <pubDate>Sun, 06 Feb 2022 09:45:55 CST</pubDate>
    </item>
    <item>
      <title>比较Flyway与Liquibase两大数据库迁移工具_Java</title>
      <link>https://itindex.net/detail/62035-flyway-liquibase-%E6%95%B0%E6%8D%AE%E5%BA%93</link>
      <description>&lt;p&gt;众所周知，对于那些使用着关系型数据库引擎的各类应用程序而言，数据库迁移工具的选用是至关重要的。它们不但能够让各种复杂且重复的过程更加自动化，而且可以让我们更加轻松且准确地完成各种大型的迁移任务。&lt;/p&gt; &lt;p&gt;下面，我将对两种最常见的开源迁移工具--Flyway和Liquibase，进行介绍与比较，以方便您在实际项目中做出合理的选择。&lt;/p&gt; &lt;h1&gt;Flyway的介绍&lt;/h1&gt; &lt;p&gt;Flyway是由Redgate公司带来的一款开源式的数据库迁移工具。该工具注重规则上的简约性，而非繁琐的配置。&lt;/p&gt; &lt;p&gt;目前，它能够支持诸如Postgres、Oracle、SQL Server、DB2、H2、以及MariaDB等大多数主流数据库引擎。同时，Flyway还可以支持诸如：Amazon RDS、Google Cloud SQL、以及Heroku等基于云端业务的数据库服务。&lt;/p&gt; &lt;p&gt;该工具所用到的脚本既可以用纯SQL(支持多种SQL语法)，又能够用Java(主要用于更复杂的转换)来进行编写。同时，它既带有命令行客户端，又提供支持Maven和Gradle的插件。此外，它的Java API，还适用于Android系统。&lt;/p&gt; &lt;p&gt;Flyway的Evolve非常适用于习惯了使用.NET和C#的用户。因此，如果您对其有兴趣的话，请查看文末列出的它在GitHub上的链接页面。&lt;/p&gt; &lt;h1&gt;Liquibase的介绍&lt;/h1&gt; &lt;p&gt;作为于2006年推出的、可用于数据库迁移的开源类工具，Liquibase是基于变更日志(changelog)和变更集(changesets)文件的相关概念实现的。这些文件可以由SQL、XML、YAML、以及JSON编写而成。它们通过存储那些针对数据库结构的更改，以便将其应用到任何其他数据库的实例上。&lt;/p&gt; &lt;p&gt;目前，Liquibase支持的数据库种类包括：Postgres、Oracle、DB2、H2、MariaDB、SQL Server、以及SQLite等。同时，它还支持诸如：Azure SQL、Amazon RDS、以及Amazon Aurora等许多基于云的数据库。&lt;/p&gt; &lt;p&gt;您可以使用诸如Maven、Gradle、甚至是Ant之类的构建工具，从Shell中运行Liquibase的迁移脚本。此外，您可以一次生成纯粹的SQL查询，以便您的DBA、Ops、DevOps团队、或负责数据库的任何人，可以进一步执行此类查询。&lt;/p&gt; &lt;p&gt;有了对于上述两种工具的基本概念，下面让我们来讨论一下它们之间的相同点和不同之处。&lt;/p&gt; &lt;h1&gt;Flyway和Liquibase之间的相似之处&lt;/h1&gt; &lt;ul&gt;  &lt;li&gt;在某种程度上，两者都属于开源的，并且能够免费提供各种功能。当然它们也都具有提供更多高级功能的付费版本。&lt;/li&gt;  &lt;li&gt;两者都可以使用简单、传统的SQL，来编写出迁移脚本。&lt;/li&gt;  &lt;li&gt;两者都能完美地“面向Java”，并且都内置了针对Maven和Gradle之类基本构建工具的支持，以及可以与诸如：Spring Boot等最常见的Java框架相集成。&lt;/li&gt;  &lt;li&gt;两者都可以从命令行处运行简单的shell脚本。&lt;/li&gt;  &lt;li&gt;虽然两者支持的数据库版本和驱动程序，可能存在着一些细微的差异，但是从整体而言，它们能够支持的数据库品种大致相似。&lt;/li&gt;  &lt;li&gt;在处理数据库更改时，两者用到了相同的方法，即：基于迁移的数据库交付。&lt;/li&gt;  &lt;li&gt;两种工具都实现了由Martin Fowler提出和诠释的数据库重构(Evolutionary database) 的概念(详见本末链接)。&lt;/li&gt;&lt;/ul&gt; &lt;h1&gt;Flyway和Liquibase之间的不同之处&lt;/h1&gt; &lt;p&gt;下面，让我们从横跨多个数据库引擎来运行相同脚本的角度，来讨论Flyway和Liquibase的不同之处。&lt;/p&gt; &lt;p&gt;首先，我们会碰到的一个实际问题是：如何针对实例生产差异(diff)。您会发现，我们可以直接使用Liquibase来生成相关差异;却无法使用Flyway来实现，而且即便是其付费版本也无法达到。这便是我们往往在项目中选择Liquibase，而非Flyway的主要原因之一。&lt;/p&gt; &lt;p&gt;其次，我们来看看Java客户端。Flyway拥有原生的Java API，它可以帮助我们进行诸如BLOB和CLOB的更改、以及高级批量数据的修改等较为复杂的迁移。这些功能在某些受限制的迁移场景中，是非常实用的。因此这反过来成为了用户选用Flyway，而非Liquibase的主要原因之一。&lt;/p&gt; &lt;p&gt;接着，我们来讨论两种工具是如何处理回滚的。我们设置Liquibase的changelog文件相对比较容易。实际上，changelog的XML结构甚至已经为回滚代码定义好了一个特殊的字段。而Flyway仅在其付费版本中提供了回滚处理的服务。因此，如果您不介意使用付费工具的话，可以考虑使用Flyway的相关功能。当然，据说Liquibase的付费版本，对于不同类型的回滚，具有更完备的支持。您如果有时间和精力的话，可以去试用一下。&lt;/p&gt; &lt;p&gt;最后，让我们来看看更改顺序的管理。对此，两种工具有着完全不同的处理方法。Flyway采取的是线性数据库版本控制的概念。这意味着，应用更改的顺序，取决于迁移脚本的名称顺序。实际上，Flyway的迁移脚本有着一个完整的命名规则。如果您希望它能够按照预期执行的话，就必须遵循该规则。而在Liquibase中，数据库实例的更改顺序，基于整个changelog文件中的特定更改位置。也就是说，如果您将更改按照某种特定的顺序放在changelog中的话，那么对于数据库的更改也将以完全相同的顺序执行并完成。&lt;/p&gt; &lt;h1&gt;小结&lt;/h1&gt; &lt;p&gt;综上所述，我们对Flyway和Liquibase两种数据库迁移工具进行了综合比较。总的说来，Flyway的优点在于，其迁移脚本更具有可读性。如果您非常熟悉SQL的话，那么它用起来更加便捷、更加顺手。当然，它的缺点是无法实现跨平台的使用。而Liquibase正好相反，其优点在于可以跨平台被使用，其不足之处在于，由于它功能强大，因此我们可能需要花费一定的精力，去维护它的迁移脚本。&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;h1&gt;相关链接&lt;/h1&gt; &lt;ul&gt;  &lt;li&gt;Martin Fowler的《数据库重构(Evolutionary Database)》&lt;/li&gt;  &lt;li&gt;GitHub上的Evolve页面&lt;/li&gt;  &lt;li&gt;Liquibase支持的数据库的完整列表&lt;/li&gt;  &lt;li&gt;Flyway支持的数据库列表&lt;/li&gt;&lt;/ul&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/62035-flyway-liquibase-%E6%95%B0%E6%8D%AE%E5%BA%93</guid>
      <pubDate>Sun, 16 Jan 2022 12:14:03 CST</pubDate>
    </item>
    <item>
      <title>容器安全扫描工具推荐 (insights.thoughtworks.cn)</title>
      <link>https://itindex.net/detail/62005-%E5%AE%B9%E5%99%A8-%E5%AE%89%E5%85%A8-%E5%B7%A5%E5%85%B7</link>
      <description>&lt;div&gt;  &lt;blockquote&gt;   &lt;p&gt;在现代软件开发中， 我们会使用一些公共镜像作为基础镜像来快速构建我们的应用镜像，并将其部署到生产环境中。&lt;/p&gt;   &lt;p&gt;随着越来越多的应用程序被容器化，容器安全也随之变得越来越重要。在项目的 流水线 中， 我们可以使用漏洞扫描器进行扫描并提前获得反馈，实现 “安全左移” ，也可以更好的实践敏捷。&lt;/p&gt;&lt;/blockquote&gt;  &lt;h3&gt;基于容器的应用程序的安全痛点&lt;/h3&gt;  &lt;p&gt;   &lt;img alt="" src="https://insights.thoughtworks.cn/wp-content/uploads/2021/12/1-container-scanning-tools-recommendation.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;现在，我们使用先进的技术来构建我们的应用程序，如 NodeJS、 Java 和 Kotlin 等，然后将代码库存储在托管的 Git 平台上，如 GitHub、Gitlab 等。代码库由我们的业务代码和依赖关系组成；对于依赖项，我们可以使用专业的扫描工具来确保安全，比如 NodeJS 的 npm audit , GitHub 的 Dependabot；至于我们的业务代码，可以使用其他的一些安全工具可以扫描，比如 SoneQube 等。&lt;/p&gt;  &lt;p&gt;因此，对于依赖（ Dependencies）和我们的业务代码，这些都在我们的控制之下，我们可以确保应用程序的安全性，并且在 Pipeline 上获得快速反馈；同时在我们将应用程序部署到生产环境之前可以通过使用各种工具建立信心。但是，通常情况下我们的应用程序运行的系统环境是不受我们控制的，可能存在潜在的安全漏洞。在这我们可以换位思考一下，如果我们不能保证我们的应用程序运行的系统的环境安全，就会导致各种各样意想不到的问题，如黑客攻击、用户信息泄露、财产损失，更会对公司的声誉造成损害。所以，确保我们产出物（Artifact）的安全是很重要的。&lt;/p&gt;  &lt;h3&gt;保持容器镜像安全的 两个方案&lt;/h3&gt;  &lt;p&gt;   &lt;img alt="" src="https://insights.thoughtworks.cn/wp-content/uploads/2021/12/2-container-scanning-tools-recommendation.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;h4&gt;方案1： 在镜像注册表中定期扫描&lt;/h4&gt;  &lt;p&gt;通过这种方式，我们需要为镜像注册表添加一个安全扫描程序，扫描程序可以是一个定时任务（Cron Job） 作业，也可以是由特定的人触发的可执行操作。&lt;/p&gt;  &lt;p&gt;如果是一个定时任务，它将在特定时刻由定时任务自动触发。例如，Docker Hub 会在特定的时间扫描他们的官方注册表，当有任何漏洞被扫描出来时，它会向镜像维护者发送报警信息。&lt;/p&gt;  &lt;h4&gt;方案2：将扫描工具集成到 Pipeline 中&lt;/h4&gt;  &lt;p&gt;另一种方法是在 Pipeline 上对镜像产物进行扫描，这样更加简单高效。当我们将代码推送到代码存储库时， Pipeline 将自动执行扫描镜像的命令。因为 Pipeline 每次都是无差别地执行，所以我们可以发现任何安全问题并及时报警修复。&lt;/p&gt;  &lt;p&gt;现在，越来越多的团队或公司使用敏捷来开发他们的项目。如果我们能够尽早地发现任何安全问题或者漏洞，我们就可以在产品发布之前降低产品的安全风险。 Pipeline 是确保每一行代码和基础运行环境的安全性是的最好方法之一，因为它可以在提交代码时自动执行。&lt;/p&gt;  &lt;h3&gt;容器安全扫描工具对比&lt;/h3&gt;  &lt;p&gt;针对上述解决方案，我们调查了 Trivy、Claire、Anchore Engine、Quay、Docker hub 和 GCR 等几种扫描工具，从不同维度进行对比。&lt;/p&gt;  &lt;p&gt;   &lt;a href="https://insights.thoughtworks.cn/wp-content/uploads/2021/12/3-container-scanning-tools-recommendation.png"&gt;    &lt;img alt="" src="https://insights.thoughtworks.cn/wp-content/uploads/2021/12/3-container-scanning-tools-recommendation-768x610.png"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;参考   &lt;a href="https://github.com/aquasecurity/trivy"&gt;Trivy 官网&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;首先，我们可以将这些扫描工具按照其执行的环境简单分类； 因为 Docker Hub、GCR 和 Quay 是需要在服务端也就是容器注册中心运行的， 所以适合方案1； Trivy、Clair 和 Anchor Engine 可以在 Pipeline 上工作，所以适合解决方案2。&lt;/p&gt;  &lt;p&gt;对于第一个维度：OS Package，这些所有的扫描工具都可以做到，但是对于第二个维度：Application dependencies，只有 Trivy 和 Anchore Engine 可以做到，对于第五个维度: Suitable for CI, 只有前三个符合条件。&lt;/p&gt;  &lt;p&gt;对于漏洞数据库的更新，Clair 会定期从一组配置的源中获取漏洞元数据库（Vulnnerability Database），并将数据存储在其数据库中，只要不获取最新的漏洞元数据，每次执行都用之前的漏洞数据库，漏洞数据库的时效性有点差。 Trivy 和 Anchore Engine 则是每次运行都将下载最新的漏洞数据库并将其缓存在本地文件中，当扫描工具再次运行时，它将检查并更新数据库以保持数据库为最新状态。&lt;/p&gt;  &lt;p&gt;同时，对于 Trivy、Clair 和 Anchore Engine，这三者的社区非常活跃，所以我们不能用没有人来帮你解决你的问题来评判； 而且作为一种工具，它必须易于使用并且有良好的文档可供参考。经过调研，发现 Trivy 的文档非常详细，非常友好， 而且 Trivy 的使用方式更加友好，比如我们可以过滤掉（.trivyignore）你指定的漏洞，对于最新发现的漏洞，官方没有给出修复版本，这时候我们就可以忽略这个漏洞继续构建，但 Anchore Engine 做不到。&lt;/p&gt;  &lt;p&gt;2020年3月16日，领先的云原生应用和基础设施安全平台供应商 Aqua Security 宣布，其开源的 Trivy 漏洞扫描器将作为一个集成选项添加到其使用的云原生平台、CNCF 的 Harbor 注册表和 Mirantis Docker Enterprise 中。你可以在这里找到   &lt;a href="https://blog.aquasec.com/trivy-vulnerability-scanner-joins-aqua-family"&gt;这篇文章&lt;/a&gt;。&lt;/p&gt;  &lt;h3&gt;Trivy集成到流水线中的使用方法&lt;/h3&gt;  &lt;p&gt;   &lt;img alt="" src="https://insights.thoughtworks.cn/wp-content/uploads/2021/12/4-container-scanning-tools-recommendation.png"&gt;&lt;/img&gt;&lt;/p&gt;  &lt;p&gt;Trivy  支持多种扫描方式，如扫描容器镜像、Git 仓库和文件系统等；下面，我们使用 GitHub Actions 以 Docker 运行 Trivy 扫描构建好的镜像产出物来展示 Trivy 的强大之处，下面是 GitHub Actions 的部分代码：&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;- name: Trivy scanner
  run: |
         docker run --rm -v 
/var/run/docker.sock:/var/run/docker.sock \
           aquasec/trivy image --severity HIGH,CRITICAL 
--exit-code 1 dashboard:${{ github.sha }}&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;在这需要对以下几个参数做特别说明：&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;    &lt;p&gt;-v /var/run/docker.sock:/var/run/docker.sock 如果想扫描本地主机上的镜像，需要挂载 docker.sock&lt;/p&gt;&lt;/li&gt;   &lt;li&gt;    &lt;p&gt;--severity 设置要扫描的漏洞级别&lt;/p&gt;&lt;/li&gt;   &lt;li&gt;    &lt;p&gt;--exit-code 发现漏洞时 Trivy 的退出状态(默认值：0)； 在 Pipeline 中，如果将该值设置为1，且有漏洞被发现，则 Pipeline 将退出，而不会继续运行。如果将其设置为0，则 Pipeline 将继续运行，但会报告结果。所以，如果你想在发现漏洞后阻止 Pipeline 继续执行，可以设置它为1。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;想了解更多关于参数和使用方法的信息，请访问 Trivy 的官方网站：   &lt;a href="https://github.com/aquasecurity/trivy"&gt;https://github.com/aquasecurity/trivy&lt;/a&gt;。&lt;/p&gt;  &lt;h3&gt;总结&lt;/h3&gt;  &lt;p&gt;无论你在哪里，安全都是一个非常重要的问题。我们可以将 “安全左移（Shift Left Security）”，这样就可以减少生产环境中的安全风险；对于扫描工具 Trivy 来说，它对于保证镜像的安全性非常有用，它不仅可以扫描镜像，还可以扫描 Git 仓库，文件系统等。   &lt;br /&gt;最后，非常感谢同事张思楚、王亦晨和邢砚敏等人的大力支持和指导，在他们热心帮助和辛苦付出之下才有了这篇文章。&lt;/p&gt;  &lt;div&gt;   &lt;div&gt;    &lt;a href="https://www.addtoany.com/add_to/wechat?linkurl=https%3A%2F%2Finsights.thoughtworks.cn%2Fcontainer-scanning-tools-recommendation%2F&amp;linkname=%E5%AE%B9%E5%99%A8%E5%AE%89%E5%85%A8%E6%89%AB%E6%8F%8F%E5%B7%A5%E5%85%B7%E6%8E%A8%E8%8D%90" rel="nofollow noopener" target="_blank" title="WeChat"&gt;&lt;/a&gt;    &lt;a href="https://www.addtoany.com/add_to/sina_weibo?linkurl=https%3A%2F%2Finsights.thoughtworks.cn%2Fcontainer-scanning-tools-recommendation%2F&amp;linkname=%E5%AE%B9%E5%99%A8%E5%AE%89%E5%85%A8%E6%89%AB%E6%8F%8F%E5%B7%A5%E5%85%B7%E6%8E%A8%E8%8D%90" rel="nofollow noopener" target="_blank" title="Sina Weibo"&gt;&lt;/a&gt;    &lt;a href="https://www.addtoany.com/add_to/evernote?linkurl=https%3A%2F%2Finsights.thoughtworks.cn%2Fcontainer-scanning-tools-recommendation%2F&amp;linkname=%E5%AE%B9%E5%99%A8%E5%AE%89%E5%85%A8%E6%89%AB%E6%8F%8F%E5%B7%A5%E5%85%B7%E6%8E%A8%E8%8D%90" rel="nofollow noopener" target="_blank" title="Evernote"&gt;&lt;/a&gt;    &lt;a href="https://www.addtoany.com/add_to/pocket?linkurl=https%3A%2F%2Finsights.thoughtworks.cn%2Fcontainer-scanning-tools-recommendation%2F&amp;linkname=%E5%AE%B9%E5%99%A8%E5%AE%89%E5%85%A8%E6%89%AB%E6%8F%8F%E5%B7%A5%E5%85%B7%E6%8E%A8%E8%8D%90" rel="nofollow noopener" target="_blank" title="Pocket"&gt;&lt;/a&gt;    &lt;a href="https://www.addtoany.com/add_to/instapaper?linkurl=https%3A%2F%2Finsights.thoughtworks.cn%2Fcontainer-scanning-tools-recommendation%2F&amp;linkname=%E5%AE%B9%E5%99%A8%E5%AE%89%E5%85%A8%E6%89%AB%E6%8F%8F%E5%B7%A5%E5%85%B7%E6%8E%A8%E8%8D%90" rel="nofollow noopener" target="_blank" title="Instapaper"&gt;&lt;/a&gt;    &lt;a href="https://www.addtoany.com/add_to/email?linkurl=https%3A%2F%2Finsights.thoughtworks.cn%2Fcontainer-scanning-tools-recommendation%2F&amp;linkname=%E5%AE%B9%E5%99%A8%E5%AE%89%E5%85%A8%E6%89%AB%E6%8F%8F%E5%B7%A5%E5%85%B7%E6%8E%A8%E8%8D%90" rel="nofollow noopener" target="_blank" title="Email"&gt;&lt;/a&gt;    &lt;a href="https://www.addtoany.com/add_to/linkedin?linkurl=https%3A%2F%2Finsights.thoughtworks.cn%2Fcontainer-scanning-tools-recommendation%2F&amp;linkname=%E5%AE%B9%E5%99%A8%E5%AE%89%E5%85%A8%E6%89%AB%E6%8F%8F%E5%B7%A5%E5%85%B7%E6%8E%A8%E8%8D%90" rel="nofollow noopener" target="_blank" title="LinkedIn"&gt;&lt;/a&gt;    &lt;a href="https://www.addtoany.com/add_to/pinterest?linkurl=https%3A%2F%2Finsights.thoughtworks.cn%2Fcontainer-scanning-tools-recommendation%2F&amp;linkname=%E5%AE%B9%E5%99%A8%E5%AE%89%E5%85%A8%E6%89%AB%E6%8F%8F%E5%B7%A5%E5%85%B7%E6%8E%A8%E8%8D%90" rel="nofollow noopener" target="_blank" title="Pinterest"&gt;&lt;/a&gt;    &lt;a href="https://www.addtoany.com/share"&gt;     &lt;img alt="Share" src="https://static.addtoany.com/buttons/favicon.png"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>jianshu</category>
      <guid isPermaLink="true">https://itindex.net/detail/62005-%E5%AE%B9%E5%99%A8-%E5%AE%89%E5%85%A8-%E5%B7%A5%E5%85%B7</guid>
      <pubDate>Tue, 04 Jan 2022 00:00:00 CST</pubDate>
    </item>
    <item>
      <title>[原]btrace 开源！基于 Systrace 高性能 Trace 工具</title>
      <link>https://itindex.net/detail/61983-btrace-%E5%BC%80%E6%BA%90-systrace</link>
      <description>&lt;div&gt;
                    

                    
                    
                    
                      &lt;h2&gt;介绍&lt;/h2&gt;  &lt;p&gt;btrace（又名 RheaTrace） 是抖音基础技术团队自研的一款高性能 Android Trace 工具，它基于 Systrace 实现，并针对 Systrace 不足之处加以改进，核心改进点如下。&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;    &lt;p&gt;     &lt;strong&gt;效率提升&lt;/strong&gt;：编译期间为 App 方法自动注入自定义事件，并提供高效、灵活配置规则。&lt;/p&gt;&lt;/li&gt;   &lt;li&gt;    &lt;p&gt;     &lt;strong&gt;性能提升&lt;/strong&gt;：改进 Systrace 文件实时写 atrace 数据方式，性能提升最大 400 % 以上。&lt;/p&gt;&lt;/li&gt;   &lt;li&gt;    &lt;p&gt;     &lt;strong&gt;实用性提升&lt;/strong&gt;：额外提供更详细 IO 等数据，大幅提升方法耗时归因效率；使用独创方案彻底来解决方法因执行异常引起 trace 数据不闭合问题。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;  &lt;p&gt;   &lt;strong&gt;项目地址：&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;   &lt;strong&gt;https://github.com/bytedance/btrace&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;目前字节跳动已有多款 App 接入，包括抖音、TikTok、今日头条、幸福里等均已接入 RheaTrace，并为其体验优化提供强有力支持。借助 RheaTrace 将为您的 App 带来极致流畅体验，RheaTrace 使用效果如下（因保密原则，每个方法用 ID 表示）。&lt;/p&gt;  &lt;img alt="2cfc201c282254c499ffd9b489c92ed2.png" src="https://img-blog.csdnimg.cn/img_convert/2cfc201c282254c499ffd9b489c92ed2.png"&gt;&lt;/img&gt;  &lt;h2&gt;Systrace 简介&lt;/h2&gt;  &lt;p&gt;如果我们使用过 Systrace 分析应用性能，我们都知道 Systrace 提供 Category 配置让用户决定采集哪些系统 atrace 数据，如下命令，从 sched 开始后续是不同类别的 atrace 数据。&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;python systrace.py -o mynewtrace.html sched freq idle am wm gfx view \
    binder_driver hal dalvik camera input res&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;atrace 的数据类型见下图：&lt;/p&gt;  &lt;img alt="3316268529430a7a614ce029e17e17c3.png" src="https://img-blog.csdnimg.cn/img_convert/3316268529430a7a614ce029e17e17c3.png"&gt;&lt;/img&gt;  &lt;p&gt;其中，用户空间 atrace 类型包括应用层自定义 Trace 事件、系统层 gfx 渲染相关 Trace、系统层锁相关 Trace 信息等，其最终都是通过调用 Android SDK 提供    &lt;code&gt;Trace.beginSection&lt;/code&gt; 或者    &lt;code&gt;ATRACE_BEGIN&lt;/code&gt; 记录到同一个文件    &lt;code&gt;/sys/kernel/debug/tracing/trace_marker&lt;/code&gt; 中。此节点允许用户层写入数据，ftrace 会记录该写入操作时间戳。当用户层发生函数调用时，ftrace 可以记录被跟踪函数的运行时间。atrace 若需记录用户层某一 trace 类型，只需激活对应 TAG 类型即可。如选择 gfx，则会激活    &lt;code&gt;ATRACE_TAG_GRAPHICS&lt;/code&gt;，并将渲染事件记录到    &lt;code&gt;trace_marker&lt;/code&gt; 文件中。&lt;/p&gt;  &lt;p&gt;内核空间的数据主要是一些补充分析数据，如 freq、sched、binder 等，常用 CPU 调度相关信息包括：&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;    &lt;p&gt;CPU 频率变化情况。&lt;/p&gt;&lt;/li&gt;   &lt;li&gt;    &lt;p&gt;任务执行情况。&lt;/p&gt;&lt;/li&gt;   &lt;li&gt;    &lt;p&gt;大小核调度情况。&lt;/p&gt;&lt;/li&gt;   &lt;li&gt;    &lt;p&gt;CPU Boost 调度情况。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;  &lt;img alt="c9027fc2459d60a053724770df81021d.png" src="https://img-blog.csdnimg.cn/img_convert/c9027fc2459d60a053724770df81021d.png"&gt;&lt;/img&gt;  &lt;p&gt;关于图中一些标签释义。&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;    &lt;p&gt;CPU 使用率：右边柱状图越高，表明使用率越高。&lt;/p&gt;&lt;/li&gt;   &lt;li&gt;    &lt;p&gt;CPU 序号：标识 CPU 核心序号，表示该设备有 8 个核心，编号 0 -7。&lt;/p&gt;&lt;/li&gt;   &lt;li&gt;    &lt;p&gt;CPU 频率：右边对应的粉色柱状图表示其频率变化趋势。&lt;/p&gt;&lt;/li&gt;   &lt;li&gt;    &lt;p&gt;调度任务：标识在该 CPU 核心上正在运行的任务，点击任务可查看其 ID、优先级等信息。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;  &lt;p&gt;这些信息 App 可以直接读取    &lt;code&gt;/sys/devices/system/cpu&lt;/code&gt; 节点下相关信息获得，而另外一部分标识线程状态信息则只能通过系统或者 adb 才能获取，且这些信息不是统一节点控制，需要激活各自对应的事件节点，让 ftrace 记录下不同事件的 tracepoint。内核在运行时，根据节点的使能状态，会往 ftrace 缓冲中记录事件。&lt;/p&gt;  &lt;p&gt;例如，激活线程调度状态信息记录，需要激活类似如下相关节点。&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;events/sched/sched_switch/enable
events/sched/sched_wakeup/enable&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;激活后，则可以获取到线程调度状态相关的信息，比如：&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;    &lt;p&gt;Running: 线程在正常执行代码逻辑。&lt;/p&gt;&lt;/li&gt;   &lt;li&gt;    &lt;p&gt;Runnable: 可执行状态，等待调度，如果长时间调度不到，说明 CPU 繁忙。&lt;/p&gt;&lt;/li&gt;   &lt;li&gt;    &lt;p&gt;Sleeping: 休眠，一般是在等待事件驱动。&lt;/p&gt;&lt;/li&gt;   &lt;li&gt;    &lt;p&gt;Uninterruptible Sleep: 不可中断的休眠，需要看 Args 描述来确定当时状态。&lt;/p&gt;&lt;/li&gt;   &lt;li&gt;    &lt;p&gt;Uninterruptible Sleep - Block I/O: IO 阻塞。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;  &lt;img alt="4b3dd88a9e29255617a3e6fc692879ce.png" src="https://img-blog.csdnimg.cn/img_convert/4b3dd88a9e29255617a3e6fc692879ce.png"&gt;&lt;/img&gt;  &lt;p&gt;最终，上述两大类事件记录都汇集到内核态同一缓冲中， Systrace 工具是通过指定抓取 trace 类别等参数，然后触发手机端    &lt;code&gt;/system/bin/atrace&lt;/code&gt; 开启对应文件节点信息，接着 atrace 会读取 ftrace 缓存，生成只包含 ftrace 信息的 atrace_raw 信息，最终通过脚本转换成可视化 HTML 文件，大致流程如下。&lt;/p&gt;  &lt;img alt="a88fef6bbe209ca2f94a0a924ad6ef00.png" src="https://img-blog.csdnimg.cn/img_convert/a88fef6bbe209ca2f94a0a924ad6ef00.png"&gt;&lt;/img&gt;  &lt;h2&gt;RheaTrace 揭秘&lt;/h2&gt;  &lt;p&gt;本章节将从 RheaTrace 重点优势一一介绍。&lt;/p&gt;  &lt;h3&gt;Systrace 源码分析&lt;/h3&gt;  &lt;p&gt;Systrace 提供    &lt;code&gt;Trace#beginSection(String)&lt;/code&gt; 和    &lt;code&gt;Trace.endSection()&lt;/code&gt; 采集 atrace 数据，首先，我们大致了解下 atrace 工作原理，以    &lt;code&gt;android.os.Trace#beginSection&lt;/code&gt; 作为分析入口。&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;public static void beginSection(@NonNull String sectionName) {
    if (isTagEnabled(TRACE_TAG_APP)) {
        if (sectionName.length() &amp;gt; MAX_SECTION_NAME_LEN) {
            throw new IllegalArgumentException(&amp;quot;sectionName is too long&amp;quot;);
        }
        nativeTraceBegin(TRACE_TAG_APP, sectionName);
    }
}&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;   &lt;code&gt;android.os.Trace#beginSection&lt;/code&gt; 会调用    &lt;code&gt;nativeTraceBegin&lt;/code&gt; 方法，该方法实现参考 frameworks/base/core/jni/android_os_Trace.cpp。&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;static void android_os_Trace_nativeTraceBegin(JNIEnv* env, jclass,
        jlong tag, jstring nameStr) {
    withString(env, nameStr, [tag](char* str) {
        atrace_begin(tag, str);
    });
}&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;   &lt;code&gt;atrace_begin&lt;/code&gt; 方法实现参考 system/core/libcutils/include/cutils/trace.h。&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;#define ATRACE_BEGIN(name) atrace_begin(ATRACE_TAG, name)
static inline void atrace_begin(uint64_t tag, const char* name)
{
    if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
        void atrace_begin_body(const char*);
        atrace_begin_body(name);
    }
}&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;   &lt;code&gt;atrace_begin_body&lt;/code&gt; 方法实现参考 system/core/libcutils/trace-dev.cpp。&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;void atrace_begin_body(const char* name)
{
    WRITE_MSG(&amp;quot;B|%d|&amp;quot;, &amp;quot;%s&amp;quot;, name, &amp;quot;&amp;quot;);
}&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;atrace_begin_body 最终实现在宏 WRITE_MSG 实现，代码如下：&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;#define WRITE_MSG(format_begin, format_end, name, value) { \
    ...
    write(atrace_marker_fd, buf, len); \
}&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;通过 WRITE_MSG 实现，可知，atrace 数据是实时写入 fd 为    &lt;code&gt;atrace_marker_fd&lt;/code&gt; 的文件中，如果多线程同时写入，则会出现锁问题，导致性能损耗加大。&lt;/p&gt;  &lt;h3&gt;RheaTrace 核心优势&lt;/h3&gt;  &lt;h4&gt;效率提升&lt;/h4&gt;  &lt;p&gt;RheaTrace 会在 App 编译期间自动插入 Trace 跟踪函数，大大提高效率。针对不同 Android Gradle Plugin 版本，我们支持 Proguard 之后插桩，这样可以减少 App 方法插桩量，同时也过滤 Empty、Set/Get 等简单方法。&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;思路基于 matrix-gradle-plugin 大量改造实现。&lt;/p&gt;&lt;/blockquote&gt;  &lt;pre&gt;   &lt;code&gt;rheaTrace {

   compilation {
      //为每个方法生成唯一 ID，若为 true，Trace#beginSection(String) 传入的是方法 ID。
      traceWithMethodID = true
      //决定哪些包名下的类您不需要做性能跟踪。
      traceFilterFilePath = &amp;quot;${project.rootDir}/rhea-trace/traceFilter.txt&amp;quot;
      //一些特定方法保持 ID 值固定不变。
      applyMethodMappingFilePath = &amp;quot;${project.rootDir}/rhea-trace/keep-method-id.txt&amp;quot;
   }
   runtime {
        ......
   }
}&lt;/code&gt;&lt;/pre&gt;  &lt;ol&gt;   &lt;li&gt;    &lt;p&gt;为减少包体积、性能影响，我们也借鉴 matrix 慢函数思路，支持为每个函数生成唯一 ID，     &lt;code&gt;traceWithMethodID&lt;/code&gt; 为 true，     &lt;code&gt;Trace#beginSection(String)&lt;/code&gt;传入的是方法 ID，不再是方法名。有时候我们想某些方法 ID 固定不变，同样借鉴 matrix 慢函数思路，我们提供      &lt;code&gt;applyMethodMappingFilePath&lt;/code&gt; 配置规则文件路径。&lt;/p&gt;&lt;/li&gt;   &lt;li&gt;    &lt;p&gt;为进一步减少 App 方法插桩量，我们提供      &lt;code&gt;traceFilterFilePath&lt;/code&gt; 文件配置让您决定哪些包、类、方法不做自定义事件跟踪，关于其用法请参考 RheaTrace Gradle Config。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;  &lt;h4&gt;性能提升&lt;/h4&gt;  &lt;p&gt;在 Systrace 概述中，我们了解到 atrace 数据是实时写入文件，且存在多线程同时写入带来的锁问题。因此，我们采取策略是拿到 atrace 文件 fd，在 atrace 数据写入前，先将其写至    &lt;code&gt;LockFreeRingBuffer&lt;/code&gt;内存中，然后再将循环读取内存中 atrace 数据，写入我们定义的文件中。&lt;/p&gt;  &lt;p&gt;首先我们通过 dlopen 获取    &lt;code&gt;libcutils.so&lt;/code&gt; 对应句柄，通过对应 symbol 从中找到    &lt;code&gt;atrace_enabled_tags&lt;/code&gt; 和    &lt;code&gt;atrace_marker_fd&lt;/code&gt; 对应指针，设置    &lt;code&gt;atrace_enabled_tags&lt;/code&gt; 用以打开 atrace，代码实现片段如下。&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;int32_t ATrace::InstallAtraceProbe() {
  ......
  {
    std::string lib_name(&amp;quot;libcutils.so&amp;quot;);
    std::string enabled_tags_sym(&amp;quot;atrace_enabled_tags&amp;quot;);
    std::string marker_fd_sym(&amp;quot;atrace_marker_fd&amp;quot;);

    ...
    ...
    atrace_marker_fd_ = reinterpret_cast&amp;lt;int*&amp;gt;(
        dlsym(handle, marker_fd_sym.c_str()));

    if (atrace_marker_fd_ == nullptr) {
      ALOGE(&amp;quot;&amp;apos;atrace_marker_fd&amp;apos; is not defined&amp;quot;);
      dlclose(handle);
      return INSTALL_ATRACE_FAILED;
    }
    if (*atrace_marker_fd_ == -1) {
      *atrace_marker_fd_ = kTracerMagicFd;
    }
  dlclose(handle);
  return OK;
  }&lt;/code&gt;&lt;/pre&gt;  &lt;blockquote&gt;   &lt;p&gt;思路参考 profilo#installSystraceSnooper，本文不做过多介绍。&lt;/p&gt;&lt;/blockquote&gt;  &lt;p&gt;接着，通过 PLT Hook    &lt;code&gt;libcutils.so&lt;/code&gt; 中 write、write_chk 方法，判定该方法传入 fd 是否与 atrace_marker_fd 一致，若一致，则将 atrace 数据写入我们定义的文件中。&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;ssize_t proxy_write(int fd, const void* buf, size_t count) {
  BYTEHOOK_STACK_SCOPE();

  if (ATrace::Get().IsATrace(fd, count)) {
    ATrace::Get().LogTrace(buf, count);
    return count;
  }
  ...

  ATRACE_END();
  return ret;
}&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;有时候，我们可能仅需要关注主线程 atrace 数据，如果能将子线程 atrace 数据过滤掉，那么整体性能将进一步提升。一种很简单的思路，就是将    &lt;code&gt;Trace#beginSection(String)&lt;/code&gt; 包装一层，如下代码片段。&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;static void t(String methodId) {
    if (!isMainProcess) {
        return;
    }
    if (mainThreadOnly) {
        if (Thread.currentThread() == sMainThread) {
            Trace.beginSection(methodId);
        }
    } else {
        Trace.beginSection(methodId);
    }
}&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;该方法仅能过滤我们为 App 方法插桩的 atrace 数据，系统层 atrace 数据无法过滤。为更彻底实现仅采集主线程数据，我们通过 PLT Hook 代理    &lt;code&gt;atrace_begin_body&lt;/code&gt; 和    &lt;code&gt;atrace_end_body&lt;/code&gt; 实现，在该方法进入前，判断当前线程 id 是否为主线程，如果不是，则不记录该条数据，代码实现片段如下。&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;void proxy_atrace_begin_body(const char *name) {
    BYTEHOOK_STACK_SCOPE();
    if (gettid() == TraceProvider::Get().GetMainThreadId()) {
        BYTEHOOK_CALL_PREV(proxy_atrace_begin_body, name);
    }
}

void proxy_atrace_end_body() {
    BYTEHOOK_STACK_SCOPE();
    if (gettid() == TraceProvider::Get().GetMainThreadId()) {
        BYTEHOOK_CALL_PREV(proxy_atrace_end_body);
    }
}&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;针对降低性能损耗，RheaTrace 提供编译配置供用户选择，针对不同使用场景配置合理参数。&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;rheaTrace {
    ......

    runtime {
        mainThreadOnly false
        startWhenAppLaunch true
        atraceBufferSize &amp;quot;500000&amp;quot;
    }
}&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;上述配置释义如下。&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;    &lt;p&gt;     &lt;code&gt;mainThreadOnly&lt;/code&gt;：为 true 表示仅采集主线程 trace 数据。&lt;/p&gt;&lt;/li&gt;   &lt;li&gt;    &lt;p&gt;     &lt;code&gt;startWhenAppLaunch&lt;/code&gt;：是否 App 启动开始就采集 trace 数据。&lt;/p&gt;&lt;/li&gt;   &lt;li&gt;    &lt;p&gt;     &lt;code&gt;atraceBufferSize&lt;/code&gt;：指定内存存储 atrace 数据 ring buffer 的大小，如果其值过小会导致 trace 数据写入不完整，若您抓取多线程 trace 数据，建议将值设为百万左右量级；最小值为 1 万，最大值为 5 百万。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;  &lt;h4&gt;实用性提升&lt;/h4&gt;  &lt;p&gt;针对已有的 atrace 数据，额外拓展 IO 等信息；另外为通过 Python 脚本彻底解决方法因执行异常导致 trace 数据闭合异常问题，保证每个方法 trace 数据的准确性。&lt;/p&gt;  &lt;p&gt;目前我们基于 JVMTI 方案，在 Android 8.0 及以上设备可以获取类加载以及内存访问相关 trace 数据，目前仅支持编译类型为 debuggable 的 App，目前处于实验功能，本文暂先不过多介绍。&lt;/p&gt;  &lt;p&gt;   &lt;strong&gt;IO 数据拓展&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;   &lt;strong&gt;背景简介&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;在抖音启动性能优化时，我们曾统计冷启动的耗时，其中占比最长的是进程处于 D 状态（不可中断睡眠态，Uninterruptible Sleep ，通常我们用 PS 查看进程状态显示 D，因此俗称 D 状态）时间。此部分耗时占总启动耗时约 40%，进程为什么会被置于 D 状态呢？处于 uninterruptible sleep 状态的进程通常是在等待 IO，比如磁盘 IO，其他外设 IO，正是因为得不到 IO 响应，进程才进入 uninterruptible sleep 状态，所以要想使进程从 uninterruptible sleep 状态恢复，就得使进程等待 IO 恢复，类似如下。&lt;/p&gt;  &lt;img alt="c125eb238e001f2baa9b99e57414c35c.png" src="https://img-blog.csdnimg.cn/img_convert/c125eb238e001f2baa9b99e57414c35c.png"&gt;&lt;/img&gt;  &lt;p&gt;但在使用 Systrace 进行优化时仅能得到如上内核态的调用状态，却无法得知具体的 IO 操作是什么。&lt;/p&gt;  &lt;p&gt;   &lt;strong&gt;方案介绍&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;因此，我们专门设计一套获取 IO 耗时信息方案，其包括用户空间和内核空间两部分。&lt;/p&gt;  &lt;p&gt;一是在用户空间，为采集所需 IO 耗时信息，我们通过 Hook IO 操作标准函数簇，包括 open，write，read，fsync，fdatasync 等，插入对应 atrace 埋点用于统计对应的 IO 耗时，以 fsync 为例。&lt;/p&gt;  &lt;img alt="0528c96745ee5e4c5ffcc9fe11c5c2d5.png" src="https://img-blog.csdnimg.cn/img_convert/0528c96745ee5e4c5ffcc9fe11c5c2d5.png"&gt;&lt;/img&gt;  &lt;p&gt;其对应 hook 代码逻辑如下：&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;int proxy_fsync(int fd) {
  BYTEHOOK_STACK_SCOPE();
  ATRACE_BEGIN_VALUE(&amp;quot;fsync:&amp;quot;, FileInfo(fd).c_str());

  int ret = BYTEHOOK_CALL_PREV(proxy_fsync, fd);

  ATRACE_END();
  return ret;
}&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;二是在内核空间，除 systrace 或 atrace 可直接支持启用功能外，ftrace 还提供其他功能，并包含对调试性能问题至关重要的一些高级功能（需要 root 访问权限，通常也可能需要新内核）。我们基于此添加显示定制 IO 信息等功能，开启   &lt;code&gt;/sys/kernel/debug/tracing/events/android_fs&lt;/code&gt;节点下 ftrace 信息，用于收集 IO 相关的信息。内核空间 IO 信息是通过 python 脚本开启，详见 io_extender.py。&lt;/p&gt;  &lt;p&gt;   &lt;strong&gt;解决方法闭合错误问题&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;   &lt;strong&gt;背景介绍&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;RheaTrace 会自动在每个方法入口、出口处分别插入    &lt;code&gt;Trace#beginSection(String)&lt;/code&gt; 和    &lt;code&gt;Trace#endSection()&lt;/code&gt; ，一个方法有且只有一个入口，但会有多个出口，方法出口对应的结束字节码指令有 return 和 throw 等。&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;public static void testCrash() {
        try {
            testA();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void testA() {
        testB();
        testC();
    }

    public static void testB() {
        int ret = 2 / 0; // &amp;lt;----- crash event
        testD(ret);
    }

    public static void testC() {
        Log.d(&amp;quot;btrace&amp;quot;, &amp;quot;do some things.&amp;quot;);
    }

    public static void testD(int num) {
        Log.d(&amp;quot;btrace&amp;quot;, &amp;quot;box size: &amp;quot; + num);
    }&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;上面的代码很简单，即 testCrash -&amp;gt; testA -&amp;gt; testB，其中 testB 出现异常，最终是在 testCrash 中捕获。通过本示例可知，testA、testB 方法出口均未正常执行完成，这也就导致 trace 数据不闭合，生成的 trace 数据如下，从中可以看出，B 和 E 数量上并不匹配，且仅从 trace 上看，我们也无法知道 E 属于哪个方法。&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;5108949.231989: B|28045| TestCrash:a
5108949.232055: B|28045| TestCrash:b
5108949.232554: B|28045| TestCrash:c
5108949.232580: E|28045&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;   &lt;strong&gt;方案介绍&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;为解决该问题，RheaTrace 做了取巧处理，方法的出口由插入    &lt;code&gt;Trace#endSection()&lt;/code&gt;改为    &lt;code&gt;Trace#beginSection(String)&lt;/code&gt;。那我们如何知道哪条 trace 是开始，哪条是结束？我们看如下示例。&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;5108949.231989: B|28045|B:TestCrash:a
5108949.232055: B|28045|B:TestCrash:b
5108949.232554: B|28045|B:TestCrash:c
5108949.232580: B|28045|E:TestCrash:a&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;如上 trace 数据，每个方法描述前都会加上    &lt;code&gt;B:&lt;/code&gt; 或    &lt;code&gt;E&lt;/code&gt;、   &lt;code&gt;T&lt;/code&gt;,    &lt;code&gt;B:&lt;/code&gt; 表示方法开始，E 表示方法 retrun 结束，   &lt;code&gt;T:&lt;/code&gt; 表示方法 throw 结束。然后通过 Python 脚本处理并还原正常 trace 数据。如此做以后，我们就可以明确知道方法开始和结束，同时针对异常结束方法，我们会做补全处理，处理后的 trace 数据如下。&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;5108949.231989: B|28045|TestCrash:a
5108949.232055: B|28045|TestCrash:b
5108949.232554: B|28045|TestCrash:c
5108949.232554: E|28045|TestCrash:c
5108949.232554: E|28045|TestCrash:b
5108949.232580: E|28045|TestCrash:a&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;关于 Python 脚本的处理过程，本文不做过多介绍，大家可以阅读相关源码即可。&lt;/p&gt;  &lt;h2&gt;RheaTrace 工作流程&lt;/h2&gt;  &lt;h3&gt;流程概述&lt;/h3&gt;  &lt;p&gt;RheaTrace 作为线下性能分析利器，我们首先看下其整体工作流程。&lt;/p&gt;  &lt;img alt="a681d9fb78f317485bc2993dbd363d8c.png" src="https://img-blog.csdnimg.cn/img_convert/a681d9fb78f317485bc2993dbd363d8c.png"&gt;&lt;/img&gt;  &lt;p&gt;如上文介绍，我们将 Systrace 中 atrace 数据做拦截，将其转存至我们自定义的文件中。&lt;/p&gt;  &lt;h3&gt;Systrace 格式&lt;/h3&gt;  &lt;p&gt;首先我们 Systrace 生成的 trace.html 中 atrace 数据格式。&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;&amp;lt;idle&amp;gt;-0     (-----) [001] d.h4 1308823.803921: sched_waking: comm=TimerDispatch pid=704 prio=97 target_cpu=001
          &amp;lt;idle&amp;gt;-0     (-----) [001] dnh5 1308823.803929: sched_wakeup: comm=TimerDispatch pid=704 prio=97 target_cpu=001
          &amp;lt;idle&amp;gt;-0     (-----) [001] d..2 1308823.803943: sched_switch: prev_comm=swapper/1 prev_pid=0 prev_prio=120 prev_state=R ==&amp;gt; next_comm=TimerDispatch next_pid=704 next_prio=97
          &amp;lt;idle&amp;gt;-0     (-----) [003] d.s3 1308823.803980: sched_waking: comm=kworker/3:0 pid=11120 prio=120 target_cpu=003
          &amp;lt;idle&amp;gt;-0     (-----) [003] d.s4 1308823.803986: sched_blocked_reason: pid=11120 iowait=0 caller=worker_thread+0x4fc/0x804
   TimerDispatch-704   (  643) [001] .... 1308823.803988: tracing_mark_write: B|643|TimerIteration #9392
          &amp;lt;idle&amp;gt;-0     (-----) [003] dns4 1308823.803988: sched_wakeup: comm=kworker/3:0 pid=11120 prio=120 target_cpu=003
   TimerDispatch-704   (  643) [001] .... 1308823.803992: tracing_mark_write: E|643
          &amp;lt;idle&amp;gt;-0     (-----) [003] d..2 1308823.803997: sched_switch: prev_comm=swapper/3 prev_pid=0 prev_prio=120 prev_state=R ==&amp;gt; next_comm=kworker/3:0 next_pid=11120 next_prio=120
   TimerDispatch-704   (  643) [001] .... 1308823.804011: tracing_mark_write: C|643|VSP-mode|0
   TimerDispatch-704   (  643) [001] .... 1308823.804014: tracing_mark_write: C|643|VSP-timePoint|405332069786762
   TimerDispatch-704   (  643) [001] .... 1308823.804016: tracing_mark_write: C|643|VSP-prediction|405332075389317
   TimerDispatch-704   (  643) [001] .... 1308823.804022: tracing_mark_write: B|643|app-alarm in:5602555 for vs:15880333
   TimerDispatch-704   (  643) [001] .... 1308823.804024: tracing_mark_write: E|643&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;文本形式打开 trace.html，在其底部是填充的 trace 数据 ，如上所示数据片段，带有    &lt;code&gt;tracing_mark_write&lt;/code&gt; 标签的即包含 atrace 数据。在 trace.html 文件中有关于 trace 格式介绍，如下数据片段。&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;# tracer: nop
#
# entries-in-buffer/entries-written: 178063/178063   #P:8
#
#                                      _-----=&amp;gt; irqs-off
#                                     / _----=&amp;gt; need-resched
#                                    | / _---=&amp;gt; hardirq/softirq
#                                    || / _--=&amp;gt; preempt-depth
#                                    ||| /     delay
#           TASK-PID    TGID   CPU#  ||||    TIMESTAMP  FUNCTION
#              | |        |      |   ||||       |         |
          &amp;lt;idle&amp;gt;-0     (-----) [003] d.s2 1308814.493991: sched_waking: comm=rcu_preempt pid=9 prio=120 target_cpu=003
          &amp;lt;idle&amp;gt;-0     (-----) [000] d.s2 1308814.493997: sched_waking: comm=rcu_sched pid=10 prio=120 target_cpu=000&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;在 trace.html 中，一条完整的 atrace 数据为：&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;.sample.android-19452 (19452) [005] .... 1308823.801863: tracing_mark_write: B|19452|activityStart
......
.sample.android-19452 (19452) [005] .... 1308824.801753: tracing_mark_write: E|19452&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;在上文介绍 Systrace 时候，我们提到，   &lt;code&gt;Trace#beginSection(String)&lt;/code&gt; 和    &lt;code&gt;Trace.endSection()&lt;/code&gt; 最终是调用如下宏。&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;#define WRITE_MSG(format_begin, format_end, name, value) { \
    ...
    write(atrace_marker_fd, buf, len); \
}&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;其中，   &lt;code&gt;write&lt;/code&gt; 函数传入的 trace 数据为：&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;B|19452|activityStart
......
E|19452&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;   &lt;code&gt;B&lt;/code&gt; 表示 Section 进入，   &lt;code&gt;E&lt;/code&gt; 表示 Section 退出，从以上数据片段可以看出，相较于 trace.html 中 atrace 数据少了很多信息，缺少的信息是内核补全。&lt;/p&gt;  &lt;p&gt;Systrace 工具中    &lt;code&gt;--from-file&lt;/code&gt; 是可以将原始 atrace 数据转化为可视化的 html 文件。因此，针对 atrace 数据我们需要补全缺少的信息。结合前面介绍的 trace 格式说明及多次验证，可被 Systrace 工具识别的 atrace 文件格式满足如下：&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;&amp;lt;ThreadName&amp;gt;-&amp;lt;TheadID&amp;gt; [001] ...1 &amp;lt;Timestamp&amp;gt;: trace_mark_write:&amp;lt;B|E&amp;gt;|&amp;lt;ProcessID&amp;gt;|&amp;lt;TAG&amp;gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;格式说明：&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;    &lt;p&gt;     &lt;code&gt;&amp;lt;ThreadName&amp;gt;&lt;/code&gt;：线程名，若为主线程，可指定为包名。&lt;/p&gt;&lt;/li&gt;   &lt;li&gt;    &lt;p&gt;     &lt;code&gt;&amp;lt;ThreadID&amp;gt;&lt;/code&gt;：线程 ID。&lt;/p&gt;&lt;/li&gt;   &lt;li&gt;    &lt;p&gt;     &lt;code&gt;&amp;lt;Time seconds&amp;gt;&lt;/code&gt;：方法开始或者结束时间戳。&lt;/p&gt;&lt;/li&gt;   &lt;li&gt;    &lt;p&gt;     &lt;code&gt;&amp;lt;B|E&amp;gt;&lt;/code&gt;：标记该条记录为方法开始(B)还是结束(E)。&lt;/p&gt;&lt;/li&gt;   &lt;li&gt;    &lt;p&gt;     &lt;code&gt;&amp;lt;ProcessID&amp;gt;&lt;/code&gt;：所在进程 ID。&lt;/p&gt;&lt;/li&gt;   &lt;li&gt;    &lt;p&gt;     &lt;code&gt;&amp;lt;TAG&amp;gt;&lt;/code&gt;：方法标记，字符长度不可超过 127。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;  &lt;p&gt;   &lt;code&gt;[001]&lt;/code&gt; 和    &lt;code&gt;...1&lt;/code&gt; 对应的数据用户层是无法获取，因此硬编码写死。&lt;/p&gt;  &lt;h3&gt;RheaTrace 格式&lt;/h3&gt;  &lt;p&gt;Systrace 中相关 atrace 数据格式有很多冗余信息，冗余信息是可以通过脚本来进行补充，这样在 atrace 存储过程中可以减少一定数据量的存储。&lt;/p&gt;  &lt;p&gt;仅采集主线程 atrace 数据，其对应格式如下：&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;1306401.857369: B|16667|VerifyClass com.bytedance.rheatrace.atrace.TraceEnableTagsHelper
1306401.857498: E|16667
1306401.857560: B|16667|VerifyClass com.bytedance.rheatrace.common.ReflectUtil
1306401.857825: E|16667
1306401.857876: B|16667|VerifyClass kotlin.jvm.internal.Intrinsics
1306401.858241: E|16667
1306401.858523: B|16667|VerifyClass com.bytedance.rheatrace.core.RheaNoticeManager
1306401.858633: E|16667&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;因为 RheaTrace 仅支持采集主进程 trace 数据，因此，进程 ID 信息无需写入，另外主线程名为包名也无需写入，主线程 ID 与进程 ID 一致也无需写入，剩余信息均为格式模板信息也无需写入，唯一需要记录的是时间戳。&lt;/p&gt;  &lt;p&gt;采集所有线程 atrace 数据，其对应格式如下。&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;1306401.859162 16667: B|16667|RheaApplication#onCreate
1306401.859173 16667: E|16667
1306401.859756 16667: E|16667
1306401.859877 16667: B|16667|activityStart
1306401.862738 16680: B|16667|JIT compiling int sun.util.locale.StringTokenIterator.nextDelimiter(int) (baseline=0, osr=0)
1306401.862772 16680: B|16667|Compiling
1306401.863154 16680: B|16667|ScopedCodeCacheWrite
1306401.863172 16680: B|16667|mprotect all
1306401.863207 16680: E|16667&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;当采集多线程数据时，我们需要获取对应的线程 ID，线程名我们没有通过在 App 期间获取，而是读取 Systrace 工具生成 trace.html 中读取。如下数据片段，我们可以获取进程 ID 为 16667 对应的所有线程 ID 及名称。当然也会存在线程 ID 如下数据片段找不到的情况，我们暂时用    &lt;code&gt;&amp;lt;...&amp;gt;&lt;/code&gt;代替。&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;USER            PID   TID CMD
root              1     1 init
root              1   548 init
root              2     2 kthreadd
root              3     3 rcu_gp
root              5     5 kworker/0:0H
root              7     7 mm_percpu_wq
root              8     8 ksoftirqd/0
root              9     9 rcu_preempt
root             10    10 rcu_sched
root             11    11 rcu_bh
.....
u0_a168       16667 16684 FinalizerWatchd
u0_a168       16667 16685 Binder:16667_1
u0_a168       16667 16686 Binder:16667_2
u0_a168       16667 16687 Binder:16667_3
u0_a168       16667 16688 Profile Saver
u0_a168       16667 16689 async-writer
u0_a168       16667 16690 RenderThread
u0_a168       16667 16693 HWC release
u0_a168       16667 16694 GPU completion&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;通过 RheaTrace 提供的脚本，我们就可以将原始 atrace 数据加工为标准 atrace 格式，如下数据片段。&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;.sample.android-16667 [001] ...1 1306401.857369: tracing_mark_write: B|16667|VerifyClass com.bytedance.rheatrace.atrace.TraceEnableTagsHelper
 .sample.android-16667 [001] ...1 1306401.857498: tracing_mark_write: E|16667
 .sample.android-16667 [001] ...1 1306401.857560: tracing_mark_write: B|16667|VerifyClass com.bytedance.rheatrace.common.ReflectUtil
 .sample.android-16667 [001] ...1 1306401.857825: tracing_mark_write: E|16667
 .sample.android-16667 [001] ...1 1306401.857876: tracing_mark_write: B|16667|VerifyClass kotlin.jvm.internal.Intrinsics
 .sample.android-16667 [001] ...1 1306401.858241: tracing_mark_write: E|16667
 .sample.android-16667 [001] ...1 1306401.858523: tracing_mark_write: B|16667|VerifyClass com.bytedance.rheatrace.core.RheaNoticeManager
 .sample.android-16667 [001] ...1 1306401.858633: tracing_mark_write: E|16667&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;当通过 Systrace 命令获取 trace.html 后，trace.html 中的 atrace 因为被 RheaTrace 拦截写入自定义文件中，因此生成的 trace.html 文件中是不包含 atrace 数据。&lt;/p&gt;  &lt;p&gt;如果不包含 atrace，那么 trace.html 的作用将非常小，因此，我们需要将 atrace 数据填充进入 trace.html 中，经过验证 atrace 数据满足如下格式，能够被 trace.html 识别。&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;&amp;lt;ThreadName&amp;gt;-&amp;lt;TheadID&amp;gt; (ProcessID) [001] ...1 &amp;lt;Timestamp&amp;gt;: trace_mark_write:&amp;lt;B|E&amp;gt;|&amp;lt;ProcessID&amp;gt;|&amp;lt;TAG&amp;gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;相较于标准 atrace 文件，它多了    &lt;code&gt;(ProcessID)&lt;/code&gt;数据。&lt;/p&gt;  &lt;p&gt;有了上述介绍，我们介绍下 RheaTrace 生成的 systrace.html 中间会生成哪些文件。&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;python rheatrace.py -a rhea.sample.android -t 3 -o ./output/systrace.html&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;在 systrace.html 同级目录下会生成    &lt;code&gt;.build&lt;/code&gt; 目录，其中包括上述中间产物文件。&lt;/p&gt;  &lt;pre&gt;   &lt;code&gt;├── .build
│   ├── atrace-standard          //标准 atrace 格式，可直接用 perfetto 打开。
│   ├── rhea-atrace              //从设备中拉取的 rhea-atrace.gz 文件解压得到，原始 RheaTrace 格式的 atrace 数据。
│   ├── systrace-fs-origin.html  //如果设备 root，其中会包括内核 IO 事件，前文有提到。
│   └── systrace-origin.html     //通过 Systrace 工具抓取的可视化 trace 文件，不包含 atrace 数据。
└── systrace.html                //atrace-standard 与 systrace-fs-origin.html 或 systrace-origin.html 合并得到。&lt;/code&gt;&lt;/pre&gt;  &lt;h2&gt;未来规划&lt;/h2&gt;  &lt;ol&gt;   &lt;li&gt;    &lt;p&gt;支持 App 独立抓取 atrace 数据，无需依赖 Systrace 环境。&lt;/p&gt;&lt;/li&gt;   &lt;li&gt;    &lt;p&gt;提供稳定、高效的 trace 采集环境，适配更多手机机型。&lt;/p&gt;&lt;/li&gt;   &lt;li&gt;    &lt;p&gt;支持更多维度的 trace 信息，比如渲染、内存等，更方便定位函数耗时原因。&lt;/p&gt;&lt;/li&gt;   &lt;li&gt;    &lt;p&gt;进一步降低性能损耗，到达线上使用要求。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;                     &lt;div&gt;
                        作者：ByteDanceTech 发表于 2021/12/30 15:24:06   &lt;a href="https://blog.csdn.net/ByteDanceTech/article/details/122248282"&gt;原文链接&lt;/a&gt; https://blog.csdn.net/ByteDanceTech/article/details/122248282                    &lt;/div&gt;
                     &lt;div&gt;
                        阅读：33                     &lt;/div&gt;
                    
                &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/61983-btrace-%E5%BC%80%E6%BA%90-systrace</guid>
      <pubDate>Thu, 30 Dec 2021 15:24:06 CST</pubDate>
    </item>
    <item>
      <title>超级好用的免费开源文件同步工具：Syncthing</title>
      <link>https://itindex.net/detail/61975-%E5%85%8D%E8%B4%B9-%E5%BC%80%E6%BA%90-%E6%96%87%E4%BB%B6%E5%90%8C%E6%AD%A5</link>
      <description>&lt;p&gt;相信不少小伙伴跟TJ君有一样的经历，当然相信也会有小伙伴是因为更换设备导致的文件丢失，不管怎么说，临时用用还行，平时大家还是不要把重要的文件直接存储在微信上，还是该备份的备份，该传输到本地的传输。存到网盘？你确定不开通个VIP那上传下载速度能用？&lt;/p&gt; &lt;p&gt;那么今天的问题就来了，平时大家都用什么文件传输工具呢？&lt;/p&gt; &lt;p&gt;今天TJ君要和大家分享的就是一款免费、开源的文件同步工具，  &lt;strong&gt;Syncthing&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="https://blog.didispace.com/images/pasted-815.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;作为一款P2P性质的文件同步工具，Syncthing在Github上广受欢迎，已经收获了42k的Star！&lt;/p&gt; &lt;p&gt;Syncthing有五大特点：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;1、避免数据丢失&lt;/li&gt;  &lt;li&gt;2、安全防范攻击者&lt;/li&gt;  &lt;li&gt;3、便捷使用&lt;/li&gt;  &lt;li&gt;4、自动化运行&lt;/li&gt;  &lt;li&gt;5、高兼容性&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;像Windwos、macOS、Android、Linux这些主流平台都支持Syncthing，所以Syncthing可以跨平台的进行文件传输。&lt;/p&gt; &lt;p&gt;目前最新的版本是v1.18.5版本，在18天前更新，说明作者也一直在优化迭代。&lt;/p&gt; &lt;p&gt;以windows为例，只需要安装好Syncthing电脑端的应用程序，打开浏览器访问   &lt;a href="http://127.0.0.1:8384/" rel="external nofollow noopener noreferrer" target="_blank"&gt;http://127.0.0.1:8384/&lt;/a&gt; 就可以进入 Syncthing传输平台。&lt;/p&gt; &lt;p&gt;  &lt;img alt="" src="https://blog.didispace.com/images/pasted-816.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;如果在其他设备，例如手机端也安装完Syncthing，便会有同步提示，通过后就可以进行文件传输了。速度很快哦！&lt;/p&gt; &lt;p&gt;不知道大家有没有从TJ的图中发现，虽然Syncthing的开源项目都是英文描述，但是其使用界面却是中文的，算不算也是给我们大天朝的一个小小的福利呢？想试试这款工具传输文件进行备份的小伙伴们，赶紧来试试吧！&lt;/p&gt; &lt;p&gt;  &lt;strong&gt;扫描下方二维码，关注公众号“TJ君”，回复“Syncthing”，获取仓库地址！&lt;/strong&gt;&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>开源推荐 开源</category>
      <guid isPermaLink="true">https://itindex.net/detail/61975-%E5%85%8D%E8%B4%B9-%E5%BC%80%E6%BA%90-%E6%96%87%E4%BB%B6%E5%90%8C%E6%AD%A5</guid>
      <pubDate>Mon, 27 Dec 2021 10:09:20 CST</pubDate>
    </item>
    <item>
      <title>尝试解析下 Epub.js：一个在浏览器上渲染 Epub 图书的工具</title>
      <link>https://itindex.net/detail/61905-%E8%A7%A3%E6%9E%90-epub-js</link>
      <description>&lt;blockquote&gt;  &lt;p&gt;一直在看 Epub 类型的图书， 很好奇一个 Epub 解析器是如果工作的。 碰巧看到了 Epub.js， 体验了一下还可以。 就尝试分析一下它的实现。本文会先介绍下 EPUB 格式,再来分析 Epubjs 的实现. 我前端经验仅限于了解常见标签含义，可能会有各种错误恳请斧正。&lt;/p&gt;&lt;/blockquote&gt; &lt;h1&gt;Epub&lt;/h1&gt; &lt;p&gt;EPub是一个自由的开放标准，属于一种可以“自动重新排版”的内容；也就是文字内容可以根据阅读设备的特性，以最适于阅读的方式显示。EPub档案内部使用了XHTML或DTBook（一种由DAISY Consortium提出的XML标准）来展现文字、并以zip压缩格式来包裹档案内容。EPub格式中包含了数位版权管理（DRM）相关功能可供选用&lt;/p&gt; &lt;blockquote&gt;  &lt;p&gt;以上来自维基百科: https://zh.wikipedia.org/wiki/EPUB&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;本文参考的规范为:  &lt;a href="https://www.w3.org/publishing/epub32/epub-spec.html#sec-epub-rs-conf"&gt;Epub 3.2&lt;/a&gt;该规范发布于 2019-05-08, 定义了 EPUB 图书格式和 EPUB 图书的浏览器应实现的功能. Epub格式由以下规范组成:&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;EPUB Packages — 定义内容的每个Rendition的要求。&lt;/li&gt;  &lt;li&gt;EPUB Content Documents — 定义了在 EPUB 出版物上下文中使用的 XHTML、SVG 和 CSS 的配置文件。&lt;/li&gt;  &lt;li&gt;EPUB Media Overlays — 定义了文本和音频同步的格式和处理模型。&lt;/li&gt;  &lt;li&gt;EPUB Open Container Format — 定义了一种文件格式和处理模型，用于将一组相关资源封装到单个文件 (ZIP) EPUB 容器中。&lt;/li&gt;  &lt;li&gt;EPUB Accessibility — 定义 EPUB 出版物的可访问性一致性和发现要求。&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;如下图:
EPUB图书的内容是由一个或多个Rendition组成，每个Rendition都由所谓的EPUB Package 表示。
EPUB Package包含呈现内容所需的所有资源。其中的关键文件是Package Document，它包括阅读系统用于向用户呈现EPUB图书的所有元数据（例如标题和作者等）。它还提供了一个完整的资源清单，并包括一个  &lt;code&gt;SPINE&lt;/code&gt;(用来表是文档出现的顺序)。
EPUB Package还包括另一个称为EPUB Navigation Document的文件。本文档提供了导航功能，例如目录，允许用户快速轻松地导航内容。
EPUB 图书的资源捆绑在一个基于 ZIP 的文件中，文件扩展名为.epub. 作为符合 ZIP 格式的文件，EPUB 出版物可以被许多软件程序解压缩，从而简化了它们的使用。  &lt;img alt="Epub Structure" src="http://itindex.net/assets/img/2021/epub/epub-format.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;h2&gt;参考规范来制作一个简单Epub 图书&lt;/h2&gt; &lt;h3&gt;按照下图的方式来组织目录和文件&lt;/h3&gt; &lt;p&gt;  &lt;img alt="Dirtctor" src="http://itindex.net/assets/img/2021/epub/director.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;h3&gt;mimetype 文件名固定, 内容也固定位:  &lt;code&gt;application/epub+zip&lt;/code&gt;&lt;/h3&gt; &lt;h3&gt;META-INF/container.xml 为入口文件,文件名固定.&lt;/h3&gt; &lt;p&gt;内容如下:
其中rootfile指定了package file&lt;/p&gt; &lt;div&gt;  &lt;div&gt;   &lt;pre&gt;    &lt;code&gt;&amp;lt;?xml version=&amp;apos;1.0&amp;apos; encoding=&amp;apos;utf-8&amp;apos;?&amp;gt;&amp;lt;containerxmlns=&amp;quot;urn:oasis:names:tc:opendocument:xmlns:container&amp;quot;version=&amp;quot;1.0&amp;quot;&amp;gt;&amp;lt;rootfiles&amp;gt;&amp;lt;rootfilefull-path=&amp;quot;OPS/package.opf&amp;quot;media-type=&amp;quot;application/oebps-package+xml&amp;quot;/&amp;gt;&amp;lt;/rootfiles&amp;gt;&amp;lt;/container&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;h3&gt;OPS/package.opf&lt;/h3&gt; &lt;p&gt;Package 文件, 定义了书籍的 meta 信息, 资源列表和阅读顺序(Spine)
内容如下:&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;metadata: 标签定义的作者等信息&lt;/li&gt;  &lt;li&gt;manifest: 定义了资源列表,如 正文页面, 目录, 封面图片, CSS, JS 等等&lt;/li&gt;  &lt;li&gt;spine: 定义了书籍连续阅读的顺序.   &lt;ul&gt;    &lt;li&gt;Liner 代表顺序是否为必要顺序. 如目录和封面不一定强制按照这个顺序来阅读. 或者说是在做阅读器是可以把 Liner=no页面弹窗&lt;/li&gt;    &lt;li&gt;和目录/书签最显著的区别, Spine 是按照资源文件来组织循序. 目录/书签等是可以指定到资源内的标签.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt; &lt;div&gt;  &lt;div&gt;   &lt;pre&gt;    &lt;code&gt;&amp;lt;?xml version=&amp;apos;1.0&amp;apos; encoding=&amp;apos;utf-8&amp;apos;?&amp;gt;&amp;lt;packagexmlns=&amp;quot;http://www.idpf.org/2007/opf&amp;quot;unique-identifier=&amp;quot;uuid_id&amp;quot;version=&amp;quot;3.2&amp;quot;prefix=&amp;quot;calibre: https://calibre-ebook.com&amp;quot;&amp;gt;&amp;lt;metadataxmlns:dc=&amp;quot;http://purl.org/dc/elements/1.1/&amp;quot;&amp;gt;&amp;lt;dc:titleid=&amp;quot;id&amp;quot;&amp;gt;Calvin用来演示 EPUBJS 的书&amp;lt;/dc:title&amp;gt;&amp;lt;dc:creatorid=&amp;quot;Creator&amp;quot;&amp;gt;Calvin Wang&amp;lt;/dc:creator&amp;gt;&amp;lt;dc:identifierid=&amp;quot;uuid_id&amp;quot;&amp;gt;urn:uuid:5F1E4C07-2A52-48BC-BBA5-E98564559794&amp;lt;/dc:identifier&amp;gt;&amp;lt;dc:language&amp;gt;zh-CN&amp;lt;/dc:language&amp;gt;&amp;lt;metaproperty=&amp;quot;dcterms:modified&amp;quot;&amp;gt;2021-11-14T08:32:29Z&amp;lt;/meta&amp;gt;&amp;lt;/metadata&amp;gt;&amp;lt;manifest&amp;gt;&amp;lt;itemid=&amp;quot;cover&amp;quot;href=&amp;quot;cover.xhtml&amp;quot;media-type=&amp;quot;application/xhtml+xml&amp;quot;/&amp;gt;&amp;lt;itemid=&amp;quot;toc&amp;quot;properties=&amp;quot;nav&amp;quot;href=&amp;quot;toc.xhtml&amp;quot;media-type=&amp;quot;application/xhtml+xml&amp;quot;/&amp;gt;&amp;lt;itemid=&amp;quot;firstpage&amp;quot;href=&amp;quot;first_page.xhtml&amp;quot;media-type=&amp;quot;application/xhtml+xml&amp;quot;/&amp;gt;&amp;lt;itemid=&amp;quot;secondpage&amp;quot;href=&amp;quot;second_page.xhtml&amp;quot;media-type=&amp;quot;application/xhtml+xml&amp;quot;/&amp;gt;&amp;lt;itemid=&amp;quot;ncxtoc&amp;quot;href=&amp;quot;toc.ncx&amp;quot;media-type=&amp;quot;application/x-dtbncx+xml&amp;quot;/&amp;gt;&amp;lt;itemid=&amp;quot;cover-image&amp;quot;properties=&amp;quot;cover-image&amp;quot;href=&amp;quot;images/cover.png&amp;quot;media-type=&amp;quot;image/png&amp;quot;/&amp;gt;&amp;lt;/manifest&amp;gt;&amp;lt;spinetoc=&amp;quot;ncxtoc&amp;quot;&amp;gt;&amp;lt;itemrefidref=&amp;quot;cover&amp;quot;linear=&amp;quot;no&amp;quot;/&amp;gt;&amp;lt;itemrefidref=&amp;quot;toc&amp;quot;linear=&amp;quot;no&amp;quot;/&amp;gt;ß&amp;lt;itemrefidref=&amp;quot;firstpage&amp;quot;linear=&amp;quot;yes&amp;quot;/&amp;gt;&amp;lt;itemrefidref=&amp;quot;secondpage&amp;quot;linear=&amp;quot;yes&amp;quot;/&amp;gt;&amp;lt;/spine&amp;gt;&amp;lt;/package&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;h3&gt;其他页面&lt;/h3&gt; &lt;div&gt;  &lt;div&gt;   &lt;pre&gt;    &lt;code&gt;&amp;lt;--!CoverPage--&amp;gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&amp;lt;htmlxmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;xmlns:epub=&amp;quot;http://www.idpf.org/2007/ops&amp;quot;&amp;gt;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;Cover Page&amp;lt;/title&amp;gt;&amp;lt;metacharset=&amp;quot;utf-8&amp;quot;/&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;div&amp;gt;&amp;lt;imgsrc=&amp;quot;images/cover.png&amp;quot;alt=&amp;quot;Cover Image&amp;quot;title=&amp;quot;Cover Image&amp;quot;/&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;lt;--!firstpage--&amp;gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&amp;lt;htmlxmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;xmlns:epub=&amp;quot;http://www.idpf.org/2007/ops&amp;quot;&amp;gt;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;First Page&amp;lt;/title&amp;gt;&amp;lt;metacharset=&amp;quot;utf-8&amp;quot;/&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;sectionepub:type=&amp;quot;bodymatter chapter&amp;quot;&amp;gt;&amp;lt;header&amp;gt;&amp;lt;h1&amp;gt;&amp;lt;spanid=&amp;quot;c002p0000&amp;quot;&amp;gt;First Page&amp;lt;/span&amp;gt;&amp;lt;/h1&amp;gt;&amp;lt;/header&amp;gt;&amp;lt;p&amp;gt;&amp;lt;spanid=&amp;quot;c002p0001&amp;quot;&amp;gt;First Page: aaaaaaaaaaaa&amp;lt;/span&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;&amp;lt;spanid=&amp;quot;c002p0002&amp;quot;&amp;gt;First Page: bbbbbbbbbbbbb&amp;lt;/span&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;/section&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&amp;lt;--!TOCPage--&amp;gt;&amp;lt;?xml version=&amp;apos;1.0&amp;apos; encoding=&amp;apos;utf-8&amp;apos;?&amp;gt;&amp;lt;htmlxmlns=&amp;quot;http://www.w3.org/1999/xhtml&amp;quot;xmlns:epub=&amp;quot;http://purl.org/dc/elements/1.1/&amp;quot;&amp;gt;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;Cover&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;navxmlns:ns0=&amp;quot;http://www.idpf.org/2007/ops&amp;quot;ns0:type=&amp;quot;toc&amp;quot;&amp;gt;&amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;&amp;lt;ahref=&amp;quot;cover.xhtml&amp;quot;&amp;gt;Cover Page&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&amp;lt;ahref=&amp;quot;toc.xhtml&amp;quot;&amp;gt;Nav Page&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&amp;lt;ahref=&amp;quot;first_page.xhtml&amp;quot;&amp;gt;First Page&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;li&amp;gt;&amp;lt;ahref=&amp;quot;second_page.xhtml&amp;quot;&amp;gt;Second Page&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;/nav&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;h3&gt;把目录打包成 EPUB 文件&lt;/h3&gt; &lt;p&gt;如下图: 用 ZIP 压缩文件至 first-epub.epub  &lt;img alt="" src="http://itindex.net/assets/img/2021/epub/create_epub.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;div&gt;  &lt;div&gt;   &lt;pre&gt;    &lt;code&gt;zip-r../first-epub.epub*&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;p&gt;把做好的图书用自带的 Book 打开来看看  &lt;img alt="" src="http://itindex.net/assets/img/2021/epub/open_by_book.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;h1&gt;Epub.js&lt;/h1&gt; &lt;p&gt;Epub.js 是一个 JavaScript 库，用于在浏览器中跨多种设备呈现 ePub 文档。
Epub.js 为常见的电子书功能（如渲染、持久化和分页）提供了一个接口，而无需开发专用的应用程序或插件。 重要的是，它是 BSD 许可证。&lt;/p&gt; &lt;blockquote&gt;  &lt;p&gt;翻译自 项目 README&lt;/p&gt;&lt;/blockquote&gt; &lt;h2&gt;来看看官方的DEMO&lt;/h2&gt; &lt;p&gt;高亮 备注 分页等等常用功能是均支持的  &lt;img alt="" src="http://itindex.net/assets/img/2021/epub/epubjs-demo.gif"&gt;&lt;/img&gt;&lt;/p&gt; &lt;h2&gt;来实现一个”Hello Word”&lt;/h2&gt; &lt;p&gt;  &lt;img alt="" src="http://itindex.net/../assets/img/2021/epub/local_demo_for_epubjs.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;div&gt;  &lt;div&gt;   &lt;pre&gt;    &lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;&amp;lt;html&amp;gt;&amp;lt;head&amp;gt;&amp;lt;metacharset=&amp;quot;utf-8&amp;quot;&amp;gt;&amp;lt;title&amp;gt;Demo for Epub.js&amp;lt;/title&amp;gt;&amp;lt;scriptsrc=&amp;quot;https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.5/jszip.min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&amp;lt;scriptsrc=&amp;quot;https://futurepress.github.io/epub.js/dist/epub.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;&amp;lt;divid=&amp;quot;nav&amp;quot;&amp;gt;&amp;lt;selectid=&amp;quot;toc&amp;quot;&amp;gt;&amp;lt;/select&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;divid=&amp;quot;viewer&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;script&amp;gt;var$viewer=document.getElementById(&amp;quot;viewer&amp;quot;);// Load the opfvarbook=ePub(&amp;quot;https://blog.calvin.wang/assets/first-epub.epub&amp;quot;,{store:&amp;quot;epubjs-test&amp;quot;});varrendition=book.renderTo(&amp;quot;viewer&amp;quot;,{width:&amp;quot;100%&amp;quot;});vardisplayed=rendition.display();book.loaded.navigation.then(function(toc){var$select=document.getElementById(&amp;quot;toc&amp;quot;),docfrag=document.createDocumentFragment();toc.forEach(function(chapter){varoption=document.createElement(&amp;quot;option&amp;quot;);option.textContent=chapter.label;option.ref=chapter.href;docfrag.appendChild(option);});$select.appendChild(docfrag);$select.onchange=function(){varindex=$select.selectedIndex,url=$select.options[index].ref;rendition.display(url);returnfalse;};});&amp;lt;/script&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt; &lt;h2&gt;来看看它有哪些 Model&lt;/h2&gt; &lt;ul&gt;  &lt;li&gt;Book: 用来代表一个 Epub 图书,用来加载和解析其内容   &lt;ul&gt;    &lt;li&gt;Container: 用来解析 Container, 主要用来从 “META-INF/container.xml”获取 PackagePath&lt;/li&gt;    &lt;li&gt;Packaging: 用来解析 Package, 主要用来获取manifest cover spine metadata nav ncx&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;  &lt;li&gt;Rendition: 负责将 Book 的内容渲染在网页上,并进行控制.   &lt;ul&gt;    &lt;li&gt;Manager: 用来控制网页上的呈现方式(default: DefaultViewManager)&lt;/li&gt;    &lt;li&gt;View: 展现在页面上的样式(default: IframeView)&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;  &lt;li&gt;EpubCFI: 是一个规范,定义了一种标准化方法，用于通过使用片段标识符来引用 EPUB® 出版物中的任意内容. 可以参考:   &lt;a href="http://idpf.org/epub/linking/cfi/epub-cfi.html"&gt;http://idpf.org/epub/linking/cfi/epub-cfi.html&lt;/a&gt;&lt;/li&gt;  &lt;li&gt;辅助类:   &lt;ul&gt;    &lt;li&gt;Theme: 样式主题&lt;/li&gt;    &lt;li&gt;Annotations: 注解&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt; &lt;h2&gt;流程怎么控制的&lt;/h2&gt; &lt;ul&gt;  &lt;li&gt;Rendition:   &lt;ul&gt;    &lt;li&gt;支持HOOK的阶段:     &lt;ul&gt;      &lt;li&gt;content: Pages/View内容被解析和加载,现有的 HOOK       &lt;ul&gt;        &lt;li&gt;handleLinks: 处理内容中的连接&lt;/li&gt;        &lt;li&gt;passEvents: 内容中产生的事件同步&lt;/li&gt;        &lt;li&gt;adjustImages: 调整图片&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;      &lt;li&gt;unloaded: Pages/View内容从屏幕中卸载&lt;/li&gt;      &lt;li&gt;render: Pages/View被渲染到屏幕&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;  &lt;li&gt;Spin:   &lt;ul&gt;    &lt;li&gt;支持HOOK的阶段:     &lt;ul&gt;      &lt;li&gt;Serialize: Pages/View 被转换为 text&lt;/li&gt;      &lt;li&gt;Content: Pages/View 被加载和解析. 现有的 HOOK       &lt;ul&gt;        &lt;li&gt;injectStylesheet: 注入样式表&lt;/li&gt;        &lt;li&gt;injectScript: 注入脚本&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt; &lt;h2&gt;还没有完全看懂的内容, 感觉是语言特性:&lt;/h2&gt; &lt;ol&gt;  &lt;li&gt;Promise 的用处?&lt;/li&gt;  &lt;li&gt;Event的传递方式?&lt;/li&gt;&lt;/ol&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>dev</category>
      <guid isPermaLink="true">https://itindex.net/detail/61905-%E8%A7%A3%E6%9E%90-epub-js</guid>
      <pubDate>Sat, 20 Nov 2021 00:00:00 CST</pubDate>
    </item>
    <item>
      <title>2021年敏捷管理工具推荐</title>
      <link>https://itindex.net/detail/61895-%E7%AE%A1%E7%90%86-%E5%B7%A5%E5%85%B7</link>
      <description>&lt;p&gt;敏捷和DevOps已经很流行了，对应的工具也不少（例如JIRA、Azure DevOps、云效等等），我们在本篇文章里捋一捋截止到2021年11月都有哪些有名的敏捷工具。&lt;/p&gt; &lt;a&gt;&lt;/a&gt; &lt;h2&gt;  &lt;a href="https://devopstools.cn/#1-JIRA" title="1. JIRA"&gt;&lt;/a&gt;1. JIRA&lt;/h2&gt; &lt;p&gt;  &lt;img alt="JIRA Logo-w200" src="https://devopstools.cn/images/jira-logo1.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;JIRA是Atlassian公司著名的敏捷管理工具，可以管理史诗（Epic)、用户故事(User Story)、任务（Task)，同时可以使用看板可以跟踪管理相关工作项的状态。  &lt;br /&gt;问题追踪和管理：用它管理项目，跟踪任务、bug、需求，通过jira的邮件通知功能进行协作通知，在实际工作中使工作效率提高很多&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;问题跟进情况的分析报告：可以随时了解问题和项目的进展情况&lt;/li&gt;  &lt;li&gt;项目类别管理功能：可以将相关的项目分组管理&lt;/li&gt;  &lt;li&gt;组件/模块负责人功能：可以将项目的不同组件/模块指派相应的负责人，来处理所负责的组件的Issues&lt;/li&gt;  &lt;li&gt;项目email地址功能：每个项目可以有不同的email（该项目的通知邮件从该地址发出）&lt;/li&gt;  &lt;li&gt;无限制的工作流：可以创建多个工作流为不同的项目使用   &lt;br /&gt;   &lt;img alt="jira-board" src="https://devopstools.cn/images/jira-board.png"&gt;&lt;/img&gt;  &lt;/li&gt;&lt;/ul&gt; &lt;h2&gt;  &lt;a href="https://devopstools.cn/#2-Azure-DevOps" title="2. Azure DevOps"&gt;&lt;/a&gt;2. Azure DevOps&lt;/h2&gt; &lt;p&gt;  &lt;img alt="Azure DevOps-w200" src="https://devopstools.cn/images/AzureDevOpslogo.png"&gt;&lt;/img&gt;  &lt;br /&gt;Azure DevOps 是由微软开发的服务平台，它提供了多种工具，可用于更好地进行团队协作。它还具有用于自动构建过程，测试，版本控制和程序包管理的工具。&lt;/p&gt; &lt;p&gt;Azure DevOps 提供了 5 个主要模块：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;Azure Boards：这些是敏捷的工具，可以帮助我们规划、跟踪和讨论我们的工作，甚至与其他团队一起工作。&lt;/li&gt;  &lt;li&gt;Azure Repos：提供无限的、云托管的私人和公共 Git 存储库。&lt;/li&gt;  &lt;li&gt;Azure Pipelines：使用适用于任何语言、平台和云的 CI/CD 进行构建、测试和部署。&lt;/li&gt;  &lt;li&gt;Azure Test Plans：使用适用于应用的手动测试和探索测试工具来提高代码整体质量。。&lt;/li&gt;  &lt;li&gt;Azure Artifacts： 与整个团队共享来自公共源和专用源的 Maven、npm、NuGet 和 Python 包。以简单且可缩放的方式将包共享集成到 CI/CD 管道中。   &lt;br /&gt;   &lt;img alt="Azure Board" src="https://devopstools.cn/images/azureboard.png"&gt;&lt;/img&gt;&lt;/li&gt;&lt;/ul&gt; &lt;h2&gt;  &lt;a href="https://devopstools.cn/#3-TAPD" title="3. TAPD"&gt;&lt;/a&gt;3. TAPD&lt;/h2&gt; &lt;p&gt;  &lt;img alt="TAPD Logo" src="https://devopstools.cn/images/tapdlogo.png"&gt;&lt;/img&gt;  &lt;br /&gt;TAPD（Tencent Agile Product Development） 是腾讯的敏捷研发协作平台，提供贯穿敏捷研发生命周期的一站式服务。覆盖从产品概念形成、产品规划、需求分析、项目规划和跟踪、质量测试到构建发布、用户反馈跟踪的产品研发全生命周期，提供了灵活的可定制化应用和强大的集成能力，帮助研发团队有效地管理需求、资源、进度和质量，规范和改进产品研发过程，提高研发效率和产品质量。&lt;/p&gt; &lt;p&gt;产品功能:&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;敏捷需求规划：能够快速高效的对需求进行全周期管理。通过需求收集、分解，规划并实施，快速响应市场变化，灵活处理需求变更，过程可追溯，清晰更透明。&lt;/li&gt;  &lt;li&gt;迭代计划：通过迭代进行目标制定与计划评审，完成工作分配，使用故事墙与燃尽图进行研发过程跟踪。迭代全程目标清晰，进度可控，研发过程敏捷迭代，小步快跑。&lt;/li&gt;  &lt;li&gt;测试计划：对于迭代质量的全程把控。通过快速编写并管理测试用例，制定测试计划并执行，利用缺陷跟踪管理进行问题跟踪与解决，能够实现对测试工作的高效管理，保障产品高质量交付。&lt;/li&gt;  &lt;li&gt;缺陷管理：对缺陷进行全方位记录与跟踪。配合缺陷统计报表对 BUG 进行统计分析，能够及时了解开发的质量并进行跟踪修复。同时可通过邮件创建定时报告发送给项目成员，让团队成员及时了解迭代开发质量。&lt;/li&gt;  &lt;li&gt;工时管理：合理分配团队资源，利用工时进行工作量统计，配合工时花费报告，能够实时掌握团队成员工作完成情况与项目进展，过程清晰，风险可控。&lt;/li&gt;  &lt;li&gt;文档管理：提供思维导图、在线文档、文件管理等功能，支持多人实时协作编辑，帮助团队集中管理项目文件，方便团队进行头脑风暴、内容分享与知识沉淀。&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;  &lt;img alt="tapd-board" src="https://devopstools.cn/images/tapd-board.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;h2&gt;  &lt;a href="https://devopstools.cn/#4-&amp;#20113;&amp;#25928;" title="4 &amp;#20113;&amp;#25928;"&gt;&lt;/a&gt;4 云效&lt;/h2&gt; &lt;p&gt;云效提供项目管理、需求管理、缺陷管理、任务管理、迭代规划等丰富的项目管理功能及效能数据统计，支持单项目管理、跨项目协作等丰富的协作场景。&lt;/p&gt; &lt;p&gt;产品功能：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;需求管理：管理需求从提出、设计、开发、测试、发布的完整流程。&lt;/li&gt;  &lt;li&gt;迭代规划：提供将需求规划进迭代并完成迭代的交付。&lt;/li&gt;  &lt;li&gt;项目管理：管理项目从创建、规划、实施、交付的完整流程。&lt;/li&gt;  &lt;li&gt;研发效能度量：支持工时统计。   &lt;br /&gt;   &lt;img alt="&amp;#20113;&amp;#25928;board" src="https://devopstools.cn/images/%E4%BA%91%E6%95%88-board.png"&gt;&lt;/img&gt;   &lt;h2&gt;    &lt;a href="https://devopstools.cn/#5-Leangoo" title="5 Leangoo"&gt;&lt;/a&gt;5 Leangoo&lt;/h2&gt;   &lt;img src="https://devopstools.cn/images/leangoo_logo_v.png"&gt;&lt;/img&gt;   &lt;br /&gt;Leangoo是一个以看板为核心的敏捷项目协作工具，通过看板共享和实时同步团队工作来实现高效协同。团队工作体现为卡片，内容可以是需求、任务、问题等。&lt;/li&gt;  &lt;li&gt;产品Backlog&lt;/li&gt;  &lt;li&gt;Scrum任务板&lt;/li&gt;  &lt;li&gt;用户故事&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;  &lt;img alt="Leangoo Board" src="https://devopstools.cn/images/leangoo-board.png"&gt;&lt;/img&gt;&lt;/p&gt; &lt;h2&gt;  &lt;a href="https://devopstools.cn/#6-Ones" title="6. Ones"&gt;&lt;/a&gt;6. Ones&lt;/h2&gt; &lt;p&gt;Ones 适用需求管理、任务管理、缺陷管理、迭代管理等敏捷场景  &lt;br /&gt;产品功能：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;需求管理&lt;/li&gt;  &lt;li&gt;迭代规划&lt;/li&gt;  &lt;li&gt;缺陷管理&lt;/li&gt;  &lt;li&gt;进度管理   &lt;br /&gt;   &lt;img alt="ones Board" src="https://devopstools.cn/images/ones-board.png"&gt;&lt;/img&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>敏捷 Agile DevOps</category>
      <guid isPermaLink="true">https://itindex.net/detail/61895-%E7%AE%A1%E7%90%86-%E5%B7%A5%E5%85%B7</guid>
      <pubDate>Mon, 15 Nov 2021 00:00:00 CST</pubDate>
    </item>
    <item>
      <title>HTTP抓包工具之Charles</title>
      <link>https://itindex.net/detail/61869-http-%E5%B7%A5%E5%85%B7-charles</link>
      <description>&lt;h2&gt;Charles简介&lt;/h2&gt;
 &lt;p&gt;Charles是一个HTTP代理服务器，当浏览器连接Charles的代理访问互联网时，Charles可以监控浏览器发送和接收的所有数据。它允许一个开发者查看所有连接互联网的HTTP通信，这些包括request, response和HTTP headers （包含cookies与caching信息）。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="197" src="https://www.biaodianfu.com/wp-content/uploads/2021/11/charles.jpg" width="560"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;Charles主要功能：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;支持SSL代理。可以截取分析SSL的请求。&lt;/li&gt;
  &lt;li&gt;支持流量控制。可以模拟慢速网络以及等待时间（latency）较长的请求。&lt;/li&gt;
  &lt;li&gt;支持AJAX调试。可以自动将json或xml数据格式化，方便查看。&lt;/li&gt;
  &lt;li&gt;支持AMF调试。可以将Flash Remoting 或 Flex Remoting信息格式化，方便查看。&lt;/li&gt;
  &lt;li&gt;支持重发网络请求，方便后端调试。&lt;/li&gt;
  &lt;li&gt;支持修改网络请求参数。&lt;/li&gt;
  &lt;li&gt;支持网络请求的截获并动态修改。&lt;/li&gt;
  &lt;li&gt;检查HTML，CSS和RSS内容是否符合W3C标准。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;以上介绍了Charles的主要功能，个人在使用过程中主要用的是抓取HTTP和HTTPS请求。特别是HTTPS的请求，抓取起来还是有一些麻烦，特此记录。&lt;/p&gt;
 &lt;h2&gt;Charles 抓包原理&lt;/h2&gt;
 &lt;p&gt;市面上绝大多数的抓包软件，背后的原理都是中间人攻击（Man-in-the-middle attack，缩写：MITM）。&lt;/p&gt;
 &lt;p&gt;维基百科是这样定义 MITM 的：中间人攻击在密码学和计算机安全领域中是指攻击者与通讯的两端分别建立独立的联系，并交换其所收到的数据，使通讯的两端认为他们正在通过一个私密的连接与对方直接对话，但事实上整个会话都被攻击者完全控制。&lt;/p&gt;
 &lt;p&gt;上面的定义写的很清晰，下图中结合箭头方向就能看懂 HTTP Packets 的流向：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="323" src="https://www.biaodianfu.com/wp-content/uploads/2021/11/MITM.png" width="674"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;Charles的使用&lt;/h2&gt;
 &lt;p&gt;Charles的安装过程是比较简单，只需到  &lt;a href="https://www.charlesproxy.com/"&gt;官网&lt;/a&gt;下载安装即可。比较困难的是HTTPS请求的配置。&lt;/p&gt;
 &lt;h3&gt;Windows下HTTPS请求抓包&lt;/h3&gt;
 &lt;p&gt;1、配置SSL支持。点击【Proxy】–&amp;gt;【SSL Proxying Settings…】，在弹出选项卡中，勾选【Enable SSL Proxying】点击【add】，在Host输入【*】表示接收任何主机，在Prot输入【*】表示任何端口，最后点击【ok】保存。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="425" src="https://www.biaodianfu.com/wp-content/uploads/2021/11/ssl.png" width="525"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;2、安装证书。点击【Help】–&amp;gt;【SSL Proxying】–&amp;gt;【Install Charles Root Certificate】，按照引导流程安装证书。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="606" src="https://www.biaodianfu.com/wp-content/uploads/2021/11/ca.png" width="431"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;完成后打开IE进行测试：出现证书错误！&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="258" src="https://www.biaodianfu.com/wp-content/uploads/2021/11/ie.png" width="500"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;打开Chrome测试：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="391" src="https://www.biaodianfu.com/wp-content/uploads/2021/11/chrome.png" width="500"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;打开Edge测试：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="317" src="https://www.biaodianfu.com/wp-content/uploads/2021/11/edge.png" width="500"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;都被安全拦截了，装了证书都不起作用。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;解决方案：安装Firefox！&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;安装完毕后在开启Charles时，使用Firefox打开，http://chls.pro/ssl，弹出如下页面：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="313" src="https://www.biaodianfu.com/wp-content/uploads/2021/11/firefox.png" width="434"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;选择保存文件后按确定。文件默认保存到下载文件夹。&lt;/p&gt;
 &lt;p&gt;打开Firefox【设置】–&amp;gt;【隐私与安全】–&amp;gt;【证书】–&amp;gt;【查看证书】&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="91" src="https://www.biaodianfu.com/wp-content/uploads/2021/11/pem-1.png" width="500"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;使用【证书管理器】–&amp;gt;【证书办法机构】–&amp;gt;【导入】进行导入操作。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="365" src="https://www.biaodianfu.com/wp-content/uploads/2021/11/pem-2.png" width="500"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;iOS下HTTPS请求抓包&lt;/h3&gt;
 &lt;p&gt;在PC上开启共享网络。将手机连接到PC共享的WIFI上。&lt;/p&gt;
 &lt;p&gt;在手机上设置代理地址，代理IP为PC的IP，端口为Charles的端口。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="156" src="https://www.biaodianfu.com/wp-content/uploads/2021/11/ios.png" width="500"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;在手机自带浏览器Safari中输入chls.pro，完成后需要进入【设置】安装描述文件。安装完毕后，如果是iOS 10 以后需要进入【设置】–&amp;gt;【通用】–&amp;gt;【关于本机】–&amp;gt;【证书信任设置】，开启证书。&lt;/p&gt;
 &lt;h3&gt;Android下HTTPS请求抓包&lt;/h3&gt;
 &lt;p&gt;在PC上开启共享网络。将手机连接到PC共享的WIFI上。&lt;/p&gt;
 &lt;p&gt;在手机上设置代理地址，代理IP为PC的IP，端口为Charles的端口。&lt;/p&gt;
 &lt;p&gt;在手机默认浏览器中输入chls.pro，下载downloadfile.crt文件，然后在【我的下载】中进行打开，按引导进行安装。&lt;/p&gt;
 &lt;div&gt;
  &lt;h3&gt;相关文章:&lt;/h3&gt;  &lt;ol&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/python-build-web-server.html" rel="bookmark" title="Python &amp;#20174;0&amp;#21040;1&amp;#25645;&amp;#24314;Web &amp;#26381;&amp;#21153;&amp;#22120;"&gt;Python 从0到1搭建Web 服务器 &lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/skyline-centos-7.html" rel="bookmark" title="Skyline&amp;#23454;&amp;#25112;&amp;#65306;CentOS 7&amp;#37096;&amp;#32626;"&gt;Skyline实战：CentOS 7部署 &lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/install-wordpress-on-ubuntu-20-04-with-lemp-stack.html" rel="bookmark" title="Ubuntu Server 20.04 WordPress&amp;#29615;&amp;#22659;&amp;#23433;&amp;#35013;&amp;#19982;&amp;#37197;&amp;#32622;"&gt;Ubuntu Server 20.04 WordPress环境安装与配置 &lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>器→工具 工具软件 抓包 爬虫</category>
      <guid isPermaLink="true">https://itindex.net/detail/61869-http-%E5%B7%A5%E5%85%B7-charles</guid>
      <pubDate>Tue, 02 Nov 2021 09:20:20 CST</pubDate>
    </item>
    <item>
      <title>数据同步工具之FlinkCDC/Canal/Debezium对比-技术圈</title>
      <link>https://itindex.net/detail/61862-%E6%95%B0%E6%8D%AE-%E5%90%8C%E6%AD%A5-%E5%B7%A5%E5%85%B7</link>
      <description>&lt;h2&gt;前言&lt;/h2&gt;  &lt;p&gt;数据准实时复制（CDC）是目前行内实时数据需求大量使用的技术，随着国产化的需求，我们也逐步考虑基于开源产品进行准实时数据同步工具的相关开发，逐步实现对商业产品的替代。本文把市面上常见的几种开源产品，Canal、Debezium、Flink CDC 从原理和适用做了对比，供大家参考。&lt;/p&gt;  &lt;blockquote&gt;    &lt;p&gt;本文首发微信公众号《import_bigdata》&lt;/p&gt;&lt;/blockquote&gt;  &lt;h3&gt;Debezium&lt;/h3&gt;  &lt;blockquote&gt;    &lt;p&gt;Debezium is an open source distributed platform for change data capture. Start it up, point it at your databases, and your apps can start responding to all of the inserts, updates, and deletes that other apps commit to your databases. Debezium is durable and fast, so your apps can respond quickly and never miss an event, even when things go wrong.&lt;/p&gt;&lt;/blockquote&gt;  &lt;p&gt;Debezium是一种CDC（Change Data Capture）工具，工作原理类似大家所熟知的Canal, DataBus, Maxwell等，是通过抽取数据库日志来获取变更。&lt;/p&gt;  &lt;p&gt;Debezium最初设计成一个Kafka Connect 的Source Plugin，目前开发者虽致力于将其与Kafka Connect解耦，但当前的代码实现还未变动。下图引自Debeizum官方文档，可以看到一个Debezium在一个完整CDC系统中的位置。&lt;/p&gt;  &lt;img src="https://filescdn.proginn.com/b3a928c97a568c48ebef867672693abc/be52c146afcab849ad5f27b8e7fbd5de.webp"&gt;&lt;/img&gt;  &lt;p&gt;Kafka Connect 为Source Plugin提供了一系列的编程接口，最主要的就是要实现SourceTask的poll方法，其返回    &lt;code&gt;List&amp;lt;SourceRecord&amp;gt;&lt;/code&gt;将会被以最少一次语义的方式投递至Kafka。&lt;/p&gt;  &lt;h4&gt;Debezium MySQL 架构&lt;/h4&gt;  &lt;img src="https://filescdn.proginn.com/789cad63362a1dfb80efb4c160ed8981/1a195756c164459e3ded9500115c561a.webp"&gt;&lt;/img&gt;Debezium抽取原理  &lt;p&gt;Reader体系构成了MySQL模块中代码的主线，我们的分析从Reader开始。&lt;/p&gt;  &lt;img src="https://filescdn.proginn.com/ab8da090cad3365d86aa4e660a7909d1/61999f94e706f06115e87c4b633d0dfe.webp"&gt;&lt;/img&gt;Reader继承关系  &lt;p&gt;从名字上应该可以看出，真正主要的是SnapshotReader和BinlogReader，分别实现了对MySQL数据的全量读取和增量读取，他们继承于AbstractReader，里面封装了共用逻辑，下图是AbstractReader的内部设计。&lt;/p&gt;  &lt;img src="https://filescdn.proginn.com/8b66e9c48aaaa4cab0e4a142796c583e/b014d49d506fc84ee1e8987b0213f361.webp"&gt;&lt;/img&gt;  &lt;p&gt;可以看到，AbstractReader在实现时，并没有直接将enqueue喂进来的record投递进Kafka，而是通过一个内存阻塞队列BlockingQueue进行了解耦，这种设计有诸多好处：&lt;/p&gt;  &lt;ol&gt;    &lt;li&gt;职责解耦&lt;/li&gt;&lt;/ol&gt;  &lt;p&gt;如上的图中，在喂入BlockingQueue之前，要根据条件判断是否接受该record；在向Kafka投递record之前，判断task的running状态。这样把同类的功能限定在特定的位置。&lt;/p&gt;  &lt;ol start="2"&gt;    &lt;li&gt;线程隔离&lt;/li&gt;&lt;/ol&gt;  &lt;p&gt;BlockingQueue是一个线程安全的阻塞队列，通过BlockingQueue实现的生产者消费者模型，是可以跑在不同的线程里的，这样避免局部的阻塞带来的整体的干扰。如上图中的右侧，消费者会定期判断running标志位，若running被stop信号置为了false，可以立刻停止整个task,而不会因MySQL IO阻塞延迟相应。&lt;/p&gt;  &lt;ol start="3"&gt;    &lt;li&gt;Single与Batch的互相转化&lt;/li&gt;&lt;/ol&gt;  &lt;p&gt;Enqueue record是单条的投递record，drain_to是批量的消费records。这个用法也可以反过来，实现batch到single的转化。&lt;/p&gt;  &lt;p&gt;可能你还知道阿里开源的另一个MySQL CDC工具canal，他只负责stream过程，并没有处理snapshot过程，这也是debezium相较于canal的一个优势。&lt;/p&gt;  &lt;p&gt;对于Debezium来说，基本沿用了官方搭建从库的这一思路，让我们看下官方文档描述的详细步骤。&lt;/p&gt;  &lt;p&gt;MySQL连接器每次获取快照的时候会执行以下的步骤：&lt;/p&gt;  &lt;ol&gt;    &lt;li&gt;获取一个全局读锁，从而阻塞住其他数据库客户端的写操作。&lt;/li&gt;    &lt;li&gt;开启一个可重复读语义的事务，来保证后续的在同一个事务内读操作都是在一个一致性快照中完成的。&lt;/li&gt;    &lt;li&gt;读取binlog的当前位置。&lt;/li&gt;    &lt;li&gt;读取连接器中配置的数据库和表的模式（schema）信息。&lt;/li&gt;    &lt;li&gt;释放全局读锁，允许其他的数据库客户端对数据库进行写操作。&lt;/li&gt;    &lt;li&gt;（可选）把DDL改变事件写入模式改变topic（schema change topic），包括所有的必要的DROP和CREATEDDL语句。&lt;/li&gt;    &lt;li&gt;扫描所有数据库的表，并且为每一个表产生一个和特定表相关的kafka topic创建事件（即为每一个表创建一个kafka topic）。&lt;/li&gt;    &lt;li&gt;提交事务。&lt;/li&gt;    &lt;li&gt;记录连接器成功完成快照任务时的连接器偏移量。&lt;/li&gt;&lt;/ol&gt;  &lt;h4&gt;部署&lt;/h4&gt;  &lt;h5&gt;基于 Kafka Connect&lt;/h5&gt;  &lt;p&gt;最常见的架构是通过 Apache Kafka Connect 部署 Debezium。Kafka Connect 为在 Kafka 和外部存储系统之间系统数据提供了一种可靠且可伸缩性的方式。它为 Connector 插件提供了一组 API 和一个运行时：Connect 负责运行这些插件，它们则负责移动数据。通过 Kafka Connect 可以快速实现 Source Connector 和 Sink Connector 进行交互构造一个低延迟的数据 Pipeline：&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;Source Connector（例如，Debezium）：将记录发送到 Kafka&lt;/li&gt;    &lt;li&gt;Sink Connector：将 Kafka Topic 中的记录发送到其他系统&lt;/li&gt;&lt;/ul&gt;  &lt;img src="https://filescdn.proginn.com/1498c2f30dc76c84989f81a1a7029e43/8b4f7c420d88d43fa11a68286d9e77cb.webp"&gt;&lt;/img&gt;  &lt;p&gt;如上图所示，部署了 MySQL 和 PostgresSQL 的 Debezium Connector 以捕获这两种类型数据库的变更。每个 Debezium Connector 都会与其源数据库建立连接：&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;MySQL Connector 使用客户端库来访问 binlog。&lt;/li&gt;    &lt;li&gt;PostgreSQL Connector 从逻辑副本流中读取数据。&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;除了 Kafka Broker 之外，Kafka Connect 也作为一个单独的服务运行。默认情况下，数据库表的变更会写入名称与表名称对应的 Kafka Topic 中。如果需要，您可以通过配置 Debezium 的 Topic 路由转换来调整目标 Topic 名称。例如，您可以：&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;将记录路由到名称与表名不同的 Topic 中&lt;/li&gt;    &lt;li&gt;将多个表的变更事件记录流式传输到一个 Topic 中&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;变更事件记录在 Apache Kafka 中后，Kafka Connect 生态系统中的不同 Sink Connector 可以将记录流式传输到其他系统、数据库，例如 Elasticsearch、数据仓库、分析系统或者缓存（例如 Infinispan）。&lt;/p&gt;  &lt;h5&gt;Debezium Server&lt;/h5&gt;  &lt;p&gt;另一种部署 Debezium 的方法是使用 Debezium Server。Debezium Server 是一个可配置的、随时可用的应用程序，可以将变更事件从源数据库流式传输到各种消息中间件上。&lt;/p&gt;  &lt;p&gt;下图展示了基于 Debezium Server 的变更数据捕获 Pipeline 架构：&lt;/p&gt;  &lt;img src="https://filescdn.proginn.com/64e54b6b8ea4238f9e704cfbda80d813/ff16300a88216ec18a497378a7ce2b75.webp"&gt;&lt;/img&gt;  &lt;p&gt;Debezium Server 配置使用 Debezium Source Connector 来捕获源数据库中的变更。变更事件可以序列化为不同的格式，例如 JSON 或 Apache Avro，然后发送到各种消息中间件，例如 Amazon Kinesis、Google Cloud Pub/Sub 或 Apache Pulsar。&lt;/p&gt;  &lt;h5&gt;嵌入式引擎&lt;/h5&gt;  &lt;p&gt;使用 Debezium Connector 的另一种方法是嵌入式引擎。在这种情况下，Debezium 不会通过 Kafka Connect 运行，而是作为嵌入到您自定义 Java 应用程序中的库运行。这对于在您的应用程序本身内获取变更事件非常有帮助，无需部署完整的 Kafka 和 Kafka Connect 集群，也不用将变更流式传输到 Amazon Kinesis 等消息中间件上。&lt;/p&gt;  &lt;h4&gt;特性&lt;/h4&gt;  &lt;p&gt;Debezium 是一组用于 Apache Kafka Connect 的 Source Connector。每个 Connector 都通过使用该数据库的变更数据捕获 (CDC) 功能从不同的数据库中获取变更。与其他方法（例如轮询或双重写入）不同，Debezium 的实现基于日志的 CDC：&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;确保捕获所有的数据变更。&lt;/li&gt;    &lt;li&gt;以极低的延迟生成变更事件，同时避免因为频繁轮询导致 CPU 使用率增加。例如，对于 MySQL 或 PostgreSQL，延迟在毫秒范围内。&lt;/li&gt;    &lt;li&gt;不需要更改您的数据模型，例如 ‘Last Updated’ 列。&lt;/li&gt;    &lt;li&gt;可以捕获删除操作。&lt;/li&gt;    &lt;li&gt;可以捕获旧记录状态以及其他元数据，例如，事务 ID，具体取决于数据库的功能和配置。&lt;/li&gt;&lt;/ul&gt;  &lt;h3&gt;Flink CDC&lt;/h3&gt;  &lt;ul&gt;    &lt;li&gt;2020 年 7 月提交了第一个 commit，这是基于个人兴趣孵化的项目；&lt;/li&gt;    &lt;li&gt;2020 年 7 中旬支持了 MySQL-CDC；&lt;/li&gt;    &lt;li&gt;2020 年 7 月末支持了 Postgres-CDC；&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;一年的时间，该项目在 GitHub 上的 star 数已经超过 800。&lt;/p&gt;  &lt;img src="https://filescdn.proginn.com/6260f47533c281f4f3abc1055df438d7/b7d4fd62a0402da92b208f46fad7abea.webp"&gt;&lt;/img&gt;  &lt;h4&gt;Flink CDC 发展&lt;/h4&gt;  &lt;p&gt;Flink CDC 底层封装了 Debezium， Debezium 同步一张表分为两个阶段：&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;全量阶段：查询当前表中所有记录；&lt;/li&gt;    &lt;li&gt;增量阶段：从 binlog 消费变更数据。&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;大部分用户使用的场景都是全量 + 增量同步，加锁是发生在全量阶段，目的是为了确定全量阶段的初始位点，保证增量 + 全量实现一条不多，一条不少，从而保证数据一致性。从下图中我们可以分析全局锁和表锁的一些加锁流程，左边红色线条是锁的生命周期，右边是 MySQL 开启可重复读事务的生命周期。&lt;/p&gt;  &lt;img src="https://filescdn.proginn.com/8ac9c8448e1c7e07e0275cb68027aed2/9d589e58cc9b5aa1aba4ae14e77ff88f.webp"&gt;&lt;/img&gt;  &lt;p&gt;以全局锁为例，首先是获取一个锁，然后再去开启可重复读的事务。这里锁住操作是读取 binlog 的起始位置和当前表的 schema。这样做的目的是保证 binlog 的起始位置和读取到的当前 schema 是可以对应上的，因为表的 schema 是会改变的，比如如删除列或者增加列。在读取这两个信息后，SnapshotReader 会在可重复读事务里读取全量数据，在全量数据读取完成后，会启动 BinlogReader 从读取的 binlog 起始位置开始增量读取，从而保证全量数据 + 增量数据的无缝衔接。&lt;/p&gt;  &lt;p&gt;表锁是全局锁的退化版，因为全局锁的权限会比较高，因此在某些场景，用户只有表锁。表锁锁的时间会更长，因为表锁有个特征：锁提前释放了可重复读的事务默认会提交，所以锁需要等到全量数据读完后才能释放。&lt;/p&gt;  &lt;p&gt;经过上面分析，接下来看看这些锁到底会造成怎样严重的后果：&lt;/p&gt;  &lt;img src="https://filescdn.proginn.com/10b5222f24e46b19337dce08b01aa159/7eb734fe6fc7cd5426d24cd799b8e121.webp"&gt;&lt;/img&gt;  &lt;p&gt;Flink CDC 1.x 可以不加锁，能够满足大部分场景，但牺牲了一定的数据准确性。Flink CDC 1.x 默认加全局锁，虽然能保证数据一致性，但存在上述 hang 住数据的风险。&lt;/p&gt;  &lt;p&gt;Flink CDC 1.x得到了很多用户在社区的反馈，主要归纳为三个：&lt;/p&gt;  &lt;img src="https://filescdn.proginn.com/704e9423a78d7bf7b081d0cd8be1c4de/5485a5ce44ab873d23995005a100af9c.webp"&gt;&lt;/img&gt;  &lt;ul&gt;    &lt;li&gt;全量 + 增量读取的过程需要保证所有数据的一致性，因此需要通过加锁保证，但是加锁在数据库层面上是一个十分高危的操作。底层 Debezium 在保证数据一致性时，需要对读取的库或表加锁，全局锁可能导致数据库锁住，表级锁会锁住表的读，DBA 一般不给锁权限。&lt;/li&gt;    &lt;li&gt;不支持水平扩展，因为 Flink CDC 底层是基于 Debezium，起架构是单节点，所以Flink CDC 只支持单并发。在全量阶段读取阶段，如果表非常大 (亿级别)，读取时间在小时甚至天级别，用户不能通过增加资源去提升作业速度。&lt;/li&gt;    &lt;li&gt;全量读取阶段不支持 checkpoint：CDC 读取分为两个阶段，全量读取和增量读取，目前全量读取阶段是不支持 checkpoint 的，因此会存在一个问题：当我们同步全量数据时，假设需要 5 个小时，当我们同步了 4 小时的时候作业失败，这时候就需要重新开始，再读取 5 个小时。&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;通过上面的分析，可以知道 2.0 的设计方案，核心要解决上述的三个问题，即支持无锁、水平扩展、checkpoint。&lt;/p&gt;  &lt;img src="https://filescdn.proginn.com/62cb88c16be3e82cb1ef7873c5e62314/3fde547f99b55512a2c5f0258a93b421.webp"&gt;&lt;/img&gt;  &lt;p&gt;目前，Flink CDC 2.0 也已经正式发布，此次的核心改进和提升包括：&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;并发读取，全量数据的读取性能可以水平扩展；&lt;/li&gt;    &lt;li&gt;全程无锁，不对线上业务产生锁的风险；&lt;/li&gt;    &lt;li&gt;断点续传，支持全量阶段的 checkpoint。&lt;/li&gt;&lt;/ul&gt;  &lt;blockquote&gt;    &lt;p&gt;本文发自微信公众号《import_bigdata》&lt;/p&gt;&lt;/blockquote&gt;  &lt;h3&gt;Canal&lt;/h3&gt;  &lt;p&gt;canal [kə&amp;apos;næl]，译意为水道/管道/沟渠，主要用途是基于 MySQL 数据库增量日志解析，提供增量数据订阅和消费。&lt;/p&gt;  &lt;p&gt;早期阿里巴巴因为杭州和美国双机房部署，存在跨机房同步的业务需求，实现方式主要是基于业务 trigger 获取增量变更。从 2010 年开始，业务逐步尝试数据库日志解析获取增量变更进行同步，由此衍生出了大量的数据库增量订阅和消费业务。&lt;/p&gt;  &lt;p&gt;基于日志增量订阅和消费的业务包括：&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;数据库镜像&lt;/li&gt;    &lt;li&gt;数据库实时备份&lt;/li&gt;    &lt;li&gt;索引构建和实时维护(拆分异构索引、倒排索引等)&lt;/li&gt;    &lt;li&gt;业务 cache 刷新&lt;/li&gt;    &lt;li&gt;带业务逻辑的增量数据处理&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;    &lt;strong&gt;当前的canal支持源端MySQL版本&lt;/strong&gt;包括 5.1.x , 5.5.x , 5.6.x , 5.7.x , 8.0.x。&lt;/p&gt;  &lt;h4&gt;工作原理&lt;/h4&gt;  &lt;p&gt;    &lt;strong&gt;MySQL主备复制原理&lt;/strong&gt;&lt;/p&gt;  &lt;img src="https://filescdn.proginn.com/53704c9541ae1aa99a71a7e34a56e8b2/80a4dfe48edecc7452f4b3c7f677e64d.webp"&gt;&lt;/img&gt;  &lt;ul&gt;    &lt;li&gt;MySQL master 将数据变更写入二进制日志( binary log, 其中记录叫做二进制日志事件binary log events，可以通过 show binlog events 进行查看)&lt;/li&gt;    &lt;li&gt;MySQL slave 将 master 的 binary log events 拷贝到它的中继日志(relay log)&lt;/li&gt;    &lt;li&gt;MySQL slave 重放 relay log 中事件，将数据变更反映它自己的数据&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;    &lt;strong&gt;canal 工作原理&lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;canal 模拟 MySQL slave 的交互协议，伪装自己为MySQL slave,向MySQL master发送dump协议&lt;/li&gt;    &lt;li&gt;MySQL master收到 dump 请求，开始推送 binary log 给 slave (即 canal )&lt;/li&gt;    &lt;li&gt;canal 解析 binary log 对象(原始为 byte 流)&lt;/li&gt;&lt;/ul&gt;  &lt;h4&gt;Binlog获取详解&lt;/h4&gt;  &lt;p&gt;Binlog发送接收流程，流程如下图所示:&lt;/p&gt;  &lt;img src="https://filescdn.proginn.com/c2ec0b12686363ecb23c75654b2110c4/fd75578e0540fa59a92e525b0d4f889e.webp"&gt;&lt;/img&gt;  &lt;p&gt;首先，我们需要伪造一个slave，向master注册，这样master才会发送binlog event。注册很简单，就是向master发送COM_REGISTER_SLAVE命令，带上slave相关信息。这里需要注意，因为在MySQL的replication topology中，都需要使用一个唯一的server id来区别标示不同的server实例，所以这里我们伪造的slave也需要一个唯一的server id。&lt;/p&gt;  &lt;p&gt;接着实现binlog的dump。MySQL只支持一种binlog dump方式，也就是指定binlog filename + position，向master发送COM_BINLOG_DUMP命令。在发送dump命令的时候，我们可以指定flag为BINLOG_DUMP_NON_BLOCK，这样master在没有可发送的binlog event之后，就会返回一个EOF package。不过通常对于slave来说，一直把连接挂着可能更好，这样能更及时收到新产生的binlog event。&lt;/p&gt;  &lt;p&gt;Dump命令包图如下所示:&lt;/p&gt;  &lt;img src="https://filescdn.proginn.com/e50bd094fdeca5db8841bce6672f723c/e3e8dcbda217354469e025313b8c6212.webp"&gt;&lt;/img&gt;  &lt;p&gt;如上图所示,在报文中塞入binlogPosition和binlogFileName即可让master从相应的位置发送binlog event。&lt;/p&gt;  &lt;h4&gt;canal结构&lt;/h4&gt;  &lt;img src="https://filescdn.proginn.com/e3876f7de70c73c957b77e17f8345489/cbcf2513c390aa84ac9fca2bc8a8c27c.webp"&gt;&lt;/img&gt;  &lt;p&gt;说明：&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;server代表一个canal运行实例，对应于一个jvm，也可以理解为一个进程&lt;/li&gt;    &lt;li&gt;instance对应于一个数据队列 （1个server对应1..n个instance)，每一个数据队列可以理解为一个数据库实例。&lt;/li&gt;&lt;/ul&gt;  &lt;h4&gt;Server设计&lt;/h4&gt;  &lt;img src="https://filescdn.proginn.com/967e1399036e10b41c44bce63d4ffdd6/9954b0ff61456f35965260e13843dfc9.webp"&gt;&lt;/img&gt;  &lt;p&gt;server代表了一个canal的运行实例，为了方便组件化使用，特意抽象了Embeded(嵌入式) / Netty(网络访问)的两种实现&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;Embeded : 对latency和可用性都有比较高的要求，自己又能hold住分布式的相关技术(比如failover)&lt;/li&gt;    &lt;li&gt;Netty : 基于netty封装了一层网络协议，由canal server保证其可用性，采用的pull模型，当然latency会稍微打点折扣，不过这个也视情况而定。(阿里系的notify和metaq，典型的push/pull模型，目前也逐步的在向pull模型靠拢，push在数据量大的时候会有一些问题)&lt;/li&gt;&lt;/ul&gt;  &lt;h4&gt;Instance设计&lt;/h4&gt;  &lt;img src="https://filescdn.proginn.com/0b18a349c4287788b19ea432719aac52/21e2856411588ee8f77ee146ec9a19dd.webp"&gt;&lt;/img&gt;  &lt;p&gt;instance代表了一个实际运行的数据队列，包括了EventPaser,EventSink,EventStore等组件。&lt;/p&gt;  &lt;p&gt;抽象了CanalInstanceGenerator，主要是考虑配置的管理方式：&lt;/p&gt;  &lt;p&gt;manager方式：和你自己的内部web console/manager系统进行对接。(目前主要是公司内部使用，Otter采用这种方式)
spring方式：基于spring xml + properties进行定义，构建spring配置.&lt;/p&gt;  &lt;p&gt;下面是canalServer和instance如何运行：&lt;/p&gt;  &lt;pre&gt;    &lt;code&gt;canalServer.setCanalInstanceGenerator(new CanalInstanceGenerator() {      &lt;br /&gt;      &lt;br /&gt;            public CanalInstance generate(String destination) {      &lt;br /&gt;                Canal canal = canalConfigClient.findCanal(destination);      &lt;br /&gt;                // 此处省略部分代码 大致逻辑是设置canal一些属性      &lt;br /&gt;      &lt;br /&gt;                CanalInstanceWithManager instance = new CanalInstanceWithManager(canal, filter) {      &lt;br /&gt;      &lt;br /&gt;                    protected CanalHAController initHaController() {      &lt;br /&gt;                        HAMode haMode = parameters.getHaMode();      &lt;br /&gt;                        if (haMode.isMedia()) {      &lt;br /&gt;                            return new MediaHAController(parameters.getMediaGroup(),      &lt;br /&gt;                                parameters.getDbUsername(),      &lt;br /&gt;                                parameters.getDbPassword(),      &lt;br /&gt;                                parameters.getDefaultDatabaseName());      &lt;br /&gt;                        } else {      &lt;br /&gt;                            return super.initHaController();      &lt;br /&gt;                        }      &lt;br /&gt;                    }      &lt;br /&gt;      &lt;br /&gt;                    protected void startEventParserInternal(CanalEventParser parser, boolean isGroup) {      &lt;br /&gt;                        //大致逻辑是 设置支持的类型      &lt;br /&gt;                        //初始化设置MysqlEventParser的主库信息，这处抽象不好，目前只支持mysql      &lt;br /&gt;                    }      &lt;br /&gt;      &lt;br /&gt;                };      &lt;br /&gt;                return instance;      &lt;br /&gt;            }      &lt;br /&gt;        });      &lt;br /&gt;        canalServer.start(); //启动canalServer      &lt;br /&gt;      &lt;br /&gt;        canalServer.start(destination);//启动对应instance      &lt;br /&gt;        this.clientIdentity = new ClientIdentity(destination, pipeline.getParameters().getMainstemClientId(), filter);      &lt;br /&gt;        canalServer.subscribe(clientIdentity);// 发起一次订阅，当监听到instance配置时，调用generate方法注入新的instance      &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;p&gt;instance模块：&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;eventParser (数据源接入，模拟slave协议和master进行交互，协议解析)&lt;/li&gt;    &lt;li&gt;eventSink (Parser和Store链接器，进行数据过滤，加工，分发的工作)&lt;/li&gt;    &lt;li&gt;eventStore (数据存储)&lt;/li&gt;    &lt;li&gt;metaManager (增量订阅&amp;amp;消费信息管理器)&lt;/li&gt;&lt;/ul&gt;  &lt;h4&gt;EventParser设计&lt;/h4&gt;  &lt;p&gt;大致过程：&lt;/p&gt;  &lt;img src="https://filescdn.proginn.com/d230e190bdf6bedafc28dec70fce5f6e/5a369b870f597237e4d0e38afbdf88ea.webp"&gt;&lt;/img&gt;  &lt;p&gt;整个parser过程大致可分为几步：&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;Connection获取上一次解析成功的位置 (如果第一次启动，则获取初始指定的位置或者是当前数据库的binlog位点)&lt;/li&gt;    &lt;li&gt;Connection建立链接，发送BINLOG_DUMP指令&lt;/li&gt;&lt;/ul&gt;  &lt;pre&gt;    &lt;code&gt;// 0. write command number      &lt;br /&gt;// 1. write 4 bytes bin-log position to start at      &lt;br /&gt;// 2. write 2 bytes bin-log flags      &lt;br /&gt;// 3. write 4 bytes server id of the slave      &lt;br /&gt;// 4. write bin-log file name      &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;  &lt;ul&gt;    &lt;li&gt;Mysql开始推送Binaly Log&lt;/li&gt;    &lt;li&gt;接收到的Binaly Log的通过Binlog parser进行协议解析，补充一些特定信息(补充字段名字，字段类型，主键信息，unsigned类型处理)&lt;/li&gt;    &lt;li&gt;传递给EventSink模块进行数据存储，是一个阻塞操作，直到存储成功&lt;/li&gt;    &lt;li&gt;存储成功后，由CanalLogPositionManager定时记录Binaly Log位置&lt;/li&gt;&lt;/ul&gt;  &lt;h4&gt;EventSink设计&lt;/h4&gt;  &lt;img src="https://filescdn.proginn.com/89e1cadc45ebb42b3f6ebfd04d08ab26/8679b22f6ffdd35e7879b639de3a1bbb.webp"&gt;&lt;/img&gt;  &lt;p&gt;说明：&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;数据过滤：支持通配符的过滤模式，表名，字段内容等&lt;/li&gt;    &lt;li&gt;数据路由/分发：解决1:n (1个parser对应多个store的模式)&lt;/li&gt;    &lt;li&gt;数据归并：解决n:1 (多个parser对应1个store)&lt;/li&gt;    &lt;li&gt;数据加工：在进入store之前进行额外的处理，比如join&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;    &lt;strong&gt;数据1:n业务&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;为了合理的利用数据库资源， 一般常见的业务都是按照schema进行隔离，然后在mysql上层或者dao这一层面上，进行一个数据源路由，屏蔽数据库物理位置对开发的影响，阿里系主要是通过cobar/tddl来解决数据源路由问题。&lt;/p&gt;  &lt;p&gt;所以，一般一个数据库实例上，会部署多个schema，每个schema会有由1个或者多个业务方关注。&lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;数据n:1业务&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;同样，当一个业务的数据规模达到一定的量级后，必然会涉及到水平拆分和垂直拆分的问题，针对这些拆分的数据需要处理时，就需要链接多个store进行处理，消费的位点就会变成多份，而且数据消费的进度无法得到尽可能有序的保证。&lt;/p&gt;  &lt;p&gt;所以，在一定业务场景下，需要将拆分后的增量数据进行归并处理，比如按照时间戳/全局id进行排序归并。&lt;/p&gt;  &lt;h4&gt;EventStore设计&lt;/h4&gt;  &lt;ol&gt;    &lt;li&gt;目前仅实现了Memory内存模式，后续计划增加本地file存储，mixed混合模式。&lt;/li&gt;    &lt;li&gt;借鉴了Disruptor的RingBuffer的实现思路&lt;/li&gt;&lt;/ol&gt;  &lt;p&gt;RingBuffer设计：&lt;/p&gt;  &lt;img src="https://filescdn.proginn.com/c07a36d6234b06daadce941f0cc3e757/3bd0c7600cfb45d05c6826834d40c057.webp"&gt;&lt;/img&gt;  &lt;p&gt;定义了3个cursor&lt;/p&gt;  &lt;p&gt;Put : Sink模块进行数据存储的最后一次写入位置
Get : 数据订阅获取的最后一次提取位置
Ack : 数据消费成功的最后一次消费位置&lt;/p&gt;  &lt;p&gt;借鉴Disruptor的RingBuffer的实现，将RingBuffer拉直来看：&lt;/p&gt;  &lt;img src="https://filescdn.proginn.com/b32902a1df77414a2a5056ef09c64688/139557144a8c05ea6822e49ab70dea1f.webp"&gt;&lt;/img&gt;  &lt;p&gt;实现说明：&lt;/p&gt;  &lt;p&gt;Put/Get/Ack cursor用于递增，采用long型存储buffer的get操作，通过取余或者与操作。(与操作：cusor &amp;amp; (size - 1) , size需要为2的指数，效率比较高)&lt;/p&gt;  &lt;h4&gt;HA机制设计&lt;/h4&gt;  &lt;p&gt;canal的ha分为两部分，canal server和canal client分别有对应的ha实现&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;canal server: 为了减少对mysql dump的请求，不同server上的instance要求同一时间只能有一个处于running，其他的处于standby状态.&lt;/li&gt;    &lt;li&gt;canal client: 为了保证有序性，一份instance同一时间只能由一个canal client进行get/ack/rollback操作，否则客户端接收无法保证有序。&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;整个HA机制的控制主要是依赖了zookeeper的几个特性，watcher和EPHEMERAL节点(和session生命周期绑定)，可以看下我之前zookeeper的相关文章。&lt;/p&gt;  &lt;p&gt;Canal Server:&lt;/p&gt;  &lt;img src="https://filescdn.proginn.com/5a507f75139c733a39ec1ab2ecd34bf9/942851ac3b9c84f0f148e727c6785792.webp"&gt;&lt;/img&gt;  &lt;p&gt;大致步骤：&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;canal server要启动某个canal instance时都先向zookeeper进行一次尝试启动判断 (实现：创建EPHEMERAL节点，谁创建成功就允许谁启动)&lt;/li&gt;    &lt;li&gt;创建zookeeper节点成功后，对应的canal server就启动对应的canal instance，没有创建成功的canal instance就会处于standby状态&lt;/li&gt;    &lt;li&gt;一旦zookeeper发现canal server A创建的节点消失后，立即通知其他的canal server再次进行步骤1的操作，重新选出一个canal server启动instance&lt;/li&gt;    &lt;li&gt;canal client每次进行connect时，会首先向zookeeper询问当前是谁启动了canal instance，然后和其建立链接，一旦链接不可用，会重新尝试connect&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;Canal Client的方式和canal server方式类似，也是利用zookeeper的抢占EPHEMERAL节点的方式进行控制。&lt;/p&gt;  &lt;blockquote&gt;    &lt;p&gt;本文发自微信公众号《import_bigdata》&lt;/p&gt;&lt;/blockquote&gt;  &lt;h3&gt;总结&lt;/h3&gt;  &lt;p&gt;CDC 的技术方案非常多，目前业界主流的实现机制可以分为两种：&lt;/p&gt;  &lt;p&gt;    &lt;strong&gt;基于查询的 CDC：&lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;离线调度查询作业，批处理。把一张表同步到其他系统，每次通过查询去获取表中最新的数据；&lt;/li&gt;    &lt;li&gt;无法保障数据一致性，查的过程中有可能数据已经发生了多次变更；&lt;/li&gt;    &lt;li&gt;不保障实时性，基于离线调度存在天然的延迟。&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;    &lt;strong&gt;基于日志的 CDC：&lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;    &lt;li&gt;实时消费日志，流处理，例如 MySQL 的 binlog 日志完整记录了数据库中的变更，可以把 binlog 文件当作流的数据源；&lt;/li&gt;    &lt;li&gt;保障数据一致性，因为 binlog 文件包含了所有历史变更明细；&lt;/li&gt;    &lt;li&gt;保障实时性，因为类似 binlog 的日志文件是可以流式消费的，提供的是实时数据。&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;对比常见的开源 CDC 方案，我们可以发现：&lt;/p&gt;  &lt;img src="https://filescdn.proginn.com/fe533b3f620f580d4a2650a44258df98/b6dfa94cd1049c800f5d38a79bcc5f65.webp"&gt;&lt;/img&gt;  &lt;ul&gt;    &lt;li&gt;      &lt;p&gt;对比增量同步能力:&lt;/p&gt;      &lt;pre&gt;        &lt;code&gt;- 基于日志的方式，可以很好的做到增量同步；          &lt;br /&gt;- 而基于查询的方式是很难做到增量同步的。          &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;    &lt;li&gt;      &lt;p&gt;对比全量同步能力，基于查询或者日志的 CDC 方案基本都支持，除了 Canal。&lt;/p&gt;&lt;/li&gt;    &lt;li&gt;      &lt;p&gt;而对比全量 + 增量同步的能力，只有 Flink CDC、Debezium、Oracle Goldengate 支持较好。&lt;/p&gt;&lt;/li&gt;    &lt;li&gt;      &lt;p&gt;从架构角度去看，该表将架构分为单机和分布式，这里的分布式架构不单纯体现在数据读取能力的水平扩展上，更重要的是在大数据场景下分布式系统接入能力。例如 Flink CDC 的数据入湖或者入仓的时候，下游通常是分布式的系统，如 Hive、HDFS、Iceberg、Hudi 等，那么从对接入分布式系统能力上看，Flink CDC 的架构能够很好地接入此类系统。&lt;/p&gt;&lt;/li&gt;    &lt;li&gt;      &lt;p&gt;在数据转换 / 数据清洗能力上，当数据进入到 CDC 工具的时候是否能较方便的对数据做一些过滤或者清洗，甚至聚合？&lt;/p&gt;&lt;/li&gt;    &lt;ul&gt;      &lt;li&gt;在 Flink CDC 上操作相当简单，可以通过 Flink SQL 去操作这些数据；&lt;/li&gt;      &lt;li&gt;但是像 DataX、Debezium 等则需要通过脚本或者模板去做，所以用户的使用门槛会比较高。&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;  &lt;p&gt;另外，在生态方面，这里指的是下游的一些数据库或者数据源的支持。Flink CDC 下游有丰富的 Connector，例如写入到 TiDB、MySQL、Pg、HBase、Kafka、ClickHouse 等常见的一些系统，也支持各种自定义 connector。&lt;/p&gt;
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/61862-%E6%95%B0%E6%8D%AE-%E5%90%8C%E6%AD%A5-%E5%B7%A5%E5%85%B7</guid>
      <pubDate>Sun, 31 Oct 2021 12:10:29 CST</pubDate>
    </item>
    <item>
      <title>使用python打造自己的信息收集工具 - FreeBuf网络安全行业门户</title>
      <link>https://itindex.net/detail/61796-python-%E4%BF%A1%E6%81%AF-%E5%B7%A5%E5%85%B7</link>
      <description>&lt;div&gt;    &lt;p&gt;      &lt;strong&gt;介绍：&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;该篇章主要介绍如何编写自己的信息收集工具，主要流程如下：&lt;/p&gt;    &lt;p&gt;1、向bing搜索引擎发起request请求，获取url数据&lt;/p&gt;    &lt;p&gt;2、使用正则表达式对获取的数据进行处理&lt;/p&gt;    &lt;p&gt;3、用多线程，对处理的数据进行二次请求，返回标题等数据&lt;/p&gt;    &lt;p&gt;4、使用openyxl模块，将数据保存为.xlsx格式&lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;请注意：&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;该篇章目的是熟悉python编程，学习python的一些常见模块，在编写程序的过程中会有很多操作和方式方法，望大家能共同加油学到东西。本文仅用于技术讨论与研究，这里使用的技术仅用于学习教育目的，如果列出的技术用于其他任何目标，本站及作者概不负责。&lt;/p&gt;    &lt;p&gt;      &lt;strong&gt;本文涉及到模块有：&lt;/strong&gt;&lt;/p&gt;    &lt;pre&gt;#coding:utf-8        &lt;br /&gt;import requests     #发起request请求      &lt;br /&gt;import urllib3      #处理请求https异常报错问题      &lt;br /&gt;import re       #使用正则表达式对请求到的数据进行处理      &lt;br /&gt;from optparse import OptionParser   #自定义输入参数      &lt;br /&gt;import threading        #多线程模块      &lt;br /&gt;import queue            #多线程辅助模块，使用队列的方式对多线程进行控制      &lt;br /&gt;from bs4 import BeautifulSoup   #与re类似 使用正则表达式对请求到的数据进行处理      &lt;br /&gt;import time,datetime    #获取当前的时间      &lt;br /&gt;from openpyxl import  * #数据处理，将获取到的数据保存在excel文件中&lt;/pre&gt;    &lt;p&gt;      &lt;strong&gt;属性：&lt;/strong&gt;&lt;/p&gt;    &lt;pre&gt;heads = {                       #全局变量  请求头      &lt;br /&gt;&amp;apos;User-Agent&amp;apos;:&amp;apos;Mozilla/5.0 (Windows NT 10.0; Win64; x64)                          AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36&amp;apos;,   #模拟浏览器请求      &lt;br /&gt;&amp;apos;Connection&amp;apos;:&amp;apos;close&amp;apos;,        &lt;br /&gt;&amp;apos;Accept-Encoding&amp;apos;:&amp;apos;gzip, deflate&amp;apos;      &lt;br /&gt;}      &lt;br /&gt;count=1                         #全局变量  用于序号字段      &lt;br /&gt;queueLock = threading.Lock()    #全局变量  使用线程锁处理 线程异常问题      &lt;br /&gt;class DoRun(threading.Thread):  #自定义 多线程运行时使用的类&lt;/pre&gt;    &lt;p&gt;      &lt;strong&gt;方法：&lt;/strong&gt;&lt;/p&gt;    &lt;pre&gt;def get_Input():    #获取search语句 和 page      &lt;br /&gt;def getUrls(search,page):       #构造搜索语句，在bing搜索引擎搜索数据并返回urls      &lt;br /&gt;def req(url):   #对url进行验证，返回numb,url,title,status      &lt;br /&gt;def init_excel(filename):  #创建.xlsx表格，并初始化内容      &lt;br /&gt;def Save_Date(date,filename):   #将数据存储到表格当中      &lt;br /&gt;def run():     #核心代码&lt;/pre&gt;    &lt;p&gt;      &lt;strong&gt;完整代码如下：&lt;/strong&gt;&lt;/p&gt;    &lt;pre&gt;#coding:utf-8        &lt;br /&gt;import requests     #发起request请求      &lt;br /&gt;import urllib3      #处理请求https异常报错问题      &lt;br /&gt;import re       #使用正则表达式对请求到的数据进行处理      &lt;br /&gt;from optparse import OptionParser   #自定义输入参数      &lt;br /&gt;import threading        #多线程模块      &lt;br /&gt;import queue            #多线程辅助模块，使用队列的方式对多线程进行控制      &lt;br /&gt;from bs4 import BeautifulSoup   #与re类似 使用正则表达式对请求到的数据进行处理      &lt;br /&gt;import time,datetime    #获取当前的时间      &lt;br /&gt;from openpyxl import  * #数据处理，将获取到的数据保存在excel文件中      &lt;br /&gt;​      &lt;br /&gt;heads = {                       #全局变量  请求头      &lt;br /&gt;&amp;apos;User-Agent&amp;apos;:&amp;apos;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36&amp;apos;,   #模拟浏览器请求      &lt;br /&gt;&amp;apos;Connection&amp;apos;:&amp;apos;close&amp;apos;,        &lt;br /&gt;&amp;apos;Accept-Encoding&amp;apos;:&amp;apos;gzip, deflate&amp;apos;      &lt;br /&gt;}      &lt;br /&gt;count=1                         #全局变量  用于序号字段      &lt;br /&gt;queueLock = threading.Lock()    #全局变量  使用线程锁处理 线程异常问题      &lt;br /&gt;class DoRun(threading.Thread):  #自定义 多线程运行时使用的类      &lt;br /&gt;def __init__(self,queue,filename):      &lt;br /&gt;threading.Thread.__init__(self)      &lt;br /&gt;self._queue=queue      &lt;br /&gt;self._filename=filename      &lt;br /&gt;def run(self):      &lt;br /&gt;while not self._queue.empty():      &lt;br /&gt;js=req(self._queue.get())      &lt;br /&gt;#print(js)      &lt;br /&gt;queueLock.acquire()      &lt;br /&gt;if(js):      &lt;br /&gt;Save_Date(js,self._filename)      &lt;br /&gt;queueLock.release()              &lt;br /&gt;def init_excel(filename):  #创建.xlsx表格，并初始化内容      &lt;br /&gt;wb=Workbook()      &lt;br /&gt;filename=filename+&amp;quot;.xlsx&amp;quot;      &lt;br /&gt;ws=wb.create_sheet(index=0,title=&amp;quot;域名&amp;quot;)      &lt;br /&gt;head=[&amp;apos;序号&amp;apos;,&amp;apos;域名&amp;apos;,&amp;apos;标题&amp;apos;,&amp;apos;状态&amp;apos;]      &lt;br /&gt;for i in range(0,4):      &lt;br /&gt;ws.cell(1,i+1).value=head[i]      &lt;br /&gt;wb.save(filename)      &lt;br /&gt;def Save_Date(date,filename):   #将数据存储到表格当中      &lt;br /&gt;filename=filename+&amp;quot;.xlsx&amp;quot;      &lt;br /&gt;wb_save=load_workbook(filename)      &lt;br /&gt;ws_save=wb_save.worksheets[0]      &lt;br /&gt;current_row=ws_save.max_row+1      &lt;br /&gt;current_col=1      &lt;br /&gt;for key in date:      &lt;br /&gt;ws_save.cell(date[&amp;apos;numb&amp;apos;]+1,current_col).value=str(date[key])      &lt;br /&gt;current_col+=1      &lt;br /&gt;wb_save.save(filename)      &lt;br /&gt;def req(url):   #对域名进行验证，返回状态码，title      &lt;br /&gt;global count      &lt;br /&gt;dir={&amp;apos;numb&amp;apos;:0,&amp;apos;url&amp;apos;:&amp;apos;url&amp;apos;,&amp;apos;title&amp;apos;:&amp;apos;None&amp;apos;,&amp;apos;status&amp;apos;:0}      &lt;br /&gt;stat=0      &lt;br /&gt;title=&amp;quot;None&amp;quot;      &lt;br /&gt;      &lt;br /&gt;try:      &lt;br /&gt;urllib3.disable_warnings()      &lt;br /&gt;response = requests.get(url=url,headers=heads,verify=False,timeout=10)   #请求漏洞的url      &lt;br /&gt;if response.status_code == 200:      &lt;br /&gt;bs=BeautifulSoup(response.content,&amp;quot;html.parser&amp;quot;)      &lt;br /&gt;title=bs.find(&amp;quot;title&amp;quot;).text      &lt;br /&gt;stat=response.status_code      &lt;br /&gt;dir[&amp;apos;numb&amp;apos;]=count      &lt;br /&gt;dir[&amp;apos;url&amp;apos;]=url      &lt;br /&gt;dir[&amp;apos;title&amp;apos;]=title      &lt;br /&gt;dir[&amp;apos;status&amp;apos;]=stat      &lt;br /&gt;count+=1      &lt;br /&gt;print(&amp;quot;[+]&amp;quot;+url+&amp;quot;\ttitle:&amp;quot;+title)      &lt;br /&gt;return dir      &lt;br /&gt;else:      &lt;br /&gt;print(&amp;apos;[-]请求失败：\t{}\t{}&amp;apos;.format(url,response.status_code))      &lt;br /&gt;except Exception as e:      &lt;br /&gt;print(&amp;apos;[-]请求失败: {}\t&amp;apos;.format(e,url))      &lt;br /&gt;def getUrls(search,page):       #构造搜索语句，在bing搜索引擎 搜索数据并返回urls      &lt;br /&gt;count=1      &lt;br /&gt;urls=[]      &lt;br /&gt;url=&amp;quot;https://cn.bing.com/search?q={}&amp;amp;first={}&amp;quot;      &lt;br /&gt;for i in range(1,page):      &lt;br /&gt;if(i!=1):      &lt;br /&gt;count=(i-2)*10+9      &lt;br /&gt;url=url.format(search,i)      &lt;br /&gt;try:      &lt;br /&gt;resp=requests.get(url=url,headers=heads)      &lt;br /&gt;html=resp.text      &lt;br /&gt;if(resp.status_code==200):      &lt;br /&gt;res=re.findall(r&amp;apos;&amp;lt;a target=&amp;quot;_blank&amp;quot; href=&amp;quot;(.*?)&amp;quot;&amp;apos;,html)      &lt;br /&gt;for u in res:      &lt;br /&gt;if(u not in urls):      &lt;br /&gt;urls.append(u)      &lt;br /&gt;else:      &lt;br /&gt;print(&amp;apos;[-]请求失败：\t{}\t{}&amp;apos;.format(url,resp.status_code))      &lt;br /&gt;except Exception as e:      &lt;br /&gt;print(&amp;apos;[-]请求失败: {}\t&amp;apos;.format(e,url))      &lt;br /&gt;​      &lt;br /&gt;return urls      &lt;br /&gt;def get_Input():    #获取search语句 和 page      &lt;br /&gt;optParser = OptionParser()      &lt;br /&gt;optParser.add_option(&amp;apos;-s&amp;apos;,&amp;apos;--search&amp;apos;,action = &amp;apos;store&amp;apos;,type = &amp;quot;string&amp;quot; ,dest = &amp;apos;search&amp;apos;,help=&amp;apos;漏扫文件的目录&amp;apos;,default=&amp;quot;search_def&amp;quot;)      &lt;br /&gt;optParser.add_option(&amp;quot;-p&amp;quot;,&amp;quot;--page&amp;quot;, action=&amp;quot;store&amp;quot;, type=&amp;quot;int&amp;quot;,dest=&amp;quot;page&amp;quot;,help=&amp;apos;要搜索的页数&amp;apos;,default=10)                  &lt;br /&gt;optParser.add_option(&amp;quot;-t&amp;quot;,&amp;quot;--threads&amp;quot;, action=&amp;quot;store&amp;quot;, type=&amp;quot;int&amp;quot;,dest=&amp;quot;threads&amp;quot;,help=&amp;apos;线程数量，默认为10&amp;apos;,default=10)                  &lt;br /&gt;(options , args) = optParser.parse_args()      &lt;br /&gt;return options.search,options.page,options.threads      &lt;br /&gt;def run():                &lt;br /&gt;que=queue.Queue()      &lt;br /&gt;print(datetime.datetime.now())              #打印开始时间      &lt;br /&gt;search,page,thread_count=get_Input()        #获取输入的参数 如searce 、线程数、页面数      &lt;br /&gt;if(search==&amp;quot;search_def&amp;quot;):      &lt;br /&gt;print(r&amp;quot;[-]错误，未输入指定参数：python3 temp.py -s site:qq.com [-p 10] [-t 20] &amp;quot;)      &lt;br /&gt;return      &lt;br /&gt;print(search)      &lt;br /&gt;threads=[]      &lt;br /&gt;urls=getUrls(search,page)     #获取urls      &lt;br /&gt;filename=&amp;apos;&amp;apos;.join(re.findall(&amp;quot;([a-z,0-9])&amp;quot;,search))      #将输入的内容进行处理 ，作为文件的名称      &lt;br /&gt;init_excel(filename)        #创建并初始化excel        &lt;br /&gt;for url in urls:      &lt;br /&gt;que.put(url)            #将获取的urls添加到queue中        &lt;br /&gt;for i in range(thread_count):      &lt;br /&gt;threads.append(DoRun(que,filename))     #使用多线程 默认调用 run()函数      &lt;br /&gt;for i in threads:      &lt;br /&gt;i.start()               #启动多线程      &lt;br /&gt;for i in threads:      &lt;br /&gt;i.join()                #等待线程结束      &lt;br /&gt;​      &lt;br /&gt;print(datetime.datetime.now())      #打印结束时间      &lt;br /&gt;run()&lt;/pre&gt;    &lt;p&gt;      &lt;strong&gt;使用说明：*&lt;/strong&gt;***&lt;/p&gt;    &lt;pre&gt;python3 .\bingying.py -s &amp;quot;site:.com&amp;quot; -p 10 -t 30      &lt;br /&gt;Options:      &lt;br /&gt;-h, --help  show this help message and exit      &lt;br /&gt;-s SEARCH, --search=SEARCH    搜索的语法(默认 site:.com)      &lt;br /&gt;-p PAGE, --page=PAGE          要搜索的页数（一页10条数据，默认10页）      &lt;br /&gt;-t THREADS, --threads=THREADS 线程数量，(默认为10)&lt;/pre&gt;    &lt;p&gt;      &lt;strong&gt;结果呈现&lt;/strong&gt;&lt;/p&gt;    &lt;p&gt;      &lt;img alt="1632291957_614acc75cf719787794df.png!small" src="https://image.3001.net/images/20210922/1632291957_614acc75cf719787794df.png!small"&gt;&lt;/img&gt;&lt;/p&gt;&lt;/div&gt;
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/61796-python-%E4%BF%A1%E6%81%AF-%E5%B7%A5%E5%85%B7</guid>
      <pubDate>Thu, 23 Sep 2021 17:36:12 CST</pubDate>
    </item>
    <item>
      <title>[译] Kubernetes 必备工具：2021</title>
      <link>https://itindex.net/detail/61617-kubernetes-%E5%B7%A5%E5%85%B7</link>
      <description>&lt;div&gt;  &lt;img title="null"&gt;&lt;/img&gt;tools  &lt;p&gt;有别于前些天的文章 -    &lt;a href="https://mp.weixin.qq.com/s?__biz=MjM5OTg2MTM0MQ==&amp;mid=2247484352&amp;idx=1&amp;sn=352f0683e6c2f61e7336eaf5363ab011&amp;scene=21#wechat_redirect" title="&amp;#24120;&amp;#29992;&amp;#30340;&amp;#20960;&amp;#27454;&amp;#24037;&amp;#20855;&amp;#35753; Kubernetes &amp;#38598;&amp;#32676;&amp;#19978;&amp;#30340;&amp;#24037;&amp;#20316;&amp;#26356;&amp;#23481;&amp;#26131;"&gt;常用的几款工具让 Kubernetes 集群上的工作更容易&lt;/a&gt; 偏重于工具类来提升工作效率，今天这篇文章更加适合用来做选型时的参考。&lt;/p&gt;  &lt;p&gt;文档翻译自 Kubernetes Essential Tools: 2021   &lt;sup&gt;[1]&lt;/sup&gt;，篇幅较长，做了部分增删。&lt;/p&gt;  &lt;hr&gt;&lt;/hr&gt;  &lt;h2&gt;介绍&lt;/h2&gt;  &lt;p&gt;在本文中，我将尝试总结我最喜欢的 Kubernetes   &lt;sup&gt;[2]&lt;/sup&gt; 工具，并特别强调最新的和鲜为人知但我认为会非常流行的工具。&lt;/p&gt;  &lt;p&gt;这只是我根据我的经验得出的个人清单，但为了避免偏见，我还将尝试提及每种工具的替代方案，以便你可以根据自己的需要进行比较和决定。我将尽可能缩短这篇文章并提供链接，以便你可以自行探索更多内容。我的目标是回答这个问题：“我如何在 Kubernetes 中做 X？” 通过描述不同软件开发任务的工具。&lt;/p&gt;  &lt;h2&gt;K3D&lt;/h2&gt;  &lt;p&gt;K3D   &lt;sup&gt;[3]&lt;/sup&gt; 是我最喜欢的在笔记本电脑上运行 Kubernetes (K8s) 集群的方式。它非常   &lt;strong&gt;轻巧且&lt;/strong&gt;速度非常快。它是使用    &lt;strong&gt;Docker&lt;/strong&gt; 围绕 K3S   &lt;sup&gt;[4]&lt;/sup&gt; 的包装器。所以，你只需要 Docker 来运行它并且资源使用率非常低。唯一的问题是   &lt;strong&gt;它不完全符合 K8s 标准&lt;/strong&gt;，但这不应该是本地开发的问题。对于测试环境，你可以使用其他解决方案。K3D 比 Kind 快，但 Kind 完全兼容。&lt;/p&gt;  &lt;h3&gt;备选&lt;/h3&gt;  &lt;p&gt;•   &lt;strong&gt;K3S&lt;/strong&gt;   &lt;sup&gt;[5]&lt;/sup&gt; 物联网或者边缘计算•   &lt;strong&gt;Kind&lt;/strong&gt;   &lt;sup&gt;[6]&lt;/sup&gt; 完全兼容 Kubernetes 的备选•   &lt;strong&gt;MicroK8s&lt;/strong&gt;   &lt;sup&gt;[7]&lt;/sup&gt;•   &lt;strong&gt;MiniKube&lt;/strong&gt;   &lt;sup&gt;[8]&lt;/sup&gt;&lt;/p&gt;  &lt;h2&gt;Krew&lt;/h2&gt;  &lt;p&gt;Krew   &lt;sup&gt;[9]&lt;/sup&gt; 是管理的必备工具    &lt;strong&gt;Kubectl 插件&lt;/strong&gt;，这是一个必须有任何 K8S 用户。我不会详细介绍超过 145 个可用插件   &lt;sup&gt;[10]&lt;/sup&gt;，但至少安装    &lt;strong&gt;kubens&lt;/strong&gt;   &lt;sup&gt;[11]&lt;/sup&gt; 和    &lt;strong&gt;kubectx&lt;/strong&gt;   &lt;sup&gt;[12]&lt;/sup&gt;。&lt;/p&gt;  &lt;h2&gt;Lens&lt;/h2&gt;  &lt;p&gt;Lens   &lt;sup&gt;[13]&lt;/sup&gt; 是适用于 SRE、Ops 和开发人员的 K8s    &lt;strong&gt;IDE&lt;/strong&gt;。它适用于任何 Kubernetes 发行版：本地或云端。它快速、易于使用并提供实时可观察性。使用 Lens 可以非常轻松地管理多个集群。如果你是集群操作员，这是必须的。&lt;/p&gt;  &lt;img title="null"&gt;&lt;/img&gt;  &lt;h3&gt;备选&lt;/h3&gt;  &lt;p&gt;•对于那些喜欢轻量级终端替代品的人来说，K9s   &lt;sup&gt;[14]&lt;/sup&gt; 是一个很好的选择。K9s 会持续观察 Kubernetes 的变化，并提供后续命令来与你观察到的资源进行交互。&lt;/p&gt;  &lt;h2&gt;Helm&lt;/h2&gt;  &lt;p&gt;Helm   &lt;sup&gt;[15]&lt;/sup&gt; 不需要介绍，它是 Kubernetes 最著名的包管理器。你应该在 K8s 中使用包管理器，就像在编程语言中使用它一样。Helm 允许你将应用程序打包到 Charts   &lt;sup&gt;[16]&lt;/sup&gt; 中，将复杂的应用程序抽象为易于定义、安装和更新的可重用简单组件。&lt;/p&gt;  &lt;p&gt;它还提供了强大的模板引擎。Helm 很成熟，有很多预定义的 charts，很好的支持，而且很容易使用。&lt;/p&gt;  &lt;h3&gt;备选&lt;/h3&gt;  &lt;p&gt;•   &lt;strong&gt;Kustomize&lt;/strong&gt;   &lt;sup&gt;[17]&lt;/sup&gt; 是 helm 的一个更新和伟大的替代品，它不使用模板引擎，而是一个覆盖引擎，在其中你有基本的定义和覆盖在它们之上。&lt;/p&gt;  &lt;h2&gt;ArgoCD&lt;/h2&gt;  &lt;p&gt;我相信 GitOps   &lt;sup&gt;[18]&lt;/sup&gt; 是过去十年中最好的想法之一。在软件开发中，我们应该使用单一的事实来源来跟踪构建软件所需的所有移动部分，而    &lt;strong&gt;Git&lt;/strong&gt; 是做到这一点的完美工具。我们的想法是拥有一个 Git 存储库，其中包含应用程序代码以及表示所需生产环境状态的基础设施 (IaC   &lt;sup&gt;[19]&lt;/sup&gt;) 的声明性描述；以及使所需环境与存储库中描述的状态相匹配的自动化过程。&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;GitOps: versioned CI/CD on top of declarative infrastructure. Stop scripting and start shipping.&lt;/p&gt;   &lt;p&gt;— Kelsey Hightower&lt;/p&gt;&lt;/blockquote&gt;  &lt;p&gt;尽管使用 Terraform   &lt;sup&gt;[20]&lt;/sup&gt; 或类似工具，你可以实现基础设施即代码（IaC   &lt;sup&gt;[21]&lt;/sup&gt;），但这还不足以将你在 Git 中的所需状态与生产同步。我们需要一种方法来持续监控环境并确保没有配置漂移。使用 Terraform，你将不得不编写脚本来运行   &lt;code&gt;terraform apply&lt;/code&gt;并检查状态是否与 Terraform 状态匹配，但这既乏味又难以维护。&lt;/p&gt;  &lt;p&gt;Kubernetes 从头开始构建控制循环的思想，这意味着 Kubernetes 一直在监视集群的状态以确保它与所需的状态匹配，例如，运行的副本数量与所需的数量相匹配复制品。GitOps 的想法是将其扩展到应用程序，因此你可以将你的服务定义为代码，例如，通过定义 Helm Charts，并使用利用 K8s 功能的工具来监控你的应用程序的状态并相应地调整集群。也就是说，如果更新你的代码存储库或Helm Chart，生产集群也会更新。这是真正的持续部署   &lt;sup&gt;[22]&lt;/sup&gt;。核心原则是应用程序部署和生命周期管理应该自动化、可审计且易于理解。&lt;/p&gt;  &lt;p&gt;对我来说，这个想法是革命性的，如果做得好，将使组织能够更多地关注功能，而不是编写自动化脚本。这个概念可以扩展到软件开发的其他领域，例如，你可以将文档存储在代码中以跟踪更改的历史并确保文档是最新的；或使用ADR   &lt;sup&gt;[23]&lt;/sup&gt;跟踪架构决策。&lt;/p&gt;  &lt;p&gt;在我看来，在最好的 GitOps 工具    &lt;strong&gt;Kubernetes&lt;/strong&gt; 是 ArgoCD   &lt;sup&gt;[24]&lt;/sup&gt;。你可以在此处   &lt;sup&gt;[25]&lt;/sup&gt;阅读更多信息。ArgoCD 是    &lt;strong&gt;Argo&lt;/strong&gt; 生态系统的一部分，其中包括一些其他很棒的工具，其中一些我们将在稍后讨论。&lt;/p&gt;  &lt;p&gt;使用    &lt;strong&gt;ArgoCD&lt;/strong&gt;，你可以在代码存储库中拥有每个环境，你可以在其中定义该环境的所有配置。Argo CD 在指定的目标环境中自动部署所需的应用程序状态。&lt;/p&gt;  &lt;img title="null"&gt;&lt;/img&gt;  &lt;p&gt;ArgoCD 被实现为一个 kubernetes 控制器，它持续监控正在运行的应用程序并将当前的实时状态与所需的目标状态（如 Git 存储库中指定的）进行比较。ArgoCD 报告并可视化差异，并且可以自动或手动将实时状态同步回所需的目标状态。&lt;/p&gt;  &lt;h3&gt;备选&lt;/h3&gt;  &lt;p&gt;•Flux   &lt;sup&gt;[26]&lt;/sup&gt; 刚刚发布了一个具有许多改进的新版本。它提供了非常相似的功能。&lt;/p&gt;  &lt;h2&gt;Argo 工作流（Workflows）和 Argo 事件（Events）&lt;/h2&gt;  &lt;p&gt;在 Kubernetes 中，你可能还需要运行批处理作业或复杂的工作流。这可能是你的数据管道、异步流程甚至 CI/CD 的一部分。最重要的是，你甚至可能需要运行对某些事件做出反应的驱动微服务，例如文件上传或消息发送到队列。对于所有这些，我们有 Argo Workflows   &lt;sup&gt;[27]&lt;/sup&gt; 和 Argo Events   &lt;sup&gt;[28]&lt;/sup&gt;。&lt;/p&gt;  &lt;p&gt;尽管它们是独立的项目，但它们往往会被部署在一起。&lt;/p&gt;  &lt;p&gt;Argo Workflows 是一个类似于 Apache Airflow   &lt;sup&gt;[29]&lt;/sup&gt; 的编排引擎，但它是 Kubernetes 原生的。它使用自定义 CRD 来定义复杂的工作流程，使用 YAML 的步骤或    &lt;strong&gt;DAG&lt;/strong&gt;，这在 K8s 中感觉更自然。它有一个漂亮的 UI、重试机制、基于 cron 的作业、输入和输出跟踪等等。你可以使用它来编排数据管道、批处理作业等等。&lt;/p&gt;  &lt;p&gt;有时，你可能希望将管道与异步服务（如    &lt;strong&gt;Kafka&lt;/strong&gt; 等流引擎、队列、webhooks 或深度存储服务）集成。例如，你可能想要对上传到 S3 的文件等事件做出反应。为此，你将使用 Argo 事件（Event）   &lt;sup&gt;[30]&lt;/sup&gt;。&lt;/p&gt;  &lt;img title="null"&gt;&lt;/img&gt;  &lt;p&gt;这两个工具组合为你的所有管道需求提供了一个简单而强大的解决方案，包括 CI/CD 管道，它允许你在 Kubernetes 中本地运行 CI/CD 管道。&lt;/p&gt;  &lt;h3&gt;备选&lt;/h3&gt;  &lt;p&gt;•对于 ML 管道，你可以使用 Kubeflow   &lt;sup&gt;[31]&lt;/sup&gt;。•对于 CI/CD 管道，你可以使用 Tekton   &lt;sup&gt;[32]&lt;/sup&gt;。&lt;/p&gt;  &lt;h2&gt;Kaniko&lt;/h2&gt;  &lt;p&gt;我们刚刚看到了如何使用 Argo Workflows 运行 Kubernetes 原生    &lt;strong&gt;CI/CD&lt;/strong&gt; 管道。一个常见的任务是构建    &lt;strong&gt;Docker 镜像&lt;/strong&gt;，这在 Kubernetes 中通常是乏味的，因为构建过程实际上是在容器本身上运行的，你需要使用变通方法来使用主机的 Docker 引擎。&lt;/p&gt;  &lt;img title="null"&gt;&lt;/img&gt;  &lt;p&gt;底线是你不应该使用    &lt;strong&gt;Docker&lt;/strong&gt; 来构建你的镜像：改用    &lt;strong&gt;Kanico&lt;/strong&gt;   &lt;sup&gt;[33]&lt;/sup&gt;。Kaniko 不依赖于 Docker 守护进程，而是完全在用户空间中执行 Dockerfile 中的每个命令。这使得在无法轻松或安全地运行 Docker 守护程序的环境中构建容器镜像成为可能，例如标准的 Kubernetes 集群。这消除了在 K8s 集群中构建镜像的所有问题。&lt;/p&gt;  &lt;h2&gt;Istio&lt;/h2&gt;  &lt;p&gt;Istio   &lt;sup&gt;[34]&lt;/sup&gt; 是市场上最著名的服务网格   &lt;sup&gt;[35]&lt;/sup&gt;，它是开源的并且非常受欢迎。我不会详细介绍什么是服务网格，因为它是一个巨大的话题，但是如果你正在构建微服务   &lt;sup&gt;[36]&lt;/sup&gt;，并且可能应该这样做，那么你将需要一个服务网格来管理通信、可观察性、错误处理、安全性。与其用重复的逻辑污染每个微服务的代码（译者：SDK 侵入），不如利用服务网格为你做这件事。&lt;/p&gt;  &lt;img title="null"&gt;&lt;/img&gt;  &lt;p&gt;简而言之，服务网格是一个专用的基础设施层，你可以将其添加到你的应用程序中。它允许你透明地添加可观察性、流量管理和安全性等功能，而无需将它们添加到你自己的代码中。&lt;/p&gt;  &lt;p&gt;如果    &lt;strong&gt;Istio&lt;/strong&gt; 用于运行微服务，尽管你可以在任何地方运行 Istio 并使用微服务，但 Kubernetes 已被一次又一次地证明是运行它们的最佳平台。   &lt;strong&gt;Istio&lt;/strong&gt; 还可以将你的 K8s 集群扩展到其他服务，例如 VM，允许你拥有在迁移到 Kubernetes 时非常有用的混合环境。&lt;/p&gt;  &lt;h3&gt;备选&lt;/h3&gt;  &lt;p&gt;•   &lt;strong&gt;Linkerd&lt;/strong&gt;   &lt;sup&gt;[37]&lt;/sup&gt; 是一种更轻巧且可能更快的服务网格。Linkerd 从头开始为安全性而构建，包括默认 mTLS   &lt;sup&gt;[38]&lt;/sup&gt;、使用 Rust 构建的数据平面   &lt;sup&gt;[39]&lt;/sup&gt;、内存安全语言   &lt;sup&gt;[40]&lt;/sup&gt;和定期安全审计   &lt;sup&gt;[41]&lt;/sup&gt;等功能•   &lt;strong&gt;Consul&lt;/strong&gt;   &lt;sup&gt;[42]&lt;/sup&gt; 是为任何运行时和云提供商构建的服务网格，因此它非常适合跨 K8s 和云提供商的混合部署。如果不是所有的工作负载都在 Kubernetes 上运行，这是一个不错的选择。&lt;/p&gt;  &lt;h2&gt;Argo Rollouts&lt;/h2&gt;  &lt;p&gt;我们已经提到，你可以使用 Kubernetes 使用 Argo Workflows 或使用 Kanico 构建图像的类似工具来运行 CI/CD 管道。下一个合乎逻辑的步骤是继续并进行持续部署。由于涉及高风险，这在真实场景中是极具挑战性的，这就是为什么大多数公司只做持续交付，这意味着他们已经实现了自动化，但他们仍然需要手动批准和验证，这个手动步骤是这是因为团队   &lt;strong&gt;不能完全信任他们的自动化&lt;/strong&gt;。&lt;/p&gt;  &lt;p&gt;那么，你如何建立这种信任以摆脱所有脚本并完全自动化从源代码到生产的所有内容？答案是：可观察性。你需要将资源更多地集中在指标上，并收集准确表示应用程序状态所需的所有数据。目标是使用一组指标来建立这种信任。如果你在 Prometheus   &lt;sup&gt;[43]&lt;/sup&gt; 中拥有所有数据，那么你可以自动部署，因为你可以根据这些指标自动逐步推出应用程序。&lt;/p&gt;  &lt;p&gt;简而言之，你需要比 K8s 开箱即用的   &lt;strong&gt;滚动更新&lt;/strong&gt;   &lt;sup&gt;[44]&lt;/sup&gt;更高级的部署技术。我们需要使用金丝雀部署   &lt;sup&gt;[45]&lt;/sup&gt;进行渐进式交付。目标是逐步将流量路由到应用程序的新版本，等待收集指标，分析它们并将它们与预定义的规则进行匹配。如果一切正常，我们增加流量；如果有任何问题，我们会回滚部署。要在 Kubernetes 中执行此操作，你可以使用提供 Canary 发布等的 Argo Rollouts 。&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;Argo Rollouts    &lt;sup&gt;[46]&lt;/sup&gt; 是一个 Kubernetes 控制器    &lt;sup&gt;[47]&lt;/sup&gt;和一组 CRD    &lt;sup&gt;[48]&lt;/sup&gt;，可提供高级部署功能，例如蓝绿、金丝雀、金丝雀分析、实验和向 Kubernetes 的渐进式交付功能。&lt;/p&gt;&lt;/blockquote&gt;  &lt;p&gt;尽管像 Istio   &lt;sup&gt;[49]&lt;/sup&gt; 这样的服务网格提供 Canary 发布，但 Argo Rollouts 使这个过程变得更加容易并且以开发人员为中心，因为它是专门为此目的而构建的。除此之外，Argo Rollouts 可以与任何服务网格集成。&lt;/p&gt;  &lt;p&gt;Argo Rollouts 功能：&lt;/p&gt;  &lt;p&gt;•蓝绿更新策略•金丝雀更新策略•细粒度、加权的流量转移•自动回滚和促销或人工判断•可定制的指标查询和业务 KPI 分析•入口控制器集成：NGINX、ALB•服务网格集成：Istio、Linkerd、SMI•指标提供者集成：Prometheus、Wavefront、Kayenta、Web、Kubernetes Jobs&lt;/p&gt;  &lt;h3&gt;备选&lt;/h3&gt;  &lt;p&gt;•Istio   &lt;sup&gt;[50]&lt;/sup&gt; 作为 Canary 版本的服务网格。Istio 不仅仅是一个渐进式交付工具，它还是一个完整的服务网格。Istio 不会自动部署，Argo Rollouts 可以与 Istio 集成来实现这一点。•Flagger   &lt;sup&gt;[51]&lt;/sup&gt; 与 Argo Rollouts 非常相似，并且与 Flux   &lt;sup&gt;[52]&lt;/sup&gt; 很好地集成在一起，因此如果你使用 Flux，请考虑使用 Flagger。•Spinnaker   &lt;sup&gt;[53]&lt;/sup&gt; 是 Kubernetes 的第一个持续交付工具，它具有许多功能，但使用和设置起来有点复杂。&lt;/p&gt;  &lt;h2&gt;Crossplane&lt;/h2&gt;  &lt;p&gt;   &lt;strong&gt;Crossplane&lt;/strong&gt;   &lt;sup&gt;[54]&lt;/sup&gt; 是我最喜欢的 K8s 工具，我对这个项目感到非常兴奋，因为它给 Kubernetes 带来了一个关键的缺失部分：像管理 K8s 资源一样管理第三方服务。这意味着，你可以使用    &lt;strong&gt;YAML&lt;/strong&gt; 中定义的 K8s 资源来配置云提供商数据库，例如    &lt;strong&gt;AWS RDS&lt;/strong&gt;   &lt;sup&gt;[55]&lt;/sup&gt; 或    &lt;strong&gt;GCP Cloud SQL&lt;/strong&gt;，就像你在 K8s 中配置数据库一样。&lt;/p&gt;  &lt;img title="null"&gt;&lt;/img&gt;  &lt;p&gt;使用 Crossplane，无需使用不同的工具和方法分离基础设施和代码。   &lt;strong&gt;你可以使用 K8s 资源定义一切&lt;/strong&gt;。这样，你就无需学习 Terraform   &lt;sup&gt;[56]&lt;/sup&gt; 等新工具并将它们分开保存。&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;Crossplane 是一个开源 Kubernetes 附加组件，它使平台团队能够组装来自多个供应商的基础设施，并公开更高级别的自助 API 供应用程序团队使用，而无需编写任何代码。&lt;/p&gt;&lt;/blockquote&gt;  &lt;p&gt;   &lt;strong&gt;Crossplane&lt;/strong&gt; 扩展了你的 Kubernetes 集群，为你提供适用于任何基础架构或托管云服务的    &lt;strong&gt;CRD&lt;/strong&gt;。此外，它允许你完全实现持续部署，因为与 Terraform 等其他工具相反，Crossplane 使用现有的 K8s 功能（例如控制循环）来持续观察你的集群并自动检测任何对其起作用的配置漂移。例如，如果你定义了一个托管数据库实例并且有人手动更改它，Crossplane 将自动检测问题并将其设置回以前的值。这将实施基础设施即代码和 GitOps 原则。Crossplane 与 ArgoCD 配合使用效果很好，它可以查看源代码并确保你的代码存储库是唯一的真实来源，并且代码中的任何更改都会传播到集群以及外部云服务。如果没有 Crossplane，你只能在 K8s 服务中实现 GitOps，而不能在不使用单独进程的情况下在云服务中实现，现在你可以做到这一点，这太棒了。&lt;/p&gt;  &lt;h3&gt;备选&lt;/h3&gt;  &lt;p&gt;•Terraform   &lt;sup&gt;[57]&lt;/sup&gt; 是最著名的 IaC 工具，但它不是 K8s 原生的，需要新技能并且不会自动监视配置漂移。•Pulumi   &lt;sup&gt;[58]&lt;/sup&gt; 是一种 Terraform 替代品，它使用开发人员可以测试和理解的编程语言工作。&lt;/p&gt;  &lt;h2&gt;Knative&lt;/h2&gt;  &lt;p&gt;如果你在云中开发应用程序，你可能已经使用了一些无服务器技术，例如 AWS Lambda    &lt;sup&gt;[59]&lt;/sup&gt;，它是一种称为 FaaS   &lt;sup&gt;[60]&lt;/sup&gt; 的事件驱动范例。&lt;/p&gt;  &lt;p&gt;我过去已经讨论过 Serverless   &lt;sup&gt;[61]&lt;/sup&gt;，因此请查看我之前的文章   &lt;sup&gt;[62]&lt;/sup&gt;以了解更多信息。Serverless 的问题在于它与云提供商紧密耦合，因为提供商可以为事件驱动的应用程序创建一个很好的生态系统。&lt;/p&gt;  &lt;p&gt;对于 Kubernetes，如果你希望将函数作为代码运行并使用事件驱动架构，那么你最好的选择是 Knative   &lt;sup&gt;[63]&lt;/sup&gt;。Knative 旨在在 Kubernetes 上运行函数，在 Pod 之上创建一个抽象。&lt;/p&gt;  &lt;p&gt;特点：&lt;/p&gt;  &lt;p&gt;•针对常见应用程序用例的具有更高级别抽象的重点 API。•在几秒钟内建立一个可扩展、安全、无状态的服务。•松散耦合的功能让你可以使用所需的部分。•可插拔组件让你可以使用自己的日志记录和监控、网络和服务网格。•Knative 是可移植的：在 Kubernetes 运行的任何地方运行它，不用担心供应商锁定。•惯用的开发者体验，支持 GitOps、DockerOps、ManualOps 等常用模式。•Knative 可以与常见的工具和框架一起使用，例如 Django、Ruby on Rails、Spring 等等。&lt;/p&gt;  &lt;h3&gt;备选&lt;/h3&gt;  &lt;p&gt;•Argo Events   &lt;sup&gt;[64]&lt;/sup&gt; 为 Kubernetes 提供了一个事件驱动的工作流引擎，可以与 AWS Lambda 等云引擎集成。它不是 FaaS，而是为 Kubernetes 提供了一个事件驱动的架构。•OpenFaas   &lt;sup&gt;[65]&lt;/sup&gt;&lt;/p&gt;  &lt;h2&gt;Kyverno&lt;/h2&gt;  &lt;p&gt;Kubernetes 提供了极大的灵活性，以赋予敏捷的自治团队权力，但能力越大，责任越大。必须有一组   &lt;strong&gt;最佳实践和规则&lt;/strong&gt;，以确保以一致且有凝聚力的方式来部署和管理符合公司政策和安全要求的工作负载。&lt;/p&gt;  &lt;p&gt;有几种工具可以实现这一点，但没有一个是 Kubernetes 原生的…… 直到现在。Kyverno   &lt;sup&gt;[66]&lt;/sup&gt; 是为 Kubernetes 设计的策略引擎，策略作为 Kubernetes 资源进行管理，并且不需要新的语言来编写策略。Kyverno 策略可以验证、改变和生成 Kubernetes 资源。&lt;/p&gt;  &lt;img title="null"&gt;&lt;/img&gt;  &lt;p&gt;你可以应用有关最佳实践、网络或安全性的任何类型的策略。例如，你可以强制所有服务都有标签或所有容器都以非 root 身份运行。你可以在此处   &lt;sup&gt;[67]&lt;/sup&gt;查看一些政策示例。策略可以应用于整个集群或给定的命名空间。你还可以选择是只想审核策略还是强制执行它们以阻止用户部署资源。&lt;/p&gt;  &lt;h3&gt;备选&lt;/h3&gt;  &lt;p&gt;•Open Policy Agent   &lt;sup&gt;[68]&lt;/sup&gt; 是著名的云原生基于策略的控制引擎。它使用自己的声明性语言，并且可以在许多环境中运行，而不仅仅是在 Kubernetes 上。它比 Kyverno   &lt;sup&gt;[69]&lt;/sup&gt; 更难管理，但更强大。&lt;/p&gt;  &lt;h2&gt;Kubevela&lt;/h2&gt;  &lt;p&gt;Kubernetes 的一个问题是开发人员需要非常了解和理解平台和集群配置。许多人会争辩说    &lt;strong&gt;K8s 的抽象级别太低&lt;/strong&gt;，这会给只想专注于编写和交付应用程序的开发人员带来很多摩擦。&lt;/p&gt;  &lt;p&gt;在开放式应用程序模型（OAM   &lt;sup&gt;[70]&lt;/sup&gt;）的设立是为了克服这个问题。这个想法是围绕应用程序创建更高级别的抽象，它独立于底层运行时。你可以在此处阅读规范。&lt;/p&gt;  &lt;blockquote&gt;   &lt;p&gt;专注于应用程序而不是容器或协调器，开放应用程序模型 [OAM] 带来了模块化、可扩展和可移植的设计，用于使用更高级别但一致的 API 对应用程序部署进行建模。&lt;/p&gt;&lt;/blockquote&gt;  &lt;p&gt;Kubevela   &lt;sup&gt;[71]&lt;/sup&gt; 是 OAM 模型的一个实现。KubeVela 与运行时无关，可本地扩展，但最重要的是，以应用程序为中心 。在 Kubevela 中，应用程序是作为 Kubernetes 资源实现的一等公民。   &lt;strong&gt;集群运营商（Platform Team）和开发者（Application Team）&lt;/strong&gt;是有区别的。集群操作员通过定义组件（组成应用程序的可部署/可配置实体，如 Helm Chart）和特征来管理集群和不同的环境。开发人员通过组装组件和特征来定义应用程序。&lt;/p&gt;  &lt;img title="null"&gt;&lt;/img&gt;  &lt;p&gt;KubeVela 是一个云原生计算基金会   &lt;sup&gt;[72]&lt;/sup&gt;沙箱项目，虽然它仍处于起步阶段，但它可以在不久的将来改变我们使用 Kubernetes 的方式，让开发人员无需成为 Kubernetes 专家即可专注于应用程序。但是，我确实对    &lt;strong&gt;OAM&lt;/strong&gt; 在现实世界中的适用性有一些担忧，因为系统应用程序、ML 或大数据过程等一些服务在很大程度上依赖于低级细节，这些细节可能很难融入 OAM 模型中。&lt;/p&gt;  &lt;h3&gt;备选&lt;/h3&gt;  &lt;p&gt;•Shipa   &lt;sup&gt;[73]&lt;/sup&gt; 遵循类似的方法，使平台和开发团队能够协同工作，轻松将应用程序部署到 Kubernetes。&lt;/p&gt;  &lt;h2&gt;Snyk&lt;/h2&gt;  &lt;p&gt;任何开发过程中一个非常重要的方面是安全性，这一直是 Kubernetes 的一个问题，因为想要迁移到 Kubernetes 的公司无法轻松实现其当前的安全原则。&lt;/p&gt;  &lt;p&gt;Snyk   &lt;sup&gt;[74]&lt;/sup&gt; 试图通过提供一个可以轻松与 Kubernetes 集成的安全框架来缓解这种情况。它可以检测容器映像、你的代码、开源项目等中的漏洞。&lt;/p&gt;  &lt;h3&gt;备选&lt;/h3&gt;  &lt;p&gt;•Falco   &lt;sup&gt;[75]&lt;/sup&gt; 是 Kubernetes 的运行时安全线程检测工具。&lt;/p&gt;  &lt;h2&gt;Velero&lt;/h2&gt;  &lt;p&gt;如果你在 Kubernetes 中运行工作负载并使用卷来存储数据，则需要创建和管理备份。Velero   &lt;sup&gt;[76]&lt;/sup&gt; 提供简单的备份/恢复过程、灾难恢复机制和数据迁移。&lt;/p&gt;  &lt;img title="null"&gt;&lt;/img&gt;  &lt;p&gt;与其他直接访问 Kubernetes etcd 数据库执行备份和恢复的工具不同，Velero 使用 Kubernetes API 来捕获集群资源的状态并在必要时恢复它们。此外，Velero 使你能够在配置的同时备份和恢复你的应用程序持久数据。&lt;/p&gt;  &lt;h2&gt;Schema Hero&lt;/h2&gt;  &lt;p&gt;软件开发中的另一个常见过程是在使用关系数据库时管理   &lt;strong&gt;模式演变&lt;/strong&gt;。&lt;/p&gt;  &lt;p&gt;SchemaHero   &lt;sup&gt;[77]&lt;/sup&gt; 是一种开源数据库架构迁移工具，可将架构定义转换为可应用于任何环境的迁移脚本。它使用 Kubernetes 声明性来管理数据库模式迁移。你只需指定所需的状态，然后    &lt;strong&gt;SchemaHero&lt;/strong&gt; 管理其余的。&lt;/p&gt;  &lt;h3&gt;备选&lt;/h3&gt;  &lt;p&gt;•LiquidBase   &lt;sup&gt;[78]&lt;/sup&gt; 是最著名的替代品。它更难使用，且不是 Kubernetes 原生的，但它具有更多功能。&lt;/p&gt;  &lt;h2&gt;Bitnami Sealed Secrets&lt;/h2&gt;  &lt;p&gt;我们已经介绍了许多    &lt;strong&gt;GitOps&lt;/strong&gt; 工具，例如 ArgoCD   &lt;sup&gt;[79]&lt;/sup&gt;。我们的目标是将所有内容保留在 Git 中，并使用 Kubernetes 声明性来保持环境同步。我们刚刚看到我们如何（并且应该）在 Git 中保留真实来源，并让自动化流程处理配置更改。&lt;/p&gt;  &lt;p&gt;在 Git 中通常很难保留的一件事是诸如数据库密码或 API 密钥之类的秘密，这是因为你永远不应该在代码存储库中存储秘密。一种常见的解决方案是使用外部保管库（例如 AWS Secret Manager   &lt;sup&gt;[80]&lt;/sup&gt; 或 HashiCorp Vault   &lt;sup&gt;[81]&lt;/sup&gt;）来存储机密，但这会产生很多摩擦，因为你需要有一个单独的流程来处理机密。理想情况下，我们希望有一种方法可以像任何其他资源一样安全地在 Git 中存储机密。&lt;/p&gt;  &lt;p&gt;   &lt;strong&gt;Sealed Secrets&lt;/strong&gt;   &lt;sup&gt;[82]&lt;/sup&gt; 旨在克服这个问题，允许你使用强加密将敏感数据存储在 Git 中。Bitnami   &lt;strong&gt;Sealed Secrets&lt;/strong&gt; 本地集成在 Kubernetes 中，允许你仅通过在 Kubernetes 中运行的 Kubernetes 控制器而不是其他任何人来解密密钥。控制器将解密数据并创建安全存储的原生 K8s 机密。这使我们能够将所有内容作为代码存储在我们的 repo 中，从而允许我们安全地执行持续部署，而无需任何外部依赖。&lt;/p&gt;  &lt;p&gt;Sealed Secrets 由两部分组成：&lt;/p&gt;  &lt;p&gt;•集群端控制器•客户端实用程序：   &lt;code&gt;kubeseal&lt;/code&gt;&lt;/p&gt;  &lt;p&gt;该    &lt;code&gt;kubeseal&lt;/code&gt; 实用程序使用非对称加密来加密只有控制器才能解密的机密。这些加密的秘密被编码在一个    &lt;code&gt;SealedSecret&lt;/code&gt; K8s 资源中，你可以将其存储在 Git 中。&lt;/p&gt;  &lt;h3&gt;备选&lt;/h3&gt;  &lt;p&gt;•AWS Secret Manager   &lt;sup&gt;[83]&lt;/sup&gt;•HashiCorp Vault   &lt;sup&gt;[84]&lt;/sup&gt;）&lt;/p&gt;  &lt;h2&gt;Capsule&lt;/h2&gt;  &lt;p&gt;许多公司使用   &lt;strong&gt;多租户&lt;/strong&gt;来管理不同的客户。这在软件开发中很常见，但在 Kubernetes 中很难实现。   &lt;strong&gt;命名空间&lt;/strong&gt;是将集群的逻辑分区创建为隔离切片的好方法，但这不足以安全地隔离客户，我们需要强制执行网络策略、配额等。你可以为每个名称空间创建网络策略和规则，但这是一个难以扩展的乏味过程。此外，租户将不能使用多个命名空间，这是一个很大的限制。&lt;/p&gt;  &lt;p&gt;创建   &lt;strong&gt;分层命名空间&lt;/strong&gt;是为了克服其中一些问题。这个想法是为每个租户拥有一个父命名空间，为租户提供公共网络策略和配额，并允许创建子命名空间。这是一个很大的改进，但它在安全和治理方面没有对租户的本地支持。此外，它还没有达到生产状态，但 1.0 版预计将在未来几个月内发布。&lt;/p&gt;  &lt;p&gt;当前解决此问题的常用方法是为每个客户创建一个集群，这是安全的并提供租户所需的一切，但这很难管理且非常昂贵。&lt;/p&gt;  &lt;p&gt;   &lt;strong&gt;Capsule&lt;/strong&gt;   &lt;sup&gt;[85]&lt;/sup&gt; 是一种为单个集群中的多个租户提供原生 Kubernetes 支持的工具。使用 Capsule，你可以为所有租户拥有一个集群。Capsule 将为租户提供 “几乎” 原生体验（有一些小限制），他们将能够创建多个命名空间并使用集群，因为它们完全可用，隐藏了集群实际上是共享的事实。&lt;/p&gt;  &lt;img title="null"&gt;&lt;/img&gt;Capsule 架构  &lt;p&gt;在单个集群中，Capsule Controller 在称为    &lt;strong&gt;Tenant&lt;/strong&gt; 的轻量级 Kubernetes 抽象中聚合多个命名空间，这是一组 Kubernetes 命名空间。在每个租户内，用户可以自由创建他们的命名空间并共享所有分配的资源，而策略引擎则使不同的租户彼此隔离。&lt;/p&gt;  &lt;p&gt;租户级别定义的网络和安全策略、资源配额、限制范围、RBAC 和其他策略由租户中的所有命名空间自动继承，类似于分层命名空间。然后用户可以自由地自治操作他们的租户，而无需集群管理员的干预。Capsule 是 GitOps 就绪的，因为它是声明性的，并且所有配置都可以存储在 Git 中。&lt;/p&gt;  &lt;h2&gt;vCluster&lt;/h2&gt;  &lt;p&gt;vCluster   &lt;sup&gt;[86]&lt;/sup&gt; 在多租户方面更进了一步，它在 Kubernetes 集群内提供了虚拟集群。每个集群都在一个常规命名空间上运行，并且是完全隔离的。虚拟集群有自己的 API 服务器和独立的数据存储，所以你在 vCluster 中创建的每个 Kubernetes 对象都只存在于 vcluster 内部。此外，您可以将 kube 上下文与虚拟集群一起使用，以像使用常规集群一样使用它们。&lt;/p&gt;  &lt;img title="null"&gt;&lt;/img&gt;  &lt;p&gt;只要您可以在单个命名空间内创建部署，您就可以创建虚拟集群并成为该虚拟集群的管理员，租户可以创建命名空间、安装 CRD、配置权限等等。&lt;/p&gt;  &lt;p&gt;   &lt;strong&gt;vCluster&lt;/strong&gt; 使用    &lt;strong&gt;k3s&lt;/strong&gt;   &lt;sup&gt;[87]&lt;/sup&gt; 作为其 API 服务器，使虚拟集群超轻量级且经济高效；由于 k3s 集群 100% 合规，虚拟集群也 100% 合规。   &lt;strong&gt;vClusters&lt;/strong&gt; 是超轻量级的（1 个 pod），消耗很少的资源并且可以在任何 Kubernetes 集群上运行，而无需对底层集群进行特权访问。与    &lt;strong&gt;Capsule&lt;/strong&gt; 相比，它确实使用了更多的资源，但它提供了更多的灵活性，因为多租户只是用例之一。&lt;/p&gt;  &lt;img title="null"&gt;&lt;/img&gt;  &lt;h2&gt;其他工具&lt;/h2&gt;  &lt;p&gt;•kube-burner   &lt;sup&gt;[88]&lt;/sup&gt; 用于   &lt;strong&gt;压力测试&lt;/strong&gt;。它提供指标和警报。•混沌工程的 Litmus   &lt;sup&gt;[89]&lt;/sup&gt;。•kubewatch   &lt;sup&gt;[90]&lt;/sup&gt; 用于监控，但主要关注基于 Kubernetes 事件（如资源创建或删除）的推送通知。它可以与 Slack 等许多工具集成。•BotKube   &lt;sup&gt;[91]&lt;/sup&gt; 是一个消息机器人，用于监控和调试 Kubernetes 集群。与 kubewatch 类似，但更新并具有更多功能。•Mizu   &lt;sup&gt;[92]&lt;/sup&gt; 是一个 API 流量查看器和调试器。•kube-fledged   &lt;sup&gt;[93]&lt;/sup&gt; 是一个 Kubernetes 插件，用于直接在 Kubernetes 集群的工作节点上创建和管理容器镜像的缓存。因此，   &lt;strong&gt;应用程序 pod 几乎立即启动&lt;/strong&gt;，因为不需要从注册表中提取图像。&lt;/p&gt;  &lt;h2&gt;结论&lt;/h2&gt;  &lt;p&gt;在本文中，我们回顾了我最喜欢的 Kubernetes 工具。我专注于可以合并到任何 Kubernetes 发行版中的开源项目。我没有涵盖诸如 OpenShift   &lt;sup&gt;[94]&lt;/sup&gt; 或 Cloud Providers Add-Ons 之类的商业解决方案，因为我想让它保持通用性，但我鼓励您探索如果您在云上运行 Kubernetes 或使用商业工具，您的云提供商可以为您提供什么。&lt;/p&gt;  &lt;p&gt;我的目标是向您展示您可以在    &lt;strong&gt;Kubernetes&lt;/strong&gt; 中完成您在本地所做的一切。我还更多地关注鲜为人知的工具，我认为这些工具可能具有很大的潜力，例如 Crossplane   &lt;sup&gt;[95]&lt;/sup&gt;、Argo Rollouts   &lt;sup&gt;[96]&lt;/sup&gt;或 Kubevela   &lt;sup&gt;[97]&lt;/sup&gt;。我更感兴趣的工具是 vCluster   &lt;sup&gt;[98]&lt;/sup&gt;、Crossplane   &lt;sup&gt;[99]&lt;/sup&gt; 和 ArgoCD/Workflows。&lt;/p&gt;  &lt;h4&gt;引用链接&lt;/h4&gt;  &lt;p&gt;   &lt;code&gt;[1]&lt;/code&gt; Kubernetes Essential Tools: 2021:    &lt;em&gt;https://itnext.io/kubernetes-essential-tools-2021-def12e84c572&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[2]&lt;/code&gt; Kubernetes:    &lt;em&gt;https://kubernetes.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[3]&lt;/code&gt; K3D:    &lt;em&gt;https://k3d.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[4]&lt;/code&gt; K3S:    &lt;em&gt;https://k3s.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[5]&lt;/code&gt;    &lt;strong&gt;K3S&lt;/strong&gt;:    &lt;em&gt;https://k3s.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[6]&lt;/code&gt;    &lt;strong&gt;Kind&lt;/strong&gt;:    &lt;em&gt;https://kind.sigs.k8s.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[7]&lt;/code&gt;    &lt;strong&gt;MicroK8s&lt;/strong&gt;:    &lt;em&gt;https://microk8s.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[8]&lt;/code&gt;    &lt;strong&gt;MiniKube&lt;/strong&gt;:    &lt;em&gt;https://minikube.sigs.k8s.io/docs/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[9]&lt;/code&gt; Krew:    &lt;em&gt;https://krew.sigs.k8s.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[10]&lt;/code&gt; 插件:    &lt;em&gt;https://krew.sigs.k8s.io/plugins/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[11]&lt;/code&gt;    &lt;strong&gt;kubens&lt;/strong&gt;:    &lt;em&gt;https://github.com/ahmetb/kubectx&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[12]&lt;/code&gt;    &lt;strong&gt;kubectx&lt;/strong&gt;:    &lt;em&gt;https://github.com/ahmetb/kubectx&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[13]&lt;/code&gt; Lens:    &lt;em&gt;https://k8slens.dev/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[14]&lt;/code&gt; K9s:    &lt;em&gt;https://k9scli.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[15]&lt;/code&gt; Helm:    &lt;em&gt;https://helm.sh/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[16]&lt;/code&gt; Charts:    &lt;em&gt;https://artifacthub.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[17]&lt;/code&gt;    &lt;strong&gt;Kustomize&lt;/strong&gt;:    &lt;em&gt;https://kustomize.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[18]&lt;/code&gt; GitOps:    &lt;em&gt;https://www.gitops.tech/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[19]&lt;/code&gt; IaC:    &lt;em&gt;https://en.wikipedia.org/wiki/Infrastructure_as_Code&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[20]&lt;/code&gt; Terraform:    &lt;em&gt;https://www.terraform.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[21]&lt;/code&gt; IaC:    &lt;em&gt;https://en.wikipedia.org/wiki/Infrastructure_as_code&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[22]&lt;/code&gt; 持续部署:    &lt;em&gt;https://en.wikipedia.org/wiki/Continuous_deployment&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[23]&lt;/code&gt; ADR:    &lt;em&gt;https://github.com/jamesmh/architecture_decision_record&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[24]&lt;/code&gt; ArgoCD:    &lt;em&gt;https://argoproj.github.io/argo-cd/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[25]&lt;/code&gt; 此处:    &lt;em&gt;https://argoproj.github.io/argo-cd/core_concepts/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[26]&lt;/code&gt; Flux:    &lt;em&gt;https://fluxcd.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[27]&lt;/code&gt; Argo Workflows:    &lt;em&gt;https://argoproj.github.io/argo-workflows/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[28]&lt;/code&gt; Argo Events:    &lt;em&gt;https://argoproj.github.io/argo-events/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[29]&lt;/code&gt; Apache Airflow:    &lt;em&gt;https://airflow.apache.org/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[30]&lt;/code&gt; Argo 事件（Event）:    &lt;em&gt;https://argoproj.github.io/argo-events/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[31]&lt;/code&gt; Kubeflow:    &lt;em&gt;https://www.kubeflow.org/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[32]&lt;/code&gt; Tekton:    &lt;em&gt;https://tekton.dev/docs/pipelines/pipelines/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[33]&lt;/code&gt;    &lt;strong&gt;Kanico&lt;/strong&gt;:    &lt;em&gt;https://github.com/GoogleContainerTools/kaniko&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[34]&lt;/code&gt; Istio:    &lt;em&gt;https://istio.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[35]&lt;/code&gt; 服务网格:    &lt;em&gt;https://en.wikipedia.org/wiki/Service_mesh&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[36]&lt;/code&gt; 微服务:    &lt;em&gt;https://microservices.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[37]&lt;/code&gt;    &lt;strong&gt;Linkerd&lt;/strong&gt;:    &lt;em&gt;https://linkerd.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[38]&lt;/code&gt; 默认 mTLS:    &lt;em&gt;https://linkerd.io/2.10/features/automatic-mtls/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[39]&lt;/code&gt; 使用 Rust 构建的数据平面:    &lt;em&gt;https://github.com/linkerd/linkerd2-proxy&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[40]&lt;/code&gt; 内存安全语言:    &lt;em&gt;https://github.com/linkerd/linkerd2-proxy&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[41]&lt;/code&gt; 定期安全审计:    &lt;em&gt;https://github.com/linkerd/linkerd2/blob/main/SECURITY_AUDIT.pdf&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[42]&lt;/code&gt;    &lt;strong&gt;Consul&lt;/strong&gt;:    &lt;em&gt;https://www.consul.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[43]&lt;/code&gt; Prometheus:    &lt;em&gt;https://prometheus.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[44]&lt;/code&gt;    &lt;strong&gt;滚动更新&lt;/strong&gt;:    &lt;em&gt;https://www.educative.io/blog/kubernetes-deployments-strategies&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[45]&lt;/code&gt; 金丝雀部署:    &lt;em&gt;https://semaphoreci.com/blog/what-is-canary-deployment&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[46]&lt;/code&gt; Argo Rollouts:    &lt;em&gt;https://kubernetes.io/docs/concepts/architecture/controller/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[47]&lt;/code&gt; Kubernetes 控制器:    &lt;em&gt;https://kubernetes.io/docs/concepts/architecture/controller/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[48]&lt;/code&gt; CRD:    &lt;em&gt;https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[49]&lt;/code&gt; Istio:    &lt;em&gt;https://istio.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[50]&lt;/code&gt; Istio:    &lt;em&gt;https://istio.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[51]&lt;/code&gt; Flagger:    &lt;em&gt;https://flagger.app/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[52]&lt;/code&gt; Flux:    &lt;em&gt;https://fluxcd.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[53]&lt;/code&gt; Spinnaker:    &lt;em&gt;https://spinnaker.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[54]&lt;/code&gt;    &lt;strong&gt;Crossplane&lt;/strong&gt;:    &lt;em&gt;https://crossplane.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[55]&lt;/code&gt;    &lt;strong&gt;AWS RDS&lt;/strong&gt;:    &lt;em&gt;https://aws.amazon.com/rds/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[56]&lt;/code&gt; Terraform:    &lt;em&gt;https://www.terraform.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[57]&lt;/code&gt; Terraform:    &lt;em&gt;https://www.terraform.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[58]&lt;/code&gt; Pulumi:    &lt;em&gt;https://www.pulumi.com/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[59]&lt;/code&gt; AWS Lambda :    &lt;em&gt;https://aws.amazon.com/lambda/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[60]&lt;/code&gt; FaaS:    &lt;em&gt;https://en.wikipedia.org/wiki/Function_as_a_service&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[61]&lt;/code&gt; Serverless:    &lt;em&gt;https://en.wikipedia.org/wiki/Serverless_computing&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[62]&lt;/code&gt; 之前的文章:    &lt;em&gt;https://itnext.io/scaling-my-app-serverless-vs-kubernetes-cdb8adf446e1&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[63]&lt;/code&gt; Knative:    &lt;em&gt;https://itnext.io/scaling-my-app-serverless-vs-kubernetes-cdb8adf446e1&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[64]&lt;/code&gt; Argo Events:    &lt;em&gt;https://argoproj.github.io/argo-events/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[65]&lt;/code&gt; OpenFaas:    &lt;em&gt;https://www.openfaas.com/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[66]&lt;/code&gt; Kyverno:    &lt;em&gt;https://kyverno.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[67]&lt;/code&gt; 此处:    &lt;em&gt;https://github.com/kyverno/policies/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[68]&lt;/code&gt; Open Policy Agent:    &lt;em&gt;https://www.openpolicyagent.org/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[69]&lt;/code&gt; Kyverno:    &lt;em&gt;https://kyverno.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[70]&lt;/code&gt; OAM:    &lt;em&gt;https://oam.dev/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[71]&lt;/code&gt; Kubevela:    &lt;em&gt;https://kubevela.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[72]&lt;/code&gt; 云原生计算基金会:    &lt;em&gt;https://cncf.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[73]&lt;/code&gt; Shipa:    &lt;em&gt;https://www.shipa.io/getting-started/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[74]&lt;/code&gt; Snyk:    &lt;em&gt;https://snyk.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[75]&lt;/code&gt; Falco:    &lt;em&gt;https://falco.org/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[76]&lt;/code&gt; Velero:    &lt;em&gt;https://velero.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[77]&lt;/code&gt; SchemaHero:    &lt;em&gt;https://schemahero.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[78]&lt;/code&gt; LiquidBase:    &lt;em&gt;https://www.liquibase.org/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[79]&lt;/code&gt; ArgoCD:    &lt;em&gt;https://argoproj.github.io/argo-cd/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[80]&lt;/code&gt; AWS Secret Manager:    &lt;em&gt;https://aws.amazon.com/secrets-manager/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[81]&lt;/code&gt; HashiCorp Vault:    &lt;em&gt;https://www.vaultproject.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[82]&lt;/code&gt;    &lt;strong&gt;Sealed Secrets&lt;/strong&gt;:    &lt;em&gt;https://github.com/bitnami-labs/sealed-secrets&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[83]&lt;/code&gt; AWS Secret Manager:    &lt;em&gt;https://aws.amazon.com/secrets-manager/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[84]&lt;/code&gt; HashiCorp Vault:    &lt;em&gt;https://www.vaultproject.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[85]&lt;/code&gt;    &lt;strong&gt;Capsule&lt;/strong&gt;:    &lt;em&gt;https://github.com/clastix/capsule&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[86]&lt;/code&gt; vCluster:    &lt;em&gt;https://www.vcluster.com/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[87]&lt;/code&gt;    &lt;strong&gt;k3s&lt;/strong&gt;:    &lt;em&gt;https://k3s.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[88]&lt;/code&gt; kube-burner:    &lt;em&gt;https://github.com/cloud-bulldozer/kube-burner&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[89]&lt;/code&gt; Litmus:    &lt;em&gt;https://github.com/litmuschaos/litmus&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[90]&lt;/code&gt; kubewatch:    &lt;em&gt;https://github.com/bitnami-labs/kubewatch&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[91]&lt;/code&gt; BotKube:    &lt;em&gt;https://www.botkube.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[92]&lt;/code&gt; Mizu:    &lt;em&gt;https://getmizu.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[93]&lt;/code&gt; kube-fledged:    &lt;em&gt;https://github.com/senthilrch/kube-fledged&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[94]&lt;/code&gt; OpenShift:    &lt;em&gt;https://www.openshift.com/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[95]&lt;/code&gt; Crossplane:    &lt;em&gt;https://crossplane.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[96]&lt;/code&gt; Argo Rollouts:    &lt;em&gt;https://kubevela.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[97]&lt;/code&gt; Kubevela:    &lt;em&gt;https://kubevela.io/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[98]&lt;/code&gt; vCluster:    &lt;em&gt;https://www.vcluster.com/&lt;/em&gt;   &lt;br /&gt;   &lt;code&gt;[99]&lt;/code&gt; Crossplane:    &lt;em&gt;https://crossplane.io/&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;   &lt;br /&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>dev</category>
      <guid isPermaLink="true">https://itindex.net/detail/61617-kubernetes-%E5%B7%A5%E5%85%B7</guid>
      <pubDate>Mon, 19 Jul 2021 00:00:00 CST</pubDate>
    </item>
    <item>
      <title>jvm 性能调优工具之 jmap - 简书</title>
      <link>https://itindex.net/detail/61580-jvm-%E6%80%A7%E8%83%BD%E8%B0%83%E4%BC%98-%E5%B7%A5%E5%85%B7</link>
      <description>&lt;h2&gt;概述&lt;/h2&gt;  &lt;p&gt;命令jmap是一个多功能的命令。它可以生成 java 程序的 dump 文件， 也可以查看堆内对象示例的统计信息、查看 ClassLoader 的信息以及 finalizer 队列。&lt;/p&gt;  &lt;h2&gt;jmap 用法&lt;/h2&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;h4&gt;参数：&lt;/h4&gt;  &lt;ul&gt;    &lt;li&gt;      &lt;strong&gt;option：&lt;/strong&gt;选项参数。&lt;/li&gt;    &lt;li&gt;      &lt;strong&gt;pid：&lt;/strong&gt;需要打印配置信息的进程ID。&lt;/li&gt;    &lt;li&gt;      &lt;strong&gt;executable：&lt;/strong&gt;产生核心dump的Java可执行文件。&lt;/li&gt;    &lt;li&gt;      &lt;strong&gt;core：&lt;/strong&gt;需要打印配置信息的核心文件。&lt;/li&gt;    &lt;li&gt;      &lt;strong&gt;server-id&lt;/strong&gt;可选的唯一id，如果相同的远程主机上运行了多台调试服务器，用此选项参数标识服务器。&lt;/li&gt;    &lt;li&gt;      &lt;strong&gt;remote server IP or hostname&lt;/strong&gt;远程调试服务器的IP地址或主机名。&lt;/li&gt;&lt;/ul&gt;  &lt;h4&gt;option&lt;/h4&gt;  &lt;ul&gt;    &lt;li&gt;      &lt;strong&gt;no option：&lt;/strong&gt;查看进程的内存映像信息,类似 Solaris pmap 命令。&lt;/li&gt;    &lt;li&gt;      &lt;strong&gt;heap：&lt;/strong&gt;显示Java堆详细信息&lt;/li&gt;    &lt;li&gt;      &lt;strong&gt;histo[:live]：&lt;/strong&gt;显示堆中对象的统计信息&lt;/li&gt;    &lt;li&gt;      &lt;strong&gt;clstats：&lt;/strong&gt;打印类加载器信息&lt;/li&gt;    &lt;li&gt;      &lt;strong&gt;finalizerinfo：&lt;/strong&gt;显示在F-Queue队列等待Finalizer线程执行finalizer方法的对象&lt;/li&gt;    &lt;li&gt;      &lt;strong&gt;dump:&amp;lt;dump-options&amp;gt;：&lt;/strong&gt;生成堆转储快照&lt;/li&gt;    &lt;li&gt;      &lt;strong&gt;F：&lt;/strong&gt;当-dump没有响应时，使用-dump或者-histo参数. 在这个模式下,live子参数无效.&lt;/li&gt;    &lt;li&gt;      &lt;strong&gt;help：&lt;/strong&gt;打印帮助信息&lt;/li&gt;    &lt;li&gt;      &lt;strong&gt;J&amp;lt;flag&amp;gt;：&lt;/strong&gt;指定传递给运行jmap的JVM的参数&lt;/li&gt;&lt;/ul&gt;  &lt;h2&gt;示例一：no option&lt;/h2&gt;  &lt;p&gt;命令：jmap pid    &lt;br /&gt;描述：查看进程的内存映像信息,类似 Solaris pmap 命令。&lt;/p&gt;  &lt;p&gt;使用不带选项参数的jmap打印共享对象映射，将会打印目标虚拟机中加载的每个共享对象的起始地址、映射大小以及共享对象文件的路径全称。这与Solaris的pmap工具比较相似。&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;h2&gt;示例二：heap&lt;/h2&gt;  &lt;p&gt;命令：jmap -heap pid    &lt;br /&gt;描述：显示Java堆详细信息&lt;/p&gt;  &lt;p&gt;打印一个堆的摘要信息，包括使用的GC算法、堆配置信息和各内存区域内存使用信息&lt;/p&gt;  &lt;pre&gt;    &lt;code&gt;C:\Users\jjs&amp;gt;jmap -heap 5932
Attaching to process ID 5932, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.91-b15

using thread-local object allocation.
Parallel GC with 4 thread(s)

Heap Configuration:
   MinHeapFreeRatio         = 0
   MaxHeapFreeRatio         = 100
   MaxHeapSize              = 1073741824 (1024.0MB)
   NewSize                  = 42991616 (41.0MB)
   MaxNewSize               = 357564416 (341.0MB)
   OldSize                  = 87031808 (83.0MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 60293120 (57.5MB)
   used     = 44166744 (42.120689392089844MB)
   free     = 16126376 (15.379310607910156MB)
   73.25337285580842% used
From Space:
   capacity = 5242880 (5.0MB)
   used     = 0 (0.0MB)
   free     = 5242880 (5.0MB)
   0.0% used
To Space:
   capacity = 14680064 (14.0MB)
   used     = 0 (0.0MB)
   free     = 14680064 (14.0MB)
   0.0% used
PS Old Generation
   capacity = 120061952 (114.5MB)
   used     = 19805592 (18.888084411621094MB)
   free     = 100256360 (95.6119155883789MB)
   16.496143590935453% used

20342 interned Strings occupying 1863208 bytes.&lt;/code&gt;&lt;/pre&gt;  &lt;h2&gt;示例三：histo[:live]&lt;/h2&gt;  &lt;p&gt;命令：jmap -histo:live pid    &lt;br /&gt;描述：显示堆中对象的统计信息&lt;/p&gt;  &lt;p&gt;其中包括每个Java类、对象数量、内存大小(单位：字节)、完全限定的类名。打印的虚拟机内部的类名称将会带有一个’*’前缀。如果指定了live子选项，则只计算活动的对象。&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;h2&gt;示例四：clstats&lt;/h2&gt;  &lt;p&gt;命令：jmap -clstats pid    &lt;br /&gt;描述：打印类加载器信息&lt;/p&gt;  &lt;p&gt;-clstats是-permstat的替代方案，在JDK8之前，-permstat用来打印类加载器的数据    &lt;br /&gt;打印Java堆内存的永久保存区域的类加载器的智能统计信息。对于每个类加载器而言，它的名称、活跃度、地址、父类加载器、它所加载的类的数量和大小都会被打印。此外，包含的字符串数量和大小也会被打印。&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;h2&gt;示例五：finalizerinfo&lt;/h2&gt;  &lt;p&gt;命令：jmap -finalizerinfo pid    &lt;br /&gt;描述：打印等待终结的对象信息&lt;/p&gt;  &lt;br /&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;br /&gt;  &lt;p&gt;Number of objects pending for finalization: 0 说明当前F-QUEUE队列中并没有等待Fializer线程执行final&lt;/p&gt;  &lt;h2&gt;示例六：dump:&amp;lt;dump-options&amp;gt;&lt;/h2&gt;  &lt;p&gt;命令：jmap -dump:format=b,file=heapdump.phrof pid    &lt;br /&gt;描述：生成堆转储快照dump文件。&lt;/p&gt;  &lt;p&gt;以hprof二进制格式转储Java堆到指定filename的文件中。live子选项是可选的。如果指定了live子选项，堆中只有活动的对象会被转储。想要浏览heap dump，你可以使用jhat(Java堆分析工具)读取生成的文件。&lt;/p&gt;  &lt;blockquote&gt;    &lt;p&gt;这个命令执行，JVM会将整个heap的信息dump写入到一个文件，heap如果比较大的话，就会导致这个过程比较耗时，并且执行的过程中为了保证dump的信息是可靠的，所以会暂停应用， 线上系统慎用。&lt;/p&gt;&lt;/blockquote&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;div&gt;    &lt;div&gt;      &lt;div&gt;&lt;/div&gt;      &lt;div&gt;        &lt;img&gt;&lt;/img&gt;&lt;/div&gt;&lt;/div&gt;    &lt;div&gt;&lt;/div&gt;&lt;/div&gt;
    &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category />
      <guid isPermaLink="true">https://itindex.net/detail/61580-jvm-%E6%80%A7%E8%83%BD%E8%B0%83%E4%BC%98-%E5%B7%A5%E5%85%B7</guid>
      <pubDate>Fri, 02 Jul 2021 09:03:12 CST</pubDate>
    </item>
  </channel>
</rss>

