<?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>用Qwen 3.6 35B本地模型作为主力编程工具替代Claude或GPT</title>
      <link>https://itindex.net/detail/63241-qwen-35b-%E6%A8%A1%E5%9E%8B</link>
      <description>&lt;p&gt;基于 Hacker News 上的这个热门讨论（关于是否有人在日常编程中用本地大模型完全替代 Claude/GPT），以下为您归纳出的几条  &lt;strong&gt;本地大模型最佳实践&lt;/strong&gt;以及该讨论的  &lt;strong&gt;主要内容概述&lt;/strong&gt;。&lt;/p&gt; &lt;h3&gt;一、 本地大模型（Local LLM）编程的 5 条最佳实践&lt;/h3&gt; &lt;ol start="1"&gt;  &lt;li&gt;   &lt;p&gt;    &lt;strong&gt;选择最适配的混合架构模型（如 Qwen 3.6 35B）&lt;/strong&gt;
讨论中多位资深用户指出，    &lt;strong&gt;Qwen 3.6 35B（激活 3B 参数的混合专家模型 MoE）&lt;/strong&gt; 是目前本地编程的“黄金甜点位（Sweet Spot）”。它在 128GB 或 36GB RAM 的设备（如 Mac Studio、Strix Halo 笔记本）上运行极快，且代码能力表现优异。对于更复杂的任务，可配合 Qwen 3.5 122B（激活 10B）作为后备。&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;    &lt;strong&gt;启用“保持思考”配置（Preserve Thinking）以优化缓存&lt;/strong&gt;
在使用推理/思考模型（如带有     &lt;code&gt;&amp;lt;think&amp;gt;&lt;/code&gt; 标签的模型）进行多轮对话或 Agent 自动化编程时，默认的模板可能会在下一轮对话中丢弃之前的思考链（CoT），导致每一轮都要重新计算完整的 KV 缓存（Context Reprocessing）。    &lt;strong&gt;最佳实践是在大模型后端（如 llama.cpp）中开启      &lt;code&gt;preserve_thinking: true&lt;/code&gt;&lt;/strong&gt;，这能大幅提升多轮对话中的缓存命中率，避免卡顿。&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;    &lt;strong&gt;使用 Vulkan 后端提升 AMD/Intel 硬件性能&lt;/strong&gt;
在特定硬件（如 AMD Strix Halo 笔记本）上运行     &lt;code&gt;llama.cpp&lt;/code&gt; 时，部分用户反馈    &lt;strong&gt;使用 Vulkan 后端甚至比官方的 ROCm 还要快且更稳定&lt;/strong&gt;。硬件平台的后端选择（Vulkan vs ROCm/Metal）应根据实际本地测试来决定。&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;    &lt;strong&gt;精确提示与迭代开发（Iterative Development），不当“甩手掌柜”&lt;/strong&gt;
本地模型（如 Qwen 3.6 35B）相比于闭源的顶尖模型（如 Claude 3.5 Sonnet / Opus），更像是一个    &lt;strong&gt;需要密切指导的“初级程序员（Junior）”&lt;/strong&gt;。最佳实践是    &lt;strong&gt;不要指望它一次性生成成千上万行代码&lt;/strong&gt;，而是采用“讨论设计方案 -&amp;gt; 达成共识 -&amp;gt; 迭代编写单个功能 -&amp;gt; 运行测试”的循环模式，且提示词必须极其精确、消除歧义，否则模型会为了偷懒选择最糟糕的架构（例如直接在 HTML 里塞满 CSS）。&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;    &lt;strong&gt;量化（Quantization）和容器化沙箱（Sandboxing）安全&lt;/strong&gt;&lt;/p&gt;   &lt;ul&gt;    &lt;li&gt;     &lt;p&gt;      &lt;strong&gt;安全：&lt;/strong&gt; 配合 Agent 框架（如 Pi 编程脚手架）时，务必将本地模型和执行环境      &lt;strong&gt;容器化（Docker）并进行沙箱隔离&lt;/strong&gt;，限制其仅能访问当前工作目录，防止断网环境下本地脚本误操作或泄露敏感凭证。&lt;/p&gt;&lt;/li&gt;    &lt;li&gt;     &lt;p&gt;      &lt;strong&gt;量化：&lt;/strong&gt; 不要盲目相信社区的激进量化。量化对代码质量影响极大（MoE 架构对量化的耐受度稍好），建议在可能的情况下优先选用更高精度的版本（如 FP8 等）。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ol&gt; &lt;h3&gt;二、 帖子主要内容概述&lt;/h3&gt; &lt;p&gt;这个帖子（Ask HN）的核心议题是：  &lt;strong&gt;“有没有人真正把本地模型作为主力编程工具，完全替代了 Claude 或 GPT？”&lt;/strong&gt; 评论区的技术人员对此展开了深度讨论，主要内容可概括为以下几点：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;   &lt;p&gt;    &lt;strong&gt;完全替代的可能性与实际体验：&lt;/strong&gt;
多数硬核开发者表示    &lt;strong&gt;完全可以替代&lt;/strong&gt;，尤其是在注重    &lt;strong&gt;隐私、离线开发和完全免费&lt;/strong&gt;的场景下。有用户分享了他们纯靠本地模型（Qwen3.6 35b + Pi 框架）重构整个 Django+Wagtail 网站主页和博客的成功经历。&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;    &lt;strong&gt;本地模型 vs 闭源大模型的差距：&lt;/strong&gt;
用户普遍认为，Claude Opus 或 Sonnet 就像一个能帮你思考架构的“高级工程师（Senior）”，能带来 15 倍的效率提升；而本地模型则是一个需要你时刻盯着的“初级工程师”，能带来约 5 倍的效率提升。虽然本地模型更容易陷入逻辑死循环或在调用编辑工具时出错，但考虑到它    &lt;strong&gt;完全免费且纯离线&lt;/strong&gt;，这种表现已经令人惊叹。&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;    &lt;strong&gt;技术层面的深究（KV缓存与Attention机制）：&lt;/strong&gt;
讨论中有很大一部分篇幅在硬核切磋本地运行的底层 Bug。大家深入探讨了为什么模型在多轮对话中会频繁触发“重新处理上下文（Re-processing context）”。多位开发者指出这通常是由于 Prompt 模板不一致、系统提示词每轮被修改（Harness Bug）或没有保存思考链导致的，并给出了具体的 Jinja 模板修改方案和命令行参数。&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;    &lt;strong&gt;人机协作哲学的思辨：&lt;/strong&gt;
开发者们辩论了“AI编程是否会导致代码质量退化”。主流观点认为，AI 不是为了让人变懒去生成一堆垃圾代码（Vibe Coding），而是作为“自动化 Google”和实时常驻的专家。高水平的开发者可以通过与本地 AI 讨论、审查其方案并编写大量测试，实现“控制权在人，生产力乘数在 AI”的高质量开发。&lt;/p&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 />
      <guid isPermaLink="true">https://itindex.net/detail/63241-qwen-35b-%E6%A8%A1%E5%9E%8B</guid>
      <pubDate>Wed, 17 Jun 2026 08:59:06 CST</pubDate>
    </item>
    <item>
      <title>如何使用NLEmbedding + Gemma4构建一个知识管理和检索友好的系统工具</title>
      <link>https://itindex.net/detail/63207-nlembedding-gemma4-%E5%BB%BA%E4%B8%80</link>
      <description>&lt;p&gt;结合 NLEmbedding 与 Gemma 4 构建知识管理系统，核心是发挥两者的互补优势：NLEmbedding 负责将知识转化为可检索的向量索引（“记忆系统”），Gemma 4 负责基于检索结果生成高质量回答（“认知大脑”）。&lt;/p&gt; &lt;h3&gt;🏗️ 系统架构核心设计&lt;/h3&gt; &lt;p&gt;下图展示了推荐的系统架构：&lt;/p&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;/div&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;div&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;div&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;/div&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;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;div&gt;   &lt;div&gt;    &lt;p&gt;查询阶段&lt;/p&gt;&lt;/div&gt;   &lt;div&gt;    &lt;p&gt;构建阶段&lt;/p&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;/div&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;div&gt;    &lt;p&gt;企业知识文档     &lt;br /&gt;PDF/Word/网页&lt;/p&gt;&lt;/div&gt;   &lt;div&gt;    &lt;p&gt;文档切片     &lt;br /&gt;语义分块&lt;/p&gt;&lt;/div&gt;   &lt;div&gt;    &lt;p&gt;NLEmbedding     &lt;br /&gt;向量化&lt;/p&gt;&lt;/div&gt;   &lt;div&gt;    &lt;p&gt;向量数据库     &lt;br /&gt;存储索引&lt;/p&gt;&lt;/div&gt;   &lt;div&gt;    &lt;p&gt;用户提问&lt;/p&gt;&lt;/div&gt;   &lt;div&gt;    &lt;p&gt;NLEmbedding     &lt;br /&gt;向量化&lt;/p&gt;&lt;/div&gt;   &lt;div&gt;    &lt;p&gt;相似度检索     &lt;br /&gt;召回Top-K相关片段&lt;/p&gt;&lt;/div&gt;   &lt;div&gt;    &lt;p&gt;构建Prompt     &lt;br /&gt;上下文+问题&lt;/p&gt;&lt;/div&gt;   &lt;div&gt;    &lt;p&gt;Gemma 4     &lt;br /&gt;生成回答&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt; &lt;h3&gt;🧠 第一步：NLEmbedding——构建记忆检索层&lt;/h3&gt; &lt;p&gt;NLEmbedding 的核心任务是将文档片段转化为高维向量，本质是建立语义空间索引。实际使用时需重点规划以下策略：&lt;/p&gt; &lt;div&gt;  &lt;div&gt;   &lt;div&gt;&lt;/div&gt;   &lt;div&gt;&lt;/div&gt;&lt;/div&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;/tr&gt;   &lt;tr&gt;    &lt;td&gt;模型选择&lt;/td&gt;    &lt;td&gt;中文场景推荐      &lt;code&gt;text2vec-large-chinese&lt;/code&gt; 或 BGE 系列；多语言/英文推荐      &lt;code&gt;nomic-embed-text&lt;/code&gt;。这些模型开源友好，支持本地部署     &lt;a href="https://dev.to/daathwi/100s-of-tools-in-your-agent-heres-how-to-actually-pick-the-right-one-547i#comments" rel="noreferrer" target="_blank"&gt;-9&lt;/a&gt;     &lt;a href="https://cloud.tencent.cn/developer/article/2421054?from=15425" rel="noreferrer" target="_blank"&gt;-8&lt;/a&gt;。&lt;/td&gt;    &lt;td&gt;兼顾隐私、成本和定制化需求，避免依赖外部API。&lt;/td&gt;&lt;/tr&gt;   &lt;tr&gt;    &lt;td&gt;文档分块&lt;/td&gt;    &lt;td&gt;语义分块优于固定长度切分。例如，按段落、Markdown标题或代码块边界切分，并使用      &lt;code&gt;RecursiveCharacterTextSplitter&lt;/code&gt; 保留重叠区域。&lt;/td&gt;    &lt;td&gt;防止表格、代码等被截断破坏语义，保证检索完整性     &lt;a href="https://blog.csdn.net/HHX_01/article/details/159805428" rel="noreferrer" target="_blank"&gt;-2&lt;/a&gt;。&lt;/td&gt;&lt;/tr&gt;   &lt;tr&gt;    &lt;td&gt;向量数据库&lt;/td&gt;    &lt;td&gt;生产环境推荐 PgVector (PostgreSQL扩展) 或 Milvus；轻量级原型可使用 Chroma 或 FAISS     &lt;a href="https://blog.csdn.net/HHX_01/article/details/159805428" rel="noreferrer" target="_blank"&gt;-2&lt;/a&gt;     &lt;a href="https://cloud.tencent.cn/developer/article/2421054?from=15425" rel="noreferrer" target="_blank"&gt;-8&lt;/a&gt;。&lt;/td&gt;    &lt;td&gt;PgVector可复用现有PG库，降低运维成本，支持混合检索     &lt;a href="https://blog.csdn.net/HHX_01/article/details/159805428" rel="noreferrer" target="_blank"&gt;-2&lt;/a&gt;。&lt;/td&gt;&lt;/tr&gt;   &lt;tr&gt;    &lt;td&gt;检索策略&lt;/td&gt;    &lt;td&gt;采用混合检索：向量相似度 + BM25关键词检索，通过RRF(Reciprocal Rank Fusion)算法融合结果。&lt;/td&gt;    &lt;td&gt;解决纯向量检索可能漏掉精确关键词（如产品型号、编号）的问题     &lt;a href="https://blog.csdn.net/HHX_01/article/details/159805428" rel="noreferrer" target="_blank"&gt;-2&lt;/a&gt;     &lt;a href="https://dev.to/daathwi/100s-of-tools-in-your-agent-heres-how-to-actually-pick-the-right-one-547i#comments" rel="noreferrer" target="_blank"&gt;-9&lt;/a&gt;。&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt; &lt;h3&gt;🚀 第二步：Gemma 4——搭建生成与交互层&lt;/h3&gt; &lt;p&gt;Gemma 4 凭借其256K超长上下文和函数调用能力，非常契合知识管理场景  &lt;a href="https://blog.csdn.net/HHX_01/article/details/159805428" rel="noreferrer" target="_blank"&gt;-2&lt;/a&gt;  &lt;a href="https://novita.ai/models/model-detail/google-gemma-4-26b-a4b-it?utm_source=blogs&amp;utm_medium=article&amp;utm_campaign=gemma-4-novita-ai" rel="noreferrer" target="_blank"&gt;-6&lt;/a&gt;。部署时需注意：&lt;/p&gt; &lt;ul&gt;  &lt;li&gt;   &lt;p&gt;部署方案：轻量场景（测试、个人）可直接通过 Ollama 一键运行     &lt;code&gt;gemma4:4b&lt;/code&gt; 或     &lt;code&gt;gemma4:26b-a4b&lt;/code&gt; 版本    &lt;a href="https://dev.to/tsunamayo7/helix-ai-studio-v210-7-ai-providers-cli-integration-gemma4-default-47ol" rel="noreferrer" target="_blank"&gt;-4&lt;/a&gt;    &lt;a href="https://dev.to/daathwi/100s-of-tools-in-your-agent-heres-how-to-actually-pick-the-right-one-547i#comments" rel="noreferrer" target="_blank"&gt;-9&lt;/a&gt;。企业级高并发场景建议使用 vLLM 框架，或通过兼容OpenAI协议的API服务调用    &lt;a href="https://novita.ai/models/model-detail/google-gemma-4-26b-a4b-it?utm_source=blogs&amp;utm_medium=article&amp;utm_campaign=gemma-4-novita-ai" rel="noreferrer" target="_blank"&gt;-6&lt;/a&gt;。&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;上下文利用：Gemma 4 的长上下文能力允许在Prompt中注入更多检索到的相关片段，减少关键信息遗漏    &lt;a href="https://www.imooc.com/article/390500?block_id=tuijian_wz" rel="noreferrer" target="_blank"&gt;-7&lt;/a&gt;。&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;函数调用：可实现让模型主动查询数据库或调用内部API的智能体（Agent）行为    &lt;a href="https://blog.csdn.net/HHX_01/article/details/159805428" rel="noreferrer" target="_blank"&gt;-2&lt;/a&gt;    &lt;a href="https://www.imooc.com/article/390500?block_id=tuijian_wz" rel="noreferrer" target="_blank"&gt;-7&lt;/a&gt;。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt; &lt;h3&gt;🔗 第三步：RAG流程串联与代码示例&lt;/h3&gt; &lt;p&gt;系统运行的完整逻辑是：查 → 拼 → 生。下面以用户提问“公司年假几天？”为例，演示核心代码逻辑：&lt;/p&gt; &lt;div&gt;  &lt;div&gt;   &lt;div&gt;    &lt;div&gt;     &lt;div&gt;python&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;/div&gt;下载       &lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;  &lt;pre&gt;from openai import OpenAI  # 假设使用OpenAI兼容接口
import numpy as np
from your_vector_db import VectorStore  # 假设的向量数据库客户端

client = OpenAI(base_url=&amp;quot;http://localhost:11434/v1&amp;quot;, api_key=&amp;quot;ollama&amp;quot;)

# 1. 检索：将用户问题向量化，从DB召回Top-K相关文档
def retrieve_context(query, top_k=5):
    query_embedding = get_nlembedding(query) # 使用你的NLEmbedding模型
    results = vector_db.similarity_search(query_embedding, k=top_k)
    return [doc.text for doc in results]

# 2. 生成：构建包含上下文的Prompt，调用Gemma 4
def ask_knowledge_base(question):
    context_chunks = retrieve_context(question)
    context_str = &amp;quot;\n\n---\n\n&amp;quot;.join(context_chunks)
    
    # 精心设计的Prompt，引导模型基于上下文回答
    prompt = f&amp;quot;&amp;quot;&amp;quot;你是一个专业的知识库助手。请严格根据下面“文档上下文”中的信息回答用户问题。
如果上下文中没有相关信息，请明确回答“根据现有知识库无法回答该问题”，不要编造信息。

&amp;lt;文档上下文&amp;gt;
{context_str}
&amp;lt;/文档上下文&amp;gt;

用户问题：{question}
回答：&amp;quot;&amp;quot;&amp;quot;

    response = client.chat.completions.create(
        model=&amp;quot;gemma4:26b-a4b-it&amp;quot;, # 或其他 Gemma 4 模型标签
        messages=[{&amp;quot;role&amp;quot;: &amp;quot;user&amp;quot;, &amp;quot;content&amp;quot;: prompt}],
        temperature=0.1 # 降低随机性，提高事实性
    )
    return response.choices[0].message.content&lt;/pre&gt;&lt;/div&gt; &lt;blockquote&gt;  &lt;p&gt;注意：代码中的    &lt;code&gt;get_nlembedding&lt;/code&gt; 和    &lt;code&gt;vector_db&lt;/code&gt; 需根据你实际选择的嵌入模型和向量数据库进行具体实现。&lt;/p&gt;&lt;/blockquote&gt; &lt;h3&gt;🔧 第四步：优化与进阶策略&lt;/h3&gt; &lt;p&gt;要让系统更稳定可靠，建议加入以下机制：&lt;/p&gt; &lt;ol start="1"&gt;  &lt;li&gt;   &lt;p&gt;多级检索架构：先使用一个极轻量的Gemma模型（如E4B版本）对用户意图进行分类（如“售后问题”、“产品咨询”），然后再在对应的小范围知识库中进行向量检索。这样可以有效降低大模型的“幻觉”并提高准确率    &lt;a href="https://dev.to/daathwi/100s-of-tools-in-your-agent-heres-how-to-actually-pick-the-right-one-547i#comments" rel="noreferrer" target="_blank"&gt;-9&lt;/a&gt;。&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;降级与容错：在生产环境中设置熔断机制。例如，当向量检索结果相似度普遍低于阈值（如0.7）时，可触发降级策略，转而调用Gemma 4的通用知识进行回答，并添加免责声明    &lt;a href="https://blog.csdn.net/HHX_01/article/details/159805428" rel="noreferrer" target="_blank"&gt;-2&lt;/a&gt;。&lt;/p&gt;&lt;/li&gt;  &lt;li&gt;   &lt;p&gt;精排优化：在向量检索初步召回后，增加一个 Reranker（重排序） 模型对结果进行二次精排，将最相关的片段排到最前，能显著提升Gemma 4的生成质量。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt; &lt;h3&gt;💎 总结：技术选型速览&lt;/h3&gt; &lt;div&gt;  &lt;div&gt;   &lt;div&gt;&lt;/div&gt;   &lt;div&gt;&lt;/div&gt;&lt;/div&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;/tr&gt;   &lt;tr&gt;    &lt;td&gt;Embedding模型&lt;/td&gt;    &lt;td&gt;     &lt;code&gt;nomic-embed-text&lt;/code&gt;,      &lt;code&gt;text2vec-large-chinese&lt;/code&gt;&lt;/td&gt;    &lt;td&gt;运行于Ollama或本地Transformers     &lt;a href="https://dev.to/daathwi/100s-of-tools-in-your-agent-heres-how-to-actually-pick-the-right-one-547i#comments" rel="noreferrer" target="_blank"&gt;-9&lt;/a&gt;     &lt;a href="https://cloud.tencent.cn/developer/article/2421054?from=15425" rel="noreferrer" target="_blank"&gt;-8&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;   &lt;tr&gt;    &lt;td&gt;向量数据库&lt;/td&gt;    &lt;td&gt;PgVector, Milvus&lt;/td&gt;    &lt;td&gt;轻量级可选Chroma     &lt;a href="https://blog.csdn.net/HHX_01/article/details/159805428" rel="noreferrer" target="_blank"&gt;-2&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;   &lt;tr&gt;    &lt;td&gt;大语言模型&lt;/td&gt;    &lt;td&gt;Gemma 4 (4B/26B-A4B/31B)&lt;/td&gt;    &lt;td&gt;根据硬件配置和性能要求选择     &lt;a href="https://blog.csdn.net/HHX_01/article/details/159805428" rel="noreferrer" target="_blank"&gt;-2&lt;/a&gt;     &lt;a href="https://novita.ai/models/model-detail/google-gemma-4-26b-a4b-it?utm_source=blogs&amp;utm_medium=article&amp;utm_campaign=gemma-4-novita-ai" rel="noreferrer" target="_blank"&gt;-6&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;   &lt;tr&gt;    &lt;td&gt;编排层&lt;/td&gt;    &lt;td&gt;Spring AI, LangChain, LlamaIndex&lt;/td&gt;    &lt;td&gt;Java技术栈首选Spring AI     &lt;a href="https://blog.csdn.net/HHX_01/article/details/159805428" rel="noreferrer" target="_blank"&gt;-2&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;   &lt;tr&gt;    &lt;td&gt;部署工具&lt;/td&gt;    &lt;td&gt;Ollama, vLLM, Docker&lt;/td&gt;    &lt;td&gt;本地测试用Ollama，生产推荐vLLM&lt;/td&gt;&lt;/tr&gt;&lt;/table&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/63207-nlembedding-gemma4-%E5%BB%BA%E4%B8%80</guid>
      <pubDate>Fri, 17 Apr 2026 14:15:33 CST</pubDate>
    </item>
    <item>
      <title>游戏开发团队测试500款AI工具 仅6款具备实用价值</title>
      <link>https://itindex.net/detail/63194-%E6%B8%B8%E6%88%8F%E5%BC%80%E5%8F%91-%E5%9B%A2%E9%98%9F-%E6%B5%8B%E8%AF%95</link>
      <description>&lt;p&gt;尽管如今许多游戏工作室已开始在开发流程中使用AI工具，但其中绝大多数工具的实用价值都远远不够。这一观点来自游戏行业领先的技术与创意解决方案提供商Keywords Studios的转型负责人乔恩·吉布森。&lt;/p&gt;                
                                                     &lt;p&gt;Keywords Studios总部位于爱尔兰，成立于1998年，曾为《沙丘：觉醒》《漫威争锋》《深渊呼唤》《心灵杀手 重制版》《死亡岛2》《漫威银河护卫队》等众多知名作品提供技术与开发支持。&lt;/p&gt; &lt;p&gt;  &lt;img alt="&amp;#28216;&amp;#25103;&amp;#24320;&amp;#21457;&amp;#22242;&amp;#38431;&amp;#27979;&amp;#35797;500&amp;#27454;AI&amp;#24037;&amp;#20855; &amp;#20165;6&amp;#27454;&amp;#20855;&amp;#22791;&amp;#23454;&amp;#29992;&amp;#20215;&amp;#20540;" src="https://img.3dmgame.com/uploads/images/news/20260411/1775887779_920230_jpg_r.jpg"&gt;&lt;/img&gt;&lt;/p&gt; &lt;p&gt;吉布森在接受专访时表示，Keywords团队针对500多款游戏开发AI工具的适用性进行了测试，其中仅有约6款具备真正的实用价值。&lt;/p&gt; &lt;p&gt;“市面上充斥着大量华而不实的产品，”吉布森说，“Keywords内部开展了大量研发工作，我们在多个研发项目中测试了约500款不同的AI工具，最终认为仅有约6款能够以合理的方式，真正为开发工作提供助力。”&lt;/p&gt; &lt;p&gt;他解释称，就目前的行业现状而言，绝大多数AI工具的开发逻辑，都是围绕“工具能实现什么功能”展开，而非“能真正解决游戏开发中的哪些具体问题”。这最终导致这些工具或许能实现一些“看起来很酷”的效果，却毫无实际应用价值。&lt;/p&gt; &lt;p&gt;“目前的AI技术，正处于野蛮生长的混乱阶段，我们需要推动它进入真正可落地应用的阶段。”他进一步解释道，“我们该如何在实际的生产环境中应用AI?如何让AI成为开发团队的助力，而非潜在的威胁?同时，我们该如何在规范的框架内使用AI，确保它处于可控状态，保障知识产权安全、法律合规，以及伦理与道德层面的正当性?”&lt;/p&gt; &lt;p&gt;“那些输入一段提示词就能生成惊艳效果的炫酷AI演示，和实际生产环境中由人工把控方向、输出稳定且高质量成果的AI应用，两者之间存在着巨大的鸿沟，想要跨越绝非易事。”&lt;/p&gt; &lt;p&gt;他补充道：“很多人只关注什么东西看起来炫酷，把注意力放在工具或模型本身，而非自己真正想要实现的目标。有些公司会在没有明确应用场景的情况下，就去使用甚至开发一款工具，然后硬把它塞进现有的生产管线里。而正确的逻辑本该是反过来的：先明确‘我们的痛点是什么，我们想要解决什么问题’，再针对性地开发工具。”&lt;/p&gt; &lt;p&gt;吉布森还提到，在游戏开发者大会（GDC）最新发布的行业现状报告中，受访的开发者里有90%表示已在开发中使用AI，但同时有52%的人并不认可AI的应用。&lt;/p&gt; &lt;p&gt;“90%的人都在用AI，却有52%的人认为这是件坏事。”他总结道，“这中间显然存在严重的断层。我认为，对AI的应用缺乏管控与规范体系，是造成这一问题的核心原因之一。很多公司根本没有向员工真正解释清楚，他们为什么要用AI、AI的价值在哪里、相关的战略规划是什么。在没有明确这些核心问题的情况下，就贸然启用AI模型，这自然会让开发者感到担忧。”&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/63194-%E6%B8%B8%E6%88%8F%E5%BC%80%E5%8F%91-%E5%9B%A2%E9%98%9F-%E6%B5%8B%E8%AF%95</guid>
      <pubDate>Sat, 11 Apr 2026 18:54:00 CST</pubDate>
    </item>
    <item>
      <title>gstack：YC总裁开源的”AI软件工厂”，68k Star的工具到底香不香？</title>
      <link>https://itindex.net/detail/63193-gstack-yc-%E6%80%BB%E8%A3%81</link>
      <description>&lt;p&gt;第一次看到这个项目的时候，我愣了几秒。&lt;/p&gt;
 &lt;p&gt;Garry Tan——Y Combinator的总裁，全球最顶级孵化器的掌舵人——居然把自己每天用的Claude Code配置开源了？而且还专门强调这是他” opinionated tools”，意思是一套带强烈个人偏好的工具链？68k star、9.4k fork，这个数字在GitHub上是什么概念我就不用说了吧。&lt;/p&gt;
 &lt;p&gt;更让我好奇的是：他声称用这套东西，60天写了60万行生产代码。一个人的效率，真的能顶一个20人团队？&lt;/p&gt;
 &lt;p&gt;我花了几天时间认真研究了一下，今天来聊聊gstack到底是个什么东西。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="533" src="https://tu.aixq.cc/wp-content/uploads/2026/04/20260408201051895.jpg!ys" width="800"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;一、gstack是什么？&lt;/h2&gt;
 &lt;p&gt;简单说，gstack是一套开源的AI软件工厂工具，把Claude Code变成了一个虚拟工程团队。&lt;/p&gt;
 &lt;p&gt;Garry Tan把它描述为”23个专业角色+8个强大工具”，每个角色各司其职：CEO帮你做战略审查、Designer帮你做设计系统、Eng Manager帮你做架构评审、QA帮你做浏览器测试……你一个人坐在那里，AI们在背后协作。&lt;/p&gt;
 &lt;p&gt;核心技术栈是TypeScript（71.2%）+ Go Template（18.9%），依赖Bun v1.0+或Node.js运行，用Playwright做浏览器自动化，支持macOS和Windows（通过WSL/Git Bash）。&lt;/p&gt;
 &lt;h2&gt;二、数据情况&lt;/h2&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;Star数&lt;/td&gt;
   &lt;td&gt;68k&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Fork数&lt;/td&gt;
   &lt;td&gt;9.4k&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;Watchers&lt;/td&gt;
   &lt;td&gt;379&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;许可证&lt;/td&gt;
   &lt;td&gt;MIT&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;数据来源：GitHub公开数据&lt;/p&gt;
 &lt;p&gt;这个量级的star说明什么？要么是真的好用，要么是Garry Tan的名字太响。从我了解到的社区反馈来看，两方面原因都有。Garry本人说他在60天内用这套工具写了60万行生产代码，35%的代码是测试代码——这个比例在行业内算是相当高的质量追求了。&lt;/p&gt;
 &lt;h2&gt;三、核心功能有哪些？&lt;/h2&gt;
 &lt;p&gt;gstack的功能分成四个阶段：&lt;/p&gt;
 &lt;h3&gt;规划阶段&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;code&gt;/office-hours&lt;/code&gt;：产品定义和需求分析，AI会追问你的具体痛点&lt;/li&gt;
  &lt;li&gt;   &lt;code&gt;/plan-ceo-review&lt;/code&gt;：CEO级别的战略审查，挑战你的产品方向&lt;/li&gt;
  &lt;li&gt;   &lt;code&gt;/plan-eng-review&lt;/code&gt;：工程架构审查，设计数据流和技术方案&lt;/li&gt;
  &lt;li&gt;   &lt;code&gt;/plan-design-review&lt;/code&gt;：设计审查，确保设计符合工程可行性&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;构建阶段&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;code&gt;/design-consultation&lt;/code&gt;：构建完整设计系统&lt;/li&gt;
  &lt;li&gt;   &lt;code&gt;/design-shotgun&lt;/code&gt;：AI原型设计探索，快速出多个方案&lt;/li&gt;
  &lt;li&gt;   &lt;code&gt;/design-html&lt;/code&gt;：生成生产级HTML/CSS&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;测试阶段&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;code&gt;/review&lt;/code&gt;：代码审查和自动修复&lt;/li&gt;
  &lt;li&gt;   &lt;code&gt;/qa&lt;/code&gt;：真实浏览器测试（用的是Playwright，防机器人检测）&lt;/li&gt;
  &lt;li&gt;   &lt;code&gt;/cso&lt;/code&gt;：安全审计（OWASP Top 10 + STRIDE模型）&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;发布阶段&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;code&gt;/ship&lt;/code&gt;：发布管理&lt;/li&gt;
  &lt;li&gt;   &lt;code&gt;/land-and-deploy&lt;/code&gt;：生产部署验证&lt;/li&gt;
  &lt;li&gt;   &lt;code&gt;/canary&lt;/code&gt;：部署后监控&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;光看功能列表可能觉得有点虚，我举个例子：你在Claude Code里说”我想做个日历应用”，然后这套工具会依次跑：产品需求分析→CEO战略审查→工程架构评审→设计系统构建→编写代码（2400行约8分钟）→代码审查修复→真实浏览器QA→创建PR。全流程自动化。&lt;/p&gt;
 &lt;h2&gt;四、面向的人群有哪些？&lt;/h2&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;独立开发者&lt;/td&gt;
   &lt;td&gt;一个人搞定全栈产品&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;小团队（2-5人）&lt;/td&gt;
   &lt;td&gt;提升工程质量和发布效率&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;初创公司&lt;/td&gt;
   &lt;td&gt;减少人力成本，加速MVP迭代&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;大公司内部工具&lt;/td&gt;
   &lt;td&gt;为AI编码代理提供标准化流程&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;说实话，这套工具对个人开发者和小团队最有价值。如果你本身就在一个大公司、有完整的工程团队，这套东西可能没那么必要——你们的流程本来就有专人负责。但对于”全栈独立开发者”或者小团队，这就是效率放大器。&lt;/p&gt;
 &lt;h2&gt;五、应用场景有哪些？&lt;/h2&gt;
 &lt;p&gt;  &lt;strong&gt;场景1：快速原型验证&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;有个点子想快速验证？用office-hours+design-shotgun，AI帮你快速出产品方案和原型，不用先招人。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;场景2：代码质量把关&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;写了代码担心有问题？/review自动修复+/qa真实浏览器测试，这俩组合基本能覆盖大部分常见bug。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;场景3：安全审计&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;发布前想做个安全检查？/cso基于OWASP Top 10 + STRIDE模型，比大多数创业公司自己做的安全审查专业多了。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;场景4：持续部署&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;接入了CI/CD但每次发布还是提心吊胆？/land-and-deploy+/canary帮你做部署验证和上线后监控。&lt;/p&gt;
 &lt;h2&gt;六、和同类竞品的差异有哪些？&lt;/h2&gt;
 &lt;p&gt;gstack本质上是一套Claude Code的技能包，和普通的AI编码代理相比，核心差异在于：&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;th&gt;对比项&lt;/th&gt;
   &lt;th&gt;普通AI编码代理&lt;/th&gt;
   &lt;th&gt;gstack&lt;/th&gt;
&lt;/tr&gt;


  &lt;tr&gt;
   &lt;td&gt;角色分工&lt;/td&gt;
   &lt;td&gt;单代理啥都干&lt;/td&gt;
   &lt;td&gt;23个专业角色各司其职&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;代码审查&lt;/td&gt;
   &lt;td&gt;基础lint&lt;/td&gt;
   &lt;td&gt;CEO/工程架构级别审查&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;测试&lt;/td&gt;
   &lt;td&gt;简单单元测试&lt;/td&gt;
   &lt;td&gt;真实浏览器QA+安全审计&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;/tr&gt;
  &lt;tr&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;
 &lt;p&gt;但要注意，gstack主要是给Claude Code用户用的。不过它也支持其他AI编码代理：OpenAI Codex CLI、Cursor、Factory Droid等8种，算是有一定的跨平台能力。&lt;/p&gt;
 &lt;h2&gt;七、使用技巧&lt;/h2&gt;
 &lt;p&gt;  &lt;img alt="" src="https://s.w.org/images/core/emoji/16.0.1/72x72/1f4a1.png"&gt;&lt;/img&gt;   &lt;strong&gt;技巧1：先用office-hours明确定义产品&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;很多人直接让AI写代码，结果写了一半发现方向不对。在开始写代码之前先用/office-hours让AI追问你的需求，这个前置动作能省很多返工时间。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://s.w.org/images/core/emoji/16.0.1/72x72/1f4a1.png"&gt;&lt;/img&gt;   &lt;strong&gt;技巧2：并行sprint不是噱头&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;Garry Tan说支持10-15个并行sprint，这不是吹的。你可以让规划、设计、测试同时跑，对于有明确里程碑的项目确实能大幅压缩周期。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://s.w.org/images/core/emoji/16.0.1/72x72/1f4a1.png"&gt;&lt;/img&gt;   &lt;strong&gt;技巧3：review之前先确认代码规范&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;gstack的代码审查很强，但如果你有自己的代码规范文档，最好先提供给AI。不然它会按自己的风格来，可能会和你现有代码风格有出入。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://s.w.org/images/core/emoji/16.0.1/72x72/1f4a1.png"&gt;&lt;/img&gt;   &lt;strong&gt;技巧4：团队模式要用对&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;gstack有团队模式（–team参数），适合多人协作场景。但如果你是个人开发者，用默认模式就够了，别把简单事情搞复杂。&lt;/p&gt;
 &lt;h2&gt;八、对企业和个人的价值&lt;/h2&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;/tr&gt;


  &lt;tr&gt;
   &lt;td&gt;效率提升&lt;/td&gt;
   &lt;td&gt;1人顶N人&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;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;知识沉淀&lt;/td&gt;
   &lt;td&gt;学习Garry的工程思维&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;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;说实话，gstack对我最有吸引力的不是那些花哨功能，而是Garry Tan这个人本身——他是YC总裁，每天看几千个项目，什么样的工程实践是好实践，他比大多数人都清楚。这套工具反映的是他的工程哲学，花68k star去围观这个人的工作方式，本身就值回票价。&lt;/p&gt;
 &lt;h2&gt;九、产品定价&lt;/h2&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;/tr&gt;


  &lt;tr&gt;
   &lt;td&gt;开源版&lt;/td&gt;
   &lt;td&gt;免费&lt;/td&gt;
   &lt;td&gt;MIT许可证，全部功能开源&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;商业使用&lt;/td&gt;
   &lt;td&gt;需遵守MIT&lt;/td&gt;
   &lt;td&gt;无额外限制&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;gstack是100%开源项目，MIT许可证，基本没什么使用限制。如果你公司在用Claude Code，直接部署就行，不用额外付费。&lt;/p&gt;
 &lt;h2&gt;十、项目地址&lt;/h2&gt;
 &lt;p&gt;  &lt;strong&gt;GitHub仓库&lt;/strong&gt;：https://github.com/garrytan/gstack&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;快速安装（在Claude Code中运行）&lt;/strong&gt;：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;
git clone --single-branch --depth 1 https://github.com/garrytan/gstack.git ~/.claude/skills/gstack
cd ~/.claude/skills/gstack &amp;amp;&amp;amp; ./setup
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;团队模式安装&lt;/strong&gt;：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;
cd ~/.claude/skills/gstack &amp;amp;&amp;amp; ./setup --team
cd &amp;lt;your-repo&amp;gt;
~/.claude/skills/gstack/bin/gstack-team-init required
&lt;/code&gt;&lt;/pre&gt;
 &lt;h3&gt;总体评价&lt;/h3&gt;
 &lt;p&gt;gstack让我想起一句话：牛人不可怕，可怕的是牛人还比你努力。Garry Tan作为YC总裁，妥妥的人生赢家，结果他把压箱底的工具配置开源了，还附赠60万行代码的生产记录。这种量级的分享，在圈子里确实不多见。&lt;/p&gt;
 &lt;p&gt;从工具本身来说，23个专业角色覆盖了产品、设计、工程、测试、发布的完整流程，对于个人开发者和小团队来说确实能大幅提升效率。真实浏览器QA和安全审计这两个功能，在同类型工具里算是比较少见的。&lt;/p&gt;
 &lt;p&gt;但也要说，这套东西不是银弹。它最适合的场景是：你想快速验证产品方向、需要高质量代码输出、但团队人又不多。如果你本身就有完整的工程团队，这套东西能提供的增量价值有限。&lt;/p&gt;
 &lt;p&gt;另外，它强依赖Claude Code，如果你是Cursor或其他工具的重度用户，迁移成本不低。&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>AI AI编程助手 AI软件工厂 Claude Code工具 Garry Tan开源</category>
      <guid isPermaLink="true">https://itindex.net/detail/63193-gstack-yc-%E6%80%BB%E8%A3%81</guid>
      <pubDate>Thu, 09 Apr 2026 22:41:07 CST</pubDate>
    </item>
    <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>
  </channel>
</rss>

