<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
  <channel>
    <title>范的博客</title>
    <link>https://felix0080.github.io</link>
    <description>范的博客</description>
    
      <item>
        <title>LLM工具栈</title>
        <link>https://felix0080.github.io/2023/08/08/llm_practice.html</link>
        <guid isPermaLink="true">https://felix0080.github.io/2023/08/08/llm_practice.html</guid>
        <pubDate>Tue, 08 Aug 2023 00:00:00 +0000</pubDate>
        <description>&lt;h2 id=&quot;简介&quot;&gt;简介&lt;/h2&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#简介&quot; id=&quot;markdown-toc-简介&quot;&gt;简介&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#hugging-face&quot; id=&quot;markdown-toc-hugging-face&quot;&gt;Hugging Face&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#模型服务&quot; id=&quot;markdown-toc-模型服务&quot;&gt;模型服务&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#简单封装&quot; id=&quot;markdown-toc-简单封装&quot;&gt;简单封装&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#fastchat&quot; id=&quot;markdown-toc-fastchat&quot;&gt;FastChat&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#langchain&quot; id=&quot;markdown-toc-langchain&quot;&gt;LangChain&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#基础功能&quot; id=&quot;markdown-toc-基础功能&quot;&gt;基础功能&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#高级功能&quot; id=&quot;markdown-toc-高级功能&quot;&gt;高级功能&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#实战&quot; id=&quot;markdown-toc-实战&quot;&gt;实战&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#向量数据库&quot; id=&quot;markdown-toc-向量数据库&quot;&gt;向量数据库&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/machine/llm_tool.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;hugging-face&quot;&gt;Hugging Face&lt;/h2&gt;

&lt;p&gt;Hugging Face 自然语言处理（NLP）的开源平台和社区，主要提供了以下几个产品和服务：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Hub：这是一个机器学习的中心，让你可以创建、发现和协作ML项目。可以从排行榜开始，了解社区中表现较好的模型。如果你没有 GPU，你必须使用小的模型。转到文件目录并查看 .bin 文件的大小。有的项目在型号卡中也会提到所需的最低规格。PS：就像github 包含代码文件一样，这里包含代码的模型文件，git clone 时要安装Git LFS（Git Large File Storage）&lt;/li&gt;
  &lt;li&gt;Transformers：这是一个自然语言处理的库，支持多种编程语言（如Python、JavaScript、Swift等）和框架（如PyTorch、TensorFlow等），并提供了简单易用的API，让你可以快速地加载、训练和部署模型。帮我们跟踪流行的新模型，并且&lt;strong&gt;提供统一的代码风格来使用BERT、XLNet和GPT等等各种不同的模型&lt;/strong&gt;。只有configuration，models和tokenizer三个主要类，基于上面的三个类，提供更上层的pipeline和Trainer/TFTrainer，从而用更少的代码实现模型的预测和微调。
    &lt;ol&gt;
      &lt;li&gt;pipeline 在底层是由 AutoModel 和 AutoTokenizer 类来实现的。AutoClass（即像 AutoModel 和 AutoTokenizer 这样的通用类）是加载模型的快捷方式，它可以从其名称或路径中自动检索预训练模型。&lt;/li&gt;
      &lt;li&gt;```python
 from transformers import MODEL_NAME # 导入模型
 model = MODEL_NAME.from_pretrained(‘MODEL_NAME’) # 实例化模型，其中 MODEL_NAME 是模型的名称或路径。
 inputs = xx             # 准备输入数据，转换为模型支持的格式。（如 tokenizer 后的文本、图像等）
 outputs = model(inputs) # 调用模型并获得输出&lt;/li&gt;
    &lt;/ol&gt;

    &lt;p&gt;model.save_pretrained(‘PATH’)   # 将模型保存到指定路径
 ```&lt;/p&gt;
    &lt;ol&gt;
      &lt;li&gt;在Linux下，模型默认会缓存到&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.cache/huggingface/transformers/&lt;/code&gt;。所有的模型都可以通过统一的from_pretrained()函数来实现加载，transformers会处理下载、缓存和其它所有加载模型相关的细节。而所有这些模型都统一在Hugging Face Models管理。&lt;/li&gt;
      &lt;li&gt;最初我认为您需要为每个模型系列使用特定的Transformers和Tokenizer（例如，如果您使用T5模型系列，则对应T5Tokenizer和 T5ForConditionalGeneration），对于所有预训练模型，您可以声明一个简单的语句：
        &lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;transformers&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AutoTokenizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AutoModelForCausalLM&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;tokenizer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AutoTokenizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from_pretrained&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;databricks/dolly-v2-3b&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AutoModelForCausalLM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from_pretrained&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;databricks/dolly-v2-3b&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;Inference API：这是一个服务，让你可以直接从Hugging Face的基础设施上运行大规模的NLP模型，并在毫秒级别得到响应。&lt;/li&gt;
  &lt;li&gt;Datasets：这是一个数据集的库，让你可以获取、加载和处理超过1400个公开可用的数据集。Datasets支持多种数据类型（如文本、图像、音频等）和格式（如JSON、CSV等），并提供了高效且统一的API，让你可以快速地加载、缓存和转换数据。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;LangChain使用 Hugging Face 模型&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;使用在线模型
    &lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;os&lt;/span&gt;
 &lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;langchain&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PromptTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HuggingFaceHub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LLMChain&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;environ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'HUGGINGFACEHUB_API_TOKEN'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'xx'&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;Question: {question}
 Answer: Let's think step by step.&quot;&quot;&quot;&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;prompt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PromptTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input_variables&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;question&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;llm&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HuggingFaceHub&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;repo_id&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;google/flan-t5-xl&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model_kwargs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;temperature&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;max_length&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;llm_chain&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LLMChain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prompt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prompt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;llm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;llm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;question&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;What NFL team won the Super Bowl in the year Justin Beiber was born?&quot;&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;llm_chain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;question&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;将 Hugging Face 模型直接拉到本地使用（有些模型无法在 Hugging Face 运行）
    &lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;langchain&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PromptTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LLMChain&lt;/span&gt;
 &lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;langchain.llms&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HuggingFacePipeline&lt;/span&gt;
 &lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;transformers&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AutoTokenizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AutoModelForCausalLM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AutoModelForSeq2SeqLM&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;model_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'google/flan-t5-large'&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;tokenizer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AutoTokenizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from_pretrained&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AutoModelForSeq2SeqLM&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from_pretrained&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;pipe&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text2text-generation&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tokenizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tokenizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;local_llm&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HuggingFacePipeline&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pipeline&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pipe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;local_llm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'What is the capital of France? '&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
    
 &lt;span class=&quot;n&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;Question: {question} Answer: Let's think step by step.&quot;&quot;&quot;&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;prompt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PromptTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input_variables&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;question&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;llm_chain&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LLMChain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prompt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prompt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;llm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;local_llm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;question&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;What is the capital of England?&quot;&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;llm_chain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;question&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;模型服务&quot;&gt;模型服务&lt;/h2&gt;

&lt;h3 id=&quot;简单封装&quot;&gt;简单封装&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/THUDM/ChatGLM-6B&quot;&gt;ChatGLM-6B&lt;/a&gt; 在github 有一个仓库，一般包含&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;模型介绍 README.md&lt;/li&gt;
  &lt;li&gt;模型的对外接口 api.py/cli_demo.py/web_demo.py&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;以api.py 为例&lt;/p&gt;
&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;fastapi&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FastAPI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;transformers&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AutoTokenizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AutoModel&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;uvicorn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;torch&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FastAPI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create_item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;global&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tokenizer&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;json_post_raw&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;json_post&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dumps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json_post_raw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;json_post_list&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;json_post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;prompt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json_post_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'prompt'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;history&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json_post_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'history'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;max_length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json_post_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'max_length'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;top_p&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json_post_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'top_p'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;temperature&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;json_post_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'temperature'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;history&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tokenizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prompt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;history&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;history&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                   &lt;span class=&quot;n&quot;&gt;max_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_length&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max_length&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2048&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                   &lt;span class=&quot;n&quot;&gt;top_p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;top_p&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;top_p&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                                   &lt;span class=&quot;n&quot;&gt;temperature&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;temperature&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;temperature&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.95&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strftime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;%Y-%m-%d %H:%M:%S&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;answer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;response&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;history&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;history&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;status&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;time&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;[&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;] &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'&quot;, prompt:&quot;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prompt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'&quot;, response:&quot;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;repr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'&quot;'&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;torch_gc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;answer&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'__main__'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;tokenizer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AutoTokenizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from_pretrained&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;THUDM/chatglm-6b&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trust_remote_code&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AutoModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from_pretrained&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;THUDM/chatglm-6b&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;trust_remote_code&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;half&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cuda&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;eval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;uvicorn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'0.0.0.0'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;8000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;workers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;fastchat&quot;&gt;FastChat&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/lm-sys/FastChat&quot;&gt;FastChat&lt;/a&gt;是一个用于训练、服务和评估基于聊天机器人的大型语言模型的开放平台。核心功能包括:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;最先进模型(如Vicuna、FastChat-T5)的权重、训练代码和评估代码。&lt;/li&gt;
  &lt;li&gt;一个具有web界面和openai兼容的RESTful api的分布式多模型服务系统。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;langchain&quot;&gt;LangChain&lt;/h2&gt;

&lt;p&gt;LangChain is a framework for developing applications powered by language models. We believe that the most powerful and differentiated applications will not only call out to a language model, but will also be:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Data-aware: connect a language model to other sources of data&lt;/li&gt;
  &lt;li&gt;Agentic: allow a language model to interact with its environment&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;如果是一个简单的应用，比如写诗机器人，或者有 token 数量限制的总结器，开发者完全可以只依赖 Prompt。当一个应用稍微复杂点，单纯依赖 Prompting 已经不够了，这时候需要&lt;strong&gt;将 LLM 与其他信息源或者 LLM 给连接起来&lt;/strong&gt;（PS：重点不是模型服务本身），比如调用搜索 API 或者是外部的数据库等。LangChain 的主要价值在于：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;组件：&lt;strong&gt;用于处理语言模型的抽象，以及每个抽象的一系列实现&lt;/strong&gt;。无论您是否使用 LangChain 框架的其他部分，组件都是模块化且易于使用的。PS： 既可以单独用，也可以组合用。&lt;/li&gt;
  &lt;li&gt;现成的链式组装：用于完成特定高级任务的结构化组件组装。一些例子包括：
    &lt;ol&gt;
      &lt;li&gt;将 LLM 与提示模板结合。&lt;/li&gt;
      &lt;li&gt;通过将第一个 LLM 的输出作为第二个 LLM 的输入，按顺序组合多个 LLM。&lt;/li&gt;
      &lt;li&gt;将 LLM 与外部数据结合，例如用于问答系统。&lt;/li&gt;
      &lt;li&gt;将 LLM 与长期记忆结合，例如用于聊天历史记录。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;基础功能&quot;&gt;基础功能&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/liaokongVFX/LangChain-Chinese-Getting-Started-Guide&quot;&gt;LangChain 中文入门教程&lt;/a&gt;：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Model，主要涵盖大语言模型（LLM）。支持流模式（就是一个字一个字的返回，类似打字效果）。&lt;/li&gt;
  &lt;li&gt;Prompt，支持各种自定义模板&lt;/li&gt;
  &lt;li&gt;拥有大量的文档加载器，从指定源进行加载数据的，比如 Email、Markdown、PDF、Youtube …当使用loader加载器读取到数据源后，数据源需要转换成 Document 对象后，后续才能进行使用。&lt;/li&gt;
  &lt;li&gt;对索引的支持
    &lt;ol&gt;
      &lt;li&gt;文档分割器，为什么需要分割文本？因为我们每次不管是做把文本当作 prompt 发给 openai api ，还是还是使用 openai api embedding 功能都是有字符限制的。比如我们将一份300页的 pdf 发给 openai api，让它进行总结，它肯定会报超过最大 Token 错。所以这里就需要使用文本分割器去分割我们 loader 进来的 Document。&lt;/li&gt;
      &lt;li&gt;向量化，数据相关性搜索其实是向量运算。以，不管我们是使用 openai api embedding 功能还是直接通过向量数据库直接查询，都需要将我们的加载进来的数据 Document 进行向量化，才能进行向量运算搜索。&lt;/li&gt;
      &lt;li&gt;对接向量存储与搜索，比如 Chroma、Pinecone、Qdrand&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;Chains
    &lt;ol&gt;
      &lt;li&gt;LLMChain&lt;/li&gt;
      &lt;li&gt;各种工具Chain&lt;/li&gt;
      &lt;li&gt;LangChainHub&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Model，主要涵盖大语言模型（LLM）&lt;/p&gt;
&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;langchain.llms&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OpenAI&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#Here we are using text-ada-001 but you can change it 
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;llm&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OpenAI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text-ada-001&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;best_of&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#Ask anything
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;llm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Tell me a joke&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# 输出
&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Why did the chicken cross the road?&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;To get to the other side.'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Prompt，我们可以提供提示模板作为输入。模板指的是我们希望获得答案的具体格式或蓝图。LangChain 提供了预先设计好的提示模板，可以用于生成不同类型任务的提示。然而，在某些情况下，预设的模板可能无法满足你的需求。在这种情况下，我们可以使用自定义的提示模板。PS：上层用户只需要输入关键词即可。&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;langchain&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PromptTemplate&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# This template will act as a blue print for prompt
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;
I want you to act as a naming consultant for new companies.
What is a good name for a company that makes {product}?
&quot;&quot;&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;prompt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PromptTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_variables&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;product&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;prompt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;colorful socks&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# -&amp;gt; I want you to act as a naming consultant for new companies.
# -&amp;gt; What is a good name for a company that makes colorful socks?
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;LLMChain ，通过链式调用的方式，把一个需要询问 AI 多轮才能解决的问题封装起来，在这个链式序列中，每个链式都有一个输入和一个输出，&lt;strong&gt;一个步骤的输出作为下一个步骤的输入&lt;/strong&gt;。把一个通过自然语言多轮调用才能解决的问题，变成了一个函数调用。&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;chain&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;LLMChain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;llm&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;llm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prompt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prompt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;想要通过大语言模型，完成一个复杂的任务，往往需要我们多次向 AI 提问，并且前面提问的答案，可能是后面问题输入的一部分。LangChain 通过将多个 LLMChain 组合成一个 SequantialChain 并顺序执行，大大简化了这类任务的开发工作。Langchain 的链式调用并不局限于使用大语言模型的接口。&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;LLMMathChain 能够通过 Python 解释器变成一个计算器，让 AI 能够准确地进行数学运算。&lt;/li&gt;
  &lt;li&gt;通过 RequestsChain，我们可以直接调用外部 API，然后再让 AI 从返回的结果里提取我们关心的内容。&lt;/li&gt;
  &lt;li&gt;TransformChain 能够让我们根据自己的要求对数据进行处理和转化，我们可以把 AI 返回的自然语言的结果进一步转换成结构化的数据，方便其他程序去处理。&lt;/li&gt;
  &lt;li&gt;VectorDBQA 能够完成和 llama-index 相似的事情，只要预先做好内部数据资料的 Embedding 和索引，通过对 LLMChain 进行一次调用，我们就可以直接获取回答的结果。&lt;/li&gt;
  &lt;li&gt;Langchain 里有 SQLDatabaseChain 可以直接让我们写需求访问数据库。
这些能力大大增强了 AI 的实用性，解决了几个之前大语言模型处理得不好的问题，包括数学计算能力、实时数据能力、和现有程序结合的能力，以及搜索属于自己的资料库的能力。你完全可以定义自己需要的 LLMChain，通过程序来完成各种任务，然后合理地组合不同类型的 LLMChain 对象，来实现连 ChatGPT 都做不到的事情。&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;高级功能&quot;&gt;高级功能&lt;/h3&gt;

&lt;p&gt;Memory，在 LangChain 中，链式和代理&lt;strong&gt;默认以无状态模式运行，即它们独立处理每个传入的查询&lt;/strong&gt;。然而，在某些应用程序（如聊天机器人）中，保留先前的交互记录对于短期和长期都非常重要。这时就需要引入 “内存” 的概念，对整个对话的过程里我们希望记住的东西做了封装。&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;我们可以通过 BufferWindowMemory 记住过去几轮的对话，通过 SummaryMemory 概括对话的历史并记下来。也可以将两者结合，使用 BufferSummaryMemory 来维护一个对整体对话做了小结，同时又记住最近几轮对话的“记忆”。&lt;/li&gt;
  &lt;li&gt;可以使用 EntityMemory，它会帮助我们记住整个对话里面的“命名实体”（Entity），保留实际在对话中我们最关心的信息。&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;langchain.memory&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ChatMessageHistory&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;langchain.chat_models&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ChatOpenAI&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;chat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ChatOpenAI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;temperature&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;history&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ChatMessageHistory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 初始化 MessageHistory 对象
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;history&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_ai_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;你好！&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 给 MessageHistory 对象添加对话内容
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;history&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_user_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;中国的首都是哪里？&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;ai_response&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;history&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;messages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 执行对话
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ai_response&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Agent，某些应用可能需要不仅预定的 LLM（大型语言模型）/其他工具调用顺序，还可能需要根据用户的输入确定不确定的调用顺序。这种情况下涉及到的序列包括一个 “代理（Agent）”，该代理可以访问多种工具。根据用户的输入，代理可能决定是否调用这些工具，并确定调用时的输入。如果我们真的想要做一个能跑在生产环境上的 AI 聊天机器人，我们需要的不只一个单项技能，对于有很多个不同的“单项技能”，AI 要能够自己判断什么时候该用什么样的技能（意图识别问题）。通过“先让 AI 做个选择题”的方式，Langchain 让 AI 自动为我们选择合适的 Tool 去调用。我们可以把回答不同类型问题的 LLMChain 封装成不同的 Tool，也可以直接让 Tool 去调用特定能力的LLMChain 等工具。比如&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;langchain.agents&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;load_tools&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;langchain.agents&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;initialize_agent&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;langchain.llms&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OpenAI&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;langchain.agents&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AgentType&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;llm&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OpenAI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;temperature&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_tokens&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2048&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;# 加载 OpenAI 模型
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tools&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;load_tools&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;serpapi&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;                  &lt;span class=&quot;c1&quot;&gt;# 加载 serpapi 工具
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;agent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;initialize_agent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tools&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;llm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AgentType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ZERO_SHOT_REACT_DESCRIPTION&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;verbose&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 工具加载后都需要初始化，verbose 参数为 True，会打印全部的执行详情
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;What's the date today? What great events have taken place today in history?&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 运行 agent
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;实战&quot;&gt;实战&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;完成一次问答
    &lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;langchain.llms&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OpenAI&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;llm&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OpenAI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text-davinci-003&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_tokens&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;llm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;怎么评价人工智能&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;通过 Google 搜索并返回答案。 用agent&lt;/li&gt;
  &lt;li&gt;对超长文本进行总结。 我们通常的做法就是直接发给 api 让他总结。但是如果文本超过了 api 最大的 token 限制就会报错。这时，我们一般会进行对文章进行分段，比如通过 tiktoken 计算并分割，然后将各段发送给 api 进行总结，最后将各段的总结再进行一个全部的总结。
    &lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;n&quot;&gt;loader&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;UnstructuredFileLoader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/content/sample_data/data/lg_test.txt&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;     &lt;span class=&quot;c1&quot;&gt;# 导入文本
&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;document&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;loader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# 将文本转成 Document 对象
&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text_splitter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RecursiveCharacterTextSplitter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chunk_size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chunk_overlap&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;     &lt;span class=&quot;c1&quot;&gt;# 初始化文本分割器
&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;split_documents&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text_splitter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split_documents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# 切分文本
&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;llm&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OpenAI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;text-davinci-003&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max_tokens&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1500&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# 加载 llm 模型
&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chain&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;load_summarize_chain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;llm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chain_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;refine&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;verbose&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 创建总结链
&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chain&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split_documents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 执行总结链，（为了快速演示，只总结前5段）
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;构建本地知识库问答机器人
    &lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;n&quot;&gt;loader&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DirectoryLoader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'/content/sample_data/data/'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;glob&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'**/*.txt'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 加载文件夹中的所有txt类型的文件
&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;documents&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;loader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 将数据转成 document 对象，每个文件会作为一个 document
&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text_splitter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CharacterTextSplitter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chunk_size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chunk_overlap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 初始化加载器
&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;split_docs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text_splitter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split_documents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;documents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# 切割加载的 document
&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;embeddings&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OpenAIEmbeddings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 初始化 openai 的 embeddings 对象
&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;docsearch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Chroma&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from_documents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split_docs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;embeddings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 将 document 通过 openai 的 embeddings 对象计算 embedding 向量信息并临时存入 Chroma 向量数据库，用于后续匹配查询
&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;qa&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RetrievalQA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from_chain_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;llm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OpenAI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chain_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;stuff&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;retriever&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;docsearch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;as_retriever&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;return_source_documents&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 创建问答对象
&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;qa&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;query&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;科大讯飞今年第一季度收入是多少？&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 进行问答
&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;向量数据库&quot;&gt;向量数据库&lt;/h2&gt;

&lt;p&gt;chroma 是个本地的向量数据库，他提供的一个 persist_directory 来设置持久化目录进行持久化。读取时，只需要调取 from_document 方法加载即可。&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;langchain.vectorstores&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Chroma&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# 持久化数据
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;docsearch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Chroma&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from_documents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;documents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;embeddings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;persist_directory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;D:/vector_store&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
&lt;span class=&quot;n&quot;&gt;docsearch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;persist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# 从本地目录加载数据
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;docsearch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Chroma&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;persist_directory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;D:/vector_store&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;embedding_function&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;embeddings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
&lt;span class=&quot;c1&quot;&gt;# 录入documents 到chroma
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;loader&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DirectoryLoader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'/content/sample_data/data/'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;glob&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'**/*.txt'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 加载文件夹中的所有txt类型的文件
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;documents&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;loader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 将数据转成 document 对象，每个文件会作为一个 document
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text_splitter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CharacterTextSplitter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chunk_size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chunk_overlap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 初始化加载器
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split_docs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text_splitter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split_documents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;documents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 切割加载的 document
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;embeddings&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OpenAIEmbeddings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 初始化 openai 的 embeddings 对象
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;docsearch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Chroma&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from_documents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split_docs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;embeddings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 将 document 通过 openai 的 embeddings 对象计算 embedding 向量信息并临时存入 Chroma 向量数据库，用于后续匹配查询
# 创建问答对象
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;qa&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RetrievalQA&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from_chain_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;llm&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OpenAI&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chain_type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;stuff&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;retriever&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;docsearch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;as_retriever&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;return_source_documents&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# 进行问答
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;qa&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;query&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;科大讯飞今年第一季度收入是多少？&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Pinecone 是一个在线的向量数据库。所以，我可以第一步依旧是注册，然后拿到对应的 api key。&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# 从远程服务加载数据
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;docsearch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Pinecone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from_existing_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;embeddings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# 录入documents 持久化数据到pinecone
# 初始化 pinecone
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pinecone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;api_key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;你的api key&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;你的Environment&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;loader&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DirectoryLoader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'/content/sample_data/data/'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;glob&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'**/*.txt'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;documents&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;loader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 将数据转成 document 对象，每个文件会作为一个 document
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text_splitter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CharacterTextSplitter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chunk_size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chunk_overlap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;split_docs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;text_splitter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split_documents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;documents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 切割加载的 document
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;docsearch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Pinecone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from_texts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;page_content&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;split_docs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;embeddings&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 持久化数据到pinecone
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
      </item>
    
      <item>
        <title>ddd从理念到代码</title>
        <link>https://felix0080.github.io/2023/07/06/ddd_practice.html</link>
        <guid isPermaLink="true">https://felix0080.github.io/2023/07/06/ddd_practice.html</guid>
        <pubDate>Thu, 06 Jul 2023 00:00:00 +0000</pubDate>
        <description>&lt;h2 id=&quot;简介&quot;&gt;简介&lt;/h2&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#简介&quot; id=&quot;markdown-toc-简介&quot;&gt;简介&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#理念&quot; id=&quot;markdown-toc-理念&quot;&gt;理念&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#设计&quot; id=&quot;markdown-toc-设计&quot;&gt;设计&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#从概念开始理解ddd&quot; id=&quot;markdown-toc-从概念开始理解ddd&quot;&gt;从概念开始理解DDD&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#从分层开始理解ddd&quot; id=&quot;markdown-toc-从分层开始理解ddd&quot;&gt;从分层开始理解DDD&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#cqrs&quot; id=&quot;markdown-toc-cqrs&quot;&gt;CQRS&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#ddd框架&quot; id=&quot;markdown-toc-ddd框架&quot;&gt;ddd框架&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;理念&quot;&gt;理念&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://programmingpercy.tech/blog/how-to-domain-driven-design-ddd-golang/&quot;&gt;How To Implement Domain-Driven Design (DDD) in Golang&lt;/a&gt;
Domain-Driven Design is a way of structuring and modeling the software after the Domain it belongs to. What this means is that a domain first has to be considered for the software that is written. The domain is the topic or problem that the software intends to work on. The software should be written to reflect the domain. The architecture in the code should also reflect on the domain.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/y6H8UG-g829o0V0EBeEwrw&quot;&gt;迄今为止最完整的DDD实践&lt;/a&gt;一些套话&lt;/p&gt;
&lt;ol&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;/ol&gt;

&lt;h2 id=&quot;设计&quot;&gt;设计&lt;/h2&gt;

&lt;h2 id=&quot;从概念开始理解ddd&quot;&gt;从概念开始理解DDD&lt;/h2&gt;
&lt;p&gt;聚合根、领域对象、领域服务、领域事件、仓储、贫血充血模型、界限上下文、通用语言&lt;/p&gt;

&lt;h2 id=&quot;从分层开始理解ddd&quot;&gt;从分层开始理解DDD&lt;/h2&gt;
&lt;p&gt;https://github.com/KendoCross/kendoDDD 概念太多了，换一个角度，从MVC/MVP/MVVM的分层架构入手，类比理解DDD经典的四层。然后融合自己已有的编码习惯和认知，按照各层的主要功能定位，可以写的代码范围约束，慢慢再结合理解DDD的概念完善代码编写。&lt;/p&gt;

&lt;p&gt;分层，分层架构有一个重要的原则：每层只能与位于其下方的层发生耦合。&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;严格分层架构，某层只能与直接位于其下方的层发生耦合；自然是最理想化的，但这样肯定会导致大量繁琐的适配代码出现，故在严格与松散之间，追寻和把握恰达好处的均衡。&lt;/li&gt;
  &lt;li&gt;松散分层架构，则允许任意上方层与任意下方层发生耦合。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;各层理解&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;用户接口层/Presentation。负责向用户显示信息和解释用户指令
    &lt;ol&gt;
      &lt;li&gt;出入口主要的功用逻辑也尽量的简单，主要承接不同“表现”形式采集到的指令/出入参等，并进行转发给应用层。和不同的Web/RPC框架有一定的耦合，不同的框架代码不全一样。&lt;/li&gt;
      &lt;li&gt;该层的核心价值在于多样化，而不在于功能有多强大，不涉及到具体的业务逻辑&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;应用层，定义软件要完成的任务，并且&lt;strong&gt;指挥&lt;/strong&gt;表达领域概念的对象来解决问题。&lt;/li&gt;
  &lt;li&gt;应用层要尽量简单，不包含业务规则，只为下一层中的领域对象协调任务，分配工作。应用层是很薄的一层，只作为计算机领域到业务领域的过渡层。&lt;/li&gt;
  &lt;li&gt;这一层直接消费领域层，并且开始记录一些系统型功能，比如运行日志、事件溯源。 这一层的也应该尽可能的业务无关，以公用代码逻辑为主。&lt;/li&gt;
  &lt;li&gt;通过直接持有领域层的聚合根，infra层等直接进行业务表达。并将不常变化的domain model，转换为可能经常变化的view model。&lt;/li&gt;
  &lt;li&gt;领域层。负责表达业务概念，业务状态信息以及业务规则。尽管保存业务状态的技术细节由基础设施层提供，但反应业务情况的状态是由本层控制并使用的。
    &lt;ol&gt;
      &lt;li&gt;可以细拆分为聚合根、实体，领域服务等一大堆其他概念。
        &lt;ol&gt;
          &lt;li&gt;聚合根，负责整个聚合业务的所有功能就行了。比如项目中的fileAggregate，该类直接负责与平台系统管理员相关的所有操作业务，对内依赖调用领域服务、其他实体，或封装一些不对外的方法、函数等，完成所有所需的功能，由聚合根对外统一提供方法。&lt;/li&gt;
          &lt;li&gt;实体有唯一的标识，有生命周期且具有延续性。例如一个交易订单，从创建订单我们会给他一个订单编号并且是唯一的这就是实体唯一标识。同时订单实体会从创建，支付，发货等过程最终走到终态这就是实体的生命周期。订单实体在这个过程中属性发生了变化，但订单还是那个订单，不会因为属性的变化而变化，这就是实体的延续性。&lt;/li&gt;
          &lt;li&gt;实体的代码形态：我们要保证实体代码形态与业务形态的一致性。那么实体的代码应该也有属性和行为，也就是我们说的充血模型，但实际情况下我们使用的是贫血模型。贫血模型缺点是业务逻辑分散，更像数据库模型，充血模型能够反映业务，但过重依赖数据库操作，而且复杂场景下需要编排领域服务，会导致事务过长，影响性能。所以我们使用充血模型，但行为里面只涉及业务逻辑的内存操作。&lt;/li&gt;
          &lt;li&gt;实体的运行形态：实体有唯一ID，当我们在流程中对实体属性进行修改，但ID不会变，实体还是那个实体。&lt;/li&gt;
          &lt;li&gt;实体的数据库形态：实体在映射数据库模型时，一般是一对一，也有一对多的情况。&lt;/li&gt;
          &lt;li&gt;值对象：在 DDD 中用来描述领域的特定方面，并且是一个没有标识符的对象。值对象没有唯一标识，没有生命周期，不可修改，当值对象发生改变时只能替换（例如String的实现）。值对象是描述实体的特征，作为实体的属性，数据库里一般是一个字段。&lt;/li&gt;
          &lt;li&gt;聚合。The reason for an aggregate is that the business logic will be applied on the aggregate, instead of each Entity holding the logic. An aggregate does not allow direct access to underlying entities( all fields in the aggregate struct begins with lower case letters). aggregates  should only have one entity act as a root entity, this means that the root entity ID is the unique identifier of aggregate.&lt;/li&gt;
        &lt;/ol&gt;
      &lt;/li&gt;
      &lt;li&gt;领域层不依赖基础层的实现，Repository接口在领域层定义好，由infra层依赖领域层实现这个接口。数据库操作都是基于聚合根操作，保证聚合根里面的实体强一致性。PS: 一个domain一个repository接口&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;基础设施层。为上面各层提供通用的技术能力：为应用层传递消息，为领域层提供持久化机制等
    &lt;ol&gt;
      &lt;li&gt;这一层也是讲求和业务逻辑无关，只重点提供通用的功能。主要需要编码的部分是仓储功能，单纯的（增删改查）数据库持久化等功能，变更的概率不大。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;cqrs&quot;&gt;CQRS&lt;/h2&gt;

&lt;p&gt;命令查询职责分离&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;命令(Command):不返回任何结果(void)，但会改变对象的状态。实践时还是让命令返回了一些主键之类的。
    &lt;ol&gt;
      &lt;li&gt;命令抽象出来之后，可以串一下表现层/应用层对该命令的消费。
        &lt;ol&gt;
          &lt;li&gt;表现层：将表现层接受来的请求主体 转换为Command ，并且进行参数校验，触发Command执行。&lt;/li&gt;
          &lt;li&gt;应用层：命令的Handler，一般都有相关领域上下文的聚合根来承担。基本逻辑
  ```
  commandHanlder.Func{&lt;/li&gt;
          &lt;li&gt;实例化领域实体 // 查询我们需要处理的实体数据，然后创建对应的领域对象，构造我们所定义的聚合。&lt;/li&gt;
          &lt;li&gt;调用实体行为 // 调用行为会修改实体的属性是内存上的&lt;/li&gt;
          &lt;li&gt;保存实体变更 // 把变更保存到基础设施层，例如 MySQL，PG等
  ```&lt;/li&gt;
        &lt;/ol&gt;
      &lt;/li&gt;
      &lt;li&gt;每一个确定的命令操作，不论成功还是失败，只要执行之后就产生相应的事件（Event）。
        &lt;ol&gt;
          &lt;li&gt;事件的Handler，某个命令处理完毕之后，也即某个事件发生了，有可能需要短信通知、邮件通知等等。事件订阅者继续后续的逻辑。&lt;/li&gt;
        &lt;/ol&gt;
      &lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;查询(Query):返回结果，但是不会改变对象的状态，对系统没有副作用。查询可以从应用层进行分离，直接操作infra层获取业务不是特别复杂的查询，这与没有引入CQRS的代码可以保持一致。PS： domain只是服务command，且domain 的持久化相对简单，一般都是根据id 进行crud。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/ddd/ddd_layer_call.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;ddd框架&quot;&gt;ddd框架&lt;/h2&gt;

&lt;p&gt;ddd这么多年一直曲高和寡的一部分原因是，在代码层面缺少框架支持，用户从0到1使用ddd从概念理解上和代码实现上都成本非常大，给人带来的困惑、给团队带来的争论相比便利来说一点都不少，这点相对“声明式API + 控制器模型”之于kubebuilder/controller-runtime 都差距很大。既提供了大量辅助代码（比如client、workqueue等）、自动生成代码（比如clientset）以减少代码量，又显式定义了实现规范（比如crd包含spec和status）和约束（实现reconcile等）。&lt;/p&gt;

&lt;p&gt;ddd 可以封装的部分。比如抽象一个入口对象engine/bootstrap&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;用户接口层&lt;/li&gt;
  &lt;li&gt;触发comamnd执行。engine.disptch(“xxcommand”,param) / engine.runCommand(xxCommand{param})&lt;/li&gt;
  &lt;li&gt;应用层&lt;/li&gt;
  &lt;li&gt;command接口规范
    ```go
   // 通用command 接口
    // 将command的基本动作: 构建domain；domain.bizFunc; 保存domain 通过接口的形式固化下来
    type ICommand interface{
    }
    type xxCommand struct {&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;}
// xxHandler 为用户 在操作domain 时注入一些能力，或者说与engine 交互
func (xx xxCommand) handle(ctx context.Context, xx xxHandler){
    1. 构建domain
    2. domain.bizFunc
    3. xxHandler.cud(domain)
    4. eventbus.publish(event)
}
``` 3. domain层   1. 基础父类，比如IEntity，每个entity均需实现IEntity，以约束其提供GetID等实现。 4. infra层   1. domain model基于id的crud
1. domain model/entity与po的转换 5. 其它   1. 事件机制，entity变更时对外发出通知，可以减少domain.bizFunc 中关于非核心域的代码   2. 事务，一个聚合包含多个entity，持久化时保持一致性   3. 锁机制，在操作一个entity的时候，不允许其他线程操作entity，以免破坏一致性
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;用户接口层、应用层模式化（不同项目基本一样，只是改改名），domain层+repo层规范化，非业务/技术特性隐藏化。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/ddd/ddd_engine.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/ddd/ddd_engine_run_command.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</description>
      </item>
    
      <item>
        <title>如何应用LLM</title>
        <link>https://felix0080.github.io/2023/05/20/llm_try.html</link>
        <guid isPermaLink="true">https://felix0080.github.io/2023/05/20/llm_try.html</guid>
        <pubDate>Sat, 20 May 2023 00:00:00 +0000</pubDate>
        <description>&lt;h2 id=&quot;简介&quot;&gt;简介&lt;/h2&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#简介&quot; id=&quot;markdown-toc-简介&quot;&gt;简介&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#业务场景&quot; id=&quot;markdown-toc-业务场景&quot;&gt;业务场景&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#技术实现&quot; id=&quot;markdown-toc-技术实现&quot;&gt;技术实现&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#引入外部知识-的几个示例&quot; id=&quot;markdown-toc-引入外部知识-的几个示例&quot;&gt;引入外部知识 的几个示例&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#autogpt&quot; id=&quot;markdown-toc-autogpt&quot;&gt;AutoGPT&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#是否自建大模型&quot; id=&quot;markdown-toc-是否自建大模型&quot;&gt;是否自建大模型&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#微调大模型&quot; id=&quot;markdown-toc-微调大模型&quot;&gt;微调大模型&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#llmops&quot; id=&quot;markdown-toc-llmops&quot;&gt;LLMOps&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#其它&quot; id=&quot;markdown-toc-其它&quot;&gt;其它&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;早期的深度模型专模专用，严重依赖有监督学习，这就需要大量的任务相关的人工标注数据，代价昂贵。天下苦标注久矣，如果能够有一个与具体任务无关的大模型，只要利用任务相关的少量数据微调，就能够大杀四方，岂不美哉。&lt;/p&gt;

&lt;p&gt;ChatGPT 的技术结构，大家应该都有被科普到，其实就相当于续写，我们给出上半段，它来生成后半段，所以也被叫做生成式的预训练 Transformer 。而所谓的涌现出的能力，可以理解为我们给它提出不同的问题，它在续写的过程中，或者在不同的能力表现上，达标了过线了，再或者说，能力可用了。随着模型参数指数级的增长，之前，很多问题上不及格的回答，逐步变得及格、变得可用，这就是涌现的逻辑。当然，从结构上来说，大模型是一个数学逻辑，也就是说，底层是数学或者计算，但运作方式上是语文，所有问题，都以对话的形式予以反馈，而它表现出的能力却又是跨学科的。所以我们可以从不同的层面上，用不同的词来形容它。但底层的根本能力，都是计算。这就有点像我们上学时说的题海战术。所谓题海战术，就是我们做的题多了，看到题干，就知道要写什么答案，这个过程，我们可能并没有过多的思考或者甚至没有思考。而对于大模型来说，我们喂给它的语料，就相当于海量的题目，当大模型收到某一个类似的题目时，也就能自然地给出答案。&lt;/p&gt;

&lt;p&gt;大模型在两个方面有得天独厚的优势&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;理解能力很强，可以准确的理解人类的自然语音到底是什么意思。&lt;/li&gt;
  &lt;li&gt;推理能力很强，可以根据已有的信息，推断接下来要做什么。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/machine/llm_usage.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;业务场景&quot;&gt;业务场景&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/NMadeQkDdoIzJzqVUZOGlQ&quot;&gt;关于大模型技术的三点思考&lt;/a&gt;企业日常经营就是在不断地做决策并执行。决策就是说企业处理一个事情的时候，比如营销、分发，有很多种不同的做法，要选择什么样的做法。但决策类 AI 明显不好做。因为决策，上游需要高质量的数据作为输入，下游的具体服务得靠人提供。&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;先说下游执行服务的人。人的不确定性太多了，你出策略，即使 AI 做得再好，只是辅助，执行如果不到位，数字化也不会有进展。 执行，过去我们是人主要驱动的，即使有 CRM、ERP、HR，都是靠人，用非数字化的方式执行了之后，把结果一项一项填到系统里面去。&lt;/li&gt;
  &lt;li&gt;上游输入的数据也是个大问题。数据怎么来呢？完蛋了，执行的过程绝大多数企业压根没收集到数据。这里的“数据”，不是说你没有历史数据，而是你今天正在开展的业务里边没有足够的反馈数据。什么是反馈数据？员工跟企业之间的数据交换其实有三条通道。
    &lt;ol&gt;
      &lt;li&gt;接受信息、接受培训，比如视频、文字、PPT；&lt;/li&gt;
      &lt;li&gt;人与人之间的交流，跟同事跟主管跟客户的交流；&lt;/li&gt;
      &lt;li&gt;是用系统。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;做决策，我们只能用系统里边的这些数据去产生策略，但在这三条路径当中，系统能占多少？系统可能 5% 都占不到， 95% 的时间你是在阅读，在跟人聊天，在写 PPT、填表格、开会，所以95% 的数据都不在系统里。&lt;strong&gt;95%都是非结构化的、多模态的，我们没法抽象化、标准化，有的甚至都没法数据化&lt;/strong&gt;。这是数据的困境。&lt;/li&gt;
  &lt;li&gt;为什么让机器下围棋这件事能搞定，但过去很多的决策 AI 却难以落地呢？因为现实环境太复杂，一些领域内没有像围棋软件这样的系统，比如销售，没有一个销售系统可以记录销售过程中的每一步。&lt;strong&gt;但无论现实环境如何复杂，都可以转为一种对话式的沟通&lt;/strong&gt;，不管是供应链，还是营销。因为我们现在做的工作，就是通过一来一回的沟通解决。一定程度上，我们也可以把“一来一回沟通”这件事比作是数字化转型中的围棋软件。过去没有“围棋软件”，是因为系统没有一来一回沟通的能力，而且我们的&lt;strong&gt;软件是固化的，围棋专家只知道下围棋的套路，不懂怎么下象棋&lt;/strong&gt;。各领域只有各自专用的大模型，得分别开发。现如今大模型的出现，意味着系统具备了一来一回沟通的能力，也就意味着有了围棋软件。而且，现如今的软件，相对来讲有流动性较强的、最普遍的基础——交互式对话，再加上 GPT 模型可以用语文的方式学习所有问题，企业就可以用一套软件，萃取不同行业不同员工的优秀经验，并且用沟通的方式，干预稍微差一些员工，让这个软件被所有人用起来。这意味着，生成式 AI 可以通过对话框的形式，实现机器与人的沟通，抓取到原来系统抓取不到的另外 95% 的数据，帮企业做更多数字化的事情，或者提高数字化转型的效率。核心区别就是，过去的执行是人为，而非数字化，即使有 CRM 或者 HR 这样的系统，&lt;strong&gt;我们也只是将结果填入到系统而已&lt;/strong&gt;，就像两人下棋，我们只是将最后的输赢结果填入到系统中一样。但是在生成式 AI 出现后，通过巧妙的软件设计，可以将业务过程中每一步的数据都记录到系统中。当然，以前的也有可以记录每一步的软件，就像「滴滴出行」，但是由于资金、人员等各种限制条件，最终导致了行业软件化的进度缓慢。但有了大模型，这个进度就可以大大提升了。&lt;strong&gt;过去我们说的信息化，多是指把所有的业务步骤抽象为表单和流程&lt;/strong&gt;。但是，抽象为表单和流程，不如抽象为对话加 Word 、PPT、或者 Excel。过去我们经常听到企业自嘲说自己的数字化特别落后，基本都是基于 Excel 在做。其实，这反而是一个比较经济的数字化方式，Excel 功能非常强大，是一种表格式的生成软件，只是很多人不会用，数据难以得到有效利用。现在，生成式的 AI，其实就是用对话的方式，加上一些多模态，&lt;strong&gt;来标准化的生成软件，实现了从菜单式到对话式的交互进化&lt;/strong&gt;。PS： 一个模型一个task，一个工作一个软件（还只是负责了录入的部分），很多工作非标连软件都没有。&lt;/li&gt;
  &lt;li&gt;ChatGPT 让业界重拾了对 IM 机器人的重视，都认为对话框是数字化的最佳解法，这应该是轻物理资产、重人力要素的互联网企业的限定答案。在其他场景比如物流、驾驶、医疗中，人和机器的交互可能需要其他形式。医生做手术的时候能点对话框吗，不能，但他可以说话，这是一种交互状态；司机开车的时候能点对话框吗，也不能，但是可以用动作，这也是一种交互状态。所以 AIGC 这个事发展到未来，人和机器一定会是多模态的交互，而且是更接近于人的自然能力。IM 只是中间的一个过渡状态。从软件、表单过渡到 IM，中间从共享文档，变为对话框，再到未来的多模态拟人化交流，这个过程是愈发朝着“人”的角度发展的。我们看早期的输入法，在算力不足的情况下，我们必须要学习五笔字型，后来变为智能拼音、整句智能、语音转写，再到后来的直接语音，这条发展路径，是从人类被迫适应机器，到机器更加符合人性，我们可以跟软件、系统进行无障碍交流。&lt;/li&gt;
  &lt;li&gt;过去我们常说，真正能够成功的 SaaS 系统非常难做。&lt;strong&gt;SaaS 公司每天都面临着到底是定制化还是标准化的问题&lt;/strong&gt;。那我们能不能两者兼得呢？可以的——降低定制化成本，用标准化的成本完成定制化的需求。当然，&lt;strong&gt;如果是按照传统表单式、流程式的软件，是无法达成的&lt;/strong&gt;，但 AIGC 可以：基于生成式的大模型，，将一部分需求转嫁给算力，一部分转嫁给企业自身对业务的理解，同时将中间部分做薄，进而逐步解决问题。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href=&quot;https://segmentfault.com/a/1190000039173086&quot;&gt;KubeEye：Kubernetes 集群自动巡检工具&lt;/a&gt; 常规的巡检工具只能给一个错误event统计、按一定规则算个健康分 ==&amp;gt; &lt;a href=&quot;https://mp.weixin.qq.com/s/0xarxj49eM329o8xlSpmyg&quot;&gt;使用 ChatGPT 诊断 Kubernetes 问题 - K8sGPT&lt;/a&gt; 有了GPT 就可以更进一步怎么办（之前凭借工程师经验），再进一步，可以通过LangChain等工具排除直观一些的比如“版本不匹配”等错误原因。&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;default_prompt = `Simplify the following Kubernetes error message delimited by triple dashes written in --- %s --- language; --- %s ---.
 Provide the most possible solution in a step by step style in no more than 280 characters. Write the output in the following format:
 Error: {Explain error here}
 Solution: {Step by step solution here}

 Error node3 has multiple conditions of type MemoryPressure,DiskPressure,PIDPressure and Ready with reason NodeStatusUnknown caused by Kubelet stopped posting node status. Solution:
 1. Restart kubelet service od node3.
 2. Check node3's connectivity with the control plane.
 3. Check if the kubelet version is compatile with the control plane version.
 4. Check for any issue with the underlying hardware.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;AI大模型如何在行业实际落地：企业对话场景拥抱大模型之路&quot;&gt;AI大模型如何在行业实际落地：企业对话场景拥抱大模型之路&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;前几年，在数字化转型中，企业将一些简单的体力劳动、能总结出规律的活动，写成具体的程序，通过自动化校对的方式来实现。未来，情况可能会发生变化。大模型不但能够替代一些简单的体力劳动，还能替代一些简单的脑力劳动，甚至包括那些能够从日志里总结出经验的脑力劳动。&lt;/li&gt;
  &lt;li&gt;之前，企业数字化主要针对企业内部的交易数据和核心业务系统，对这些数据通过数据挖掘的方法进行建模，实现降本增效。随着近些年大模型的高速发展，对话数据成为企业愈发重视的数据资源。无论在现阶段还是未来，无论是企业与外部客户沟通还是企业内部员工的培训和协作，对话都一直是最主要、最自然的交互形式。在这期间，会产生很多对话数据，包括线下营销和线上营销、文字沟通和电话沟通等场景。过去，&lt;strong&gt;企业的数据只是存了下来，并没有进行结构化的表示和挖掘&lt;/strong&gt;，更遑论理解这些非结构化数据中蕴含的语义，提取出智能服务。企业希望充分利用对话数据、挖掘对话数据的价值，从而更好地服务于数字化的需求。比如通过电子工牌或呼叫中心将销售过程录下来，采用 ASR 语音转写技术将录音转成文本；再通过对话文本挖掘出用户的意图；随着对话过程不断进行，大模型可以实时生成流程图谱，给销售提供对话建议，分析潜在的话题引导方向，提升销售人员的营销技能，提高成单概率和用户的留存率。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/631250773&quot;&gt;大模型“吞噬”应用和生态&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;简单的应用，比如提供数据源(日历)和弱控制（家电控制），会被大模型系统聚合，这些应用蜕变成一个Plugin接入到这个大模型系统中，成为南向，而大模型系统则变成了一个超级应用，为所有这些plugin提供自然交互和控制能力，典型的例子是ChatGPT Plugin体系。&lt;/li&gt;
  &lt;li&gt;复杂的应用，则会被重构，演进到AI Native的架构，特点就是与大模型结合，使用大模型系统提供的SDK，实现全新的交互、控制和认知能力，成为大模型系统的北向，比如微软 365加持copilot，为365提供：
    &lt;ol&gt;
      &lt;li&gt;内容生成—释放创造力，word/ppt/excel等内容生成，帮你写草稿；&lt;/li&gt;
      &lt;li&gt;推理和归纳—释放生产力，自动执行任务/内容总结/自动回复邮件等；&lt;/li&gt;
      &lt;li&gt;提升技能：原来90%没有被大家普遍使用的功能和特性，都逐步被挖掘出来，比如在Excel中大量的公式，以前需要熟手才能使用，现在通过自然语言的交互和理解，就可以轻松使用。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/machine/llm_ecology.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/vTJkGHrTIhKNx79m6WLmhQ&quot;&gt;从原理到应用，人人都懂的ChatGPT指南&lt;/a&gt;按照感知其能力的直观性&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;聊天即交付&lt;/strong&gt;（Chat to Delivery(C2D)）。聊天能力，GPT的回答就是给客户的交付物，是GPT模型最简单、最直观的用法。
    &lt;ol&gt;
      &lt;li&gt;套壳聊天机器人&lt;/li&gt;
      &lt;li&gt;场景化问答。 对GPT的回复场景进行了约束。通过限定提示词、嵌入大量特定领域知识以及微调技术，使GPT能够仅基于某类身份回答特定类型的问题。对于其他类型的问题，机器人会告知用户不了解相关内容。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;语言能力。需要使用one-shot或few-shot（在提示词中给ChatGPT一个或多个示例）来提升ChatGPT的表现。与用户的交互不再局限于聊天窗口，提前预制&lt;strong&gt;提示词模板&lt;/strong&gt;，用户只能输入限定的信息，对应提示词的空槽位。将用户输入 放入预制好提示词模版的指定槽位，传给GPT。
 &lt;img src=&quot;/public/upload/machine/llm_prompt_template.jpg&quot; alt=&quot;&quot; /&gt;&lt;/li&gt;
  &lt;li&gt;文本能力，使用few-shot技巧，能解决训练数据中不存在的问题。&lt;/li&gt;
  &lt;li&gt;推理能力，用ChatGPT理解人类意图的能力，以GPT的推理能力替代手动点击操作流，将带来B端和C端的产品设计的颠覆式变化。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;企业会根据三大能力衍生出三大类角色：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;问题分解者，这类角色很清楚大语言模型能力的边界，能够将一个业务问题有效的分解为GPT能处理的子问题，并能根据问题结果，将子问题进行拼装。&lt;/li&gt;
  &lt;li&gt;提示工程师，这类角色深谙与GPT沟通之道，能够根据不同的问题类型，给出有效的提示词模板，极大提升GPT的输出质量。&lt;/li&gt;
  &lt;li&gt;知识拥有者，这类角色有大量的行业knowhow，并且能够将知识进行结构化，传授给GPT。对应现在的领域专家。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/machine/llm_role.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;基于某个规则让大模型帮忙筛选简历。一些简历pdf，从中提取特定信息，组成json 返回。再进一步，可以提出自己的需求，让大模型筛选出最接近满足需求的。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/JfEEJQyv9pdFB0-mBKC6UA&quot;&gt;通过大语言模型（LLM）识别与修复风险代码&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;技术实现&quot;&gt;技术实现&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/gr1zom0j361R5h9dDU84YA&quot;&gt;LLMOps的现在与将来&lt;/a&gt; 使用LLMs制作一些很酷的东西很容易，但是很难使它们成为生产型的。LLMOps是针对LLM的MLOps，&lt;strong&gt;我们的重点不是从头开始训练LLM，而是适应预训练的LLM用于下游任务&lt;/strong&gt;。这涉及选择基础模型、在下游任务中使用LLM、评估它们以及部署和监视模型。&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;选择基础模型。主流是Decoder-Only,但Encoder-Decoder仍有前景。如何挑选基座模型：模型的效果，推理速度，context大小、价格开销，能否微调，数据安全，许可协议等。&lt;/li&gt;
  &lt;li&gt;适应下游任务。 主要挑战在于，尽管LLM很强大，但它们并非万能的，因此关键问题是：如何使LLM给出您想要的输出？
    &lt;ol&gt;
      &lt;li&gt;Prompt Engineering，是一种调整输入以使输出与您的期望相匹配的技术，已经出现了像LangChain或HoneyHive的工具，帮助您管理和版本化提示模板。guardrails/guidance封装了输出的结构化检查和修复（如果不符合格式，生成相关错误信息，将上一次的生成内容和检查的错误信息告知 LLM，进行下一次的修正生成，直到生成的内容完全符合我们的要求）。
        &lt;ol&gt;
          &lt;li&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/647134737&quot;&gt;构建高性能 Prompt 之路——结构化 Prompt&lt;/a&gt;&lt;/li&gt;
          &lt;li&gt;https://github.com/dair-ai/Prompt-Engineering-Guide&lt;/li&gt;
        &lt;/ol&gt;
      &lt;/li&gt;
      &lt;li&gt;Fine-tuning pre-trained models，可以帮助改善模型在特定任务上的性能。虽然这将增加训练工作量，但它可以减少推理成本。&lt;strong&gt;LLM API的成本取决于输入和输出序列长度&lt;/strong&gt;，因此，减少输入标记的数量可以减少API成本，因为不再需要在提示中提供示例。 一般来说优先选择前者， 尤其是当前开源模型，fine tune 技术都没有那么成熟的情况下。什么时候需要 fine tune 呢？
        &lt;ol&gt;
          &lt;li&gt;你需要节省成本，比如用更小的模型，不想每次都带一大段 prompt 之类。&lt;/li&gt;
          &lt;li&gt;你有大量的数据，且 retrieval 的方法表现不够理想。&lt;/li&gt;
          &lt;li&gt;LoRA 与 QLoRA 训练技术 https://github.com/microsoft/LoRA 。对预训练好的大模型参数进行冻结，也就是在微调训练的时候，这些模型的参数设置为不可训练。然后往模型中加入额外的网络层，并只训练这些新增的网络层参数。这样可训练的参数就会变的非常少，可以以低成本的 GPU 微调大语言模型。LoRA 在 Transformer 架构的每一层注入可训练的秩分解矩阵，与使用 Adam 微调的 GPT-3 175B 相比，LoRA 可以将可训练参数数量减少 10000 倍，GPU 内存需求减少 3 倍，并且在效果上相比于传统微调技术表现的相当或更好。&lt;/li&gt;
          &lt;li&gt;所谓后训练量化是指在模型训练完成之后进行量化，模型的权重会从 32 位浮点数（或其他较高精度格式）转换为较低精度格式，例如 4 位整数。这种转换大大减小了模型的大小，并减少了运行模型所需的计算量。但是，这也可能会导致一定程度的精度损失。 GPTQ（Generative Pretrained Transformer Quantization）是一种新的后训练量化方法，可以有效地执行对有数百亿参数的模型的量化，并且能够将这些模型压缩到每个参数 3 或 4 位，而不会有显著的精度损失。&lt;/li&gt;
        &lt;/ol&gt;
      &lt;/li&gt;
      &lt;li&gt;外部数据，已经有一些工具可用，例如LlamaIndex（GPT Index）、LangChain或DUST，可以作为中央接口连接（“chaining”）LLM和其他代理和外部数据。&lt;/li&gt;
      &lt;li&gt;嵌入/embedding，从LLM API中提取嵌入形式的信息，（例如，电影摘要或产品描述），并在其上构建应用程序（例如，搜索、比较或推荐）。如果 np.array 不足以用于长期存储您的嵌入，可以使用向量数据库，如Pinecone、Weaviate或Milvus。PS：LLM加向量数据库这个范式，很像一个硬盘巨大但内存很小的电脑。随着LLM 支持更长的context，向量数据库可能会过时。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;评估，如何评估LLM的性能？如何确定响应是好还是坏？LLM 的能力非常强大，能处理各种任务，这对其评估造成了很大的困难，比如我们很难判断一篇总结是否比另外一篇总结写得更好。对于不同的 prompt，模型甚至 fine tune 的效果，如何进行快速，低成本且准确的评估是一个大问题。目前，似乎组织正在对其模型进行A/B测试。为了帮助评估LLM，出现了像HoneyHive或HumanLoop这样的工具。PS：一个办法是做好业务与大模型交互的抽象，没事按大模型榜单多换换，看看效果。
    &lt;ol&gt;
      &lt;li&gt;https://github.com/PKU-YuanGroup/ChatLaw 如何合理地评估垂直领域大模型的性能一直是一个问题，因为测试数据和真实场景存在差异，我们暂时没有完美的思路。我们只是收集了十余年的国家司法考试题目，整理出了一个包含2000个问题及其标准答案的测试数据集，用以衡量模型处理法律选择题的能力。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;部署和监控，例如，OpenAI已经更新了其模型以减轻不当内容的生成，例如仇恨言论。目前已经出现了一些监控LLM的工具，如Whylabs或HumanLoop。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/machine/llmops_workflow.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;假设一个场景，我希望chatgpt是个咨询专家，他可以回答我的一些问题。我问chatgpt一些问题，它会基于外部数据库和自身的能力给我回复。
这里会有两个问题需要考虑：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;提问的技巧，就是现在人们在热烈讨论的prompt engineer，是一种调整输入以使输出与您的期望相匹配的技术。PS：&lt;strong&gt;有点像 RHLF之后的二次对齐&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;如何利用外部知识，LLM通常缺乏上下文信息（例如，无法访问某些特定文档或电子邮件）并且可能很快过时，因为LLM如果没有足够的信息会产生幻觉，所以我们需要能够给它们提供相关的外部数据。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/machine/use_llm.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;由于LLM生成结果的不确定性和不准确性，目前还无法仅依靠LLM提供智能化服务。&lt;strong&gt;构建GPT应用远远不是调用API&lt;/strong&gt;。PS：就像操作linux 远远不止调用系统调用，有很多工程问题/发挥空间。&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;它的“脑子”也不完美，OpenAI 的训练数据截止至 2021 年，并且没有任何企业和个人的私有数据，这让模型只能根据自己的“记忆”回答问题，并且经常给出与事实相悖的答案。一个解决方法是在 Prompt 中将知识告诉模型，但是这往往受限于 token 数量，在 GPT-4 之前一般是 4000 个字的限制。&lt;/li&gt;
  &lt;li&gt;它只有“脑子”没有“手臂”，无法在外部世界行动，不论是搜索网页、调用 API 还是查找数据库，这些能力都无法被OpenAI的 API 提供；说白了只使用openAI不是商业应用，不能产品化。&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;引入外部知识-的几个示例&quot;&gt;引入外部知识 的几个示例&lt;/h3&gt;

&lt;p&gt;大语言模型的原理，就是利用训练样本里面出现的文本的前后关系，通过前面的文本对接下来出现的文本进行概率预测。如果类似的前后文本出现得越多，那么这个概率在训练过程里会收敛到少数正确答案上，回答就准确。如果这样的文本很少，那么训练过程里就会有一定的随机性，对应的答案就容易似是而非。&lt;/p&gt;

&lt;p&gt;LLM 擅长于一般的语言理解与推理，而不是某个具体的知识点。如何为ChatGPT/LLM大语言模型添加额外知识？&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;通过fine-tuning来和新知识及私有数据进行对话，OpenAI 模型微调的过程，并不复杂。你只需要把数据提供给 OpenAI 就好了，对应的整个微调的过程是在云端的“黑盒子”里进行的。需要提供的数据格式是一个文本文件，每一行都是一个 Prompt，以及对应这个 Prompt 的 Completion 接口会生成的内容。
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; {&quot;prompt&quot;: &quot;&amp;lt;prompt text&amp;gt;&quot;, &quot;completion&quot;: &quot;&amp;lt;ideal generated text&amp;gt;&quot;}
 {&quot;prompt&quot;: &quot;&amp;lt;prompt text&amp;gt;&quot;, &quot;completion&quot;: &quot;&amp;lt;ideal generated text&amp;gt;&quot;}
 {&quot;prompt&quot;: &quot;&amp;lt;prompt text&amp;gt;&quot;, &quot;completion&quot;: &quot;&amp;lt;ideal generated text&amp;gt;&quot;}
 ...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;有了准备好的数据，我们只要再通过 subprocess 调用 OpenAI 的命令行工具，来提交微调的指令就可以了。&lt;/p&gt;
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; subprocess.run('openai api fine_tunes.create --training_file data/prepared_data_prepared.jsonl --model curie --suffix &quot;ultraman&quot;'.split())
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
    &lt;p&gt;微调模型还有一个能力，不断收集新的数据，不断在前一个微调模型的基础之上继续微调我们的模型。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;通过word embeddings + pinecone数据库来搭建自己私有知识库。 chatgpt预训练完成后，会生成一个embeddings向量字典，比如我们可以将我们的私有知识库各个章节通过openai的相关api获取到对应的embeddings，然后将这些embeddings保存到向量数据库（比如 Facebook 开源的 Faiss库、Pinecone 和 Weaviate），当用户要对某个领域后者问题进行语义查询时，则将用户的输入同样通过openai的相关api来获取相应的embeddings向量，然后再和向量数据库pinecone中的我们的私有知识库类型做&lt;strong&gt;语义相似度查询&lt;/strong&gt;，然后返回给用户。PS： 内容向量化
    &lt;ol&gt;
      &lt;li&gt;比如判断某一段文本 是积极还是消极，向chatgpt 查询目标文本的向量，然后计算其与“积极” “消极” 两个词 embedding 向量的“距离”，谁更近，说明这段文本更偏向于积极或消极。&lt;/li&gt;
      &lt;li&gt;过几天openAI的模型版本升级了，这些保存的embedding会失效吗？特定模型也有带日期的快照版本，选取那些快照版本就好了。&lt;/li&gt;
      &lt;li&gt;向量是基于大模型生成的，因此对两段文本向量相似度计算必须基于同一个模型，不同的模型算出来的向量之间是没有任何关系的，甚至连维数都不一样。不过你可以把基于A 模型来算向量相似度进行检索把文本找出来，然后把找到的文本喂给B模型来回答问题。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;通过langchain这个chatgpt编程框架来给chatgpt赋能。 langchain可以将不同的工具模块和chatgpt给链接（chain）起来。&lt;/li&gt;
  &lt;li&gt;chatgpt 插件，比如有一个提供酒旅租车信息的插件
 &lt;img src=&quot;/public/upload/machine/chatgpt_plugins.jpg&quot; alt=&quot;&quot; /&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;比如针对问题：鲁迅先生去日本学习医学的老师是谁。因为 LLM（大语言模型）对上下文长度的限制，你不能将《藤野先生》整体作为提示语然后问“鲁迅在日本的医学老师是谁？”。 先通过搜索的方式，找到和询问的问题最相关的语料。可以用传统的基于关键词搜索的技术。也可以先分块存到向量数据库中（向量和文本块之间的关系），使用 Embedding 的相似度进行语义搜索的技术。然后，我们将和问题语义最接近的前几条内容，作为提示语的一部分给到 AI（&lt;strong&gt;使用检索结果作为 LLM 的 Prompt&lt;/strong&gt;）。然后请 AI 参考这些内容，再来回答这个问题。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/machine/use_llm_with_search.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/machine/llm_with_embedding.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这也是利用大语言模型的一个常见模式（这个模式实在太过常用了，所以有人为它写了一个开源 Python 包，叫做 llama-index）。因为&lt;strong&gt;大语言模型其实内含了两种能力&lt;/strong&gt;。PS：有点像推荐的粗排和精排，纯向量化的召回在一些Benchmark上表现还不如关键字搜索。&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;海量的语料中，本身已经包含了的知识信息。比如，我们前面问 AI 鱼香肉丝的做法，它能回答上来就是因为语料里已经有了充足的相关知识。我们一般称之为“世界知识”。&lt;/li&gt;
  &lt;li&gt;根据你输入的内容，理解和推理的能力。这个能力，不需要训练语料里有一样的内容。而是大语言模型本身有“思维能力”，能够进行阅读理解。这个过程里，“知识”不是模型本身提供的，而是我们找出来，临时提供给模型的。如果不提供这个上下文，再问一次模型相同的问题，它还是答不上来的。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Embedding 生成向量使用的模型  跟最后prompt 调用的模型可以不是同一个&lt;/strong&gt;，因此有的向量数据库也包含Segment 和 Embedding，&lt;strong&gt;通过自然语言就能直接和向量数据库交互&lt;/strong&gt;。也就是说，我们可以直接把文档扔给数据库，大段文本的切分，以及文本向量化，向量数据库 会帮我们处理。我们也可以直接把问题扔给数据库，请他来查询相似度较高的文本块，问题向量化以及检索的细节，向量数据库会帮我们处理。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/627655485&quot;&gt;基于大语言模型构建知识问答系统&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;传统搜索系统基于关键字匹配，在面向：游戏攻略、技术图谱、知识库等业务场景时，缺少对用户问题理解和答案二次处理能力。&lt;/li&gt;
  &lt;li&gt;领域知识不在预训练的数据集中，比如：
    &lt;ol&gt;
      &lt;li&gt;较新的内容。同一个知识点不断变更：修改、删除、添加。如何反馈当前最新的最全面的知识。比如对于 ChatGpt 而言，训练数据全部来自于 2021.09 之前。&lt;/li&gt;
      &lt;li&gt;未公开的、未联网的内容。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;基于 LLM 搭建问答系统的解决方案有以下几种：
    &lt;ol&gt;
      &lt;li&gt;Fine-Tuning&lt;/li&gt;
      &lt;li&gt;基于 Prompt Engineering，比如 Few-Shot方式。&lt;strong&gt;将特定领域的知识作为输入消息提供给模型&lt;/strong&gt;。类似于短期记忆，容量有限但是清晰。举个例子给 ChatGPT 发送请求，将特定的知识放在请求中，让 ChatGPT 对消息中蕴含的知识进行分析，并返回处理结果。&lt;/li&gt;
      &lt;li&gt;与普通搜索结合，使用基础模型对搜索结果加工。在做问答时的方式就是把 query 转换成向量，然后在文档向量库中做相似度搜索。
 &lt;img src=&quot;/public/upload/machine/llm_with_knowledge_base.jpg&quot; alt=&quot;&quot; /&gt;&lt;/li&gt;
      &lt;li&gt;用户输入query之后，首先先从知识库搜索到结果，然后基于搜索到的结果进行解析构造，生成新的prompt，然后调用LLM，LLM根据输入的prompt自行进行知识库的检索与plugins的调用
 &lt;img src=&quot;/public/upload/machine/use_llm_with_prompt.jpg&quot; alt=&quot;&quot; /&gt;&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;LLM 距离 AGI 的一大差距是没法与真实世界连接&lt;/strong&gt;。&lt;a href=&quot;https://mp.weixin.qq.com/s/weH_7K2g3sBMbtei1_dTng&quot;&gt;LLM 应用开发全栈指南&lt;/a&gt;以 SQL 工具为例展示了下 LLM 如何来利用外部工具。&lt;strong&gt;根据自然语言生成SQL语句&lt;/strong&gt;（Text2SQL）大致为以下几个步骤：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;用户问了一个问题&lt;/li&gt;
  &lt;li&gt;将用户问题和数据库的 meta 信息放到 prompt 里，让 LLM 去生成 SQL&lt;/li&gt;
  &lt;li&gt;利用数据库来执行这个 SQL 查询，这就是工具的调用&lt;/li&gt;
  &lt;li&gt;将数据库查询结果与问题再扔给 LLM 做最终回答&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;当然这个步骤可以说是由 Chain 定义固定下来的，也可以采用类似 agent/plugin 的方式来让 LLM 自行决定在何时使用什么工具。用户只需要提供 API 的 spec 和描述，就可以快速接入到 plugin 体系中。这两种方式主要的权衡在于可靠性或者是流程的确定程度。Chain 的运作流程是人工定义好的，流程不会出错，且对 LLM 来说生成具体的工具指令也会准确率更高。而 plugin 的优势在于极大的流程灵活度，可以用统一入口满足用户各类诉求。虽然可靠性会下降不少，但也可以考虑引入人工交互来弥补。&lt;/p&gt;

&lt;p&gt;LangChain实时推荐天气&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;通过一个 HTTP 请求，根据搜索词拿到 Google 的搜索结果页。&lt;/li&gt;
  &lt;li&gt;把我们定义的 Prompt 提交给 OpenAI，然后把我们搜索的问题和结果页都发给了 OpenAI，让它从里面提取出搜索结果页里面的天气信息。&lt;/li&gt;
  &lt;li&gt;最后我们通过 transform_func 解析拿到的天气信息的文本，被转换成一个 dict。这样，后面的程序就好处理了。&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;autogpt&quot;&gt;AutoGPT&lt;/h3&gt;

&lt;p&gt;AutoGPT是一个基于GPT-4语言模型的、实验性的开源应用程序，可以根据用户给定的目标，自动生成所需的提示，并执行多步骤的项目，无需人类的干预和指导（自己给自己提示）。AutoGPT的本质是一个自主的AI代理，可以利用互联网、记忆、文件等资源，来实现各种类型和领域的任务。这意味着它可以扫描互联网或执行用户计算机能够执行的任何命令，然后将其返回给GPT-4，以判断它是否正确以及接下来要做什么。下面举一个简单的例子，来说明AutoGPT的运行流程。假设我们想让AutoGPT帮我们写一篇关于太空的文章，我们可以给它这样的一个目标：“写一篇关于太空的文章”。然后AutoGPT会开始运行，它会这样做：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;AutoGPT会先在PINECONE里面查找有没有已经写好的关于太空的文章，如果有，它就会直接把文章展示给我们，如果没有，它就会继续下一步。&lt;/li&gt;
  &lt;li&gt;AutoGPT会用GPT-4来生成一个提示，比如说：“太空是什么？”，然后用GPT-4来回答这个提示，比如说：“太空是指地球大气层之外的空间，它包含了许多星球，卫星，彗星，小行星等天体。”&lt;/li&gt;
  &lt;li&gt;AutoGPT会把生成的提示和回答都存储在PINECONE里面，并且用它们来作为文章的第一段。&lt;/li&gt;
  &lt;li&gt;AutoGPT会继续用GPT-4来生成新的提示，比如说：“太空有什么特点？”，然后用GPT-4来回答这个提示，比如说：“太空有很多特点，比如说，太空没有空气，没有重力，没有声音，温度变化很大等等。”&lt;/li&gt;
  &lt;li&gt;AutoGPT会把生成的提示和回答都存储在PINECONE里面，并且用它们来作为文章的第二段。&lt;/li&gt;
  &lt;li&gt;AutoGPT会重复这个过程，直到它觉得文章已经足够长或者足够完整了，或者达到了一定的字数限制或者时间限制。&lt;/li&gt;
  &lt;li&gt;AutoGPT会把最终生成的文章展示给我们，并且询问我们是否满意。如果我们满意，它就会结束运行；如果我们不满意，它就会根据我们的反馈来修改或者补充文章。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;是否自建大模型&quot;&gt;是否自建大模型&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/625186095&quot;&gt;自建行业大模型的思考&lt;/a&gt;是否需要构建行业大模型？&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;通用 LLM会不会大力出奇迹，在没有经过专业领域数据训练的条件下就可以很好的完成专业领域任务？BloombergGPT的论文中实现表明：基于专业领域语料训练的大模型，在领域内的理解要超过通用大模型；&lt;/li&gt;
  &lt;li&gt;能否通过为Prompt填充领域知识的方式，让LLM具备解决专业领域任务的能力？可以，角色扮演就是一个例子
    &lt;ol&gt;
      &lt;li&gt;优点：灵活多变，可以适应各种场景，几乎没有训练成本，所有的行业知识通过prompt注入&lt;/li&gt;
      &lt;li&gt;缺点：大量领域知识会限制多轮对话或者prompt构造，模型输入有长度限制，如果加入了较多领域知识，就没有空间留给理会对话以及prompt；对于专业词汇的理解可能不准确&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;能否通过检索的方式，从本地知识库中查找出符合要求的答案返回？可以，例如：插件、AutoGPT
    &lt;ol&gt;
      &lt;li&gt;优点：没有训练成本；返回结果完全可控，即知识库内容&lt;/li&gt;
      &lt;li&gt;缺点：大模型+检索的方式分成两步走，性能会较慢；回答内容单一，缺乏泛化性；对于专业词汇的理解可能不准确&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;自建 or 购买&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;购买。
    &lt;ol&gt;
      &lt;li&gt;数据安全风险；比如使用 ChatCompletion 的接口，需要传入大量的上下文信息。&lt;/li&gt;
      &lt;li&gt;缺乏商业护城河；&lt;/li&gt;
      &lt;li&gt;不够灵活：新的需求和模型效果提升可能需要重新签订协议购买&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;自建。训练成本较高：数据成本、算力成本、试错成本；模型效果难以保证；对于模型训练算法工程师有较高要求&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;自建大语言模型可能遇到的问题&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;容易解决的问题
    &lt;ol&gt;
      &lt;li&gt;模型训练方案容易实现，现阶段开源LLM都有完善的训练和推理流程；&lt;/li&gt;
      &lt;li&gt;效果优异的基座模型，虽然中文开源LLM效果还没有英文的那么多、那么好，但是也已经达到了够用的水平；&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;不容易解决的问题
    &lt;ol&gt;
      &lt;li&gt;明确的LLM需求
        &lt;ol&gt;
          &lt;li&gt;高级收益需求：某个功能以前无法实现，LLM可以助力实现，并为公司带来较高收益；&lt;/li&gt;
          &lt;li&gt;次级收益需求：某个已经实现的功能，LLM可以对其优化，降本增效；&lt;/li&gt;
        &lt;/ol&gt;
      &lt;/li&gt;
      &lt;li&gt;训练数据难以构建，训练数据应该具备一下特征：训练大模型需要巨量数据；涵盖多种问题，同一问题还应该有多种表达方式；数据应该尽量准确，不能含有有毒数据；要包含大量通用文本语料，在二次训练过程中会有灾难性遗忘问题，需要通用语料保持LLM通用语言能力；要包含本公司业务领域的文本语料，提升LLM对于行业数据理解能力；最好包含多轮对话语料，使模型具备连续对话的能力；最好能有同一个问题的不同得分的回答，实现RLHF的训练&lt;/li&gt;
      &lt;li&gt;部署成本高昂：
        &lt;ol&gt;
          &lt;li&gt;LLM模型参数量巨大，一般自建模型参数量最小是7B，需要占用大量显存；&lt;/li&gt;
          &lt;li&gt;即使使用量化技术，推理服务QPS也不会很高；&lt;a href=&quot;https://mp.weixin.qq.com/s/HSJDNdgtFlaW16x2430m9g&quot;&gt;字节跳动提出高性能 transformer 推理库，获 IPDPS 2023 最佳论文奖&lt;/a&gt; 针对自然语言处理常见的可变长输入，论文提出了一套优化算法，这些算法在保证运算正确性的前提下，成功避免了传统实现中的冗余运算，实现了端到端的推理过程的大幅优化。&lt;/li&gt;
          &lt;li&gt;使用量化技术会让模型虽然会提升性能，降低显存占用，但是会极大损害效果；&lt;/li&gt;
          &lt;li&gt;较长的sequence length会占用大量的显存，增加计算消耗（时间复杂度是sequence length的平方）；&lt;/li&gt;
          &lt;li&gt;高性能GPU服务器成本较高&lt;/li&gt;
        &lt;/ol&gt;
      &lt;/li&gt;
      &lt;li&gt;大语言模型生成结果不可控：生成模型普遍存在生成结果不可控的问题；需要尝试不同的prompt才能得到令人满意的结果。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;微调大模型&quot;&gt;微调大模型&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;加载数据集，pytorch Dataset/DataLoader&lt;/li&gt;
  &lt;li&gt;构建模型，在实际操作中，除了使用预训练模型编码文本外，我们通常还会进行许多自定义操作，因此在大部分情况下我们都需要自己编写模型，不过不用从0写，更为常见的写法是继承 Transformers 库中的预训练模型来创建自己的模型。
    &lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;BertForPairwiseCLS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BertPreTrainedModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;     &lt;span class=&quot;c1&quot;&gt;# 继承 BERT 模型（BertPreTrainedModel 类）
&lt;/span&gt;     &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
         &lt;span class=&quot;nb&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
         &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bert&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BertModel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;add_pooling_layer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
         &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dropout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dropout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hidden_dropout_prob&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
         &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;classifier&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Linear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;768&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
         &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post_init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        
     &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;forward&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
         &lt;span class=&quot;n&quot;&gt;bert_output&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
         &lt;span class=&quot;n&quot;&gt;cls_vectors&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bert_output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;last_hidden_state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[:,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:]&lt;/span&gt;
         &lt;span class=&quot;n&quot;&gt;cls_vectors&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dropout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cls_vectors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
         &lt;span class=&quot;n&quot;&gt;logits&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;classifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cls_vectors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
         &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logits&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AutoConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from_pretrained&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;checkpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 通过预置的 from_pretrained 函数来加载模型参数
&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BertForPairwiseCLS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;from_pretrained&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;checkpoint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;device&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 加载 预置模型
&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;c1&quot;&gt;# Transformers 库同样实现了很多的优化器，相比 Pytorch 固定学习率，Transformers 库的优化器会随着训练过程逐步减小学习率（通常会产生更好的效果）
&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;optimizer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AdamW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parameters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;learning_rate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;train_loop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dataloader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;loss_fn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;optimizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,...):&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_loop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dataloader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'Test'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;epoch_num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;total_loss&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;train_loop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;train_dataloader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;loss_fn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;optimizer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lr_scheduler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;total_loss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;valid_acc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test_loop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;valid_dataloader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'Valid'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;valid_acc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;best_acc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
         &lt;span class=&quot;n&quot;&gt;best_acc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;valid_acc&lt;/span&gt;
         &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'saving new weights...&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
         &lt;span class=&quot;n&quot;&gt;torch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state_dict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;...)&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;#     # 保存模型
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;llmops&quot;&gt;LLMOps&lt;/h2&gt;

&lt;p&gt;钱，算力，数据哪个会成为大模型继续 scale 的瓶颈？总体来说最有可能成为瓶颈的是数据。在特定数据量下即使是无限的参数量都没法打败拥有更多数据量训练出来的有限参数量的模型。因为 retrieval 模式非常有效，所以大家自然会有想法说是不是不需要那么大的模型来记住各种知识点，而只需要一个拥有推理能力的小模型就可以？小模型可以在手机端，机器人设备上直接部署使用，想象空间还是非常大的。&lt;/p&gt;

&lt;p&gt;我们的重点不是从头开始训练LLM，而是适应预训练的LLM用于下游任务。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/machine/mlops_vs_llmops.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;其它&quot;&gt;其它&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/machine/beauty_of_llm.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

</description>
      </item>
    
      <item>
        <title>小鼠如何驾驭大象（LLM）？</title>
        <link>https://felix0080.github.io/2023/03/25/llm.html</link>
        <guid isPermaLink="true">https://felix0080.github.io/2023/03/25/llm.html</guid>
        <pubDate>Sat, 25 Mar 2023 00:00:00 +0000</pubDate>
        <description>&lt;h2 id=&quot;简介&quot;&gt;简介&lt;/h2&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#简介&quot; id=&quot;markdown-toc-简介&quot;&gt;简介&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#发展脉络&quot; id=&quot;markdown-toc-发展脉络&quot;&gt;发展脉络&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#自然语言处理基础&quot; id=&quot;markdown-toc-自然语言处理基础&quot;&gt;自然语言处理基础&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#机器怎么看懂人类的文字&quot; id=&quot;markdown-toc-机器怎么看懂人类的文字&quot;&gt;机器怎么看懂人类的文字？&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#pre-training&quot; id=&quot;markdown-toc-pre-training&quot;&gt;Pre-training&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#task-specific-fine-tuning&quot; id=&quot;markdown-toc-task-specific-fine-tuning&quot;&gt;Task-specific Fine-tuning&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#对于大模型的两种不同的期待&quot; id=&quot;markdown-toc-对于大模型的两种不同的期待&quot;&gt;对于大模型的两种不同的期待&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#gpt-1到chatgpt的演进&quot; id=&quot;markdown-toc-gpt-1到chatgpt的演进&quot;&gt;GPT-1到ChatGPT的演进&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#prompt&quot; id=&quot;markdown-toc-prompt&quot;&gt;Prompt&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#rlhf&quot; id=&quot;markdown-toc-rlhf&quot;&gt;RLHF&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#chatgpt-是怎么炼成的&quot; id=&quot;markdown-toc-chatgpt-是怎么炼成的&quot;&gt;chatgpt 是怎么炼成的？&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#其它&quot; id=&quot;markdown-toc-其它&quot;&gt;其它&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/pjUaRD0YV2qb6MXZ-oVstQ&quot;&gt;张宏江：大模型发展机会与挑战&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;当模型足够大，语料足够多的时候，涌现这件事情出现就不足为奇。这就好比把你甩到一个外语环境中，见得多听得多，根本不用专门学语法就可以学会语言，这就是语料和模型规模的重要性。看的句子多了，就懂得语法；见的世面多了，就懂得推理和逻辑。ChatGPT在认知能力上前进了一大步，通过强化学习与NLP（自然语言处理）相结合，通过人的反馈强化学习，基本解决了自然语言理解与生成问题，并且展现出人类无中生有的原创能力。&lt;/li&gt;
  &lt;li&gt;人们对知识的表示和调用发生了根本性变化。从关系数据库（SQL），到互联网信息检索，科技史上每次知识表示与调用方式的跃迁，都会掀起一次巨大的技术变革。&lt;/li&gt;
  &lt;li&gt;大模型作为基础平台支撑无数智能应用。大模型在内容创意生成、对话、语言或风格互译、搜索等方面的能力，将为各应用领域带来百花齐放。而大模型基础平台，在数据层、模型层、中间层、应用层，都蕴藏着巨大发展机遇。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/wvLj4eWCX8jdpCECLjIZmw&quot;&gt;热议的“中国版ChatGPT”，如何理解其意义？&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;张亚勤：我觉得可以把GPT这个系列的生成式AI模型看作一个由大模型组成的AI操作系统，和PC上的Windows，以及移动的安卓、iOS基本具有相似的意义。一个新的操作系统出来是什么意思？下面的硬件、上面的应用都会被重构、重塑，形成一个新的生态。&lt;/li&gt;
  &lt;li&gt;生成式对话产品会颠覆搜索引擎现有的商业模式，科技公司不得不自我革命。你也会这么认为吗？我觉得不是。要是你没有这个产品的话，别人会革你的命。我们在搜索的时候，其实是在找知识，那现在有了生成式技术，它确实提供了一种找到知识的新能力。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;姚志强：以前我们做的小模型，是就很像是人类的社会分工，直奔目标，比如这个算法就只做人脸识别、车牌识别，我去收集数据标注数据，然后去训练它，未来的可扩展性其实就没有很多。但现在大模型的训练，是&lt;strong&gt;将它真正作为一个人来训练&lt;/strong&gt;。我们用专家知识去引导他，给他启发性的这样思维，真正的去教导他去引导他，这是出现了“涌现”现象的原因。&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;未来，产品和技术演进思路全都要重新迭代，可能我们不会再有所谓的语音识别、自然语言理解、图像识别这些技术分类，剩下的只有AI——你是不是一家AI公司？判断AI基础能力的标准，可能就是大模型做得如何，在这个基础上再谈其他的行业模型、场景应用。&lt;/li&gt;
  &lt;li&gt;对我们整个产品体系来说或产品架构来说其实影响不太大，更多是提升了我们的性能。原先我们可能说要不断的要在这里面添加小模型，那现在，我只要替换成一个大模型。而中间可能会有一些特殊的行业线应用我可能会变成行业模型。&lt;/li&gt;
  &lt;li&gt;我们所说的人工智能三浪，第一浪是做是做单点的AI技术，比如做人脸识别、车牌识别，这是小模型时代了；第二浪是指把很多的技术进行组合，就串联起来，做成一个业务闭环，解决客户的实际问题，这里的流程会比较长，有时候可能不会因为一个点的技术提升就会导致整个流程有很大收益；第三浪是AI将成为未来世界的入口，形成平台和生态。我们认为，现在属于二浪到三浪的共振的时期。改变未来的交互的入口，大模型确实在做这样的事情。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;池建强：未来企业，AI 会成为一个生产要素。除了员工之外，还会有一批“AI 员工”。AIGC 在应用上确实是超出所有人预料，但 ChatGPT，从技术视角来看，其实没有太强冲击性。技术的发展是线性的，只是公众的感知是阶段性的。AI 发展，到现在真正有突破性成功的有 4 个阶段。&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;人来写规则，用人的经验来写程序。把专家的经验变成程序这个事现在业界还在实践。&lt;/li&gt;
  &lt;li&gt;机器利用数据，写少量的规则。这时候开始有一些模型，比如决策树、简单的神经网络。&lt;/li&gt;
  &lt;li&gt;机器用海量的数据，写大量的规则。这其实就是深度学习，或者叫专用大模型。广告、人脸识别就是用的这个。每个模型，只能解决一个问题。&lt;/li&gt;
  &lt;li&gt;通用大模型。海量的数据，同一个模型，解决不同的问题。形式都是 Transformer 的续写形式，但能力是多样的，比如问答、总结、扩写。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;发展脉络&quot;&gt;发展脉络&lt;/h2&gt;

&lt;p&gt;从分析式AI（input ==&amp;gt; 预测值）到生成式AI，从domain-specific到通用，从fine-tune 到Prompt。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/snXaWHr0VYFNYirSHRAvRw&quot;&gt;ChatGPT成功背后的技术原因及其对生命科学领域的启发&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;早在上个世纪五十年代，就有学者提出了人工智能（Artificial Intelligence）的概念，其目的是希望让计算机拥有人类智能（或部分人类智能）。这个领域经过很多年的发展，依然没有突破，直到2012年出现了深度学习技术。深度学习主要解决了模型表示能力的瓶颈。我们面对的建模问题，比如图像理解、语言翻译、语音识别、分子-蛋白结合构象预测等技术，都是非常复杂的非线性问题，在深度学习出现之前，模型表示能力很弱，无法对这些复杂问题进行精确表示。而&lt;strong&gt;深度学习技术，可以通过模型的层次堆叠，理论上可以构建任意深度的模型，突破了模型表示能力的瓶颈&lt;/strong&gt;，从而在语音识别、计算机视觉、自然语言理解等领域取得了突破性进展。&lt;/li&gt;
  &lt;li&gt;这个阶段的主要局限，是非常依赖于标注数据的数量。由于模型参数变多，想要求解这么多的模型参数，需要大量的训练数据作为约束。而&lt;strong&gt;想获得大量的标注数据非常贵，到亿级别之后就很难再有提升，数据支撑的有效模型大小也受到限制&lt;/strong&gt;。&lt;/li&gt;
  &lt;li&gt;2017年，一个重要的基础工作Transformer出现了。2019年，一个叫作BERT的工作脱颖而出，BERT采用了一个叫作自监督预训练的思路，无需标注数据&lt;strong&gt;仅利用文本语料本身存在的约束就可以训练模型&lt;/strong&gt;（比如某句话的某个位置只能用某些限定的词），这样互联网上存在的优质语料不需要进行人工标定就可以用来做训练，从而一下子使得可用训练数据的数量有了巨大的提高，再配合上大模型，使得BERT模型的效果远远超过过去的模型，并且在不同任务间具有很好的通用性，成为NLP领域里程碑工作之一。其实在BERT出现之前的2018年，还有个工作叫作GPT（即GPT1.0），更早利用了自监督预训练的思路来做文本生成，即输入前面的文本，模型预测输出后面的文本，领域里面的优质语料无需标注就可以做训练。&lt;strong&gt;BERT和GPT都是在Transformer基础上发展而来的，而Transformer也逐渐发展成为AI领域的通用模型&lt;/strong&gt;。&lt;/li&gt;
  &lt;li&gt;在自监督预训练技术出现之后，我们可以认为新一代人工智能发展到了第二个阶段，即自监督预训练技术使得可用训练数据有了几个数量级的提升，&lt;strong&gt;在训练数据大幅提升的支撑下，模型大小也有了数个数量级的提升（有效模型达到了千亿规模）&lt;/strong&gt;，而在模型效果上，这些模型变得不再依赖于下游任务领域数据的再训练，所以，&lt;strong&gt;领域进入到基于自监督预训练的通用大模型时代&lt;/strong&gt;。&lt;/li&gt;
  &lt;li&gt;ChatGPT为什么能有这样惊艳的效果？其中一个核心原因是ChatGPT基于生成大模型GPT3.5构建，这应该是当前自然语言理解领域文本生成最好的模型（GPT3.5比GPT3.0使用了更多的数据和更大的模型，具有更好的效果）。第二个核心原因则是基于人类反馈的强化学习技术：第一步，先收集用户对于同一问题不同答案的偏好数据；第二步，利用这个偏好数据重新训练GPT模型，这一步是基于监督信息的精调；第三步，根据用户对于不同答案的偏好，训练一个打分函数，对于ChatGPT的答案会给出分数，这个分数会体现出用户对于不同答案的偏好；第四步，用这个打分函数作为强化学习的反馈（Reward）训练强化学习模型，使得ChatGPT最终输出的答案更偏向于用户喜欢的答案。通过上述过程，ChatGPT在GPT3.5的基础上，针对用户输入，输出对用户更友好的回答。ChatGPT第一阶段训练GPT生成模型使用的训练数据非常多，大约在几十TB，训练一次模型需要花费千万美元，而第二个阶段，基于强化学习的少量优质数据反馈则只需要数万条优质数据。这种新的范式，&lt;strong&gt;有可能成为第三阶段人工智能的核心驱动技术，即首先基于自监督预训练的大模型，再结合基于少量优质数据反馈的强化学习、Prompting等技术，形成模型和数据的闭环反馈，获得进一步的技术突破&lt;/strong&gt;。如果这个技术走通，那么无人驾驶、机器人以及生命科学等数据获取昂贵的领域将显著受益。&lt;/li&gt;
  &lt;li&gt;ChatGPT并不能证明人工智能已经有了人类心智，ChatGPT表现出来的一些创造性和心智，是因为自然语言理解语料中包含了语义、逻辑，基于自然语言语料训练出来的生成模型，&lt;strong&gt;统计意义上学习到了这些对应关系&lt;/strong&gt;，看起来似乎有了智能，但并不是真的有人类心智。ChatGPT很棒，但说他智力等于几岁小朋友的说法，都不够严谨。因为从根本上讲，人学习新知识、进行逻辑推理、想象、运动反馈这些能力，目前AI还没有具备。&lt;/li&gt;
  &lt;li&gt;ChatGPT并不是一两个研究人员做出的算法突破，而是在先进理念指导下，非常复杂的算法工程体系创造出来的成果，需要在团队和组织上匹配（类比OpenAI和DeepMind）。纯研究型的团队恐怕不能成功，对深度学习理解不够、太工程化的团队也不会成功。这只团队需要：第一要有足够资源支持，可以支撑昂贵的深度学习训练和人才招聘；第二要有真正在工业界领导过工程化大模型团队的专家领导，ChatGPT不仅有算法创新，更是工程体系创新；第三，也可能是最重要的，需要一个团结协作有统一领导且不追求论文发表的组织（松散型的组织利于算法创新，但不利于工程化算法攻坚），且配备足够多优秀的工程和算法人才。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;脉络：由于自然语言任务种类繁多，且任务之间的差别不太大，所以为每个任务单独微调一份大模型很不划算 ==&amp;gt; pre-train model ==&amp;gt; 如何驾驭pre-train model?(Prompt Learning &amp;amp; Prompt Tuning）)&lt;/p&gt;

&lt;h2 id=&quot;自然语言处理基础&quot;&gt;自然语言处理基础&lt;/h2&gt;

&lt;p&gt;上世纪 50 年代到 70 年代，人们对用计算机处理自然语言的认识都局限在人类学习语言的方式上，当时学术界普遍认为，要让机器完成 NLP 任务，首先必须让机器理解语言。因此分析语句和获取语义成为首要任务，而这主要依靠语言学家人工总结文法规则。但是人类语言既复杂又灵活，仅靠手工编写的文法规则根本无法覆盖，规则之间还可能存在矛盾。因此这一阶段，可以说自然语言处理的研究进入了一个误区。正如人类是通过空气动力学而不是通过模仿鸟类造出了飞机，事实上进行自然语言处理也未必要让机器完全理解语言。70 年代，随着统计语言学的提出，基于数学模型和统计的方法开始兴起，NLP 领域才在接下来的四十多年里取得了一系列突破。80 年代以来，随着硬件计算能力的不断提高，以及互联网发展产生的海量数据，越来越多的统计机器学习方法被应用到自然语言处理中。2006 年，基于神经网络和反向传播算法进行训练的深度学习方法开始兴起，各种神经网络模型也被引入到自然语言处理任务中。&lt;/p&gt;

&lt;p&gt;自然语言处理(Natural Language Processing, NLP) 被誉为“人工智能皇冠上的明珠”，一方面表明了它的重要性，另一方面也突出了它的技术难度。简单来说，NLP要做的事就是利用计算机实现自然语言数据的智能化处理、分析和生成，以期让计算机实现听、说、读、写、译这些人类所具备的语言能力。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;词表示。
    &lt;ol&gt;
      &lt;li&gt;synonym and hypernym。用一个词相关的词来表示一个词&lt;/li&gt;
      &lt;li&gt;one-hot。假定所有的文字一共有 N 个单词（也可以是字符），我们可以将每个单词赋予一个单独的序号 id，那么对于任意一个单词，我们都可以采用一个 N 位的列表（向量）对其进行表示。 缺点：这样会导致词汇与词汇之间是没有任何关联的。&lt;/li&gt;
      &lt;li&gt;represent word by context。词通常有多重含义，需要根据单词出现的上下文以不同的向量表示同一个词。&lt;/li&gt;
      &lt;li&gt;word embedding。基于神经网络的词的向量表示方法。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;关键词的提取，关键词，顾名思义，就是能够表达文本中心内容的词语。
    &lt;ol&gt;
      &lt;li&gt;基于统计特征的方法&lt;/li&gt;
      &lt;li&gt;基于词图模型的关键词提取&lt;/li&gt;
      &lt;li&gt;基于主题模型的关键词提取&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;语言模型。语言模型是根据语言客观事实而进行的语言抽象数学建模，是一种对应关系。根据前文预测下一个词是什么：计算一个序列的词成为一句话的概率是多少；根据已经出现的词，计算某个词出现的概率
    &lt;ol&gt;
      &lt;li&gt;统计语言模型。本质是基于词与词共现频次的统计。给定一个句子 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;S=w1,w2,w3,…,wn&lt;/code&gt;，则生成该句子的概率为：&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;p(S)=p(w1,w2,w3,w4,w5,…,wn)&lt;/code&gt;，再由链式法则我们可以继续得到：&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;p(S)=p(w1)p(w2|w1)p(w3|w1,w2)…p(wn|w1,w2,…,wn-1)&lt;/code&gt;。那么这个 p(S) 就是我们所要的统计语言模型。有一个非常本质的问题并没有被解决，那就是语料中数据必定存在稀疏的问题，公式中的很多部分是没有统计值的，那就成了 0 了，而且参数量真的实在是太大了。
        &lt;ol&gt;
          &lt;li&gt;N-gram，前面出现的N个词，出现xx概率多大。但是如果 n 比较大，或者相关语料比较少的时候，数据稀疏问题仍然不能得到很好地解决。这就好比我们把水浒传的文本放入模型中进行统计训练，最后却问模型林冲和潘金莲的关系，这就很难回答了。因为基于 ngram 的统计模型实在是收集不到两者共现的文本。&lt;/li&gt;
        &lt;/ol&gt;
      &lt;/li&gt;
      &lt;li&gt;神经网络语言模型。a neural language model is a language model based on netual networks to learn distributed representations of words. 给每个词分别赋予了向量空间的位置作为表征，从而计算它们在高维连续空间中的依赖关系。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;有没有一种方法，可以&lt;strong&gt;把语言变成一种数学计算过程&lt;/strong&gt;，比如采用概率、向量等方式对语言的生成和分析加以表示呢？之前很方案都是基于 符号、统计的，引入神经网络之后，一般用一个向量表示一个词，向量是学习出来的，并且可以根据新的语料学习调整。&lt;/p&gt;

&lt;p&gt;什么是语言模型？其实每个人都用到过语言模型，那就是拼音输入法。拼音输入法的本质就是一个语言模型，用户给定一串拼音之后，比如“wo he ni”，每个拼音下都有很多的候选汉字，多个拼音就会组合爆炸，屏幕上不可能把所有组合展示出来让用户选，只能展示一个或几个最可能的汉字组合。可能性如何判断？这就要用到中文语言模型，任意一个中文串，语言模型可以给出这个中文串是一个正常人类句子的概率大小。比如“我和你”是正常人类句子的概率要明显大于“窝核腻”、“我喝泥”等等，所以最终给用户呈现“我和你”。此外，现代输入法通常都有联想功能，当你输入“wo he ni”之后，输入法不止能猜到你想输入“我和你”，还会给出后面的联想“我和你一起”，这也是基于语言模型。给定前面若干个单词，预测下一个单词是什么；然后模型将预测的单词加入到给定单词序列，继续预测下一个；如此递归直到预测下一个单词为结束符号或达到要求长度。如果一开始给定的单词序列为我们提问的一个问句，我们认为它的后续输出为问题的答案，那这个语言模型就变成了一个问答系统，ChatGPT的本质正是如此。&lt;/p&gt;

&lt;h2 id=&quot;机器怎么看懂人类的文字&quot;&gt;机器怎么看懂人类的文字？&lt;/h2&gt;

&lt;p&gt;OPENAI首席科学家llya：大型语言模型的理念是：如果有一个大型神经网络，我们可以对其进行训练，让它根据前面的文本内容预测下一个单词。再看最初的猜想：也许生物神经元和人工神经元极为类似，没有太大区别。那么，如果有一个可以准确预测下一个单词的大型神经网络，它的运转方式也许类似于人们谈话时生物神经元的运转方式。如果我们和这样的神经网络对话，因为它能够准确预测下一个单词，所以可以在理解对话的基础上，准确地缩小生成对话的可能性范围。精确猜测下一个单词需要进行预测，这也是理解的方式。我们很难清楚定义神经网络的“理解“，但我们可以轻易测量和优化网络对下一个单词的预测误差。&lt;strong&gt;我们想要神经网络拥有“理解”能力，但能做的是优化预测&lt;/strong&gt;。通过优化预测得到了目前的大型语言模型，它们都是用强大的反向传播算法训练的神经网络。&lt;/p&gt;

&lt;p&gt;llya：专业化训练 vs 通用训练？在某些情况下，专业化训练肯定能发挥巨大作用。我们进行通用化训练的原因仅仅是为了让神经网络能够理解我们所提出的问题。&lt;strong&gt;只有当它具有非常强大的理解能力时，我们才能进行专业化训练&lt;/strong&gt;，并真正从中受益。所以，这两种训练方向都有前景。在开源领域，人们已经开始进行专业化训练，因为他们使用的模型性能较弱，所以要尽可能地提升模型的性能。我们可以将AI看成是由多个元素组成的集合，每个元素都能对其性能作出贡献。在特定任务中，专业数据集可以使AI表现得更好；从所有任务角度出发，性能更强的基础模型无疑也更有用。所以答案就是：我们不必非要二选一，也可以将两者结合起来。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1eV411d7Kp&quot;&gt;李宏毅-BERT and its family&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/machine/pre_train_fine_tune.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;以往的NLP任务都是为每个任务分配一个模型，之后用&lt;strong&gt;对应任务大量的标注数据&lt;/strong&gt;去训练这个模型。Transformer出世后，模型对文字上下文的理解能力得到显著增强&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;首先，用无标注的数据（可以理解为一段普通的文字）训练一个预训练模型。在这个环节里，我们培养模型文字接龙的能力，也就是给定前k个词，模型能预测出第k+1个词。（pre-training）。&lt;/li&gt;
  &lt;li&gt;然后，在模型能够理解文字含义的基础上，用有标注的数据训练模型去定向做一些下游任务。例如文本分类，文本相似性比较等。有标注的数据集是远小于无标注数据集的，在这个环节，我们只是对模型做了一些微小的调整。（fine-tuning）。
这个过程和人类学习语言的过程是十分接近的。比如考托福考试，需要有听说读写这些测试。但是人类的学习的过程并不是直接去学习这些题目的做法，而是通过阅读大量的英文语料去掌握英文，之后再学习这个题目的解法，达到解题的效果。&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;pre-training&quot;&gt;Pre-training&lt;/h3&gt;

&lt;p&gt;给出一段文本，OpenAI 就能返回给你一个 Embedding 向量，这是因为它的背后是 GPT-3 这个超大规模的预训练模型（Pre-trained Model）。&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;这个 API 可以把任何你指定的一段文本，变成一个大语言模型下的向量，也就是用一组固定长度的参数来代表任何一段文本。&lt;/li&gt;
  &lt;li&gt;提前计算“好评”和“差评”这两个字的 Embedding。对于任何一段文本评论，我们也都可以通过 API 拿到它的 Embedding。&lt;/li&gt;
  &lt;li&gt;我们把这段文本的 Embedding 和“好评”以及“差评”通过余弦距离（Cosine Similarity）计算出它的相似度。然后我们拿这个 Embedding 和“好评”之间的相似度，去减去和“差评”之间的相似度，就会得到一个分数。如果这个分数大于 0，那么说明我们的评论和“好评”的距离更近，我们就可以判断它为好评。如果这个分数小于 0，那么就是离差评更近，我们就可以判断它为差评。
PS： 对于大模型，即便没有在针对具体的业务场景进行专门的训练（比如电影评论、商品评论），以上面情感分析为例，使用预训练模型给出来的向量，直接根据距离做的判断，其准确率也很厉害了。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;what is pre-train model（也被称为foundation model）？ &lt;strong&gt;represent each token by a embedding vector&lt;/strong&gt;.&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;contextualized word embedding。比如bank 单词有两种或三种意思，对于不同的意思的token，会根据其经常出现的上下文，学习一个 embedding token。 相关方法如ELMO等。&lt;/li&gt;
  &lt;li&gt;不同于以往的embedding：输入一个token，输出一个embedding。contextualized word embedding是在输入一整个句子以后，输出这个句子中各个token的embedding。这样这个token的embedding就是在看过这个token的上下文后，输出token embedding，这个embedding就包含了上下文的信息。&lt;strong&gt;每个词的向量，随着位置以及前后词的不同，编码出来的结果是不一样的。&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;contextualized word embedding的模型就像是一个encoder，用于编码信息。通常是非常深的网络，可以用到lstm，可以用自注意力层。
 &lt;img src=&quot;/public/upload/machine/pre_train_model.jpg&quot; alt=&quot;&quot; /&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;NLP任务一般又被分为自然语言理解（NLU）和自然语言生成（NLG）两大类，对应的不同的无监督预训练目标。其中，自回归（Autoregressive，简称AR）语言建模和自编码（Autoencoder，简称AE）一直是最成功的两个预训练目标。Transformer encoder是一个AE模型，Transformer decoder则是一个AR模型。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/machine/transformer_develop.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;AR模型，代表作GPT，从左往右学习的模型。AR模型从一系列time steps中学习，并将上一步的结果作为回归模型的输入，以预测下一个time step的值。AR模型通常用于生成式任务，优缺点如下：
    &lt;ol&gt;
      &lt;li&gt;优点：AR模型擅长生成式NLP任务。AR模型使用注意力机制，预测下一个token，因此自然适用于文本生成。此外，AR模型可以简单地将训练目标设置为预测语料库中的下一个token，因此生成数据相对容易。&lt;/li&gt;
      &lt;li&gt;缺点：AR模型只能用于前向或者后向建模，不能同时使用双向的上下文信息，不能完全捕捉token的内在联系。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;AE模型，代表作BERT，它不会进行精确的估计，但却具有从被mask的输入中，重建原始数据的能力，即fill in the blanks（填空）。&lt;strong&gt;测出该位置应该出现的那个正确词汇，这就意味着模型能够“吃透”上下文语义信息&lt;/strong&gt;。AE模型通常用于内容理解任务/只需要理解输入语义的任务，比如自然语言理解（NLU）中的分类任务：情感分析、提取式问答。AE模型的优缺点如下：
    &lt;ol&gt;
      &lt;li&gt;BERT使用双向transformer，&lt;strong&gt;在语言理解相关的任务中表现很好&lt;/strong&gt;。&lt;/li&gt;
      &lt;li&gt;输入噪声：BERT在预训练过程中使用【mask】符号对输入进行处理，这些符号在下游的finetune任务中永远不会出现，这会导致预训练-微调差异。而AR模型不会依赖于任何被mask的输入，因此不会遇到这类问题。且基于一个独立假设：在给定了unmasked tokens时，所有待预测（masked）的tokens是相互独立的。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;针对机器翻译这种序列到序列的任务（同时需要内容理解和生成的任务/适用于需要基于输入的生成式任务），往往会将模型组织为完整的编码器-解码器架构。它将每个task视作序列到序列的转换/生成（比如，文本到文本，文本到图像或者图像到文本的多模态任务）。
    &lt;ol&gt;
      &lt;li&gt;理论上是结合了 GPT 和 BERT 的优点，但会带来参数的暴涨，训练成本很高，google 提出T5后并未过多发展。T5 统一了 NLP 任务的形式，一切都可以是 Text2Text 的形式，与 GPT 解决问题的思路是一致的。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/machine/bert_vs_gpt.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;BERT&lt;/th&gt;
      &lt;th&gt;GPT&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&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;tr&gt;
      &lt;td&gt;预训练数据集不同&lt;/td&gt;
      &lt;td&gt;主要使用了BooksCorpus和Wikipedia数据集&lt;/td&gt;
      &lt;td&gt;使用了互联网上的大规模文本数据集，包括维基百科、新闻、小说等等&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;模型结构不同&lt;/td&gt;
      &lt;td&gt;由多个Transformer编码器组成，最后一层会输出整个输入序列的表示&lt;/td&gt;
      &lt;td&gt;由多个Transformer解码器组成，只输出最后一个单词的表示&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;how to pre-train?&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;最早的跟预训练有关的模型，应该是CoVe，是一个基于翻译任务的一个模型，其用encoder的模块做预训练。但是CoVe需要大量的翻译对，这是不容易获得的，能不能通过一大段没有标注的语料进行预训练呢？&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;比较直觉的self-supervised learning就是&lt;strong&gt;预测下一个单词是什么&lt;/strong&gt;。给出的解法就是将一个token输入到网络中，经过softmax之后，得到下一个token的概率分布。&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/public/upload/machine/pre_train.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;其中使用LSTM（上图的model = LSTM）做predict next token的工作有elmo，以及ulmfit。&lt;/li&gt;
  &lt;li&gt;使用self-attention的方式（上图的model = LSTM）进行next token prediction。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;有哪些预训练模型？&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Left-to-Right LM: GPT, GPT-2, GPT-3。适合文字接龙&lt;/li&gt;
  &lt;li&gt;Masked LM: BERT, RoBERTa。适合文字填空&lt;/li&gt;
  &lt;li&gt;Prefix LM: UniLM1, UniLM2&lt;/li&gt;
  &lt;li&gt;Encoder-Decoder: T5, MASS, BART&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;task-specific-fine-tuning&quot;&gt;Task-specific Fine-tuning&lt;/h3&gt;

&lt;p&gt;语言模型虽然可以对训练过的语言产生统计意义上的理解，例如可以根据上下文预测被遮盖掉的词语，但是如果直接拿来完成特定任务，效果往往并不好。因此，我们通常还会采用迁移学习 (transfer learning) 方法，使用特定任务的标注语料，以有监督学习的方式对预训练模型参数进行微调 (fine-tune)，以取得更好的性能。预训练是一种从头开始训练模型的方式：所有的模型权重都被随机初始化，然后在没有任何先验知识的情况下开始训练：这个过程不仅需要海量的训练数据，而且时间和经济成本都非常高。因此，大部分情况下，我们都不会从头训练模型，而是将别人预训练好的模型权重通过迁移学习应用到自己的模型中，即使用自己的任务语料对模型进行“二次训练”，通过微调参数使模型适用于新任务。&lt;/p&gt;

&lt;p&gt;how to fine-tune? fine-tune部分旨在根据预训练的model添加部分层从而可以解决下游任务。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/machine/fine_tune.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;如何在预训练好的模型中再加入一部分让其可以实现下游任务？&lt;/strong&gt;现有的NLP任务的分类，其中按照输入可以分为两类，按照输出可以分为四类(one class;class for each token;copy from input;general sequence)。&lt;/p&gt;

&lt;p&gt;输入可以分为两类&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;one sentence; 如句子分类&lt;/li&gt;
  &lt;li&gt;mutliple sentences； 比如QA，自然语言推理，需要在两个句子之间加入一个特殊的符号&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[SEP]&lt;/code&gt;（转为一个句子）。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;输出可以分为四类&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;one class; BERT的解法是让一个特殊的符号&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[CLS]&lt;/code&gt;作为整个句子的表示，之后将&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[CLS]&lt;/code&gt;的embedding输入到一个分类器中，进行分类任务。也可以像其他模型一样，将所有token的embedding都输入到一个模型中（可以是各个token embedding的均值，也可以是各个token输入到RNN，再得到下一层的嵌入），进行分类。&lt;/li&gt;
  &lt;li&gt;class for each token; 如果任务是对每一个token进行分类的话，就需要将每一个token的embedding输入到一个网络中，对其进行分类。&lt;/li&gt;
  &lt;li&gt;copy from input。 比如说Extraction-based QA任务，其任务就是输入一段原文和一个问题，之后在原文中标注好哪个token是开始，哪个token是结尾。BERT论文中的解法就是设计了两个可以训练的embedding（一个是start，一个是end），之后用start和end向量分别和BERT得到的嵌入求内积，之后再通过softmax，计算哪个最大，就是最后的结果。&lt;/li&gt;
  &lt;li&gt;general sequence。生成任务
    &lt;ol&gt;
      &lt;li&gt;BERT可以作为一个encoder，需要我们自己去设计一个decoder，但是decoder是没有经过预训练的。&lt;/li&gt;
      &lt;li&gt;让pre-training模型当作decoder来使用，其方法就是输入一个[sep]之后让model输出一个东西，再将模型的输出作为模型的输入，以此类推，不断的得到输出结果。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;如何进行fine-tune呢？&lt;a href=&quot;https://mp.weixin.qq.com/s/oqhi58NcEVH1oVrW2p4xlQ&quot;&gt;大模型参数高效微调技术原理综述（一）-背景、参数高效微调简介&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;全部微调：使用新的数据集对整个模型进行微调，包括预训练的部分和新加的任务特定层。这种方法通常需要大量的数据和计算资源，并且需要更长的训练时间，但能够获得最好的性能。&lt;/li&gt;
  &lt;li&gt;部分微调：只对特定的层或几个层进行微调，通常是模型的最后几层或添加的任务特定层。这种方法通常需要较少的数据和计算资源，训练时间也较短，但性能可能不如全局微调。&lt;/li&gt;
  &lt;li&gt;冻结部分层：将预训练模型的前几层（如 BERT 的前若干层）冻结，只对后面的层进行微调。这种方法可以减少微调的参数量和计算量，但可能会影响性能。&lt;/li&gt;
  &lt;li&gt;动态掩码微调：对于需要进行序列标注任务的预训练模型，可以使用动态掩码微调的方法，即只对标注序列的位置进行微调，对其他位置的参数进行冻结。这种方法可以减少微调的参数量，提高计算效率。&lt;/li&gt;
  &lt;li&gt;半监督微调：使用少量的标注数据和大量的无标注数据对模型进行微调，以提高性能。这种方法需要使用半监督学习的技术，如自监督学习或基于对抗学习的方法。&lt;/li&gt;
  &lt;li&gt;Prompt 工程：更偏向应用与业务，通过修改输入给大模型的提示词，调优输出结果。
前面五种，都需要一定的操作成本，且一般由算法工程师或所谓 AIGC 工程师来完成，最后一种是在解决大模型从生产到应用的“最后一公里”问题，目前是必备的，当下一般由算法工程师或其他技术角色兼任，或是企业招聘专人负责。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;对于大模型的两种不同的期待&quot;&gt;对于大模型的两种不同的期待&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1jA411274Q?p=9&quot;&gt;对于大模型的不同期待所衍生的两类使用方式&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;成为专才。比如bert（做文字填空的，所以不能“说话”）
    &lt;ol&gt;
      &lt;li&gt;在进行具体的任务之前需要改造：加外挂（即为模型加额外模块）和微调参数（fine tune）。&lt;/li&gt;
      &lt;li&gt;有机会在专一任务有机会跑赢通才。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;成为通才。
    &lt;ol&gt;
      &lt;li&gt;需要额外进行prompt。&lt;/li&gt;
      &lt;li&gt;Multitask learning as Question Answering。所有自然语言的处理都是问答问题。&lt;/li&gt;
      &lt;li&gt;只要重新设计 prompt 就可以快速开发新功能，不用写程序。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;比如做情感分析，你可以专门做一个情感分析的模型。也可以你给一点情感分析的例子，让模型去做文字接龙。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/machine/in_context_learning.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;再进一步，毕竟找范例有点麻烦，让机器根据叙述知道我们让它干嘛（instruction-tuning读懂人类的指令）。把任务用人类的语言描述出来，变成一个dataset，然后让llm 学到你让它干嘛，进而可以 generalized 到没有看过的指令上。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/machine/instruction_tuning.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;gpt-1到chatgpt的演进&quot;&gt;GPT-1到ChatGPT的演进&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/iohLXnQy_r1E_a2J6h5JAw&quot;&gt;ChatGPT解析系列：GPT1、GPT2与GPT3&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/iGV9ddCnH3M3-EW4l-TOYA&quot;&gt;GPT-1到ChatGPT的演进和技术原理&lt;/a&gt;Transformer的出现完全打开了大规模预训练语言模型(Pre-trained Language Model , PLM)的空间，并且奠定了生成式AI的游戏规则。&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Transformer相比之前论文的novalty在于：大胆地抛弃了传统的CNN和RNN基础模型，整个网络结构完全是由Attention机制组成。更准确地说，Transformer由且仅由自注意力(self-Attenion)机制和前馈神经网络(Feed Forward Neural Network)组成。&lt;/li&gt;
  &lt;li&gt;从实际应用的角度来看，Transformer的主要贡献(contribution)在于以下几个方面：&lt;/li&gt;
  &lt;li&gt;突破了RNN模型不能并行计算的限制&lt;/li&gt;
  &lt;li&gt;精度和模型复杂度相比RNN + Attention系列模型更优&lt;/li&gt;
  &lt;li&gt;Transformer本身也可以作为base model扩展&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;BERT仅使用了Transformer的编码器(Encoder)部分进行训练，而GPT-1则只使用了Transformer的解码器(Decoder)部分，实现了NLG层的统一。将海量知识融入到一个统一的模型中，而&lt;strong&gt;不针对每个特定任务分别训练模型&lt;/strong&gt;，使AI解决多类型问题的能力大大加强。&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;GPT-1: 预训练+微调模式，117M参数、12层、2亿单词。 &lt;strong&gt;利用 transformer 架构成功捕捉到了文本中相距较远的依赖关系&lt;/strong&gt;。PS：学习到了知识？
    &lt;ol&gt;
      &lt;li&gt;预训练阶段：基于Transformer Decoder架构，以语言建模作为训练目标（文字接龙）。&lt;/li&gt;
      &lt;li&gt;微调阶段：将训练好的Decoder参数固定，接上一层线性层，通过有监督训练任务（分类（Classification）、文本蕴含（Entailment）、相似性（Similarity）、多选题（Multiple Choice））微调线性层的参数，从而进行预测。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;GPT采用&lt;strong&gt;Masked-Attention&lt;/strong&gt;，对模型和训练数据的要求会更高，&lt;strong&gt;因为模型能读到的信息只有上文&lt;/strong&gt;。而采用&lt;strong&gt;普通Attention&lt;/strong&gt;的Bert在训练阶段就能同时读到上下文。这个性质决定了GPT模型越来越大的趋势（GPT需要把模型做大，训练数据做丰富，才能达到超越Bert的效果）。但是，长远来看，Masked-Attention是push模型更好理解文字的重要手段，毕竟在现实中，我们更希望培养模型知上文补下文，而不是单纯地做完形填空。GPT2的核心思想是：&lt;strong&gt;只要我的数据够多够好，只要我的模型够大够强。我可以直接去掉fine-tune，训练出一个通用的模型&lt;/strong&gt;。&lt;/li&gt;
  &lt;li&gt;GPT-2的目标是试图&lt;strong&gt;用一个模型去做多个NLP任务&lt;/strong&gt;，它的核心思想就反映在论文标题里：语言模型=无监督多任务学习。
    &lt;ol&gt;
      &lt;li&gt;通俗地解释一下：语言模型实际上是一种自监督的方式，根据已知的词预测未知的词，只是不需要显示地定义哪些字段是要预测的输出。那如何用无监督多任务的训练方式实现语言模型自监督训练+多任务微调的效果呢？我们只需要将input、output和task都表示为数据，例如在一个英文翻译成法语的机器翻译任务中，我们只需要将样本、标签和任务表示成&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(translate to french,english text,french text)&lt;/code&gt;，就实现了对&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;P(output|input,task)&lt;/code&gt;的建模。&lt;/li&gt;
      &lt;li&gt;重要的是，这种方式可以实现无监督训练，并且里面的task可以变化，也就是说现在GPT-2可以实现无监督多任务训练而不需要第二阶段分不同任务有监督的微调！所以，GPT2在训练数据上，玩出了花样。它从著名的在线社区Reddit上爬取训练数据（数据具有一定的问答特性），并按社区用户的投票结果筛选出优质的内容。&lt;/li&gt;
      &lt;li&gt;最后我们看到，GPT-2相对于GPT-1，最大的改进就是&lt;strong&gt;去掉了第二阶段的微调&lt;/strong&gt;(fine-tune)层，实现了多任务训练和zero-shot方式(Zero-shot learning，零样本学习)直接接诸多的下游任务，在多个任务下都可以取得很好的效果。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;GPT-3 基本继承了GPT-2的模型架构和训练模式，主要的改进点在于：in-context learning(上下文情境学习，ICL) 和 few-shot learning(小样本学习，FSL)配合服用。在 GPT2 的思路的指导下，我们通过把参数增加到 1750 亿，真地学到了‘世界知识’！但是，&lt;strong&gt;GPT3 掌握了知识，但是，它还不会干活呀&lt;/strong&gt;。‘1+1=2’这个知识，你知道它存储在你大脑里的具体哪个神经元上么？你当然不能。比如，你给它输入“给我写一段简介”，模型理解你确实说了“给我写一段简介”，但是，它此刻可以生成很多东西。比如生成一个“要简介限制在 120 个字以内”。它只是个生成模型，把你说的话续写下去，我们还得教一个理解了我们的话模型，按照我们想要的方式去生成结果。顾名思义，就是要通过一些‘问题-回答’对的训练数据。
    &lt;ol&gt;
      &lt;li&gt;in-context learning(上下文情境)学习机制：可以理解为给模型加一定的先验知识，适当对模型进行引导，教会它应当输出什么内容。只给提示就是zero-shot，给一个示例叫做one-shot，给少量多个示例就是few-shot。&lt;/li&gt;
      &lt;li&gt;zero-shot，只给出任务描述（description）和任务提示（prompt）。&lt;/li&gt;
      &lt;li&gt;one-shot/few-shot，给出任务描述，给出一个或若干个例子，给出任务提示。
 &lt;img src=&quot;/public/upload/machine/zero_one_few_shot.jpg&quot; alt=&quot;&quot; /&gt;&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;从GPT2到GPT3，Zero-shot和Few-shot的方式被证明可以使得模型能够去除fine-tune部分，训练一个通用的语言模型。&lt;/li&gt;
  &lt;li&gt;InstructGPT相对GPT-3要解决的是大模型的alignment(对齐)问题。积累大量指令对齐的 QA（问题-回答）训练数据，成本很高，此外大型语言模型会生成一些不真实、有毒(不符合人类道德伦理等)或对用户毫无帮助的输出，显然这些与用户期待的不一致。
    &lt;ol&gt;
      &lt;li&gt;大模型在预训练过程中见识了各种各样的数据，因此针对一个prompt/instruct(提示)会输出什么东西，也可能是多种多样的。但是&lt;strong&gt;预训练数据中出现的数据模式，不代表都是人类在使用模型时希望看到的模式&lt;/strong&gt;，原因是 predicting the next token on a webpage from the internet is different from the objective “follow the user’s instructions helpfully and safely”，&lt;strong&gt;预训练阶段的目标函数与人类对LLM真实的期望不一样&lt;/strong&gt;。因此需要一个alignment(对齐)的过程，来规范模型的“言行举止”。而实现这个过程InstructGPT引入了RLHF机制(人类反馈强化学习)，实际上6年前的AlphaGo正是充分利用了强化学习，才在围棋领域实现了所到之处无敌手。简单点说，InstructGPT就是在GPT-3基础上利用RLHF机制(人类反馈强化学习)做了微调，以解决大模型的alignment(对齐)问题。&lt;/li&gt;
      &lt;li&gt;整个过程就是老师（人类标注员）先注入一些精华知识，然后让模型试着模仿老师的喜好做出一些尝试，然后老师对模型的这些尝试进行打分，打分之后，学习一个打分机器，最后打分机器就可以和模型配合，自动化地进行模型的迭代，总体思路称为RLHF：基于人类反馈的强化学习。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/3Pr82xKpZ7mAWQcxPPB1xA&quot;&gt;从 GPT 到 ChatGPT 的演进与应用思考&lt;/a&gt;使用GPT-3执行NLP任务时，不需要重新更新模型，只需要向其发送一句提示（Prompt）例如「请给这段文字分类，类别标签有A、B、C」即可完成分类，或者可以使用少量标注数据作为例子告诉模型（Few-Shot），能够取得更优的效果。ChatGPT是从GPT-3发展而来的，符尧等人在《拆解追溯 GPT-3.5 各项能力的起源》一文中总结了GPT-3到GPT-3.5的进化树，GPT-3在OpenAI API中的模型名称为Davinci（达芬奇），之后经历在代码上训练、指令微调、RLHF（Reinforcement Learning from Human Feedback，基于人类反馈的强化学习）等过程，进化成ChatGPT，这里不再赘述。2022年11月，OpenAI除了发布ChatGPT之外，还发布了text-davinci-003模型，两者都是在text-davinci-002模型的基础上使用RLHF方法训练得到的，&lt;strong&gt;ChatGPT实际上不仅是一个单独的模型，而是一个完整的Web聊天机器人产品&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;‘1+1=2’这个知识，你知道它存储在你大脑里的具体哪个神经元上么？你当然不能。对齐，就是根据我们的目的，去整理模型学到了的知识。&lt;/p&gt;

&lt;h3 id=&quot;prompt&quot;&gt;Prompt&lt;/h3&gt;

&lt;p&gt;GPT-3论文定义：如果需要（基于梯度下降为主的算法）对模型参数进行更新，就是fine-tune。如果不需要修改模型和参数，只要给模型一些提示和样例，就让模型复合我们的一些要求完成一些任务就叫in-context learning，后面大家开始叫prompt。GPT 模型可以使用prompt 和 fine tune两种方式进行训练和使用，取决于具体的应用场景和任务需求。prompt 是第一种针对GPT 模型的特殊训练方式，&lt;strong&gt;不需要大量数据，不需要对模型参数进行改动，也就意味着可以不部署模型，而是接入公开的大预言模型服务（MaaS）&lt;/strong&gt;。缺点是，模型生成的内容受限于提示信息，不能够完全发挥模型的潜力。PS：即便是AGI，你也得在具体提问上需要告诉模型任务是什么。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/fLtYMwqefoDSeypOuY8bUA&quot;&gt;我对Prompt Engineering的理解&lt;/a&gt;“提示工程（Prompt Engineering）” 这一概念源于语言模型的发展，它描述了如何有效地&lt;strong&gt;利用提示从语言模型中提取信息&lt;/strong&gt;的过程，包括选择合适的词汇、语法、上下文和主题等元素。 PS：准备样本 ==&amp;gt; 准备提示。&lt;strong&gt;提示词的本质就是收窄范围&lt;/strong&gt;。p(input)=output，为了让模型输出期待的output，找到合适的input。&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;在语言模型中，“提示” 是用户提供给模型的输入。在 ChatGPT 中，它可以理解为你输入文本框的文字。然后，语言模型根据你的提示 “推断” 出一个 “补全”。&lt;/li&gt;
  &lt;li&gt;“提示工程” 是利用提示作为一种从模型中提取所需信息的方法。这种方法很有吸引力，因为你不需要大量的离线训练数据集，不需要离线访问模型，而且对于非工程师来说也很直观。提示只是调整模型的一种方式。&lt;/li&gt;
  &lt;li&gt;“提示工程” 是一种更严谨的领域，&lt;strong&gt;旨在利用提示作为一种为实际应用构建可靠功能的方法&lt;/strong&gt;。它与 ChatGPT 式的提示有所不同，因为通过提示工程生成的提示通常用于在高频、多样化的场景中反复使用，&lt;strong&gt;以便为应用程序可靠地解决特定问题&lt;/strong&gt;。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/620222350&quot;&gt;一文详解Prompt学习和微调（Prompt Learning &amp;amp; Prompt Tuning）&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;过去许多机器学习方法是基于全监督学习（fully supervised learning）的。由于监督学习需要大量的数据学习性能优异的模型，而在 NLP 中大规模训练数据（指为特定任务而标注好的数据）是不足的，因此在深度学习出现之前研究者通常聚焦于&lt;strong&gt;特征工程&lt;/strong&gt;（feature engineering），即利用领域知识从数据中提取好的特征；&lt;/li&gt;
  &lt;li&gt;在深度学习出现之后, 由于特征可以从数据中习得，因此研究者转向了&lt;strong&gt;结构工程&lt;/strong&gt;（architecture engineering），即通过通过设计一个合适的网络结构来把归纳偏置（inductive bias）引入模型中，从而有利于学习好的特征。&lt;/li&gt;
  &lt;li&gt;在 2017-2019 年，NLP 模型开始转向一个新的模式（BERT），即预训练 + 微调（pre-train and fine-tune）。在这个模式中, 先用一个固定的结构预训练一个语言模型（language model, LM），预训练的方式就是让模型补全上下文（比如完形填空）。由于预训练不需要专家知识，因此可以在网络上搜集的大规模文本上直接进行训练。然后这个 LM 通过引入额外的参数或微调来适应到下游任务上。此时研究者转向了&lt;strong&gt;目标工程&lt;/strong&gt;（objective engineering），即为预训练任务和微调任务设计更好的目标函数。&lt;/li&gt;
  &lt;li&gt;在做 objective engineering 的过程中，研究者发现&lt;strong&gt;让下游任务的目标与预训练的目标对齐是有好处的&lt;/strong&gt;（预训练是文字接龙，就把目标任务也改为文字接龙的形式，预训练是文字填空，就把目标任务也改为文字填空）。因此下游任务通过引入文本提示符（textual prompt），把原来的任务目标重构为与预训练模型一致的填空题。比如一个输入 “I missed the bus today.” 的重构：
    &lt;ol&gt;
      &lt;li&gt;情感预测任务。输入：“I missed the bus today.I felt so___.” 其中 “I felt so” 就是提示词（prompt），然后使用 LM 用一个表示情感的词填空。&lt;/li&gt;
      &lt;li&gt;翻译任务。输入：“English:I missed the bus today. French: ___.” 其中 “English:” 和 “French:” 就是提示词，然后使用 LM 应该再空位填入相应的法语句子。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;我们发现用不同的 prompt 加到相同的输入上，就能实现不同的任务，从而使得下游任务可以很好的对齐到预训练任务上，实现更好的预测效果。后来研究者发现，在同一个任务上使用不同的 prompt，预测效果也会有显著差异，因此现在有许多研究开始聚焦于 &lt;strong&gt;prompt engineering&lt;/strong&gt;。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/-lfq63NrsqUgmvYNzogCew&quot;&gt;五万字综述！Prompt Tuning：深度解读一种新的微调范式&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Prompt-Tuning自从GPT-3被提出以来，从传统的离散、连续的Prompt的构建、走向面向超大规模模型的In-Context Learning、Instruction-tuning和Chain-of-Thought。&lt;/li&gt;
  &lt;li&gt;自从GPT、EMLO、BERT的相继提出，以Pre-training + Fine-tuning 的模式在诸多自然语言处理（NLP）任务中被广泛使用，其先在Pre-training阶段通过一个模型在大规模无监督语料上预先训练一个 预训练语言模型（Pre-trained Language Model，PLM） ，然后在Fine-tuning阶段基于训练好的语言模型在具体的下游任务上再次进行 微调（Fine-tuning） ，以获得适应下游任务的模型。这种模式在诸多任务的表现上超越了传统的监督学习方法，不论在工业生产、科研创新还是竞赛中均作为新的主流方式。然而，这套模式也存在着一些问题。例如，在大多数的下游任务微调时，&lt;strong&gt;下游任务的目标与预训练的目标差距过大 导致提升效果不明显，微调过程中依赖大量的监督语料&lt;/strong&gt; 等。&lt;/li&gt;
  &lt;li&gt;至此，以GPT-3、PET为首提出一种基于预训练语言模型的新的微调范式——Prompt-Tuning ，其旨在&lt;strong&gt;通过添加模板的方法来避免引入额外的参数&lt;/strong&gt;，从而让语言模型可以在小样本（Few-shot）或零样本（Zero-shot）场景下达到理想的效果。&lt;/li&gt;
  &lt;li&gt;以二分类的情感分析作为例子，给定一个句子&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[CLS] I like the Disney films very much. [SEP]&lt;/code&gt;
    &lt;ol&gt;
      &lt;li&gt;传统的Fine-tuning方法是将其通过BERT的Transformer获得 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[CLS]&lt;/code&gt;表征之后再喂入新增加的MLP分类器进行二分类，预测该句子是积极的（positive）还是消极的（negative），因此需要一定量的训练数据来训练。&lt;/li&gt;
      &lt;li&gt;Prompt-Tuning则执行如下步骤：
        &lt;ol&gt;
          &lt;li&gt;构建模板（Template Construction） ：通过人工定义、自动搜索、文本生成等方法，生成与给定句子相关的一个含有&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[MASK]&lt;/code&gt;标记的模板。例如&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;It was [MASK]&lt;/code&gt;，并拼接到原始的文本中，获得Prompt-Tuning的输入：&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[CLS] I like the Disney films very much. [SEP] It was [MASK]. [SEP]&lt;/code&gt;。将其喂入BERT模型中，并复用预训练好的MLM分类器，即可直接得到&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[MASK]&lt;/code&gt;预测的各个token的概率分布；&lt;/li&gt;
          &lt;li&gt;标签词映射（Label Word Verbalizer） ：因为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[MASK]&lt;/code&gt;部分我们只对部分词感兴趣，因此需要建立一个映射关系。例如如果&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[MASK]&lt;/code&gt;预测的词是“great”，则认为是positive类，如果是“terrible”，则认为是negative类。&lt;/li&gt;
        &lt;/ol&gt;
      &lt;/li&gt;
      &lt;li&gt;不同的句子应该有不同的template和label word，没错，因为每个句子可能期望预测出来的label word都不同，因此如何最大化的寻找当前任务更加合适的template和label word是Prompt-tuning非常重要的挑战。&lt;/li&gt;
      &lt;li&gt;其实我们可以理解，引入的模板和标签词本质上也属于一种数据增强，通过添加提示的方式引入先验知识。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;GPT-3开创性提出 in-context learning 概念，即无须修改模型即可实现few-shot/zero-shot learning。同时引入了demonstrate learning，即让模型知道与标签相似的语义描述，提升推理能力。
    &lt;ol&gt;
      &lt;li&gt;In-context Learning ：是Prompt的前身。其通过从训练集中挑选一些样本作为任务的提示提示（Natural Language Prompt），来实现免参数更新的模型预测；&lt;/li&gt;
      &lt;li&gt;Demonstration Learning ：添加一些新的文本作为提示。例如在对“I like the Disney film. It was [MASK]”进行情感分析时，可以拼接一些相似场景的ground-truth文本“I like the book, it was great.”、“The music is boring. It is terrible for me.”等。此时模型在根据新添加的两个样例句子就可以“照葫芦画瓢”式地预测结果了。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/AizUBFssilX2S9aRfuz3_g&quot;&gt;ChatGPT Prompt工程：设计、实践与思考&lt;/a&gt; 未细读。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/7eah_qMhol4EBwrz1bzxeA&quot;&gt;关于AI未来，我有十二条自用的思考Prompt分享&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;AI大模型如何在行业实际落地：企业对话场景拥抱大模型之路&quot;&gt;AI大模型如何在行业实际落地：企业对话场景拥抱大模型之路&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;提示工程主要是为了解决预训练语言模型训练过程的任务和实际业务的任务之间不一致的问题。通过提示语，可以让预训练语言模型理解当前任务的类型，从而可以更好地完成任务。随着 NLP 技术的飞速发展，现在的提示工程已变得更为复杂，&lt;strong&gt;提示语通常包含任务指令、任务目标、行为约束、输出规范、资源清单、样例展示和思维能力提示等要素&lt;/strong&gt;。给每个任务找到合适的提示语还是一个很大的挑战。不同的任务需要差异化的 Prompt 模版（PromptTemplate），从指令设计、样例选择、样例的顺序以及推理过程等细节进行 prompt 的优化微调，每一个环节都可能影响到 Prompt 在实际应用的场景效果。测试结果显示，在意图识别上，不同的 Prompt 的准确率能达到 2%~80% 的巨大差距。&lt;/li&gt;
  &lt;li&gt;Prompt 工程可能是暂时的一个中间过程。只是说，&lt;strong&gt;现在大模型的能力还没有达到基于人工设定的复杂任务目标去自主性进行任务分解，然后根据这些任务转化成一种它可以直接解决的细粒度的自然语言任务&lt;/strong&gt;。现在大模型需要中间的提示工程师帮助它理解任务，然后转化成它可以直接执行的自然语言任务，这中间是一个适配的过程”。未来随着大模型的能力向更高层级提升，会覆盖掉现有的 Prompt 工程。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;零样本&lt;/p&gt;
&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;langchain&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PromptTemplate&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;What is a good name for a company that makes {product}?&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;prompt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PromptTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;input_variables&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;product&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;prompt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;product&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;colorful socks&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;小样本&lt;/p&gt;
&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;langchain&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PromptTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FewShotPromptTemplate&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;examples&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;word&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;happy&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;antonym&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;sad&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;word&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;tall&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;antonym&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;short&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;example_template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;
Word: {word}
Antonym: {antonym}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;
&quot;&quot;&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;example_prompt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PromptTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;input_variables&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;word&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;antonym&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;example_template&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;few_shot_prompt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FewShotPromptTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;examples&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;examples&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;example_prompt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;example_prompt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Give the antonym of every input&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;suffix&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Word: {input}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Antonym:&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;input_variables&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;input&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;example_separator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;few_shot_prompt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;big&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以上代码将生成一个提示模板，并根据提供的示例和输入组合成以下提示：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Give the antonym of every input

Word: happy
Antonym: sad

Word: tall
Antonym: short

Word: big
Antonym:
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;rlhf&quot;&gt;RLHF&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/4USDakdomupWuwwhex6fMg&quot;&gt;为什么ChatGPT用强化学习而非监督学习？&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;模型通过生成答案并接收反馈来学习
    &lt;ol&gt;
      &lt;li&gt;在强化学习中，我们为模型提供指令，但并不提供人工编写的答案。模型需要自己生成答案。评分机制（例如人类）会读取生成的答案，并告诉模型这些答案的质量。模型的目标是如何回答以获得高分。&lt;/li&gt;
      &lt;li&gt;另一种机制是模型生成多个答案，评分机制告诉模型哪个答案最好。模型的目标是学习生成高分的答案，而不是低分的答案。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;语言模型（至少）有三种交互模式：
    &lt;ol&gt;
      &lt;li&gt;文本型（text-grounded）： 为模型提供文本和说明（“总结此文本”，“基于此文本，以色列的人口是多少”，“本文中提到的化学名称是什么”，“将此文本翻译成西班牙语”等），让模型基于我们提供的文本生成答案；&lt;/li&gt;
      &lt;li&gt;求知型（knowledge-seeking）： 向模型提供问题或指导，让模型根据内在知识（“流感的常见原因是什么”）提供（真实）回答。&lt;/li&gt;
      &lt;li&gt;创造型（creative）： 为模型提供问题或说明，然后让模型进行创造性输出。（“写一个关于…的故事”）&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;对于语言生成模型来说，监督学习/指令调优最大的问题是它们只能复制演示者给出的确切答案，但实际上，人类语言可以用多种方式传递相同的信息，它们都是切实可行的。如果因模型轻微偏离人类规定的文本而受到“惩罚”，可能会使模型产生困惑。我们当然可以继续逼迫模型去学习更难学习的遣词造句，尽管模型已经学会了生成具有同样意思、合法的替代性回答。因此，我们非常看好强化学习训练提供的多样性表达。&lt;/li&gt;
  &lt;li&gt;监督学习只允许正反馈（我们向模型展示一系列问题及其正确答案），而RL允许负反馈（模型被允许生成答案并得到反馈说“这答案是不正确的”），相比正反馈，负反馈要强大得多。&lt;/li&gt;
  &lt;li&gt;对于求知型查询，我们希望模型在对答案没把握的情况下能够如实回答“我不知道”或拒绝回答这一问题。对于这类交互模式，由于监督训练可能会让模型撒谎，所以我们必须使用RL。核心问题是：我们希望模型根据内部知识进行回答，但我们并不知道模型内部知识包含的内容。 在监督训练中，我们给模型提供问题及正确答案，并训练模型复制提供的答案。这里有两种情况：（1）模型“知道”答案。这种情况下，监督学习能够正确推动模型将答案与问题相关连，并且有望让模型执行相似的步骤，回答将来遇到的类似问题。这是所期望的行为。（2）模型不知道答案。在这种情况下，监督训练还是会促使模型给出答案。现在，我们有两种选择。一种可能是，它会促使模型记住特定的问答对。这种做法本身并没什么坏处，但不太高效，因为我们的目的是让模型具有泛化能力，并且能回答任何问题，而不只是那些在训练数据中出现的问题。但如果我们使模型在这些情况下能做到泛化，那么实际上就是在教模型捏造答案，相当于鼓励模型“说谎”，这很不好。由于我们无法确定模型知道哪些信息或不知道哪些信息，所以无法避免第二种情况，这对监督训练来说是一个真实且严重的问题。我们不能仅依靠监督学习来训练模型生成可信任回答，还需要强化学习的加持。与监督学习不同，强化学习不会鼓励模型编造答案：即使模型最初确实猜对了一些答案并错误地学习了“编造”行为，但长远来看，模型会因编造答案的得分较低（很可能是不正确的）而学会依赖内部知识或选择放弃回答。&lt;/li&gt;
  &lt;li&gt;长期以来，使用强化学习训练生成语言任务对大多数玩家来说都不切实际：由于缺乏可靠的自动评分指标，强化学习训练需要对每个训练样本进行人工反馈。这既耗时又昂贵，特别是对于需要查看数千到数万甚至数十万个示例才能学习的模型。然而，强化学习训练现在变得实用了：首先，出现了可以从较少示例中学习的大型预训练语言模型。更重要的是，这些模型为强化学习循环（RL loop）中去掉人类参与铺平了道路。监督训练对于文本相关的任务非常有效，而且大型模型可以很好地学习执行一些任务。例如，让模型确定两个文本是否意思相同，或者一个文本是否包含另一个文本中没有的事实，根据经验来看，大型语言模型（甚至中型语言模型）可以使用监督学习可靠地学习执行这些任务，这为我们提供了可用于强化学习设置的有效自动评分机制。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/t5lT1NIZ6TysfgJks7kYKA&quot;&gt;一键式 RLHF 训练 DeepSpeed Chat（一）：理论篇&lt;/a&gt;ChatGPT模型的训练是基于InstructGPT论文中的RLHF方式。这与常见的大语言模型的预训练和微调截然不同，目前仍缺乏&lt;strong&gt;一个支持端到端的基于人工反馈机制的强化学习（RLHF）的规模化系统&lt;/strong&gt;，为使RLHF训练真正普及到AI社区，&lt;strong&gt;DeepSpeed-Chat应运而生&lt;/strong&gt;。
&lt;a href=&quot;https://mp.weixin.qq.com/s/M3odD3dR2bPOar2ZIUsABg&quot;&gt;一键式RLHF训练 DeepSpeed Chat（二）：实践篇&lt;/a&gt; 值得细读&lt;/p&gt;

&lt;h2 id=&quot;chatgpt-是怎么炼成的&quot;&gt;chatgpt 是怎么炼成的？&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1U84y167i3&quot;&gt;ChatGPT (可能)是怎么炼成的 - GPT社会化的过程 (李宏毅)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/machine/gpt_to_chatgpt.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;学习文字接龙
    &lt;ol&gt;
      &lt;li&gt;输入“你好”，gpt 可以接“美”（你好美）。当然，“你好” 后面也可以是 “嘛”，也可以是“高”。gpt 的输出是随机的，哪个在它学习的素材里出现的概率高就显示哪个。&lt;/li&gt;
      &lt;li&gt;文字接龙的学习是不需要人工标注的，网络上的每一段文字都可以教机器做文字接龙。&lt;/li&gt;
      &lt;li&gt;文字接龙有什么用？可以回答问题。你问gpt “台湾最高的山是那一座？”，gpt 可能接“玉”，你输入“台湾最高的山是那一座？玉”，它可能会接“山”。我们就知道答案是“玉山”。&lt;/li&gt;
      &lt;li&gt;你问gpt “台湾最高的山是那一座？”，gpt 也能有其它回答，比如gpt学习的素材里有一个选择题：“台湾最高的山是那一座？A 玉山；B 雪山”，也可能“台湾最高的山是那一座？谁能告诉我答案？”。这个时候chatgpt 是一个自由自在的孩子。PS：基础LLM(Base LLM)的特点是基于大量文本训练数据，来预测下一个最有可能出现的单词，缺点是不可控，容易输出有问题的文本。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;人类老师引导文字接龙方向。gpt ==&amp;gt; chatgpt
    &lt;ol&gt;
      &lt;li&gt;gpt 在网络上看到过各式各样奇怪的东西，可能产生各式各样奇怪的回答，不知道哪些是人类希望它回答的。&lt;/li&gt;
      &lt;li&gt;找人来思考想问gpt的问题，并人工提供正确答案。针对“台湾最高的山是那一座？”，gpt 有多个回答，然后由人来告诉它（标注） 人类想要的回答是“玉山”，人类只需要告诉机器，哪个答案是比较好的，哪个答案是比较差。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;模仿人类老师的喜好
    &lt;ol&gt;
      &lt;li&gt;训练一个模仿人类老师的模型，teacher model： 给一个问题，再给一个gpt 提供的答案，它负责输出一个分数。它学习的目标就是模仿人类的标准/偏好。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;用增强式学习向模拟老师学习
    &lt;ol&gt;
      &lt;li&gt;问gpt “世界最高的山是哪座？”，gpt 接了一句“世界上最深的海又在哪里？”，这是 对gpt 是一个合理的回答，但不是人类想要的。&lt;/li&gt;
      &lt;li&gt;把上述问题 和答案丢给 teacher model（已经学会了人类的偏好），teacher model 给一个score，也就是增强式学习（reinforcement learning）的reward。&lt;/li&gt;
      &lt;li&gt;接下来就是根据 reward 调整gpt的参数，目标是获取更高的score。经过reinforcement learning之后，就是chatgpt 了。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;使用者感受chatgpt 的功能是：生成式学习。但chatgpt 实际做的事儿是 分类：给一句话，从所有的word 中选出一个word输出。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1jA411274Q?p=3&quot;&gt;chatgpt 带来的研究问题&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;如何精准提出需求（Prompting）？它不是一个聊天机器人，它只是根据你说的话生成后面的话，得调教它，靠人工还是靠系统？&lt;/li&gt;
  &lt;li&gt;如果更正错误？chatgpt的预训练资料只有到2021年。&lt;strong&gt;chatgpt 在生成内容的时候，并不真正理解所生成的内容，虽然很多时候它是对的，但因为不理解所以还是会出错&lt;/strong&gt;，甚至是一些虚假的内容，这在比如企业客服场景中是不合适的（客服场景以结果为导向，引导客户购买或者 告诉客户哪个更好，有时候需要给出标准答案，比如价格等，这个时候说不知道也比给出错误的结果强，企业很难接受不可控的输出）&lt;/li&gt;
  &lt;li&gt;侦测一段话是不是AI 生成的。&lt;/li&gt;
  &lt;li&gt;泄露机密。比如教普通人制备生物武器&lt;/li&gt;
  &lt;li&gt;限制其一次性输入的文本量：限制其在训练、推理期间与世界互动的能力 &lt;a href=&quot;https://mp.weixin.qq.com/s/_i0eQgYNSLJydv3qOTqr-Q&quot;&gt;语言大模型100K上下文窗口的秘诀&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;其它&quot;&gt;其它&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/628511161&quot;&gt;大(语言)模型推理原理及加速&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/wWWGP7-lReON_b5-Pm5plg&quot;&gt;投身LLM，要从本质上想明白的三个问题：未来是什么，哪些机会更好，我们要怎么准备&lt;/a&gt; 好文。&lt;/p&gt;

&lt;p&gt;神经网络训练目标是使预测损失最小化，在各个参数展开的空间内找到最优的点，如果从头开始找，会比较慢；但是从之前已经训练好的其他类似模型开始，&lt;strong&gt;就相当于在最优点附近的点开始&lt;/strong&gt;，自然收敛的速度和效果会比从零训练好得多。取预训练好的网络的部分结构和权重，与自己新增的网络部分一起训练，称为微调（fine-tune）。&lt;/p&gt;

&lt;p&gt;从深度学习发展前10年的历程来看，模型精度提升，主要依赖网络在结构上的变革。 例如，从AlexNet到ResNet50，再到NAS搜索出来的EfficientNet，ImageNet Top-1 精度从58提升到了84。但是，随着神经网络结构设计技术，逐渐成熟并趋于收敛，&lt;strong&gt;想要通过优化神经网络结构从而打破精度局限非常困难&lt;/strong&gt;。近年来，随着数据规模和模型规模的不断增大，模型精度也得到了进一步提升，研究实验表明，模型和数据规模的增大确实能突破现有精度的一个局限。&lt;/p&gt;

&lt;p&gt;BERT由两阶段构成，每个阶段有自己的特点和目标。第一个阶段是预训练阶段，第二个阶段是Fine-Tuning阶段。预训练阶段用大量无监督的文本通过自监督方式进行训练，把文本包含的语言知识以参数形式编码到Transformer中，Fine-Tuning一般是有监督的，数据量比较小，在模型结构上做分类任务以解决当前任务。第一阶段跟第二阶段怎么连接起来的？在预训练阶段Transformer学到了很多初始化的知识，第二阶段就把初始化网络学到的语言知识拿来用，Fine-Tuning引入新的特征解决你的问题。所以，为什么BERT效果这么好？为什么以前的模型效果没有BERT好？因为，第一阶段编码了文本中大量的语言学知识，在Bert之前，没有用那么多的文本数据，而且是无监督的方式。那么我们关心的是：BERT里的Transformer到底学到了什么？比传统模型多学了什么知识？这是关键。如果归纳一下目前的研究结论的话，大致概述一下：BERT训练好之后，低层Transformer主要学习自然语言表层的特征，中层学习编码句法信息，高层编码了NLP的语义特征。很多实验都已证明这一结论。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/w_1FdCSD_S-otNVEXytxCQ&quot;&gt;如何利用LLM做多模态任务？&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/_n1lOhyFewFeF1ALINO1qg&quot;&gt;张俊林：GPT4等LLM模型具备类人智慧了吗？&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;一种观点认为GPT 4这种LLM模型仅仅学会了语言中的单词共现等浅层的表面统计关系，其实并未具备智能，只是类似鹦鹉学舌的语言片段缝合怪而已；另外一种观点则认为：GPT 4不仅学会了语言元素间的表面统计关系，而且学到了人类语言甚至包括物理世界的内在运行规律，文字是由内在智能产生的，所以LLM具备类人智能。&lt;/li&gt;
  &lt;li&gt;假设LLM模型训练好了，在使用时输入Prompt，GPT模型是如何把知识提取出来的？假设输入的Prompt是：“Beat music is owned by”，GPT可以通过NTP返回正确答案：Apple。这个例子里，“Beat music”是个实体，“owned by Apple”是这个实体对应的某个属性。经过研究，发现GPT在提取这条知识的时候，经历了明显的三阶段过程：
    &lt;ol&gt;
      &lt;li&gt;首先，单词“music”是描述这个实体最后的、也是最关键的词汇，它的信息在顺着Transformer block往上走的过程中，&lt;strong&gt;先通过Attention把之前的修饰语“beats”相关信息集成到“music”对应位置&lt;/strong&gt;。之后，随着Transformer层数越来越高，通过每个Transformer Block的FFN层，&lt;strong&gt;不断往“music”对应的Embedding里增加信息&lt;/strong&gt;，所以随着信息往上层流动，“music”这个单词对应层数的Embedding，能够触发越来越多的与“beats music”相关“属性”词汇。这是第一个步骤，整个过程总体发生在Transformer的低层。&lt;/li&gt;
      &lt;li&gt;第二步，GPT模型在“by”单词这个位置，也就是NTP要产生输出token的最后一个位置，通过Attention把单词“own”的信息集成到最后位置。这里需要注意一下，最后一个单词对应的Transformer位置是比较关键的，因为在它的最上层会给出Next Token输出。在推理过程中，GPT会把输入上文中的重要信息通过Attention逐步集成到这个位置上来。这个操作也发生在Transformer的低层。&lt;/li&gt;
      &lt;li&gt;第三步，在“by”单词位置，也就是最后一个位置的Transformer高层，它在低层已经集成了单词“own”的信息，这个信息在高层，通过Attention把“beats music”对应的属性“apple”提取出来。具体提取动作是通过某个Attention Head来做到的，而且这篇文章证明了Attention Head里会编码&lt;实体-属性&gt;信息。过去一般认为Attention主要是用来进行信息比较和搬运的，它证明了Attention也会存储某种知识。 通过以上三步，GPT完成了对某条知识的提取过程。&lt;/实体-属性&gt;&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;一个GPT知识提取的轮廓：当训练好GPT模型后输入Prompt，对于Transformer某个位置对应的输入单词，随着Transformer 不断往上走，GPT通过Attention，&lt;strong&gt;把这个单词上文中与自己有关的信息集成到自己的Embedding里&lt;/strong&gt;，而每层的FFN对当前单词Embedding做变换增加信息，以此方式来不断触发FFN里存储的知识并逐层Refine单词对应的Embedding（类似上面例子里单词“music”的过程）。Transformer的last token位置也是如此，它的特殊之处在于，从底层到上层，首先会把整个输入上文中最关键的信息，通过Attention拷贝到自己的位置，之后通过这个关键信息来逐步过滤出上文中比较重要的信息。在这个位置的Transformer底层，应该有很多候选答案可供输出，正确答案排名并不靠前，&lt;strong&gt;随着Transformer往上走，正确答案排名越来越靠前，而且可以和正确答案竞争的候选答案越来越少&lt;/strong&gt;，体现为分配给正确答案的概率分布得分越来越高，直到last token的最高层，GPT可以输出正确答案（类似上面例子中单词“by”的过程）。&lt;/li&gt;
  &lt;li&gt;在训练过程中，GPT模型会优先学习具备以下特性的知识点：高频知识点、通用知识点（被复用概率高的则通用）、具体而非抽象的知识点。应该遵循这三个原则。为什么会这样呢？因为根据Next Token Prediction的原则，越是高频出现的知识点，如果GPT本次预测错了，则会做反向传播修正模型参数，以保证下次再见到类似情况会预测对，高频知识点因为出现次数多，所以获得反向传播修正模型参数的次数多，也就更容易建立起对应的知识点，及其和其它知识点的连接通路。高频知识点如果学会了，在后面的训练数据会很容易碰到这个知识点，所以对降低NTP任务的loss贡献就大。其它两类知识点也是类似的道理，通用知识点因为通用性强，所以在后续预测中被使用的机会多，所以获得反向传播修正模型参数的次数也多，也容易被模型学会，具体而非抽象的知识点也因为在训练数据中见到的次数多，所以容易被建立起来。诸如此类。反过来，低频的、领域或任务专用的、抽象的知识点，就会越晚被GPT模型学会。或者说，&lt;strong&gt;如果想学会这类知识点，则需要让模型见到更大量的数据，以增加这些知识点在学习过程中必要的反向传播修正参数的机会&lt;/strong&gt;。&lt;/li&gt;
&lt;/ol&gt;

</description>
      </item>
    
      <item>
        <title>go依赖注入</title>
        <link>https://felix0080.github.io/2023/02/16/go_ioc.html</link>
        <guid isPermaLink="true">https://felix0080.github.io/2023/02/16/go_ioc.html</guid>
        <pubDate>Thu, 16 Feb 2023 00:00:00 +0000</pubDate>
        <description>&lt;h2 id=&quot;前言&quot;&gt;前言&lt;/h2&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#前言&quot; id=&quot;markdown-toc-前言&quot;&gt;前言&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#go和依赖注入&quot; id=&quot;markdown-toc-go和依赖注入&quot;&gt;Go和依赖注入&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#ioc-golang&quot; id=&quot;markdown-toc-ioc-golang&quot;&gt;IOC-golang&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#代码生成&quot; id=&quot;markdown-toc-代码生成&quot;&gt;代码生成&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#aop&quot; id=&quot;markdown-toc-aop&quot;&gt;AOP&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#源码分析&quot; id=&quot;markdown-toc-源码分析&quot;&gt;源码分析&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#其它方案&quot; id=&quot;markdown-toc-其它方案&quot;&gt;其它方案&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;设计模式分为创建、结构和行为三大类，如果自己构造依赖关系， 则创建 与 行为 两个目的的代码容易耦合在一起， 代码较长，给理解造成困难。&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;组件（比如go语言中的structs）在初始化期间构建其自己的依赖关系&lt;/li&gt;
  &lt;li&gt;依赖注入：组件（比如go语言中的structs）在创建时应该接收它的依赖关系。PS：这个理念在java、spring 已经普及多年。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;按照常规的应用开发模式，在一个“开发单元”内，开发者需要关注哪些事情？我们习惯于编写一个构造函数返回需要的对象，这个构造函数的入参，&lt;strong&gt;包含了一些参数以及下游依赖&lt;/strong&gt;，我们在构造函数中会把这些对象和参数拼接成一个结构，再执行初始化逻辑，最后返回。比如A 调用B且由不同人负责，B有多个构造函数，对于A来说，调用B 还要明白构造函数细节。所以ioc 一般要求 组件不提供构造函数（即使用默认构造函数），依赖的组件、配置 由ioc 负责注入，通过框架将 业务无关代码（用来表示对象的 代码中的耦合关系） 形式化为 &lt;strong&gt;代码外的xml/yaml&lt;/strong&gt;（代码中的annotation），减少了代码量，明确了关系。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/Zp-OqCVVr9CbDv1Y1zWN-w&quot;&gt;深入浅出依赖注入及其在抖音直播中的应用&lt;/a&gt;在软件工程中，依赖注入（dependency injection）的意思为：给予调用方它所需要的事物。“注入”是指将“依赖”传递给调用方的过程。在“注入”之后，调用方才会调用该“依赖”。传递依赖给调用方，而不是让让调用方直接获得依赖，这个是该设计的根本需求。该设计的目的是为了&lt;strong&gt;分离调用方和依赖方&lt;/strong&gt;，从而实现代码的高内聚低耦合，提高可读性以及重用性。&lt;/p&gt;

&lt;h2 id=&quot;go和依赖注入&quot;&gt;Go和依赖注入&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://www.zhihu.com/question/521822847/answer/2451020694&quot;&gt;为什么依赖注入只在 Java 技术栈中流行，在 go 和 cpp 没有大量使用？&lt;/a&gt;依赖注入Dependency-Injection (DI)只是Inversion-of-Control (IoC) 的一种实现方式，IOC还有许多更常见的实现，比如callback，胖指针。为什么java选择了di而不是callback或者其他，java中定义callback的开销无异于一个实体bean。所以java走得更进一步，把bean抽取出来，使用delegation，proxy等设计模式，实现了DI。对于go或者cpp这种native语言来说，既有闭包又有函数指针，实现ioc的手段有很多，但di却是开销很大的一种，所以比较少见，大白话就是可以但没有必要。&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HandleFunc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ResponseWriter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Fprintf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Hello world!&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;ioc-golang&quot;&gt;IOC-golang&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/Ar-JdkrQ5NnCWcGOoCuVgg&quot;&gt;Alibaba/IOC-golang 正式开源 ——打造服务于go开发者的IOC框架&lt;/a&gt;在面向对象编程的思路下，开发者需要直接关心对象之间的依赖关系、对象的加载模型、对象的生命周期等等问题。对于较为复杂的业务应用系统，随着对象数目增长，&lt;strong&gt;对象之间的拓扑关系呈指数级增加&lt;/strong&gt;，如果这些逻辑全部由开发人员手动设计和维护，将会在应用内保存较多业务无关的冗余代码，影响开发效率，提高代码学习成本，增加了模块之间的耦合度，容易产生循环依赖等等问题。随着开发者的增多，设计模型的复杂化，将会产生&lt;strong&gt;对象管理框架&lt;/strong&gt;的诉求，例如 Java 生态的 Spring 框架，其设计的核心就是控制反转思路，从而为开发者提供依赖注入、配置注入、生命周期管理等能力。Go 语言生态在开源侧也有较多基于该思路的实现，但普遍能力较为单一，相比于我们的设计思路 ，在可扩展性、易用性等方面有所不足。&lt;strong&gt;IOC-golang 不是 Go 语言实现的 Spring 框架！&lt;/strong&gt;我们致力于打造一款针对 Go 开发人员的框架，它适配与 Go 的语法和各种基本概念，符合 Go 语言开发习惯，能真正为开发人员提供编程、思考、运维、以及代码阅读上的便利。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/go/go_ioc_layer.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Spring的核心抽象是使用了BeanDefinition、BeanFactory、BeanDefinitionRegistry、BeanDefinitionReader等核心抽象实现了Bean的定义、获取和创建。&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;spring&lt;/th&gt;
      &lt;th&gt;ioc-golang&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;BeanDefinition&lt;/td&gt;
      &lt;td&gt;StructDescriptor&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;BeanFactory、BeanDefinitionRegistry&lt;/td&gt;
      &lt;td&gt;singleton/normal&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;注册方式&lt;/td&gt;
      &lt;td&gt;Xml/注解&lt;/td&gt;
      &lt;td&gt;自动生成代码用于注册&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;使用&lt;/td&gt;
      &lt;td&gt;beanFacory.GetBean&lt;/td&gt;
      &lt;td&gt;每一个struct 自动生成代码。singleton.GetImpl(sdid, nil)&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/AjBhlLYF31xPHCBtR1ONVw&quot;&gt;IOC-golang 的 AOP 原理与应用&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;通过标签注入依赖对象&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// +ioc:autowire=true&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// +ioc:autowire:type=singleton&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;App&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;c&quot;&gt;// 将实现注入至结构体指针，字段本身已经可以定位期望被注入的结构，因此不需要在标签中给定期望被注入的结构名&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ServiceStruct&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ServiceStruct&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;`singleton:&quot;&quot;`&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;// 将实现注入至接口，框架自动为 main.ServiceImpl 结构创建代理，并将代理结构注入在 ServiceImpl 字段&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ServiceImpl&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Service&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;`singleton:&quot;main.ServiceImpl&quot;`&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;通过 API 的方式获取对象&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GetAppSingleton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;   &lt;span class=&quot;c&quot;&gt;// 获取真实结构体指针&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GetAppSingleton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_appSDID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;_appSDID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;util&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetSDIDByStructPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;singleton&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetImpl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_appSDID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;
   &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;impl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;


&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GetAppIOCInterfaceSingleton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;   &lt;span class=&quot;c&quot;&gt;// 获得封装了代理层的接口&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;代码生成&quot;&gt;代码生成&lt;/h3&gt;

&lt;p&gt;对于自定义一个struct ，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;iocli gen&lt;/code&gt; 将会生成&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;专属interface，iocli 会为任何&lt;strong&gt;期望注册在框架的结构&lt;/strong&gt;生成专属接口，专属接口的命名规则为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$(结构名)IOCInterface&lt;/code&gt;
    &lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AppIOCInterface&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;代理struct，实现专属interface，包含一个代理方法。任何被注入到接口的字段（字段类型是 interface），都会被框架自动封装代理 AOP 层，即注入到接口的结构体指针，并非真实结构体指针，而是封装了结构体的代理指针。
    &lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app_&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;Run_&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Run_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;注册自定义struct、代理struct到 StructDescriptor
    &lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;c&quot;&gt;// 注册代理结构&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;normal&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RegisterStructDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;autowire&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StructDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;Factory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
         &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;appStructDescriptor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;autowire&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StructDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;Factory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
         &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;Metadata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}{&lt;/span&gt;
         &lt;span class=&quot;s&quot;&gt;&quot;aop&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;      &lt;span class=&quot;k&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}{},&lt;/span&gt;
         &lt;span class=&quot;s&quot;&gt;&quot;autowire&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}{},&lt;/span&gt;
     &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;singleton&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RegisterStructDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;appStructDescriptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;aop&quot;&gt;AOP&lt;/h3&gt;

&lt;p&gt;使用 Go 语言实现方法代理的思路有二，分别为通过反射实现接口代理，和基于 Monkey 补丁的函数指针交换。后者不依赖接口，可以针对任何结构的方法封装函数代理，需要侵入底层汇编代码，关闭编译优化，对于 CPU 架构有要求，并且在处理并发请求时会显著削弱性能。&lt;/p&gt;

&lt;h3 id=&quot;源码分析&quot;&gt;源码分析&lt;/h3&gt;

&lt;p&gt;获取真实结构体指针&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;// 注册
singleton.RegisterStructDescriptor(appStructDescriptor)
  // &amp;lt;id,sd&amp;gt;
  var singletonStructDescriptorsMap = make(map[string]*autowire.StructDescriptor)
  autowire.RegisterStructDescriptor(sd)
    // &amp;lt;id,sd&amp;gt;
    structDescriptorsMap[sd.ID()] = sd
    registerImplements(sd)        // 建立 sd 实现的interface 与 sd的关联关系


// 获取
singleton.GetImpl(_appSDID, nil)
  autowire.Impl(Name, sdId, param)
    sd := GetStructDescriptor(targetSDID)
    WrapperAutowireImpl.ImplWithParam(targetSDID, param, expectWithProxy=false, force)
      rawPtr, err = w.Autowire.Factory(sdID)    // 实例化目标对象 得到 rawPtr, sd.Factory
      rawPtr, err = w.Autowire.Construct(sdID, rawPtr, param) //  construct field, sd.Contstruct
      w.inject(rawPtr, sdID)  // 根据rawPtr 获取目标对象的类型，进而获取其包含的字段，再根据字段类型 实例化字段对象   
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;获得封装了代理层的接口。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;// 注册
normal.RegisterStructDescriptor(&amp;amp;autowire.StructDescriptor{
    Factory: func() interface{} {
        return &amp;amp;app_{}
    },
})

// 获取
singleton.GetImplWithProxy(_appSDID, nil)
  autowire.ImplWithProxy(Name, sdId, param)
    wrapperAutowire.ImplWithParam(targetSDID, param, expectWithProxy=true, force)
      rawPtr, err = w.Autowire.Factory(sdID)    
      rawPtr, err = w.Autowire.Construct(sdID, rawPtr, param)
      w.inject(rawPtr, sdID)  
      finalPtr = GetProxyFunction()(rawPtr)
        proxyFunction(rawPtr)
          proxyStructPtr, err := normal.GetImpl(proxySDID, nil) // 即代理struct 
          implProxy(rawPtr, proxyStructPtr, sdid)   // 为代理struct field（本质是一个方法） 赋值
            proxyFunc = makeProxyFunction  // 构造代理后的方法
          return proxyStructPtr
      proxyPtr = finalPtr
impl := i.(AppIOCInterface)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;框架目前提供的aop 是针对全部对象的。每一个raw struct 会有一个对应的 proxy struct，都实现了 专属接口，proxy struct 与raw struct 拥有相同的个方法，每个方法对应一个 function field，初始化时会被赋值为 interceptor 与 raw stuct 合成后的方法。比如Run 方法，proxy.Run() ==&amp;gt; proxy.Run field ==&amp;gt; interceptor.BeforeInvoke + raw.Run + interceptor.AfterInvoke。&lt;/p&gt;

&lt;p&gt;将  Interceptor 与 rawFunction “合成”一个新的方法&lt;/p&gt;
&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;makeProxyFunction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;proxyPtr&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{},&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rf&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reflect&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sdid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;methodName&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isVariadic&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reflect&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reflect&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;rawFunction&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rf&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;interceptorImpls&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getInterceptors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;proxyFunc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reflect&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reflect&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;invocationCtx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NewInvocationContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;proxyPtr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sdid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;methodName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;common&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CurrentCallingMethodName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;range&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interceptorImpls&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BeforeInvoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;invocationCtx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;    &lt;span class=&quot;c&quot;&gt;// 前置方法&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;range&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interceptorImpls&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AfterInvoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;invocationCtx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;// 前置方法&lt;/span&gt;
			&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;}()&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isVariadic&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;varParam&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;l&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;varParam&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;varParam&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
			&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rawFunction&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Call&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;// 原方法调用&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;invocationCtx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetReturnValues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;proxyFunc&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Interceptor 的来源&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// github.com/alibaba/IOC-golang/aop/aop.go&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Interceptor&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;BeforeInvoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvocationContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;AfterInvoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvocationContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interceptorFactories&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;make&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;interceptorFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;RegisterAOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aopImpl&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;aops&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aops&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;aopImpl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;aopImpl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InterceptorFactory&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;interceptorFactories&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;interceptorFactories&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;aopImpl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InterceptorFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;aopImpl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RPCInterceptorFactory&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;rpcInterceptorFactories&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rpcInterceptorFactories&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;aopImpl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RPCInterceptorFactory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;aopImpl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GRPCServiceRegister&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;grpcServiceRegisters&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;grpcServiceRegisters&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;aopImpl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GRPCServiceRegister&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;aopImpl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConfigLoader&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;configLoaderFuncs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;configLoaderFuncs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;aopImpl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ConfigLoader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以log 为例&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// github.com/alibaba/IOC-golang/extension/aop/log/aop.go&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;aop&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RegisterAOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aop&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AOP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;InterceptorFactory&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;aop&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Interceptor&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;c&quot;&gt;// get loaded logInterceptor singleton&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;logInterceptorSingleton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GetlogInterceptorIOCInterfaceSingleton&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logInterceptorSingleton&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;GRPCServiceRegister&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;grpc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;ConfigLoader&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aopConfig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;common&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// github.com/alibaba/IOC-golang/extension/aop/log/interceptor.go&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// +ioc:autowire=true&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// +ioc:autowire:type=singleton&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// +ioc:autowire:proxy:autoInjection=false&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// +ioc:autowire:paramType=logInterceptorParams&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// +ioc:autowire:constructFunc=initLogInterceptor&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logInterceptor&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logInterceptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BeforeInvoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aop&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvocationContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logInterceptor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;AfterInvoke&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aop&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InvocationContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;在go build 编译的时候 通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-tags disableAOP&lt;/code&gt; 编译参数即可引入/排除所有 aop 扩展实现。&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ioc-golang
    /extension
        /aop
            /log
            /monitor
            /trace
        /imports
            /cli
            /boot
                /imports.go    # 将extension/aop 下的所有目录都引入了一遍，包含 `//go:build !disableAOP`
                /imports_default.go  # 空文件，包含 `//go:build disableAOP`
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;其它方案&quot;&gt;其它方案&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/yHB9BzEGIki1fyjYojdpYQ&quot;&gt;Go 语言官方依赖注入工具 Wire 使用指北&lt;/a&gt;Wire 是一个强大的依赖注入工具。与 Inject 、Dig 等不同的是，Wire只生成代码而不是使用反射在运行时注入，不用担心会有性能损耗。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.jianshu.com/p/cb3682ad34a7&quot;&gt;Go中的依赖注入&lt;/a&gt; 推荐使用 &lt;a href=&quot;https://github.com/uber-go/dig&quot;&gt;uber-go/dig&lt;/a&gt; 
A reflection based dependency injection toolkit for Go.&lt;/p&gt;
</description>
      </item>
    
      <item>
        <title>多类型负载协调员Koordinator</title>
        <link>https://felix0080.github.io/2022/12/17/koordinator.html</link>
        <guid isPermaLink="true">https://felix0080.github.io/2022/12/17/koordinator.html</guid>
        <pubDate>Sat, 17 Dec 2022 00:00:00 +0000</pubDate>
        <description>&lt;h2 id=&quot;简介&quot;&gt;简介&lt;/h2&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#简介&quot; id=&quot;markdown-toc-简介&quot;&gt;简介&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#使用&quot; id=&quot;markdown-toc-使用&quot;&gt;使用&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#设计思路&quot; id=&quot;markdown-toc-设计思路&quot;&gt;设计思路&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#优先级和qos&quot; id=&quot;markdown-toc-优先级和qos&quot;&gt;优先级和QoS&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#如何保证qos&quot; id=&quot;markdown-toc-如何保证qos&quot;&gt;如何保证QoS&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#干扰检测&quot; id=&quot;markdown-toc-干扰检测&quot;&gt;干扰检测&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#源码分析&quot; id=&quot;markdown-toc-源码分析&quot;&gt;源码分析&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#调度器&quot; id=&quot;markdown-toc-调度器&quot;&gt;调度器&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#koordlet&quot; id=&quot;markdown-toc-koordlet&quot;&gt;koordlet&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;问题：是不是可以认为，用户设置的request 和limit ，除了最开始作为一个参考值之外，其它时候是没啥用的，全靠pod 真实的资源使用和 node 真实负载 根据priority 和 qos 进行调度了。 若不是为了最开始 有个参考作用，是不是 连request和limit 都不用设置，仅需设置下priority 和 qos  即可。&lt;/p&gt;

&lt;p&gt;大佬解答：资源规格智能托管是调度技术里面非常重要的一个课题。&lt;/p&gt;

&lt;p&gt;大体思路：先分级，再给低优原本高优的资源，尽量确保高优QoS，保证不了了把低优任务重调度，重调度用资源预留确保迁移成功。&lt;/p&gt;

&lt;h2 id=&quot;使用&quot;&gt;使用&lt;/h2&gt;

&lt;p&gt;假如创建一个pod&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Pod&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;koordinator.sh/enable-colocation&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;true&quot;&lt;/span&gt;  
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;test-pod&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;app&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;nginx:1.15.1&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;limits&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;cpu&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;1&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;memory&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;3456Mi&quot;&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;cpu&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;1&quot;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;memory&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;3456Mi&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Koordinator 提供了一个 ClusterColocationProfile CRD 和 对应的 Webhook ，修改和验证新创建的 Pod，注入 ClusterColocationProfile 中描述的字段。&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;config.koordinator.sh/v1alpha1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ClusterColocationProfile&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;colocation-profile-example&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespaceSelector&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;matchLabels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;koordinator.sh/enable-colocation&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;true&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;matchLabels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;koordinator.sh/enable-colocation&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;true&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;qosClass&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;BE&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;priorityClassName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;koord-batch&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;koordinatorPriority&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1000&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;schedulerName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;koord-scheduler&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;koordinator.sh/mutated&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;true&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;annotations&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; 
    &lt;span class=&quot;s&quot;&gt;koordinator.sh/intercepted&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;true&quot;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;patch&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;terminationGracePeriodSeconds&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;30&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;上面的 profile.yaml 文件描述了对所有含有标签 koordinator.sh/enable-colocation=true 的 Namespace 下的所有含有标签 koordinator.sh/enable-colocation=true 的 Pod 进行修改，注入 Koordinator QoSClass、Koordinator Priority 等。&lt;/p&gt;

&lt;p&gt;pod 创建完成后 真实的样子&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Pod&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;annotations&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; 
    &lt;span class=&quot;s&quot;&gt;koordinator.sh/intercepted&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;koordinator.sh/qosClass&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;BE&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;koordinator.sh/priority&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1000&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;koordinator.sh/mutated&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;terminationGracePeriodSeconds&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;30&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;priority&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5000&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;priorityClassName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;koord-batch&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;schedulerName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;koord-scheduler&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;# schedulerName 换成了 koord-scheduler&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;app&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;nginx:1.15.1&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;limits&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;kubernetes.io/batch-cpu&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;1000&quot;&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;kubernetes.io/batch-memory&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;3456Mi&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;kubernetes.io/batch-cpu&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;1000&quot;&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;kubernetes.io/batch-memory&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;3456Mi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;设计思路&quot;&gt;设计思路&lt;/h2&gt;

&lt;p&gt;打造现代化的云原生调度系统，Koordinator 坚持了如下的设计思路：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;拥抱 Kubernetes 上游标准，基于 Scheduler-Framework 来构建调度能力，而不是实现一个全新的调度器。构建标准形成共识是困难的，但破坏是容易的，Koordinator 社区与 Kubernetes sig-scheduling 社区相向而行。&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;QoS 是系统的一等公民&lt;/strong&gt;，与业界大多数调度器更多的关注编排结果（静态）不同，Koordinator &lt;strong&gt;更关注 Pod 运行时质量（QoS）&lt;/strong&gt;，因为对于调度系统的用户而言，运行时稳定性是其业务成功的关键。&lt;/li&gt;
  &lt;li&gt;状态自闭环，Koordinator 认为调度必须是一个完整的闭环系统，才能满足企业级应用要求。因此，我们在第一个版本就引入了状态反馈回路，节点会根据运行时状态调优容器资源，中心会根据节点运行时状态反馈做调度决策。
    &lt;ol&gt;
      &lt;li&gt;中心调度 + 单机调度联合决策，中心调度看到全局视角，其决策可以找到集群中最适合应用需求的节点，而单机调度可以在节点侧做一定的灵活度，以应对应用突发的流量颠簸。&lt;/li&gt;
      &lt;li&gt;调度 + 重调度密切配合，调度解决 Pod 一次性放置的问题，而重调度才是驱动集群资源编排长期保持最优化的关键。Koordinator 将建设面向 SLO 的重调度能力，持续的驱动应用的编排符合预定义的 SLO。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;智能化、简单化，Koordinator 并不是就所有的选择暴露把问题留给客户，而是根据应用特征智能的为用户提供优化配置建议，简化用户使用 Kubernetes 的成本。
    &lt;ol&gt;
      &lt;li&gt;调度系统不止于解决调度、重调度、弹性伸缩等领域问题，一个完整的调度系统，需要具备&lt;strong&gt;基于历史数据驱动的自我迭代演进的能力&lt;/strong&gt;。为工作负载的运行历史状态建立数据仓库，基于这些运行历史的大数据分析，持续的改进应用间在各个维度的的亲和、互斥关系，才能在用户运行时体验、集群资源利用效率同时达到最佳状态。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;优先级和qos&quot;&gt;优先级和QoS&lt;/h3&gt;

&lt;p&gt;资源优先级策略的激进与保守，决定了集群资源的超卖容量，与资源稳定性的高低成反相关，&lt;strong&gt;以k8s PriorityClass声明&lt;/strong&gt;，分为Product、Mid、Batch、Free四个等级，表现为 超卖资源从PriorityClass 高的Pod来，用到低的地方去。&lt;/p&gt;

&lt;p&gt;QoS主要表现为使用的隔离参数不同，&lt;strong&gt;以Pod Label 的形式声明&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;Koordinator 针对智能化调度的设计思路如下：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;优先级：智能资源超卖，&lt;strong&gt;超卖的基本思想是去利用那些已分配但未使用的资源来运行低优先级的任务&lt;/strong&gt;。Koordinator 首先解决的是节点资源充分利用的问题，通过分析节点容器的运行状态计算可超卖的资源量，并结合 QoS 的差异化诉求将超卖的资源分配给不同类型的任务，大幅提高集群的资源利用率。 PS：已分配但未使用的资源，对应下图灰色和深蓝色之间的部分。
  &lt;img src=&quot;/public/upload/kubernetes/koordinator_resource_model.jpg&quot; alt=&quot;&quot; /&gt;&lt;/li&gt;
  &lt;li&gt;Priority 和 QoS 是两个正交的维度，可以排列组合使用，部分排列组合不合法。&lt;/li&gt;
  &lt;li&gt;QoS 感知的重调度，当节点中 Pod 的运行时 QoS 不符合预期时（干扰检测），Koordinator 将智能决策抑制更低优先级的任务亦或是迁移当前受到干扰的容器，从而解决应用 QoS 不满足导致的问题。问题：重调度的细节问题很多，Pod驱逐后 集群是否有资源保证可以运行，涉及到资源预留。&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;如何保证qos&quot;&gt;如何保证QoS&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/oOjg8j9tDBs5jOm30XjCMA&quot;&gt;Koordinator v0.7: 为任务调度领域注入新活力&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;QoS 表示应用在节点上运行时得到的物理资源质量，包含了System、LS、BE三类，LS又细分为LSE（Latency-Sensitive Excluded）、LSR（Latency-Sensitive Reserved）、LS（Latency-Sensitive）和 LS（Best Effort），保障策略&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;CPU 方面，通过内核自研的 Group Identity 机制，针对不同 QoS 等级设置内核调度的优先级，优先保障 LSR/LS 的 cpu 调度，允许抢占 BE 的 CPU 使用，以达到最小化在线应用调度延迟的效果；对于 LS 应用的突发流量，提供了 CPU Burst 策略以规避非预期的 CPU 限流。&lt;/li&gt;
  &lt;li&gt;内存方面，由于容器 cgroup 级别的直接内存回收会带来一定延时，LS 应用普遍开启了容器内存异步回收能力，规避同步回收带来的响应延迟抖动。除此之外，针对末级缓存（Last-Level Cache，LLC）这种共享资源，为了避免大数据等 BE 应用大量刷 Cache 导致 LS/LSR 应用的 Cache Miss Rate 异常增大，降低流水线执行效率，引入了 RDT 技术来限制 BE 应用可分配的 Cache 比例，缩小其争抢范围。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/kubernetes/koordinator_qos.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/fkX_lStva96HEbmPbR6iZw&quot;&gt;阿里云容器服务差异化 SLO 混部技术实践&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;CPU 资源质量
    &lt;ol&gt;
      &lt;li&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/cR6MpQu-n1cwMbXmVaXqzQ&quot;&gt;添加 K8S CPU limit 会降低服务性能？&lt;/a&gt; 可以查看container_cpu_cfs_throttled_periods_total 指标&lt;/li&gt;
      &lt;li&gt;CPU Burst，例如对于 CPU Limit = 2 的容器，操作系统内核会限制容器在每 100 ms 周期内最多使用 200 ms 的 CPU 时间片，进而导致请求的响应时延（RT）变大。当容器真实 CPU 资源使用小于 cfs_quota 时，内核会将多余的 CPU 时间“存入”到 cfs_burst 中；当容器有突发的 CPU 资源需求，需要使用超出 cfs_quota 的资源时，内核的 CFS 带宽控制器（CFS Bandwidth Controller，简称 BWC） 会允许其消费其之前存到 cfs_burst 的时间片。最终达到的效果是将容器&lt;strong&gt;更长时间的平均 CPU 消耗限制在 quota 范围内，允许短时间内的 CPU 使用超过其 quota&lt;/strong&gt;。&lt;/li&gt;
      &lt;li&gt;CPU 拓扑感知调度。在多核节点下，进程在运行过程中经常会被迁移到其不同的核心，考虑到有些应用的性能对 CPU 上下文切换比较敏感，kubelet 提供了 static 策略，允许 Guarantee 类型 Pod 独占 CPU 核心。CPU 绑核并不是“银弹”，若同一节点内大量 Burstable  类型 Pod 同时开启了拓扑感知调度，CPU 绑核可能会产生重叠，在个别场景下反而会加剧应用间的干扰。因此，拓扑感知调度更适合针对性的开启。&lt;/li&gt;
      &lt;li&gt;针对低优先级离线容器的 CPU 资源压制能力：内核Group Identity，Group Identity 功能可以对每一个容器设置身份标识，以区分容器中的任务优先级，系统内核在调度包含具有身份标识的任务时，会根据不同的优先级做相应处理。比如高优先级任务 有更多资源抢占机会。&lt;/li&gt;
      &lt;li&gt;在 CPU 被持续压制的情况下，BE 任务自身的性能也会受到影响，将其驱逐重调度到其他空闲节点反而可以使任务更快完成。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;内存资源质量
    &lt;ol&gt;
      &lt;li&gt;时延敏感型业务（LS）和资源消耗型（BE）任务共同部署时，资源消耗型任务时常会瞬间申请大量的内存，使得系统的空闲内存触及全局最低水位线（global wmark_min），引发系统所有任务进入直接内存回收的慢速路径，进而导致延敏感型业务的性能抖动。&lt;/li&gt;
      &lt;li&gt;后台异步回收，当容器内存使用超过 memory.wmark_ratio 时，内核将自动启用异步内存回收机制，提前于直接内存回收，改善服务的运行质量。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;干扰检测&quot;&gt;干扰检测&lt;/h3&gt;

&lt;p&gt;干扰检测和优化的过程可以分为以下几个过程：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;干扰指标的采集和分析：选取干扰检测使用的指标需要考虑通用性和相关性，并尽量避免引入过多额外的资源开销。&lt;/li&gt;
  &lt;li&gt;干扰识别模型及算法：分为横向和纵向两个维度，横向是指分析同一应用中不同容器副本的指标表现，纵向是指分析在时间跨度上的数据表现，识别异常并确定“受害者”和“干扰源”。&lt;/li&gt;
  &lt;li&gt;干扰优化策略：充分评估策略成本，通过精准的压制或驱逐策略，控制“干扰源”引入的资源竞争，或将“受害者”进行迁移。同时建设相关模型，用于指导应用后续的调度，提前规避应用干扰的发生。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/EsyMNqP5OZl9LgX6xmULvw&quot;&gt;Koordinator v1.1发布：负载感知与干扰检测采集&lt;/a&gt;当前 Koordinator 已经实现了一系列 Performance Collector，在单机侧采集与应用运行状态高相关性的底层指标，并通过 Prometheus 暴露出来，为干扰检测能力和集群应用调度提供支持。目前提供以下几个指标采集器：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;CPICollector：用于控制 CPI 指标采集器。CPI：Cycles Per Instruction。指令在计算机中执行所需要的平均时钟周期数。CPI 采集器基于 Cycles 和 Instructions 这两个 Kernel PMU（Performance Monitoring Unit）事件以及 perf_event_open(2) 系统调用实现。&lt;/li&gt;
  &lt;li&gt;PSICollector：用于控制 PSI 指标采集器。PSI：Pressure Stall Information。表示容器在采集时间间隔内，因为等待 cpu、内存、IO 资源分配而阻塞的任务数。使用 PSI 采集器前，需要在 Anolis OS 中开启 PSI 功能，您可以参考文档获取开启方法。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;腾讯云：存量 Pod 在节点上也有可能发生高负载，这时我们在节点上部署 Pod-Problem-Detecor、NodeProblem-Detecor，检测出哪个 Pod 会导致节点高负载，哪些 Pod 属于敏感 Pod，通过事件上报告诉API Server，让调度器将异常 Pod、敏感 Pod 重新调度到空闲节点。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/qlmm2h8RpQnKOnEjlK0pMA&quot;&gt;云原生场景下，如何缓减容器隔离漏洞，监控内核关键路径？&lt;/a&gt;OpenCloudOS 社区中的容器级别的性能跟踪机制——SLI，从容器的角度对 CPU、内存资源的竞争情况进行跟踪、观测，从而为容器性能问题的定位、分析提供可靠的指标。&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;用户态周期性监测：SLI 通过cgroup 接口提供容器的性能数据，用户态可以通过这些数据对容器性能进行监控。&lt;/li&gt;
  &lt;li&gt;容器内负载情况监控。&lt;strong&gt;容器内处于 R/D 态的进程平均数量&lt;/strong&gt;（分别是 1min、5min、15min 的容器平均负载），R 进程数量指标：评估容器内进程数量是否 overload。D进程数量指标：D状态进程的数量可以反馈IO等待以及锁竞争等的情况&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;进程在内核态执行时间&lt;/strong&gt;。内核态时间可能是因为系统调用、中断或者缺页异常等原因引起的。内核态的执行时间过长，会导致用户业务有较大延迟，从而引起性能抖动问题。&lt;/li&gt;
  &lt;li&gt;调度延迟，	监控容器进程的调度延迟信息（&lt;strong&gt;容器进程在调度队列上的等待时间&lt;/strong&gt;），反馈容器的 CPU 竞争情况，过大的调度延迟会导致业务出现性能抖动。&lt;/li&gt;
  &lt;li&gt;iowait 延迟，进程 IO 完成而引起的延迟时间。反馈容器进程的 IO 性能问题，过大的 IO 延迟会让业务文件访问产生性能问题&lt;/li&gt;
  &lt;li&gt;内存延迟，监控容器进程的内存申请延迟信息，反馈容器的内存申请性能情况，过大的内存申请延迟会导致业务出现性能抖动。&lt;/li&gt;
  &lt;li&gt;容器场景下，各个容器是相互独立的应用程序。由于不同容器在运行过程中，各自的资源使用情况、运行情况都不同，需要有一个&lt;strong&gt;独立的地方记录不同容器内核层面的异常日志信息&lt;/strong&gt;，上层应用可以&lt;strong&gt;根据日志信息，直接定位到对应容器&lt;/strong&gt;，从而进行合理的调度等操作；mbuf 就应运而生。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/kubernetes/open_cloud_os.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;借助mbuf 如何实现混部时的干扰检测呢？ 比如在线容器A延迟增加，主要原因是离线计算容器B导致（如何定位到）？在线容器A延迟增加，触发阈值打印的是prev进程的信息以及堆栈，这样就可以知道是被谁干扰的。PS：从cpu 执行的视角，cpu 时间被分摊给容器ABC等等，比例一段时间内是固定的，现在容器A延迟增加，cpu时间分摊比例相对了之前，多了很多容器B的，就知道是被B干扰的？&lt;/p&gt;

&lt;h2 id=&quot;源码分析&quot;&gt;源码分析&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;koordinator
  /cmd
    /koord-descheduler   
    /koord-manager       #  中心管控
    /koord-runtime-proxy #  充当 Kubelet 和 Containerd 之间的代理，它用于拦截 CRI 请求，并应用一些资源管理策略， 如混合工作负载编排场景下按实例优先级设置不同的 cgroup 参数，针对最新的 Linux 内核、CPU 架构应用新的隔离策略等。
    /koord-scheduler     
    /koordlet     
  /pkg
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;调度器&quot;&gt;调度器&lt;/h3&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;koordinator
  /cmd
    /koord-scheduler     
  /pkg
    /scheduler
      /apis
      /eventhandlers
      /frameworkext
      /plugins
        /batchresource
        /elasticquota
        /loadaware
        /nodenumaresource
        /reservation
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;k8s scheduler本身代码抽象比较好，对外提供&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;	&quot;k8s.io/kubernetes/cmd/kube-scheduler/app&quot;&lt;/code&gt; 包，一两行就可以启动一个调度器，koord-scheduler  主要提供了 plugin 实现，注册到 plugin  regisry 中即可。
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  cmd := app.NewSchedulerCommand(
 ... // 添加plugin
  );
  cmd.Execute()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;各个plugin 代码按需求分开看即可，后续的有 看到感兴趣的技术点持续补充&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Koordinator 将各优先级资源以标准的extend-resource 形式更新到Node信息中。&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;v1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Node&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;node-1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;allocatable&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;cpu&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;64&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;memory&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;256Gi&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;kubernetes.io/batch-cpu&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;500&quot;&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# 节点可以超卖的资源总量&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;kubernetes.io/batch-memory&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;50Gi&lt;/span&gt;   
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;koordlet&quot;&gt;koordlet&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;细粒度的容器指标采集，包括资源消耗、容器进程性能等&lt;/li&gt;
  &lt;li&gt;面向不同QoS等级Pod的干扰检测和调节策略能力&lt;/li&gt;
  &lt;li&gt;提供一系列的Runtime Proxy 插件，支持精细化的QoS 参数注入&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/kubernetes/koordlet_overview.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;koordinator
  /cmd
    /koordlet
  /pkg
    /koordlet
      /executor
      /metrics
      /pleg
      /qosmanager
      /resmanager
        /cpu_burst.go
        /cpu_evict.go
        /memory_evict.go
      /koordlet.go
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;koordlet 依次启动各个组件，各个组件又由多个功能模块/plugin 组成，每个功能模块运行一个小型control loop。比如 resManager 包含 cpu burst 模块，大致实现是check 所在node 上的所有pod，有需要 cpu burst的pod，就向pod 及container 对应cgroup 目录写入一个文件/配置。&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// koordlet 依次启动各个组件&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;daemon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;agent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewDaemon&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cfg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;daemon&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stopCtx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Done&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;go&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;metricCache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Run&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}()&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;go&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;resManager&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Run&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}()&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;go&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;qosManager&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Run&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}()&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;以resManager 组件为例&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// resManager 启动各个功能模块&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resmanager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stopCh&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;chan&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{})&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;utilruntime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;HandleCrash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;klog&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Starting resmanager&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;podsEvicted&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stopCh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;go&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;configextensions&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RunQOSGreyCtrlPlugins&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kubeClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stopCh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;util&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RunFeature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reconcileBECgroup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;cgroupResourceReconcile&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NewCgroupResourcesReconcile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;util&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RunFeatureWithInit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cgroupResourceReconcile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reconcile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;cpuSuppress&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NewCPUSuppress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;util&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RunFeature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cpuSuppress&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suppressBECPU&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;cpuBurst&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NewCPUBurst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;util&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RunFeatureWithInit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cpuBurst&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;cpuEvictor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NewCPUEvictor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;util&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RunFeature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cpuEvictor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cpuEvict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;memoryEvictor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NewMemoryEvictor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;util&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RunFeature&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;memoryEvictor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;memoryEvict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;rdtResCtrl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NewResctrlReconcile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;util&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RunFeatureWithInit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rdtResCtrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reconcile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;klog&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Infof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;start resmanager extensions&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetupPlugins&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kubeClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;metricCache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;statesInformer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;utilruntime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Must&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StartPlugins&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;QOSExtensionCfg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stopCh&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;klog&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Starting resmanager successfully&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stopCh&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;以cpu burst 为例，找到 需要 burst 的pod，更新其cgroup 配置。&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CPUBurst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;c&quot;&gt;// sync config from node slo&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;nodeSLO&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resmanager&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getNodeSLOCopy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nodeCPUBurstStrategy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nodeSLO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Spec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CPUBurstStrategy&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;podsMeta&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resmanager&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;statesInformer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetAllPods&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

	&lt;span class=&quot;c&quot;&gt;// get node state by node share pool usage&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;nodeState&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getNodeStateForBurst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nodeCPUBurstStrategy&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SharePoolThresholdPercent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;podsMeta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;podMeta&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;range&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;podsMeta&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;c&quot;&gt;// ignore non-burstable pod, e.g. LSR, BE pods&lt;/span&gt;
		&lt;span class=&quot;c&quot;&gt;// merge burst config from pod and node&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;cpuBurstCfg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genPodBurstConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;podMeta&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nodeCPUBurstStrategy&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CPUBurstConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;applyCPUBurst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cpuBurstCfg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;podMeta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;// set cpu.cfs_burst_us for containers&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;applyCFSQuotaBurst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cpuBurstCfg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;podMeta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nodeState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;// scale cpu.cfs_quota_us for pod and containers&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Recycle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// set cpu.cfs_burst_us for containers&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CPUBurst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;applyCPUBurst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;burstCfg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;slov1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CPUBurstConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;podMeta&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;statesinformer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PodMeta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;pod&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;podMeta&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pod&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;containerMap&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;make&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;corev1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Container&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;range&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pod&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Spec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Containers&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;container&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pod&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Spec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Containers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;containerMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;container&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;container&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;podCFSBurstVal&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;range&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pod&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Status&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ContainerStatuses&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;containerStat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pod&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Status&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ContainerStatuses&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;container&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exist&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;containerMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;containerStat&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;containerCFSBurstVal&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;calcStaticCPUBurstVal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;container&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;burstCfg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;containerDir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;burstPathErr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;koordletutil&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetContainerCgroupPathWithKube&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;podMeta&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CgroupDir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;containerStat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		
		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ValidateResourceValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;containerCFSBurstVal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;containerDir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CPUBurst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;podCFSBurstVal&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;containerCFSBurstVal&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;ownerRef&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ContainerOwnerRef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pod&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Namespace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pod&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;container&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;containerCFSBurstValStr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;strconv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FormatInt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;containerCFSBurstVal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;updater&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewCommonCgroupResourceUpdater&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ownerRef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;containerDir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CPUBurst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;containerCFSBurstValStr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;updated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UpdateByCache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;updater&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;// end for containers&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;podDir&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;koordletutil&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetPodCgroupDirWithKube&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;podMeta&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CgroupDir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ValidateResourceValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;podCFSBurstVal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;podDir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CPUBurst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;ownerRef&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PodOwnerRef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pod&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Namespace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pod&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;podCFSBurstValStr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;strconv&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FormatInt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;podCFSBurstVal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;updater&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewCommonCgroupResourceUpdater&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ownerRef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;podDir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CPUBurst&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;podCFSBurstValStr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;updated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UpdateByCache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;updater&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;executor.UpdateByCache 实质是 特定cgroup 目录写入一个文件。&lt;/p&gt;
</description>
      </item>
    
      <item>
        <title>go collection</title>
        <link>https://felix0080.github.io/2022/12/13/go_collection.html</link>
        <guid isPermaLink="true">https://felix0080.github.io/2022/12/13/go_collection.html</guid>
        <pubDate>Tue, 13 Dec 2022 00:00:00 +0000</pubDate>
        <description>&lt;h2 id=&quot;前言&quot;&gt;前言&lt;/h2&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#前言&quot; id=&quot;markdown-toc-前言&quot;&gt;前言&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#数组&quot; id=&quot;markdown-toc-数组&quot;&gt;数组&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#slice&quot; id=&quot;markdown-toc-slice&quot;&gt;slice&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#扩容&quot; id=&quot;markdown-toc-扩容&quot;&gt;扩容&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#slice-作为函数参数&quot; id=&quot;markdown-toc-slice-作为函数参数&quot;&gt;slice 作为函数参数&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#map&quot; id=&quot;markdown-toc-map&quot;&gt;map&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#实现&quot; id=&quot;markdown-toc-实现&quot;&gt;实现&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#string&quot; id=&quot;markdown-toc-string&quot;&gt;string&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;go&lt;/th&gt;
      &lt;th&gt;java&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;list&lt;/td&gt;
      &lt;td&gt;slice&lt;/td&gt;
      &lt;td&gt;ArrayList&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;map&lt;/td&gt;
      &lt;td&gt;map&lt;/td&gt;
      &lt;td&gt;HashMap&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;线程安全map&lt;/td&gt;
      &lt;td&gt;sync.Map&lt;/td&gt;
      &lt;td&gt;ConcurrentHashMap&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;对象池&lt;/td&gt;
      &lt;td&gt;对带缓冲的channel进行封装&lt;/td&gt;
      &lt;td&gt;commons-pool中的ObjectPool&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;hashmap和sync.Map都是unscalable，即参与的cpu越多，性能越差。&lt;/p&gt;

&lt;h2 id=&quot;数组&quot;&gt;数组&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://medium.com/@dwdraju/deep-dive-into-pointers-arrays-slice-309a843c63ad&quot;&gt;Deep Dive into Pointers, Arrays &amp;amp; Slice&lt;/a&gt;Go’s arrays are values rather than memory address.&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;myarr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;myarr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;myarr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;//output&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;// 打印的时候直接把值给打印出来了&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在 Go 中，与 C 数组变量隐式作为指针使用不同，Go 数组是值类型，赋值和函数传参操作都会复制整个数组数据。值类型还体现在&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;相同维数且包含相同个数元素的数组才可以比较&lt;/li&gt;
  &lt;li&gt;每个元素都相同的才相等&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;slice&quot;&gt;slice&lt;/h2&gt;

&lt;p&gt;切片与数组的关系非常密切，切片引入了一个&lt;strong&gt;抽象层&lt;/strong&gt;，提供了对数组中部分片段的引用，我们可以在运行区间可以修改它的长度，如果底层的数组长度不足就会触发扩容机制，切片中的数组就会发生变化，不过在上层看来切片是没有变化的，上层只需要与切片打交道不需要关心底层的数组变化。&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// $GOROOT/src/runtime/slice.go&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;slice&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unsafe&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pointer&lt;/span&gt;        &lt;span class=&quot;c&quot;&gt;// 指向底层数组的指针&lt;/span&gt;
	&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;   &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;                   &lt;span class=&quot;c&quot;&gt;// 可以用下标访问的元素个数&lt;/span&gt;
	&lt;span class=&quot;nb&quot;&gt;cap&lt;/span&gt;   &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;                   &lt;span class=&quot;c&quot;&gt;// 底层数组长度&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;img src=&quot;/public/upload/go/slice.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;func makeslice(et *_type, len, cap int) unsafe.Pointer {...}
func makeslice64(et *_type, len64, cap64 int64) unsafe.Pointer {...}
// growslice handles slice growth during append.It is passed the slice element type, the old slice, and the desired new minimum capacity,and it returns a new slice with at least that capacity, with the old data copied into it.
func growslice(et *_type, old slice, cap int) slice {...}
func slicecopy(to, fm slice, width uintptr) int {...}
func slicestringcopy(to []byte, fm string) int {...}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;s2 := s1[2:4]&lt;/code&gt; （slice 截取） s1 和s2 共用底层数组。&lt;/p&gt;

&lt;p&gt;与java ArrayList相比，slice 本身不提供类似 Add/Set/Remove方法。只有一个builtin 的append和切片功能，因为不提供crud方法，&lt;strong&gt;slice 更多作为一个“受体”&lt;/strong&gt;，与数组更近，与“ArrayList”更远。&lt;/p&gt;

&lt;h3 id=&quot;扩容&quot;&gt;扩容&lt;/h3&gt;

&lt;p&gt;扩容的本质过程：扩容实际上就是重新分配一块更大的内存，将原先的Slice数据拷贝到新的Slice中，然后返回新Slice，扩容后再将数据追加进去。&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// $GOROOT/src/builtin/builtin.go&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// The append built-in function appends elements to the end of a slice. If it has sufficient capacity, the destination is resliced to accommodate the new elements. If it does not, a new underlying array will be allocated. Append returns the updated slice. It is therefore necessary to store the result of append, often in the variable holding the slice itself:&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;//	slice = append(slice, elem1, elem2)&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;//	slice = append(slice, anotherSlice...)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;slice&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elems&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;slice-作为函数参数&quot;&gt;slice 作为函数参数&lt;/h3&gt;

&lt;p&gt;在 Go 语言中，数组更多是“退居幕后”，承担的是底层存储空间的角色。&lt;strong&gt;切片之于数组就像是文件描述符之于文件&lt;/strong&gt;。也正是因为这一特性，切片才能在函数参数传递时避免较大性能开销。因为我们传递的并不是数组本身，而是数组的“描述符”，而这个描述符的大小是固定的&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Go中slice作为函数参数丢失修改的问题&lt;/strong&gt;：根据我们的直觉，向s中append了一个元素1，s应该是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[1, 2, 1]&lt;/code&gt;，实际上，我们对slice的append的操作的确发生了，这里slice的cap不够，需要进行扩容，数组会被搬迁到一个新的位置，函数中s中的array指针也被赋值了，但是main 函数的s.array 还是指向原来的位置，s.len 也不会变。&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;appendToSlice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;   &lt;span class=&quot;c&quot;&gt;// 传参的时候实际上传的是 struct{unsafe.Pointer}&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1024&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;appendToSlice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;对于所有的 range 循环，Go 语言都会在编译期将原切片或者数组（下例中的arr）赋值给一个新的变量 ha，在赋值的过程中就发生了拷贝，&lt;strong&gt;所以我们遍历的切片已经不是原始的切片变量（arr）了&lt;/strong&gt;。&lt;/p&gt;

&lt;h2 id=&quot;map&quot;&gt;map&lt;/h2&gt;

&lt;p&gt;与常见编程语言的不同之处：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;在访问的key不存在时，仍会返回零值，不能通过返回nil 来判断元素是否存在。&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Map的value 可以是一个方法，与Go的Dock type 方式一起， 可以方便的实现单一方法对象的工厂模式。&lt;/p&gt;

    &lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;op&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;op&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;op&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;op&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;op&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;op&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;op&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;op&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;op&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;op&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;](&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;](&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;](&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;Go的内置集合中没有Set实现， 可以&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map[type]bool&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;map 类型对 value 的类型没有限制，但是&lt;strong&gt;对 key 的类型却有严格要求&lt;/strong&gt;，因为 map 类型要保证 key 的唯一性。Go 语言中要求，key 的类型必须支持“==”和“!=”两种比较操作符。PS：类似java 的hashmap key支持equal 和 hashcode&lt;/li&gt;
  &lt;li&gt;map 实例不是并发写安全的，也不支持并发读写。Go 1.9 版本中引入了支持并发写安全的 sync.Map 类型&lt;/li&gt;
  &lt;li&gt;考虑到 map 可以自动扩容，map 中数据元素的 value 位置可能在这一过程中发生变化，所以 Go 不允许获取 map 中 value 的地址，这个约束是在编译期间就生效的。&lt;/li&gt;
  &lt;li&gt;map 存的是值，put 和 get 操作都是会发生copy的，所以不要放很大的 value。&lt;/li&gt;
  &lt;li&gt;map delete key后不会自动缩容。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;对于slice 来说， index, value 可以视为一个kv&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;range&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;range&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;slice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;实现&quot;&gt;实现&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://qcrao.com/post/dive-into-go-map/&quot;&gt;深度解密 Go 语言之 map&lt;/a&gt; 未读。&lt;/p&gt;

&lt;p&gt;map是由 Go &lt;strong&gt;编译器与运行时联合实现的&lt;/strong&gt;。Go 编译器在编译阶段会将语法层面的 map 操作，重写为运行时对应的函数调用。语法层面 map 类型变量一一对应的是 runtime.hmap 的实例。&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;m := make(map[string]string)&lt;/code&gt; 中 m 是一个 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*hmap&lt;/code&gt; 类型，所以map 不用担心像slice 一样当函数参数且扩容后有丢失修改的问题。&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hmap&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;c&quot;&gt;// map中存入元素的个数， golang中调用len(map)的时候直接返回该字段&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt;     &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;
   &lt;span class=&quot;c&quot;&gt;// 状态标记位，通过与定义的枚举值进行&amp;amp;操作可以判断当前是否处于这种状态&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;flags&lt;/span&gt;     &lt;span class=&quot;kt&quot;&gt;uint8&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;B&lt;/span&gt;         &lt;span class=&quot;kt&quot;&gt;uint8&lt;/span&gt;  &lt;span class=&quot;c&quot;&gt;// 2^B 表示bucket的数量， B 表示取hash后多少位来做bucket的分组&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;noverflow&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uint16&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;// overflow bucket 的数量的近似数&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;hash0&lt;/span&gt;     &lt;span class=&quot;kt&quot;&gt;uint32&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;// hash seed （hash 种子） 一般是一个素数&lt;/span&gt;

   &lt;span class=&quot;n&quot;&gt;buckets&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;unsafe&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pointer&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;// 共有2^B个 bucket ，但是如果没有元素存入，这个字段可能为nil&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;oldbuckets&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unsafe&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pointer&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;// 在扩容期间，将旧的bucket数组放在这里， 新buckets会是这个的两倍大&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;nevacuate&lt;/span&gt;  &lt;span class=&quot;kt&quot;&gt;uintptr&lt;/span&gt;        &lt;span class=&quot;c&quot;&gt;// 表示已经完成扩容迁移的bucket的指针， 地址小于当前指针的bucket已经迁移完成&lt;/span&gt;

   &lt;span class=&quot;n&quot;&gt;extra&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mapextra&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;// optional fields&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/go/go_map_structure.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;与java map 类似，基于 bucket 数组（&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[]bmap&lt;/code&gt;），java中数组元素是链表（或指向链表），go map 的数组元素指向bmap（也算一个链表，只是链表节点可以存8个元素）
    &lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;c&quot;&gt;// A bucket for a Go map.&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bmap&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;tophash&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;bucketCnt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;uint8&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;c&quot;&gt;// 编译期间会给它加料，动态地创建一个新的结构&lt;/span&gt;
 &lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bmap&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;topbits&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;uint8&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keytype&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;values&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;valuetype&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;pad&lt;/span&gt;      &lt;span class=&quot;kt&quot;&gt;uintptr&lt;/span&gt;        &lt;span class=&quot;c&quot;&gt;// 内存对齐使用，可能不需要&lt;/span&gt;
     &lt;span class=&quot;n&quot;&gt;overflow&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uintptr&lt;/span&gt;        &lt;span class=&quot;c&quot;&gt;//  一个 bucket 在存储满 8 个元素后，就再也放不下了，这时候会创建新的 bucket，挂在原来的 bucket 的 overflow 指针成员上&lt;/span&gt;
 &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;定位
    &lt;ol&gt;
      &lt;li&gt;对key 做hashcode ，运行时会把 hashcode“一分为二”来看待，其中低位区的值用于选定 bucket，高位区的值用于在某个 bucket 中确定 key 的位置。每个 bucket 的 tophash 区域其实是用来快速定位 key 位置的，这样就&lt;strong&gt;避免了逐个 key 进行比较这种代价较大的操作&lt;/strong&gt;。尤其是当 key 是 size 较大的字符串类型时，好处就更突出了。这是一种以空间换时间的思路。PS：有点两次hash的意思&lt;/li&gt;
      &lt;li&gt;key 和 value 分开存储，而不是采用一个 kv 接着一个 kv 的 kv 紧邻方式存储，这带来的其实是算法上的复杂性，但却减少了因内存对齐带来的内存浪费。例如，有这样一个类型的 map：&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map[int64]int8&lt;/code&gt;，如果按照 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;key/value/key/value/...&lt;/code&gt; 这样的模式存储，那在每一个 key/value 对之后都要额外 padding 7 个字节；而将所有的 key，value 分别绑定到一起，这种形式 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;key/key/.../value/value/...&lt;/code&gt;，则只需要在最后添加 padding。&lt;/li&gt;
      &lt;li&gt;当我们声明一个 map 类型变量，比如 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;var m map[string]int&lt;/code&gt; 时，Go 运行时就会为这个变量对应的特定 map 类型，生成一个 runtime.maptype 实例。 存储key value 类型及类型大小等信息，用以辅助 key value 的定位&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;如果 key 或 value 的数据长度大于一定数值，那么运行时不会在 bucket 中直接存储数据，而是会存储 key 或 value 数据的指针。&lt;/li&gt;
  &lt;li&gt;对于新老bucket，扩容时 真正的排空和迁移工作是在 assign 和 delete 时逐步进行的。在rehash的过程中，oldbuckets到newbuckets是值拷贝，这样开销会比较大。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/UT8tydajjOUJkfc-Brcblw&quot;&gt;Golang 中 map 探究&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/go/go_map_overview.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;string&quot;&gt;string&lt;/h2&gt;

&lt;p&gt;Go&lt;strong&gt;原生支持&lt;/strong&gt;字符串（比如底层结构有专门字段存储字符串长度），string 类型的数据是不可变的，string 是值类型， 其默认初始化值为空字符串，不是nil&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// $GOROOT/src/reflect/value.go&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// StringHeader是一个string的运行时表示&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;StringHeader&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Data&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;uintptr&lt;/span&gt;    &lt;span class=&quot;c&quot;&gt;// 真实的字符串值数据就存储在一个被 Data 指向的底层数组中&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Len&lt;/span&gt;  &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;了解了 string 类型的实现原理后，我们还可以得到这样一个结论：那就是&lt;strong&gt;我们直接将 string 类型通过函数 / 方法参数传入也不会带来太多的开销&lt;/strong&gt;。因为传入的仅仅是一个“描述符”，而不是真正的字符串数据。其传递的开销也是恒定的，不会随着字符串大小的变化而变化。PS: go 中都是值传递，是不是可以认为，如果不想因为值传递 copy 太多数据，可以值传递的数据结构 不能直接 包含 指向的数据&lt;/p&gt;

&lt;p&gt;与常见编程语言的不同之处：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;string 是数据类型， 不是引用或指针类型&lt;/li&gt;
  &lt;li&gt;string 是只读的byte slice，len函数 返回的是byte 数&lt;/li&gt;
  &lt;li&gt;string的 byte 数组可以存放任何数据&lt;/li&gt;
&lt;/ol&gt;
</description>
      </item>
    
      <item>
        <title>gc的基本原理</title>
        <link>https://felix0080.github.io/2022/12/11/gc.html</link>
        <guid isPermaLink="true">https://felix0080.github.io/2022/12/11/gc.html</guid>
        <pubDate>Sun, 11 Dec 2022 00:00:00 +0000</pubDate>
        <description>&lt;h2 id=&quot;简介&quot;&gt;简介&lt;/h2&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#简介&quot; id=&quot;markdown-toc-简介&quot;&gt;简介&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#内存回收gc&quot; id=&quot;markdown-toc-内存回收gc&quot;&gt;内存回收/gc&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#gc-策略&quot; id=&quot;markdown-toc-gc-策略&quot;&gt;gc 策略&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#最基本的-mark-and-sweep-算法伪代码&quot; id=&quot;markdown-toc-最基本的-mark-and-sweep-算法伪代码&quot;&gt;最基本的 mark-and-sweep 算法伪代码&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#其他&quot; id=&quot;markdown-toc-其他&quot;&gt;其他&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;内存回收gc&quot;&gt;内存回收/gc&lt;/h2&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1234&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;cm&quot;&gt;/* ... */&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;num&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;在各种流传甚广的 C 语言葵花宝典里，一般都有这么一条神秘的规则，不能返回局部变量。当函数返回后，函数的栈帧（stack frame）就会被销毁，引用了被销毁位置的内存，轻则数据错乱，重则 segmentation fault。&lt;strong&gt;依赖人去处理复杂的对象内存管理的问题是不科学、不合理的&lt;/strong&gt;。C 和 C++ 程序员已经被折磨了数十年，我们不应该再重蹈覆辙了，于是，后来的很多编程语言就用上垃圾回收（GC）机制。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;reference counting&lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;tracing 包含两个部分：gc root；gc heap。 有两种风格：copying gc；mark-sweep gc。&lt;/p&gt;

    &lt;p&gt;&lt;img src=&quot;/public/upload/basic/gc_root.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;基本组件&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;精确扫描各种 gc root的 引用&lt;/li&gt;
  &lt;li&gt;精确扫描 gc heap中的引用&lt;/li&gt;
  &lt;li&gt;扫描过程中 对象图发生了变化怎么办&lt;/li&gt;
  &lt;li&gt;何时触发gc 对应用影响最小&lt;/li&gt;
  &lt;li&gt;让多个应用线程停顿下来&lt;/li&gt;
  &lt;li&gt;处理Finalizer&lt;/li&gt;
  &lt;li&gt;记录跨堆、跨代引用&lt;/li&gt;
  &lt;li&gt;cgo/jni 访问gc heap&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;更多挑战&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;最小化/去除stw阶段&lt;/li&gt;
  &lt;li&gt;partial stack scan&lt;/li&gt;
  &lt;li&gt;work stealing&lt;/li&gt;
  &lt;li&gt;read/write barrier 消除&lt;/li&gt;
  &lt;li&gt;footprint 控制&lt;/li&gt;
  &lt;li&gt;并发扫描线程栈&lt;/li&gt;
  &lt;li&gt;gc 硬件的设计&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;对象头是实现自动内存管理的关键元信息&lt;/strong&gt;，内存分配器和垃圾收集器都会访问对象头以获取相关的信息。不同的自动内存管理机制会在对象头中存储不同的信息，使用垃圾回收的编程语言会存储标记位 MarkBit/MarkWord，例如：Java 和 Go 语言；使用自动引用计数的会在对象头中存储引用计数 RefCount，例如：Objective-C。编程语言会选择将对象头与对象存储在一起，不过因为对象头的存储可能影响数据访问的局部性，所以有些编程语言可能会单独开辟一片内存空间来存储对象头并通过内存地址建立两者之间的隐式联系。&lt;/p&gt;

&lt;h2 id=&quot;gc-策略&quot;&gt;gc 策略&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/lx0vX-4wlQWxxfj017T9HA&quot;&gt;内存管理设计精要&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;基于引用计数的垃圾收集器是直接垃圾收集器，当我们改变对象之间的引用关系时会修改对象之间的引用计数，每个对象的引用计数都记录了当前有多少个对象指向了该对象，当对象的引用计数归零时，当前对象就会被自动释放。基于引用计数的垃圾收集是在用户程序运行期间实时发生的，所以在理论上也就不存在 STW 或者明显地垃圾回收暂停。缺点是递归的对象回收（一个对象引用计数归0 ==&amp;gt; 回收 ==&amp;gt; 其引用的对象都归0并回收）和循环引用。引用计数垃圾收集器是一种&lt;strong&gt;非移动&lt;/strong&gt;（Non-moving）的垃圾回收策略，它在回收内存的过程中不会移动已有的对象，很多编程语言都会对工程师直接暴露内存的指针，所以 C、C++ 以及 Objective-C 等编程语言其实都可以使用引用计数来解决内存管理的问题。&lt;/li&gt;
  &lt;li&gt;标记清除。分代算法中一般用于老年代
    &lt;ol&gt;
      &lt;li&gt;一般需要在对象头中加入表示对象存活的标记位（Mark Bit），也可以 使用位图（Bitmap）标记&lt;/li&gt;
      &lt;li&gt;一般会使用基于&lt;strong&gt;空闲链表&lt;/strong&gt;的分配器，因为对象在不被使用时会被&lt;strong&gt;就地回收&lt;/strong&gt;，所以长时间运行的程序会出现很多内存碎片&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;标记压缩。会把活跃的对象重新整理，从头开始排列，减少内存碎片。一种 moving 收集器，如果编程语言支持使用指针访问对象，那么我们就无法使用该算法。&lt;/li&gt;
  &lt;li&gt;标记复制
    &lt;ol&gt;
      &lt;li&gt;复制阶段 — 从 GC 根节点出发遍历内存中的对象，将发现的存活对象迁移到右侧的内存中；&lt;/li&gt;
      &lt;li&gt;转发阶段 — 在原始对象的对象头或者在原位置设置新对象的转发地址（Forwarding Address），如果其他对象引用了该对象可以从转发地址转到新的地址；&lt;/li&gt;
      &lt;li&gt;修复指针 — 遍历当前对象持有的引用，如果引用指向了左侧堆中的对象，回到第一步迁移发现的新对象；&lt;/li&gt;
      &lt;li&gt;交换阶段 — 当内存中不存在需要迁移的对象之后，交换左右两侧的内存区域；&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;高级策略&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;分代垃圾收集。也是一种 moving 收集器
    &lt;ol&gt;
      &lt;li&gt;基于弱分代假设（Weak Generational Hypothesis）上 —— 大多数的对象会在生成后马上变成垃圾&lt;/li&gt;
      &lt;li&gt;常见的分代垃圾回收会将堆分成青年代（Young、Eden）和老年代（Old、Tenured）。为了处理分代垃圾回收的跨代引用，我们需要解决两个问题，分别是如何识别堆中的跨代引用以及如何存储识别的跨代引用，在通常情况下我们会使用&lt;strong&gt;写屏障（Write Barrier）识别跨代引用并使用卡表（Card Table）&lt;/strong&gt;存储相关的数据。&lt;/li&gt;
      &lt;li&gt;卡表与位图比较相似，它也由一系列的比特位组成，其中每一个比特位都对应着老年区中的一块内存，如果该内存中的对象存在指向青年代对象的指针，那么这块内存在卡表中就会被标记，当触发 Minor GC 循环时，除了从根对象遍历青年代堆之外，我们还会从卡表标记区域内的全部老年代对象开始遍历青年代。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;增量并发收集器。今天的计算机往往都是多核的处理器，有条件在用户程序执行时并发标记和清除垃圾
    &lt;ol&gt;
      &lt;li&gt;需要使用屏障技术保证垃圾收集的正确性；与此同时，应用程序也不能等到内存溢出时触发垃圾收集，因为当内存不足时，应用程序已经无法分配内存，这与直接暂停程序没有什么区别&lt;/li&gt;
      &lt;li&gt;增量式的垃圾收集需要与三色标记法一起使用，为了保证垃圾收集的正确性，我们需要在垃圾收集开始前打开写屏障，这样用户程序对内存的修改都会先经过写屏障的处理，保证了堆内存中对象关系的强三色不变性或者弱三色不变性。&lt;/li&gt;
      &lt;li&gt;用户程序可能在标记执行的过程中修改对象的指针（改变引用关系），所以&lt;strong&gt;三色标记清除算法本身是不可以并发或者增量执行的&lt;/strong&gt;，它仍然需要 STW。想要并发或者增量地标记对象还是需要使用屏障技术。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;内存屏障技术是一种屏障指令，它可以让 CPU 或者编译器在执行内存相关操作时遵循特定的约束，目前的多数的现代处理器都会乱序执行指令以最大化性能，但是该技术能够保证代码对内存操作的顺序性，在内存屏障前执行的操作一定会先于内存屏障后执行的操作。垃圾收集中的屏障技术更像是一个钩子方法，它是在用户程序读取对象、创建新对象以及更新对象指针时执行的一段代码，根据操作类型的不同，我们可以将它们分成读屏障（Read barrier）和写屏障（Write barrier）两种，因为读屏障需要在读操作中加入代码片段，对用户程序的性能影响很大，所以编程语言往往都会采用写屏障保证三色不变性。&lt;strong&gt;写屏障是当对象之间的指针发生改变时调用的代码片段&lt;/strong&gt;。PS：内存屏障保障指令有序执行 ==&amp;gt; 编译器针对类似&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*slot = ptr&lt;/code&gt;代码A 插入内存屏障及代码片段B ==&amp;gt; 可以保证代码片段B 在A 之前执行。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/basic/gc_strategy.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://time.geekbang.org/column/article/465516&quot;&gt;垃圾回收：如何避免内存泄露？&lt;/a&gt; 未整理。&lt;/p&gt;

&lt;h2 id=&quot;最基本的-mark-and-sweep-算法伪代码&quot;&gt;最基本的 mark-and-sweep 算法伪代码&lt;/h2&gt;

&lt;p&gt;mutator 通过 new 函数来申请内存&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;new():
    ref = allocate()
    if ref == null
        collect()
        ref = allocate()
        
        if ref == null
            error &quot;Out of memory&quot;
    return ref
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;collect 分为mark 和 sweep 两个基本步骤&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;atomic collect():  // 这里 atomic 表明 gc 是原子性的，mutator 需要暂停
    markFromRoots()
    sweep(heapStart, heapEnd)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;从roots 对象开始mark&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;markFromRoots():
    initialize(worklist)
    for each reference in Roots  // Roots 表示所有根对象，比如全局对象，stack 中的对象
        if ref != null &amp;amp;&amp;amp; !isMarked(reference)
            setMarked(reference)
            add(worklist, reference)
            mark()          // mark 也可以放在循环外面
                    
initialize():
    // 对于单线程的collector 来说，可以用队列实现 worklist
    worklist = emptyQueue()
//如果 worklist 是队列，那么 mark 采用的是 BFS（广度优先搜索）方式来遍历引用树                
mark():
    while !isEmpty(worklist):
        ref = remove(worklist)  // 从 worklist 中取出第一个元素
        for each field in Pointers(ref)  // Pointers(obj) 返回一个object的所有属性，可能是数据，对象，指向其他对象的指针
            child = *field
            if child != null &amp;amp;&amp;amp; !isMarked(child)
                setMarked(child)
                add(worklist, child)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;sweep逻辑，实质也是调用free&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sweep(start, end):
    scan = start
    while scan &amp;lt; end
        if isMarked(scan)
            unsetMarked(scan)
        else
            free(scan)
        scan = nextObject(scan)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;其他&quot;&gt;其他&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/jvm/gc.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</description>
      </item>
    
      <item>
        <title>golang性能分析及优化</title>
        <link>https://felix0080.github.io/2022/12/09/go_performance.html</link>
        <guid isPermaLink="true">https://felix0080.github.io/2022/12/09/go_performance.html</guid>
        <pubDate>Fri, 09 Dec 2022 00:00:00 +0000</pubDate>
        <description>&lt;h2 id=&quot;前言&quot;&gt;前言&lt;/h2&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#前言&quot; id=&quot;markdown-toc-前言&quot;&gt;前言&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#性能优化流程&quot; id=&quot;markdown-toc-性能优化流程&quot;&gt;性能优化流程&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#profiling&quot; id=&quot;markdown-toc-profiling&quot;&gt;Profiling&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#如何看懂火焰图以从下到上为例&quot; id=&quot;markdown-toc-如何看懂火焰图以从下到上为例&quot;&gt;如何看懂火焰图（以从下到上为例）&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#实操&quot; id=&quot;markdown-toc-实操&quot;&gt;实操&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#实现&quot; id=&quot;markdown-toc-实现&quot;&gt;实现&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#trace&quot; id=&quot;markdown-toc-trace&quot;&gt;trace&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#其它&quot; id=&quot;markdown-toc-其它&quot;&gt;其它&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;我们或许都有这样的体会，自己思考明白，设计出来的程序，可以很清晰明了的将细节解释明白，对功能的增删改也是可以做到灵活应对。可是让我们一下子去修改别人写的功能或模块的时候，很多时候会一脸懵逼，这也不敢动，那也不敢动，在不理解的情况下，有疑问，一定要问清楚原理和逻辑，否则搞不好就是线上问题。如上情况，最重要的一个原因就是自己对当前模块/功能的熟悉程度，以及自己的&lt;strong&gt;思维模型是否可迁移&lt;/strong&gt;。&lt;/p&gt;

&lt;h2 id=&quot;性能优化流程&quot;&gt;性能优化流程&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;理清待优化代码的常用逻辑与场景&lt;/li&gt;
  &lt;li&gt;根据实际场景编写压测用例&lt;/li&gt;
  &lt;li&gt;使用pprof 或者火焰图等工具取得数据&lt;/li&gt;
  &lt;li&gt;找到热点代码重点优化&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;profiling&quot;&gt;Profiling&lt;/h2&gt;

&lt;p&gt;pprof 是用于可视化和分析性能分析数据的工具。&lt;/p&gt;

&lt;p&gt;有两种类型的 profiler ：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;追踪型：任何时候触发提前设定的事件就会做测量，例如：函数调用，函数退出，等等&lt;/li&gt;
  &lt;li&gt;采样型：常规时间间隔做一次测量&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Go CPU profiler 是一个采样型的 profiler。也有一个追踪型的 profiler，Go 执行追踪器，用来追踪特定事件像请求锁，GC 相关的事件，等等。&lt;/p&gt;

&lt;p&gt;采样型 profiler 通常包含两个主要部分：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;采样器：一个在时间间隔触发的回调，一个堆栈信息一般会被收集成 profiling data。不同的 profiler 用不同的策略去触发回调。&lt;/li&gt;
  &lt;li&gt;数据收集：这个是 profiler 收集数据的地方：它可能是内存占用或者是调用统，基本上跟堆栈追踪相关的数据&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/go/pprof.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Profiling 这个词比较难翻译，一般译成画像。比如在案件侦破的时候会对嫌疑人做画像，从犯罪现场的种种证据，找到嫌疑人的各种特征，方便对嫌疑人进行排查；还有就是互联网公司会对用户信息做画像，通过了解用户各个属性（年龄、性别、消费能力等），方便为用户推荐内容或者广告。在计算机性能调试领域里，profiling 就是对应用的画像，这里画像就是应用使用 CPU 和内存的情况。也就是说应用使用了多少 CPU 资源？都是哪些部分在使用？每个函数使用的比例是多少？有哪些函数在等待 CPU 资源？知道了这些，我们就能对应用进行规划，也能快速定位性能瓶颈。&lt;/p&gt;

&lt;p&gt;CPU Profiling 是如何工作的？stack trace + statistics 的模型。当我们准备进行 CPU Profiling 时，通常需要选定某一&lt;strong&gt;时间窗口&lt;/strong&gt;，在该窗口内，CPU Profiler 会向目标程序注册一个定时执行的 hook（有多种手段，譬如 SIGPROF 信号），在这个 hook 内我们每次会获取业务线程此刻的 &lt;strong&gt;stack trace&lt;/strong&gt;。我们将 hook 的执行频率控制在特定的数值，譬如 100hz，这样就做到每 10ms 采集一个业务代码的调用栈样本。当时间窗口结束后，我们将采集到的所有样本进行聚合，最终得到每个函数被采集到的次数，相较于总样本数也就得到了每个函数的&lt;strong&gt;相对占比&lt;/strong&gt;。借助此模型我们可以发现占比较高的函数，进而定位 CPU 热点。&lt;/p&gt;

&lt;p&gt;Heap Profiling 也是stack trace + statistics 的模型。数据采集工作并非简单通过定时器开展，而是需要侵入到内存分配路径内，即直接将自己集成在内存分配器内，当应用程序进行内存分配时拿到当前的 stack trace，最终将所有样本聚合在一起，这样我们便能知道每个函数直接或间接地内存分配数量了。由于 Heap Profiling 也是采样的（默认每分配 512k 采样一次），所以&lt;strong&gt;展示的内存大小要小于实际分配的内存大小&lt;/strong&gt;。同 CPU Profiling 一样，这个数值仅仅是用于计算&lt;strong&gt;相对占比&lt;/strong&gt;，进而定位内存分配热点。&lt;/p&gt;

&lt;h3 id=&quot;如何看懂火焰图以从下到上为例&quot;&gt;如何看懂火焰图（以从下到上为例）&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://cloud.tencent.com/developer/article/1873597&quot;&gt;如何看懂火焰图&lt;/a&gt;火焰图的调用顺序从下到上，每个方块代表一个函数，它上面一层表示这个函数会调用哪些函数，方块的大小代表了占用 CPU 使用的长短。火焰图的配色并没有特殊的意义，默认的红、黄配色是为了更像火焰而已。&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;每一列代表一个调用栈，每一个格子代表一个函数&lt;/li&gt;
  &lt;li&gt;纵轴展示了栈的深度，按照调用关系从下到上排列。最顶上格子代表采样时，正在占用 cpu 的函数。&lt;/li&gt;
  &lt;li&gt;横轴的意义是指：火焰图将采集的多个调用栈信息，通过按字母横向排序的方式将众多信息聚合在一起。需要注意的是它并不代表时间。&lt;/li&gt;
  &lt;li&gt;横轴格子的宽度代表其在采样中出现频率，所以一个格子的宽度越大，说明它是瓶颈原因的可能性就越大。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/go/flame_graph.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;总的来说&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;颜色本身没有什么意义&lt;/li&gt;
  &lt;li&gt;纵向表示调用栈的深度&lt;/li&gt;
  &lt;li&gt;横向表示消耗的时间&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;实操&quot;&gt;实操&lt;/h3&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;net/http&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;net/http/pprof&quot;&lt;/span&gt;  &lt;span class=&quot;c&quot;&gt;// 会自动注册handler到http server，方便通过http 接口获取程序运行采样报告&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;go&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ListenAndServe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;:6060&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}()&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;以空导入的方式导入 net/http/pprof 包，并在一个单独的 goroutine 中启动一个标准的 http 服务，就可以实现对 pprof 性能剖析的支持。pprof 工具可以通过 6060 端口采样到我们的 Go 程序的运行时数据。然后就可以通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://192.168.10.18:6060/debug/pprof&lt;/code&gt; 查看程序的采样信息，但是可读性非常差，需要借助pprof 的辅助工具来分析。&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;// 192.168.10.18为服务端的主机地址
&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;go tool pprof &lt;span class=&quot;nt&quot;&gt;-http&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;:9090 http://192.168.10.18:6060/debug/pprof/profile?seconds&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;30
Fetching profile over HTTP from http://192.168.10.18:6060/debug/pprof/profile
Saved profile &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; /Users/tonybai/pprof/pprof.server.samples.cpu.004.pb.gz
Serving web UI on http://localhost:9090
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;实现&quot;&gt;实现&lt;/h3&gt;

&lt;p&gt;cpu采样&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;采样对象： 函数调用和它们占用的时间&lt;/li&gt;
  &lt;li&gt;采样率：100次/秒，固定值&lt;/li&gt;
  &lt;li&gt;采样时间：从手动启动到手动结束&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/DRQWcU2dN-FycoyFZfnklA&quot;&gt;深究 Go CPU profiler&lt;/a&gt;在Linux中，Go runtime 使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;setitimer/timer_create/timer_settime&lt;/code&gt; API来设置SIGPROF 信号处理器。这个处理器在runtime.SetCPUProfileRate 控制的周期内被触发，默认为100Mz（10ms）。一旦 pprof.StartCPUProfile 被调用，Go runtime 就会在特定的时间间隔产生SIGPROF 信号。内核向应用程序中的一个运行线程发送 SIGPROF 信号。&lt;strong&gt;由于 Go 使用非阻塞式 I/O，等待 I/O 的 goroutines 不被计算为运行&lt;/strong&gt;，Go CPU profiler 不捕获这些。顺便提一下：这是实现 fgprof 的基本原因。fgprof 使用 runtime.GoroutineProfile来获得等待和非等待的 goroutines 的 profile 数据。&lt;/p&gt;

&lt;p&gt;一旦一个随机运行的goroutine 收到 SIGPROF 信号，它就会被中断，然后信号处理器的程序开始运行。被中断的 goroutine 的堆栈 在这个信号处理器的上下文中被检索出来，然后和当前的 profiler 标签一起被保存到一个无锁的日志结构中（每个捕获的堆栈追踪都可以和一个自定义的标签相关联，你可以用这些标签在以后做过滤）。这个特殊的无锁结构被命名为 profBuf ，它被定义在 runtime/profbuf.go 中，它是一个单一写、单一读的无锁环形缓冲 结构，与这里发表的结构相似。writer 是 profiler 的信号处理器，reader 是一个 goroutine(profileWriter)，定期读取这个缓冲区的数据，并将结果汇总到最终的 hashmap。这个最终的 hashmap 结构被命名为 profMap，并在 runtime/pprof/map.go中定义。PS：goroutine 堆栈信息 ==&amp;gt; sigProfHandler ==write==&amp;gt; profBuf ==read==&amp;gt; profWriter ==&amp;gt; profMap&lt;/p&gt;

&lt;p&gt;heap 采样&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;采样程序通过内存分配器 在堆上分配和释放内存，记录分配/释放的大小和数量&lt;/li&gt;
  &lt;li&gt;采样率：每分配512KB 记录一次，可在运行开头修改，1为每次分配均记录。&lt;/li&gt;
  &lt;li&gt;采样时间：从程序运行开始到结束&lt;/li&gt;
  &lt;li&gt;采样指标：alloc_space,alloc_objects,inuse_space,inuse_objects&lt;/li&gt;
  &lt;li&gt;计算方式： inuse = alloc - free&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;goroutine&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;记录所有用户发起且在运行中的goroutine（即入口非runtime开头的）的调用栈信息&lt;/li&gt;
  &lt;li&gt;runtime.main 的调用栈信息&lt;/li&gt;
  &lt;li&gt;采样方式：stop the world ==&amp;gt; 遍历 allg slice ==&amp;gt; 输出创建g的堆栈 ==&amp;gt; start the world
ThreadCreate&lt;/li&gt;
  &lt;li&gt;记录程序创建的所有系统线程信息&lt;/li&gt;
  &lt;li&gt;采样方式：stop the world ==&amp;gt; 遍历 allm 链表 ==&amp;gt; 输出创建m的堆栈 ==&amp;gt; start the world&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;block&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;采样阻塞操作的次数和耗时&lt;/li&gt;
  &lt;li&gt;采样率：阻塞耗时超过阈值的才会被记录。1 为每次阻塞均记录&lt;/li&gt;
  &lt;li&gt;采样方式：阻塞操作 ==&amp;gt; Profiler 上报调用栈和消耗时间（时间未到阈值则丢弃） ==&amp;gt; 遍历阻塞记录  ==&amp;gt; 统计阻塞次数和耗时&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;锁竞争&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;采样争抢锁的次数和耗时&lt;/li&gt;
  &lt;li&gt;采样率：只记录固定比例的锁操作，1 为每次加锁均记录&lt;/li&gt;
  &lt;li&gt;采样方式：阻塞操作 ==&amp;gt; Profiler 上报调用栈和消耗时间（比例未命中则丢弃） ==&amp;gt; 遍历锁记录  ==&amp;gt; 统计锁竞争次数和耗时&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;trace&quot;&gt;trace&lt;/h2&gt;

&lt;p&gt;虽然CPU分析器做了一件很好的工作，告诉你什么函数占用了最多的CPU时间，但它并不能帮助你确定是什么阻止了goroutine运行。这里可能的原因：被syscall阻塞 、阻塞在共享内存(channel/mutex etc)、阻塞在运行时(如 GC)、甚至有可能是运行时调度器不工作导致的。这种问题使用pprof很难排查。&lt;/p&gt;

&lt;p&gt;go tool trace 能够跟踪捕获各种执行中的事件，例如：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Goroutine 的创建/阻塞/解除阻塞。&lt;/li&gt;
  &lt;li&gt;Syscall 的进入/退出/阻止，GC 事件。&lt;/li&gt;
  &lt;li&gt;Heap 的大小改变。&lt;/li&gt;
  &lt;li&gt;Processor 启动/停止等等。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/go/trace_view.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;时间线: 显示执行的时间单元 根据时间的纬度不同 可以调整区间&lt;/li&gt;
  &lt;li&gt;stats 区域
    &lt;ol&gt;
      &lt;li&gt;堆，显示执行期间内存的分配和释放情况（折线图）&lt;/li&gt;
      &lt;li&gt;协程(Goroutine)，显示每个时间点哪些Goroutine在运行 哪些goroutine等待调度 ，其包含 GC 等待（GCWaiting）、可运行（Runnable）、运行中（Running）这三种状态。&lt;/li&gt;
      &lt;li&gt;Threads，显示在执行期间有多少个线程在运行，其包含正在调用 Syscall（InSyscall）、运行中（Running）这两种状态。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;proc区域，虚拟处理器Processor：每个虚拟处理器显示一行，虚拟处理器的数量一般默认为系统内核数。数量由环境变量GOMAXPROCS控制。 分两层
    &lt;ol&gt;
      &lt;li&gt;上一层表示Processor上运行的goroutine的信息，一个P 某个时刻只能运行一个G，选中goroutine 可以查看特定时间点 特定goroutine的执行堆栈信息以及关联的事件信息&lt;/li&gt;
      &lt;li&gt;下一层表示processor附加的事件比如SysCall 或runtime system events&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;其它&quot;&gt;其它&lt;/h2&gt;

&lt;p&gt;Linux perf 使用 PMU（Performance Monitor Unit）计数器进行采样。你指示 PMU 在某些事件发生 N 次后产生一个中断。一个例子，可能是每 1000 个 CPU 时钟周期进行一次采样。一旦数据收集回调被定期触发，剩下的就是收集堆栈痕迹并适当地汇总。Linux perf 使用 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;perf_event_open(PERF_SAMPLE_STACK_USER,...)&lt;/code&gt; 来获取堆栈追踪信息。捕获的堆栈痕迹通过 mmap’d 环形缓冲区写到用户空间。&lt;/p&gt;
</description>
      </item>
    
      <item>
        <title>controller-runtime细节分析</title>
        <link>https://felix0080.github.io/2022/11/24/controller_runtime_detail.html</link>
        <guid isPermaLink="true">https://felix0080.github.io/2022/11/24/controller_runtime_detail.html</guid>
        <pubDate>Thu, 24 Nov 2022 00:00:00 +0000</pubDate>
        <description>&lt;h2 id=&quot;简介&quot;&gt;简介&lt;/h2&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#简介&quot; id=&quot;markdown-toc-简介&quot;&gt;简介&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#client&quot; id=&quot;markdown-toc-client&quot;&gt;client&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#初始化&quot; id=&quot;markdown-toc-初始化&quot;&gt;初始化&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#请求分发从cache中读取还是直连&quot; id=&quot;markdown-toc-请求分发从cache中读取还是直连&quot;&gt;请求分发：从cache中读取还是直连&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#直连apiserver-取数据根据gvk分发restclient&quot; id=&quot;markdown-toc-直连apiserver-取数据根据gvk分发restclient&quot;&gt;直连apiserver 取数据：根据gvk分发restclient&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#从cache中取数据根据gvk分发informer&quot; id=&quot;markdown-toc-从cache中取数据根据gvk分发informer&quot;&gt;从cache中取数据：根据gvk分发informer&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#option-花样多&quot; id=&quot;markdown-toc-option-花样多&quot;&gt;option 花样多&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#clientobject&quot; id=&quot;markdown-toc-clientobject&quot;&gt;client.Object&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;以下部分是controller-runtime 组件&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Cache，Kubebuilder 的核心组件，负责在 Controller 进程里面根据 Scheme 同步 Api Server 中所有该 Controller 关心 GVKs 的 GVRs，其核心是 GVK -&amp;gt; Informer 的映射，Informer 会负责监听对应 GVK 的 GVRs 的创建/删除/更新操作，以触发 Controller 的 Reconcile 逻辑。&lt;/li&gt;
  &lt;li&gt;Controller，Kubebuidler 为我们生成的脚手架文件，我们只需要实现 Reconcile 方法即可。&lt;/li&gt;
  &lt;li&gt;Clients，在实现 Controller 的时候不可避免地需要对某些资源类型进行创建/删除/更新，就是通过该 Clients 实现的，其中查询功能实际查询是本地的 Cache，写操作直接访问 Api Server。&lt;/li&gt;
  &lt;li&gt;Index，由于 Controller 经常要对 Cache 进行查询，Kubebuilder 提供 Index utility 给 Cache 加索引提升查询效率。&lt;/li&gt;
  &lt;li&gt;OwnerReference，K8s GC 在删除一个对象时，任何 ownerReference 是该对象的对象都会被清除，与此同时，Kubebuidler 支持所有对象的变更都会触发 Owner 对象 controller 的 Reconcile 方法。&lt;/li&gt;
  &lt;li&gt;Finalizer，在一般情况下，如果资源被删除之后，我们虽然能够被触发删除事件，但是这个时候从 Cache 里面无法读取任何被删除对象的信息，这样一来，导致很多垃圾清理工作因为信息不足无法进行，K8s 的 Finalizer 字段用于处理这种情况。在 K8s 中，&lt;strong&gt;只要对象 ObjectMeta 里面的 Finalizers 不为空，则delete只是 update deletionTimestamp 字段&lt;/strong&gt;。删掉 Finalizers 后，Finalizers 为空且deletionTimestamp不为空时，K8s 的 GC会立马删除掉该对象”。所以一般的使用姿势是
    &lt;ol&gt;
      &lt;li&gt;在DeletionTimestamp 为空时， 若对象没有Finalizers 就把 Finalizers 设置好，&lt;/li&gt;
      &lt;li&gt;在DeletionTimestamp 不为空时， 根据 Finalizers 的值执行完所有的 pre-delete hook（此时可以在 Cache 里面读取到被删除对象的任何信息），之后将 Finalizers 置为空。
 一个使用场景时：正常情况下 A 创建B，则B的 ownerreference 指向A，删除A时会自动删除B。但 ownerreference 不能跨ns，因此在对 跨ns 进行级联删除时，可以使用Finalizer&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;client&quot;&gt;client&lt;/h2&gt;

&lt;p&gt;使用示例&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;pod&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;core&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;		&lt;span class=&quot;c&quot;&gt;// 底层 通过反射获取 到pod 类型，进而获取到 pod gvk，拿到对应的client 或informer，再根据 objName 获取真实数据。&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;podName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;使用：client.Get 可以根据 obj 获取到 gvk对应的  client 或informer ，进而获取到 obj的真实数据，赋值给 obj。client的厉害之处就在于 &lt;strong&gt;无论从informer 缓存取数据 还是直连apiserver/restclient 取数据 ，都屏蔽了 gvk 的差异&lt;/strong&gt;。用户只要提供一个 空的go struct 以及 资源name ，client 即可为 空的go struct 赋值。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;controller-runtime
	/pkg
		/cache
			/cache.go					# 定义了Cache interface
			/informer_cache.go
			/multi_namespace_cache.go	
		/client
			/split.go
			/interfaces.go				# 定义了Reader interface
			/type_client.go
			/unstructured_client.go
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;初始化&quot;&gt;初始化&lt;/h3&gt;

&lt;p&gt;manager.Manager interface 聚合了 cluster.Cluster interface，对应的 controllerManager struct聚合了 cluster struct。manager 除了健康检查、选主之外，&lt;strong&gt;主要的能力是cluster 提供的&lt;/strong&gt;，即manager.GetXX ==&amp;gt; manager.client.GetXX&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ Scheme: scheme,...})
	cluster, err := cluster.New(config, func(clusterOptions *cluster.Options)
		mapper, err := options.MapperProvider(config)
		cache, err := options.NewCache(config, ...)
		apiReader, err := client.New(config, clientOptions)
		writeObj, err := options.NewClient(cache, config, clientOptions, options.ClientDisableCacheFor...)	// 即DefaultNewClient
			c, err := client.New(config, options)
			return client.NewDelegatingClient(client.NewDelegatingClientInput{
				CacheReader:     cache,
				Client:          c,
				UncachedObjects: uncachedObjects,
			})
		return &amp;amp;cluster{
			config:           config,
			scheme:           options.Scheme,
			cache:            cache,
			fieldIndexes:     cache,
			client:           writeObj,
			apiReader:        apiReader,
			mapper:           mapper,
		}, nil
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;其中 apiReader 是直连apiserver的client， writeObj 即包装了缓存后的client&lt;/p&gt;

&lt;h3 id=&quot;请求分发从cache中读取还是直连&quot;&gt;请求分发：从cache中读取还是直连&lt;/h3&gt;

&lt;p&gt;请求分发主力是delegatingClient&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;delegatingClient&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;Reader&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;Writer&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;StatusClient&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;scheme&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;runtime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Scheme&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;mapper&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;meta&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RESTMapper&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NewDelegatingClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NewDelegatingClientInput&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;uncachedGVKs&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;schema&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GroupVersionKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}{}&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;range&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UncachedObjects&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;gvk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;apiutil&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GVKForObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Scheme&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;uncachedGVKs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gvk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}{}&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;delegatingClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;scheme&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Scheme&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;mapper&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RESTMapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;Reader&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;delegatingReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;CacheReader&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;       &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CacheReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;	&lt;span class=&quot;c&quot;&gt;// 实际为cache&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;ClientReader&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;      &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;scheme&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;            &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Scheme&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;uncachedGVKs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;      &lt;span class=&quot;n&quot;&gt;uncachedGVKs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;cacheUnstructured&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CacheUnstructured&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;Writer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;       &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;StatusClient&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;以 Get 方法为例，如果对象 shouldBypassCache，则分发给 ClientReader，如果对象是缓存的，则把请求转发到 cache.Get。&lt;/p&gt;
&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;delegatingReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ObjectKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetOption&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isUncached&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shouldBypassCache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isUncached&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;	&lt;span class=&quot;c&quot;&gt;// 直连apiserver 读取&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ClientReader&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CacheReader&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;	&lt;span class=&quot;c&quot;&gt;// 执行cache.Get&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;直连apiserver-取数据根据gvk分发restclient&quot;&gt;直连apiserver 取数据：根据gvk分发restclient&lt;/h3&gt;

&lt;p&gt;为了支持多种类型，非缓存client 包含 unstructuredClient/typedClient。&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// controller-runtime/pkg/client/client.go&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ObjectKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetOption&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unstructured&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Unstructured&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unstructuredClient&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;metav1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PartialObjectMetadata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;c&quot;&gt;// Metadata only object should always preserve the GVK coming in from the caller.&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resetGroupVersionKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetObjectKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GroupVersionKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;metadataClient&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;typedClient&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// controller-runtime/pkg/client/typed_client.go&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;typedClient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ObjectKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetOption&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getResource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;getOpts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GetOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;getOpts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApplyOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;NamespaceIfScoped&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Namespace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isNamespaced&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;Resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;VersionedParams&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;getOpts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AsGetOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;paramCodec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Do&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Into&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;clientCache  是 k8s teype与client 的cache，不是数据的cache。&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// controller-runtime/pkg/client/client_cache.go&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// clientCache creates and caches rest clients and metadata for Kubernetes types.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;clientCache&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Config&lt;/span&gt;			&lt;span class=&quot;c&quot;&gt;// config is the rest.Config to talk to an apiserver&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;scheme&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;runtime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Scheme&lt;/span&gt;		&lt;span class=&quot;c&quot;&gt;// scheme maps go structs to GroupVersionKinds	&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;mapper&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;meta&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RESTMapper&lt;/span&gt;		&lt;span class=&quot;c&quot;&gt;// mapper maps GroupVersionKinds to Resources&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;codecs&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;serializer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CodecFactory&lt;/span&gt;		&lt;span class=&quot;c&quot;&gt;// codecs are used to create a REST client for a gvk&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;structuredResourceByType&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;schema&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GroupVersionKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resourceMeta&lt;/span&gt;		&lt;span class=&quot;c&quot;&gt;// structuredResourceByType caches structured type metadata&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;unstructuredResourceByType&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;schema&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GroupVersionKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resourceMeta&lt;/span&gt;	&lt;span class=&quot;c&quot;&gt;// unstructuredResourceByType caches unstructured type metadata&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;mu&lt;/span&gt;                         &lt;span class=&quot;n&quot;&gt;sync&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RWMutex&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// resourceMeta caches state for a Kubernetes type.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resourceMeta&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;c&quot;&gt;// client is the rest client used to talk to the apiserver&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;rest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Interface&lt;/span&gt;
	&lt;span class=&quot;c&quot;&gt;// gvk is the GroupVersionKind of the resourceMeta&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;gvk&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;schema&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GroupVersionKind&lt;/span&gt;
	&lt;span class=&quot;c&quot;&gt;// mapping is the rest mapping&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;mapping&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;meta&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RESTMapping&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// getResource returns the resource meta information for the given type of object.If the object is a list, the resource represents the item's type instead.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;clientCache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getResource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;runtime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resourceMeta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;gvk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;apiutil&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GVKForObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scheme&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isUnstructured&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unstructured&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Unstructured&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isUnstructuredList&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unstructured&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UnstructuredList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;isUnstructured&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isUnstructured&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isUnstructuredList&lt;/span&gt;
	&lt;span class=&quot;c&quot;&gt;// It's better to do creation work twice than to not let multiple people make requests at once&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mu&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RLock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;resourceByType&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;structuredResourceByType&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isUnstructured&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;resourceByType&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unstructuredResourceByType&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;known&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resourceByType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gvk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mu&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RUnlock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;known&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;c&quot;&gt;// Initialize a new Client&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mu&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Lock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mu&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Unlock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;newResource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gvk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;meta&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsListType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isUnstructured&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;resourceByType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gvk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;从cache中取数据根据gvk分发informer&quot;&gt;从cache中取数据：根据gvk分发informer&lt;/h2&gt;

&lt;p&gt;cache 实现了 client.Reader 接口，具体实现是  informerCache，它聚合了 InformersMap&lt;/p&gt;
&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Cache&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;c&quot;&gt;// Cache acts as a client to objects stored in the cache.&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Reader&lt;/span&gt;
	&lt;span class=&quot;c&quot;&gt;// Cache loads informers and adds field indices.&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;Informers&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// controller-runtime/pkg/cache/informer_cache.go&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ip&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;informerCache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ObjectKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetOption&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;gvk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;apiutil&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GVKForObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ip&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Scheme&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;started&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ip&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InformersMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gvk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;specificInformersMap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gvk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;	&lt;span class=&quot;c&quot;&gt;// 返回MapEntry，有informer 则返回，无则创建&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ok&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ip&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;informersByGVK&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gvk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ok&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;ip&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;addInformerToMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gvk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;lw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ip&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;createListWatcher&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gvk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;ni&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewSharedIndexInformer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lw&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Indexers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
				&lt;span class=&quot;k&quot;&gt;go&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Informer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ip&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WaitForCacheSync&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Reader&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;		&lt;span class=&quot;c&quot;&gt;// CacheReader.Get&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;InformersMap create and caches Informers for (runtime.Object, schema.GroupVersionKind) pairs. 如果informer 已经存在则返回informer，否则新建一个 informer 并加入到map中，后续的Get 就交给 informer 了。&lt;/p&gt;
&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InformersMap&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;structured&lt;/span&gt;   &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;specificInformersMap&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;unstructured&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;specificInformersMap&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;metadata&lt;/span&gt;     &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;specificInformersMap&lt;/span&gt;

	&lt;span class=&quot;c&quot;&gt;// Scheme maps runtime.Objects to GroupVersionKinds&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;Scheme&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;runtime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Scheme&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// Get will create a new Informer and add it to the map of InformersMap if none exists.  Returns the Informer from the map.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;m&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InformersMap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gvk&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;schema&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GroupVersionKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;runtime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MapEntry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unstructured&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Unstructured&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unstructured&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gvk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unstructured&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UnstructuredList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unstructured&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gvk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;metav1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PartialObjectMetadata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gvk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;metav1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PartialObjectMetadataList&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gvk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;structured&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gvk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;因为不确定调用方是否会更改out 的数据，cache.Get 时会对 cache中的数据做deepcopy 再赋值给out。对于List 操作 且object 数据量很大时，deepcopy 可能会成为性能瓶颈。&lt;/p&gt;
&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// sigs.k8s.io/controller-runtime/pkg/cache/internal/cache_reader.go&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// Get checks the indexer for the object and writes a copy of it if found.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CacheReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ObjectKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;storeKey&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;objectKeyToStoreKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;c&quot;&gt;// Lookup the object from the indexer cache&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indexer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetByKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;storeKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;disableDeepCopy&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;c&quot;&gt;// skip deep copy which might be unsafe you must DeepCopy any object before mutating it outside&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;c&quot;&gt;// deep copy to avoid mutating cache&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;runtime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DeepCopyObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;c&quot;&gt;// Copy the value of the item in the cache to the returned value&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;outVal&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reflect&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ValueOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;objVal&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reflect&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ValueOf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objVal&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;AssignableTo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;outVal&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Errorf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;cache had type %s, but %s was asked for&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;objVal&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;outVal&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;reflect&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Indirect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;outVal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reflect&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Indirect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;objVal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;disableDeepCopy&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetObjectKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetGroupVersionKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;groupVersionKind&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;option-花样多&quot;&gt;option 花样多&lt;/h2&gt;

&lt;p&gt;使用示例&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;corev1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PodList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Limit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;ListOption 是一个interface，可以修改ListOptions，Limit 是一个ListOption 实现，CacheReader.List 可以传入多个 ListOption 用来按需修改ListOptions。&lt;/p&gt;
&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// controller-runtime/pkg/client/interfaces.go&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Reader&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ObjectKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;obj&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetOption&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;list&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ObjectList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ListOption&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// controller-runtime/pkg/cache/internal/cache_reader.go&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CacheReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ObjectList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ListOption&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;objs&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;listOpts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ListOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;listOpts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApplyOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// controller-runtime/pkg/client/options.go&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ListOption&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;ApplyToList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ListOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ListOptions&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;LabelSelector&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Selector&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;FieldSelector&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fields&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Selector&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;Namespace&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;Limit&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int64&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;Continue&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;UnsafeDisableDeepCopy&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;Raw&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;metav1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ListOptions&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Limit&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int64&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;l&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Limit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ApplyToList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ListOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Limit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;在java 中，假如一个类包含多个 成员类， 每个成员都有多个构造方法（比如参数数量不一样），那这个类 的构造方法也会非常多。 Go 里有什么招儿嘛？&lt;/p&gt;

&lt;p&gt;Options 里不仅包含了很多字段，有些字段还是 函数类型的，比如下面的NewCache，直接 cache.New 创建一个 cache 不好嘛？其实Cache 有多种实现，且cache.New 也包含 cache.Options （很明显花样很多）。&lt;/p&gt;
&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// controller-runtime/pkg/cluster/cluster.go&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;New&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Cluster&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;range&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;opt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;setOptionsDefaults&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewCache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Scheme&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Scheme&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Mapper&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mapper&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Resync&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SyncPeriod&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Namespace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;setOptionsDefaults&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Options&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewCache&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewCache&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;New&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Options&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;NewCache&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewCacheFunc&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;NewClient&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NewClientFunc&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;使用 NewCacheFunc ，可以让我们灵活的调整 构造Cache的方式，比如&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;cluster&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;New&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewCache&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Cache&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;xx&lt;/span&gt;						&lt;span class=&quot;c&quot;&gt;// 对cache.Optiosn 做一些调整&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;New&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;	&lt;span class=&quot;c&quot;&gt;// 调整Cache 的构造方式&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;clientobject&quot;&gt;client.Object&lt;/h2&gt;

&lt;p&gt;golang的鸭子类型 给了框架设计很多灵活性，比如&lt;strong&gt;先定义子类后定义父类&lt;/strong&gt;。Pod 等k8s core object 绝对是先出现的，controller-runtime 是后出的，但因为 Pod 实现了 metav1.Object 和 runtime.Object，Pod 也就实现了 controller-runtime 定义的Object，controller-runtime 就可以拿着Object 去指代 任意k8s 对象了。&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// controller-runtime/pkg/client/object.go&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;metav1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;           &lt;span class=&quot;c&quot;&gt;// interface k8s.io/apimachinery/pkg/runtime/interfaces.go&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;runtime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;          &lt;span class=&quot;c&quot;&gt;// interface k8s.io/apimachinery/pkg/apis/meta/v1/meta.go&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// k8s.io/api/core/v1/types.go&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Pod&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;metav1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TypeMeta&lt;/span&gt; 
	&lt;span class=&quot;n&quot;&gt;metav1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ObjectMeta&lt;/span&gt; 
	&lt;span class=&quot;n&quot;&gt;Spec&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PodSpec&lt;/span&gt; 
	&lt;span class=&quot;n&quot;&gt;Status&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PodStatus&lt;/span&gt; 
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
      </item>
    
      <item>
        <title>finops学习</title>
        <link>https://felix0080.github.io/2022/11/19/finops.html</link>
        <guid isPermaLink="true">https://felix0080.github.io/2022/11/19/finops.html</guid>
        <pubDate>Sat, 19 Nov 2022 00:00:00 +0000</pubDate>
        <description>&lt;h2 id=&quot;简介未完成&quot;&gt;简介（未完成）&lt;/h2&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#简介未完成&quot; id=&quot;markdown-toc-简介未完成&quot;&gt;简介（未完成）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/M4taU-hvKhXvBS7hKtSarg&quot;&gt;Kubecost开源OpenCost：Kubernetes成本监控的开源标准&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/4RHc3axLmflp9ZLA51CI5A&quot;&gt;云计算的成本与价值概要分析&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/2ulduH_zKKcCsB64sVI0bg&quot;&gt;FinOps 时代如何玩转应用资源配置&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/UazEbztac0asam9qb6zHgg&quot;&gt;节省数亿IT成本，B站FinOps实践&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;经过一段时间的实践，尽管拉着资源使用者聊利用率、巡检各部门空闲机器、甚至定期制作各类排名榜单，我们还是发现降本工作推进缓慢。技术中台与业务部门间难以联动、研发人员缺乏成本意识、无法量化降本产出等核心问题依旧难以解决。&lt;/li&gt;
  &lt;li&gt;在反复研究相关理论后，我们定下了B站基于FinOps视角下降本的实验路径：
    &lt;ol&gt;
      &lt;li&gt;成本量化打基础，通过账单提升业务对成本的感知。&lt;/li&gt;
      &lt;li&gt;技术降本和运营降本“双驾马车”并行推进。&lt;/li&gt;
      &lt;li&gt;通过事前计划、事中控制、事后分析多项举措，将成本的指标纳入业务方案和商务采购的考量，成本控制贯穿在整个资源生命周期的管理。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;“成本=单价*用量”，可以从折旧（Opex）的角度，客观反映出平台空闲与超卖情况，推动技术中台和业务协同优化，并量化成本收益。
    &lt;ol&gt;
      &lt;li&gt;技术中台：角色转变为对单价负责，通过提升资源利用率、技术架构升级，减少平台底层资源采买，多供应商的议价能力，降低各类SKU的单价，各优化项目可以明确的转化为成本收益，让技术中台的成本更具竞争力。&lt;/li&gt;
      &lt;li&gt;业务方：通过治理应用实例的数量/存储量、规模、使用时长、共享与独占方式切换，降低SKU的用量。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;整个流程就是：平台出账=&amp;gt;业务对账=&amp;gt;账单分析=&amp;gt;针对性优化=&amp;gt;优化效果反映到下一出账周期的资源对账，这样一套闭环流程。通过对账，将IT成本及时同步给Finops中各类干系人，强化成本责任制，为IT成本优化决策提供数据支撑。同时反映IT成本优化效果、预算执行情况等指标。&lt;/li&gt;
  &lt;li&gt;服务器成本优化
    &lt;ol&gt;
      &lt;li&gt;服务器硬件迭代， 英特尔CPU从Skylake =&amp;gt; Cascadelake =&amp;gt; Icelake =&amp;gt;Sapphire Rapids，每一代在功耗和单核性能上都有提升。硬件的迭代速度是飞速的，每一次的硬件迭代，也刷新了单位算力的成本的下限。&lt;strong&gt;既然新的硬件更有成本优势，那么应该尽量引导业务配合硬件升级&lt;/strong&gt;，每个硬件代次的迭代成本优化效果都是非常显著的。&lt;/li&gt;
      &lt;li&gt;服务器虚拟化和混部&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/kubernetes/finops_work.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如下图，IT成本除了带宽、服务器、公有云，还有很多其他资源成本，各项成本FinOps都需要分析并推动优化。在分析和推进的过程中，形成了一套完整的成本模型。随着业务不断的发展，成本模型和优化方案也在不断进步。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/kubernetes/finops_cost.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</description>
      </item>
    
      <item>
        <title>kubevela多集群</title>
        <link>https://felix0080.github.io/2022/11/10/kubevela_multi_cluster.html</link>
        <guid isPermaLink="true">https://felix0080.github.io/2022/11/10/kubevela_multi_cluster.html</guid>
        <pubDate>Thu, 10 Nov 2022 00:00:00 +0000</pubDate>
        <description>&lt;h2 id=&quot;简介未完成&quot;&gt;简介（未完成）&lt;/h2&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#简介未完成&quot; id=&quot;markdown-toc-简介未完成&quot;&gt;简介（未完成）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;kubevela 与 常规的多集群管理工具 clusternet 与karmada 有以下区别&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;一般的多集群方案 都尽量避免用户感知到 api 的变化，比如用户 想向多集群 提交一个deployment，由多集群控制器负责 将deployment 分发到不同的集群上。为了 让deployment 提交后不被 deployment controller 直接感知并立即被处理，clusternet 选择更改 用户提交的deployment 的group，karmada 选择直接 把用户提交的deployment 存放在一个独立的apiserver上。对于kubevela 来说，本来用户提交的就是 Application，就省去了 clusternet 和 karmada 所面对的问题。&lt;/li&gt;
  &lt;li&gt;kubevela 目前的多集群处理 相对简单，更多是把 workload 发布到 多个集群上 作为workflow的step，不像karmada 等还要进行复杂的多集群调度、重调度等工作（当然，后续可能也会有）。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/8eh8aSsAPqb3Zwmpr0zdqQ&quot;&gt;KubeVela v1.3 多集群初体验，轻松管理应用分发和差异化配置&lt;/a&gt; 未细读。&lt;/p&gt;
</description>
      </item>
    
      <item>
        <title>kubevela中cue的应用</title>
        <link>https://felix0080.github.io/2022/11/10/kubevela_cue.html</link>
        <guid isPermaLink="true">https://felix0080.github.io/2022/11/10/kubevela_cue.html</guid>
        <pubDate>Thu, 10 Nov 2022 00:00:00 +0000</pubDate>
        <description>&lt;h2 id=&quot;简介&quot;&gt;简介&lt;/h2&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#简介&quot; id=&quot;markdown-toc-简介&quot;&gt;简介&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#基本使用&quot; id=&quot;markdown-toc-基本使用&quot;&gt;基本使用&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#数据类型&quot; id=&quot;markdown-toc-数据类型&quot;&gt;数据类型&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#渲染&quot; id=&quot;markdown-toc-渲染&quot;&gt;渲染&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#与k8s结合&quot; id=&quot;markdown-toc-与k8s结合&quot;&gt;与k8s结合&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#导入包&quot; id=&quot;markdown-toc-导入包&quot;&gt;导入包&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#kubevela-中使用&quot; id=&quot;markdown-toc-kubevela-中使用&quot;&gt;kubevela 中使用&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#描述-componenttraitpolicy等definition&quot; id=&quot;markdown-toc-描述-componenttraitpolicy等definition&quot;&gt;描述 Component/Trait/Policy等Definition&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#描述workflowstepdefinition&quot; id=&quot;markdown-toc-描述workflowstepdefinition&quot;&gt;描述WorkflowStepDefinition&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#workflowstepdefinition-深入理解&quot; id=&quot;markdown-toc-workflowstepdefinition-深入理解&quot;&gt;WorkflowStepDefinition 深入理解&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#实现-operator-效果&quot; id=&quot;markdown-toc-实现-operator-效果&quot;&gt;实现 Operator 效果&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#官方-deploy-step-示例&quot; id=&quot;markdown-toc-官方-deploy-step-示例&quot;&gt;官方 deploy step 示例&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#provider-机制-未完成&quot; id=&quot;markdown-toc-provider-机制-未完成&quot;&gt;provider 机制 （未完成）&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/8xnlkhi3tq6Cay_hbyRcqQ&quot;&gt;使用 CUE 语言管理 K8s 资源清单模板&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/yWKdXsfAZvwc5Y1aao6XAw&quot;&gt;数据约束语言 CUE 简易教程&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;BCL 全名 Borg Configuration Language，是 Google 内部基于 GCL (Generic Configuration Language) 在 Borg 场景的实践。&lt;strong&gt;用户通过 BCL 描述对 Borg 的使用需求&lt;/strong&gt;，通过基于 BCL 的抽象省去对 Borg 复杂配置细节的感知提高单位效率，通过工程化手段满足可抽象、可复用、可测试的协作方式提高团队效率和稳定性，并在其上建立了相应的生态平台，作为 Borg 生态的重要抽象层在 Google 内部服务了超过 10 年，帮助 Google 内部数万开发者更好的使用 Infra。CUE 是一种服务于云化配置的&lt;strong&gt;强类型配置语言&lt;/strong&gt;，由 Go team 成员 Marcel van Lohiuzen 结合 BCL 及多种其他语言研发并开源，可以说是 BCL 思路的开源版实现。PS：大家都对直接使用yaml 的方式不满意。&lt;/p&gt;

&lt;h2 id=&quot;基本使用&quot;&gt;基本使用&lt;/h2&gt;

&lt;h3 id=&quot;数据类型&quot;&gt;数据类型&lt;/h3&gt;

&lt;p&gt;cue 的基本数据类型&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;// first.cue
a: 1.5
a: float
b: 1
b: int
d: [1, 2, 3]
g: {
 h: &quot;abc&quot;
}
e: &quot;abc&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;如何自定义 CUE 类型？使用 # 符号来指定一些表示 CUE 类型的变量&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;#abc: {
  x: int
  y: string
  z: {
    a: float
    b: bool
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://cuetorials.com/zh/deep-dives/closedness/&quot;&gt;开放性和封闭性&lt;/a&gt; 未读。&lt;/p&gt;

&lt;h3 id=&quot;渲染&quot;&gt;渲染&lt;/h3&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cue eval first.cue
a: 1.5
b: 1
d: [1, 2, 3]
g: {
h: &quot;abc&quot;
}
e: &quot;abc&quot;    
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;默认情况下, 渲染结果会被格式化为 JSON 格式。&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cue export first.cue
{
    &quot;a&quot;: 1.5,
    &quot;b&quot;: 1,
    &quot;d&quot;: [
        1,
        2,
        3
    ],
    &quot;g&quot;: {
        &quot;h&quot;: &quot;abc&quot;
    },
    &quot;e&quot;: &quot;abc&quot;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;渲染为yaml 格式&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ cue export first.cue --out yaml
a: 1.5
b: 1
d:
- 1
- 2
- 3
g:
  h: abc
e: abc
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;与k8s结合&quot;&gt;与k8s结合&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;CUE 用于模板化 Kubernetes 资源&lt;/strong&gt;：在 KubeVela 中，抽象层由 CUE 支持，这是一种新颖的配置编程语言，可以描述复杂的渲染逻辑，它在使用层面与 JSON 一致，是 JSON 的超集。抽象层简化了 Kubernetes 中资源的配置，隐藏了实现的细节，并仅向业务开发人员暴露有限参数。使用 KubeVela 应用程序，开发人员可以轻松地专注于应用程序的中心逻辑，例如应该使用哪个容器镜像以及如何访问服务。为了实现这一目标，使用 Kubernetes 原生资源的最佳实践被总结到 KubeVela X-Definitions 中，并使用 CUE 提供资源的渲染模板。PS: 开发一个paas平台，一个核心工作就是将项目、应用（包括使用xxcpu、xx内存）保存到db中，在发布时将这些信息 转换为Deployment 发给k8s apiserver，这个转换过程可以 代码手动一个字段一个字段转换为apps.Deployment，也可以是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;apps.Deployment = 参数 + apps.Deployment模版&lt;/code&gt; 渲染得到。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/kubernetes/kubevela_cue.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;// deployment.cue
parameter:{
   name: &quot;mytest&quot;
   image: &quot;nginx:v1&quot;
}
template: {
 apiVersion: &quot;apps/v1&quot;
 kind:       &quot;Deployment&quot;
 spec: {
  selector: matchLabels: {
   &quot;app.oam.dev/component&quot;: parameter.name
  }
  template: {
   metadata: labels: {
    &quot;app.oam.dev/component&quot;: parameter.name
   }
   spec: {
    containers: [{
     name:  parameter.name
     image: parameter.image
    }]
   }}}
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cue export deployment.cue -e template --out yaml&lt;/code&gt; 导出指定template变量的结果。&lt;/p&gt;

&lt;h3 id=&quot;导入包&quot;&gt;导入包&lt;/h3&gt;

&lt;p&gt;可以在 CUE 模版中通过 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kube/&amp;lt;apiVersion&amp;gt;&lt;/code&gt; 导入 kubernetes 的包，就像使用 CUE 内部包一样。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;import (
   apps &quot;kube/apps/v1&quot;
)
parameter: {
    name:  string
}
output: apps.#Deployment                ## output 的类型是 deployment
output: {
    metadata: name: parameter.name      ## 给output 的metadata.name 字段赋值
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;kubevela-中使用&quot;&gt;kubevela 中使用&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubevela
  /pkg
    /appfile
      /parser.go
    /cue
      /definition
      /model
      /process
      /task
      utils.go
    /workflow             # 工作流相关
      /operation
      /providers
      /step
      /template
      /workflow.go
    /stdlib
      /op.cue
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;kubevela 可扩展的的核心是复用，主要复用两方面的能力&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;component/trait 主要复用 crd 的能力，由ComponentDefinition/TraitDefinition 使用cue 来定义crd 模版，Application controller 负责渲染出 真实crd&lt;/li&gt;
  &lt;li&gt;workflow 复用http/email/k8s(apply等) 等能力，由 WorkflowStepDefinition 使用cue 来定义对底层provider 能力/函数的调用（provider 函数被封装为taskRunner），executor 从 WorkflowStepDefinition 拿到 provider+op（底层能力标识） “渲染”为taskRunner 并调用执行。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;你可以通过 在StepDefinition 中引用cue 官方以及KubeVela内置的包，来使用HTTP 请求、crud 资源、配置日志来源、条件等待、使当前步骤失败等函数，从而通过配置的方式来覆盖你的业务场景，极大提升 Step的可扩展性。&lt;/p&gt;

&lt;h3 id=&quot;描述-componenttraitpolicy等definition&quot;&gt;描述 Component/Trait/Policy等Definition&lt;/h3&gt;

&lt;p&gt;webservice ComponetDefinition 内容&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;import (
	&quot;strconv&quot;
)
webservice: {...}
template: {
  mountsArray: {...}  # 根据 parameter.volumeMounts 计算mount 相关的内容，最终会被output 使用
  volumesList: {...}
  ...
  output: {            # deployment yaml的核心部分
    apiVersion: &quot;apps/v1&quot;
		kind:       &quot;Deployment&quot;
		spec: {...}
  }         
  exposePorts: {}
  outputs: {
    if len(exposePorts) != 0 {
      webserviceExpose: {
				apiVersion: &quot;v1&quot;
				kind:       &quot;Service&quot;
				metadata: name: context.name
				spec: {
					selector: &quot;app.oam.dev/component&quot;: context.name
					ports: exposePorts
					type:  parameter.exposeType
				}
			}
    }
  }
  parameter: {...}    # template的参数部分
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// kubevela/pkg/appfile/parser.go&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generateComponentFromCUEModule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Workload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;appName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;revision&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ns&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v1alpha2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;pCtx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;PrepareProcessContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;appName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;revision&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;range&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Traits&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;tr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;EvalContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pCtx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acComp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;evalWorkloadWithContext&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pCtx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;appName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sc&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;range&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Scopes&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;acComp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Scopes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acComp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Scopes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v1alpha2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ComponentScope&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acComp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;从Application 中拿到 component/trait name，找到对应的Definition 拿到cue 模版，加上Application 提供的参数，转成Component ，并将trait 挂在 ApplicationConfigurationComponent 对象里。&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;PrepareProcessContext
  Workload.EvalContext
    workloadDef.Complete(ctx process.Context, abstractTemplate string, params interface{})
      bi := build.NewContext().NewInstance(&quot;&quot;, nil)
      bi.AddFile(&quot;-&quot;, abstractTemplate)
      var paramFile = &quot;parameter: {}&quot;
      bt, err := json.Marshal(params)
      paramFile = fmt.Sprintf(&quot;%s: %s&quot;, mycue.ParameterTag, string(bt))
      bi.AddFile(&quot;parameter&quot;, paramFile)
      bi.AddFile(&quot;-&quot;, ctx.ExtendedContextFile())
      var r cue.Runtime
      inst, err := r.Build(bi)
      output := inst.Lookup(&quot;output&quot;) # 返回cue文件中 output 字段的值 
      base, err := model.NewBase(output)
      ctx.SetBase(base)
Trait.EvalContext
  traitDef.Complete(ctx, trait.Template, trait.Params)
    bi := build.NewContext().NewInstance(&quot;&quot;, nil)
    bi.AddFile(&quot;-&quot;, abstractTemplate)
    var paramFile = &quot;parameter: {}&quot;
    bt, err := json.Marshal(params)
    paramFile = fmt.Sprintf(&quot;%s: %s&quot;, mycue.ParameterTag, string(bt))
    bi.AddFile(&quot;parameter&quot;, paramFile)
    bi.AddFile(&quot;context&quot;, ctx.ExtendedContextFile())
    instances := cue.Build([]*build.Instance{bi})
    for _, inst := range instances {
      patcher := inst.Lookup(&quot;patch&quot;) # 返回cue文件中 patch 字段的值
      base, _ := ctx.Output()
      p, err := model.NewOther(patcher)
      base.Unify(p)
    }
evalWorkloadWithContext
  base, assists := pCtx.Output()
	componentWorkload, err := base.Unstructured()
  component := &amp;amp;v1alpha2.Component{}
  // we need to marshal the workload to byte array before sending them to the k8s
	component.Spec.Workload = util.Object2RawExtension(componentWorkload)

  acComponent := &amp;amp;v1alpha2.ApplicationConfigurationComponent{}
  for _, assist := range assists {
		tr, err := assist.Ins.Unstructured()
    acComponent.Traits = append(acComponent.Traits, v1alpha2.ComponentTrait{
			Trait: util.Object2RawExtension(tr),
		})
  }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;k8s patch语法参考 &lt;a href=&quot;https://kubernetes.io/docs/tasks/manage-kubernetes-objects/update-api-object-kubectl-patch/&quot;&gt;Update API Objects in Place Using kubectl patch&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;描述workflowstepdefinition&quot;&gt;描述WorkflowStepDefinition&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://www.bookstack.cn/read/kubevela-1.5-zh/5aed23036bb4523c.md&quot;&gt;自定义工作流&lt;/a&gt;比如以下场景：使用 Helm 部署一个 Tomcat，并在部署完成后自动向 Slack 发送消息通知。&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;core.oam.dev/v1beta1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Application&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;first-vela-workflow&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;default&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;tomcat&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;helm&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;workflow&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;tomcat&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;my-helm&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 指定步骤类型&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;outputs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;msg&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;# 将 my-helm 中读取到的 deployment status 作为信息导出&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;valueFrom&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;resource.value.status.conditions[0].message&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;send-message&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;webhook-notification&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我们可以使用 vela def 通过编写 Cue template 来定义工作流步骤。 KubeVela 提供了一些 &lt;strong&gt;CUE 操作类型&lt;/strong&gt;用于编写工作流步骤。这些操作均由 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vela/op&lt;/code&gt; 包提供。比如以下 3 个 CUE 操作：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&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;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;ApplyApplication&lt;/td&gt;
      &lt;td&gt;部署应用中的所有资源&lt;/td&gt;
      &lt;td&gt;-&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Read&lt;/td&gt;
      &lt;td&gt;读取 Kubernetes 集群中的资源。&lt;/td&gt;
      &lt;td&gt;value: 描述需要被读取资源的元数据，比如 kind、name 等，操作完成后，集群中资源的数据会被填充到 value 上。&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;br /&gt;err: 如果读取操作发生错误，这里会以字符串的方式指示错误信息。&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;ConditionalWait&lt;/td&gt;
      &lt;td&gt;会让 Workflow Step 处于等待状态，直到条件被满足。&lt;/td&gt;
      &lt;td&gt;continue: 当该字段为 true 时，Workflow Step 才会恢复继续执行。&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;部署 Tomcat 步骤 my-helm WorkflowStepDefinition 实现步骤&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;通过 vela def init 来生成一个 WorkflowStepDefinition 模板：
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; $ vela def init my-helm -t workflow-step --desc &quot;Apply helm charts and wait till it's running.&quot; -o my-helm.cue
 # 得到以下结果
 $ cat my-helm.cue
 &quot;my-helm&quot;: {
     annotations: {}
     attributes: {}
     description: &quot;Apply helm charts and wait till it's running.&quot;
     labels: {}
     type: &quot;workflow-step&quot;
 }
 template: {
 }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;引用 vela/op 包，并将 Cue 代码补充到 template 中：
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; import (
   &quot;vela/op&quot;
 )
 &quot;my-helm&quot;: {
     annotations: {}
     attributes: {}
     description: &quot;Apply helm charts and wait till it's running.&quot;
     labels: {}
     type: &quot;workflow-step&quot;
 }
 template: {
   // 部署应用中的所有资源
   apply: op.#ApplyApplication &amp;amp; {}
   resource: op.#Read &amp;amp; {
     value: {
       kind: &quot;Deployment&quot;
       apiVersion: &quot;apps/v1&quot;
       metadata: {
         name: &quot;tomcat&quot;
         // 可以使用 context 来获取该 Application 的任意元信息
         namespace: context.namespace
       }
     }
   }
   workload: resource.value
   // 等待 helm 的 deployment 可用
   wait: op.#ConditionalWait &amp;amp; {
     continue: workload.status.readyReplicas == workload.status.replicas &amp;amp;&amp;amp; workload.status.observedGeneration == workload.metadata.generation
   }
 }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;部署到集群中：
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; $ vela def apply my-helm.cue
 WorkflowStepDefinition my-helm in namespace vela-system updated.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;workflowstepdefinition-深入理解&quot;&gt;WorkflowStepDefinition 深入理解&lt;/h2&gt;

&lt;h3 id=&quot;实现-operator-效果&quot;&gt;实现 Operator 效果&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/ypXynPXOGZ1iN-3CMrAmlQ&quot;&gt;开源工作流引擎如何支撑企业级 Serverless 架构？&lt;/a&gt;比如 工作流中新增一个镜像预热的节点，以往多集群中下发 ImagePullJob 工作负载为例，你不仅需要管理多集群的配置，还需要 Watch 工作负载（CRD）的状态，直到工作负载的状态变成 Ready，才继续下一步。而这个流程其实对应了一个简单的 Kubernetes Operator 的 Reconcile 逻辑：先是创建或者更新一个资源，如果这个资源的状态符合了预期，则结束此次 Reconcile，如果不符合，则继续等待。&lt;strong&gt;难道我们运维操作中每新增一种资源的管理，就需要实现一个 Operator 吗？&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;template: {
  // 第一步：从指定集群中读取资源
  read: op.#Read &amp;amp; {
    value: {
      apiVersion: parameter.apiVersion
      kind: parameter.kind
      metadata: {
        name: parameter.name
        namespace: parameter.namespace
      }
    }
    cluster: parameter.cluster
  }
  // 第二步：直到资源状态 Ready，才结束等待，否则步骤会一直等待
  wait: op.#ConditionalWait &amp;amp; {
    continue: read.value.status != _|_ &amp;amp;&amp;amp; read.value.status.phase == &quot;Ready&quot;
  }
  // 第三步（可选）：如果资源 Ready 了，那么...
  // 其他逻辑...

  // 定义好的参数，用户在使用该步骤类型时需要传入
  parameter: {
    apiVersion: string
    kind: string
    name: string
    namespace: *context.namespace | string
    cluster: *&quot;&quot; | string
   }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;对应到当前这个场景就是：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;读取指定集群（如：上海集群）中的 ImagePullJob 状态。&lt;/li&gt;
  &lt;li&gt;如果 ImagePullJob Ready，镜像已经预热完毕，则当前步骤成功，执行下一个步骤。&lt;/li&gt;
  &lt;li&gt;当 ImagePullJob Ready 后，清理集群中的 ImagePullJob。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;通过这样自定义的方式，不过后续在运维场景下新增了多少 Region 的集群或是新类型的资源，都可以先将集群的 KubeConfig 纳管到 KubeVela Workflow 的管控中后，再使用已经定义好的步骤类型，通过传入不同的集群名或者资源类型，来达到一个简便版的 Kubernetes Operator Reconcile 的过程，从而极大地降低开发成本。&lt;/p&gt;

&lt;h3 id=&quot;官方-deploy-step-示例&quot;&gt;官方 deploy step 示例&lt;/h3&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;import (
	&quot;vela/op&quot;
)
deploy: {
	alias: &quot;&quot;
	annotations: {}
	attributes: {}
	description: &quot;A powerful and unified deploy step for components multi-cluster delivery with policies.&quot;
	labels: {}
	type: &quot;workflow-step&quot;
}

template: {
	deploy: op.#Deploy &amp;amp; {
		policies:                 parameter.policies
		parallelism:              parameter.parallelism
		ignoreTerraformComponent: parameter.ignoreTerraformComponent
	}
	parameter: {
		//+usage=If set to false, the workflow will suspend automatically before this step, default to be true.
		auto: *true | bool
		//+usage=Declare the policies that used for this deployment. If not specified, the components will be deployed to the hub cluster.
		policies?: [...string]
		//+usage=Maximum number of concurrent delivered components.
		parallelism: *5 | int
		//+usage=If set false, this step will apply the components with the terraform workload.
		ignoreTerraformComponent: *true | bool
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubevela/pkg/providers&lt;/code&gt; 设计了一套机制，当workflow executor 执行deploy step时，executor.doSteps 首先从 template.deploy 的  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#provider&lt;/code&gt; 和 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;#do&lt;/code&gt; 中拿到 provider和 do，接着执行exec.Handle(…,provider,do) ==&amp;gt; handler := providers.GetHandler(provider,do);handler()。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;// kubevela/pkg/stdlib/op.cue
#Deploy: multicluster.#Deploy
// kubevela/pkg/stdlib/pkgs/multicluster.cue
#Deploy: {
	#provider: &quot;multicluster&quot;
	#do:       &quot;deploy&quot;
	policies: [...string]
	parallelism:              int
	ignoreTerraformComponent: bool
}
// kubevela/pkg/workflow/providers/multicluster/multicluster.go
// Install register handlers to provider discover.
func Install(p wfTypes.Providers, c client.Client, app *v1beta1.Application, af *appfile.Appfile,...) {
	prd := &amp;amp;provider{Client: c, app: app, af: af, apply: apply, healthCheck: healthCheck, renderer: renderer}
	p.Register(ProviderName, map[string]wfTypes.Handler{
		&quot;read-placement-decisions&quot;:              prd.ReadPlacementDecisions,
		&quot;make-placement-decisions&quot;:              prd.MakePlacementDecisions,
		&quot;patch-application&quot;:                     prd.PatchApplication,
		&quot;list-clusters&quot;:                         prd.ListClusters,
		&quot;get-placements-from-topology-policies&quot;: prd.GetPlacementsFromTopologyPolicies,
		&quot;deploy&quot;:                                prd.Deploy,
	})
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;从效果看，根据 op.Deploy ==&amp;gt;  multicluster.#Deploy 可以找到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubevela/pkg/workflow/providers/multicluster/multicluster.go&lt;/code&gt; 的Deploy 方法。换句话说，workflow/provider 实现了一些通用的能力函数，加上stdlib 等机制，我们在workflowStep cue 模版中可以直接使用这些函数。&lt;/p&gt;

&lt;p&gt;suspend step 示例&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;suspend: {
	alias: &quot;&quot;
	annotations: {}
	attributes: {}
	description: &quot;Suspend the current workflow, it can be resumed by 'vela workflow resume' command.&quot;
	labels: {}
	type: &quot;workflow-step&quot;
}

template: parameter: {
	// +usage=Specify the wait duration time to resume workflow such as &quot;30s&quot;, &quot;1min&quot; or &quot;2m15s&quot;
	duration?: string
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;provider-机制-未完成&quot;&gt;provider 机制 （未完成）&lt;/h3&gt;

</description>
      </item>
    
      <item>
        <title>基于k8s的工作流</title>
        <link>https://felix0080.github.io/2022/11/09/workflow.html</link>
        <guid isPermaLink="true">https://felix0080.github.io/2022/11/09/workflow.html</guid>
        <pubDate>Wed, 09 Nov 2022 00:00:00 +0000</pubDate>
        <description>&lt;h2 id=&quot;简介&quot;&gt;简介&lt;/h2&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#简介&quot; id=&quot;markdown-toc-简介&quot;&gt;简介&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#渐进式发布&quot; id=&quot;markdown-toc-渐进式发布&quot;&gt;渐进式发布&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#argo-rollout-和-flagger&quot; id=&quot;markdown-toc-argo-rollout-和-flagger&quot;&gt;Argo Rollout 和 Flagger&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#kruise-rollout&quot; id=&quot;markdown-toc-kruise-rollout&quot;&gt;Kruise Rollout&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#kubevela-workflow&quot; id=&quot;markdown-toc-kubevela-workflow&quot;&gt;kubevela workflow&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#kubevela-workflow-源码分析&quot; id=&quot;markdown-toc-kubevela-workflow-源码分析&quot;&gt;kubevela workflow 源码分析&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#reconcile逻辑&quot; id=&quot;markdown-toc-reconcile逻辑&quot;&gt;Reconcile逻辑&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#task-生成&quot; id=&quot;markdown-toc-task-生成&quot;&gt;task 生成&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#基于自定义cue模版生成task&quot; id=&quot;markdown-toc-基于自定义cue模版生成task&quot;&gt;基于自定义cue模版生成task&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#executor执行框架&quot; id=&quot;markdown-toc-executor执行框架&quot;&gt;executor执行框架&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#resume-逻辑&quot; id=&quot;markdown-toc-resume-逻辑&quot;&gt;resume 逻辑&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#go-workflow未完成&quot; id=&quot;markdown-toc-go-workflow未完成&quot;&gt;go-workflow（未完成）&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;看似非常碎片化和复杂的各类应用交付与管理场景，其背后确实是有一个非常本质的基础模型存在的。这个基础模型就是“工作流”。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/P8gYuDHM7ZDWM3Y7fYSK8g&quot;&gt;Kruise Rollout：灵活可插拔的渐进式发布框架&lt;/a&gt; K8s 当中所有的 Pod 都是由工作负载来管理的，其中最常见的两个工作负载就是 Deployment 和 statefulset。Deployment 对于升级而言提供了 maxUnavailable 和 maxSurge 两个参数，但是本质上来讲 Deployment 它只支持流式的一次性发布，用户并不能控制分批。StatefulSet 虽然支持分批，但是跟我们想要的渐进式发布的能力还有比较大的距离。&lt;/p&gt;

&lt;p&gt;当我们说到工作流时，一般涉及到几个问题&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;一个workflow 对象代表一个工作流，workflow 里包含多个step，step 记录了上游和下游step&lt;/li&gt;
  &lt;li&gt;一个执行引擎 按照拓扑排序 执行workflow下的step，需要采集step的运行状态，并将上游step的运行结果传递给下游，一般情况下，一个step失败即终止整个workflow 并标记workflow 失败，也可能有其他成功、失败策略。&lt;/li&gt;
  &lt;li&gt;step 可以是一些预定义的工作，也可以是一些自定义工作。一般用代码实现，有时也支持dsl（这就要求附带一个dsl引擎）。可以由一个pod封装运行，或者其它workload（比如tfjob、 vcjob等），或者controller 本身直接触发执行（按约定的模版编写逻辑）。
一个常规的workflow工具，要提供workflow与step抽象，包括step的形式（pod等workload或其它dsl，如果有一个dsl引擎的话），step之间的依赖关系，采集step的运行状态、结果传给下一个step，某个step的成功、失败策略等等，&lt;strong&gt;越通用就越臃肿，针对具体业务域就可以相对简化一些&lt;/strong&gt;。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;其实机器学习平台 因为有明显的工作流：样本生成 ==&amp;gt; 训练 ==&amp;gt; 评估 ==&amp;gt; 推理，顺理成章的用上了工作流。体现在微服务的paas上，构建 ==&amp;gt; 灰度 ==&amp;gt; 集群1全量 ==&amp;gt; 集群2全量。 新人加入一个公司，如果没有明确的&lt;strong&gt;流程抽象&lt;/strong&gt;，就要靠冗长的说明文档来操作平台了，&lt;/p&gt;

&lt;h2 id=&quot;渐进式发布&quot;&gt;渐进式发布&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/Hor4BnBq8_gV0TXlhaHPTQ&quot;&gt;发布策略知多少？蓝绿/红黑/灰度/滚动…&lt;/a&gt; 未读。&lt;/p&gt;

&lt;h3 id=&quot;argo-rollout-和-flagger&quot;&gt;Argo Rollout 和 Flagger&lt;/h3&gt;

&lt;p&gt;Argo Rollout 是 Argo 公司推出的 Workload，它的实现思路是：&lt;strong&gt;重新定义一个类似于 Deployment 的工作负载&lt;/strong&gt;，在实现 Deployment 原有能力的基础上，又扩展了 Rollout 的相关能力。它的优点是工作负载内置了 Rollout 能力，配置简单、实现也会比较简单，并且目前支持的功能也非常的丰富，支持各种发布策略、流量灰度和 metrics 分析，是一个比较成熟的项目。但是它也存在一些问题，因为它本身就是一个工作负载，所以它不能适用于社区 Deployment，尤其是针对已经用 Deployment 部署的公司，需要一次线上迁移工作负载的工作。&lt;/p&gt;

&lt;p&gt;Argo 为 Kubernetes 提供 container-native（工作流中的每个步骤是通过容器实现）工作流程。可以让用户用一个类似于传统的 YAML 文件定义的 DSL 来运行多个步骤的 Pipeline。该框架提供了复杂的循环、条件判断、依赖管理等功能，这有助于提高部署应用程序的灵活性以及配置和依赖的灵活性。使用 Argo，用户可以定义复杂的依赖关系，以编程方式构建复杂的工作流、制品管理，可以将任何步骤的输出结果作为输入链接到后续的步骤中去，并且可以在可视化 UI 界面中监控调度的作业任务。&lt;/p&gt;

&lt;p&gt;另一个社区项目是 Flagger，它的实现思路跟 Argo-Rollout 完全不同。它没有单独的实现一个 workload，而是在现有 Deployment 的基础之上，扩展了流量灰度、分批发布的能力。Flagger 的优势是支持原生 Deployment 、并且与社区的 Helm、Argo-CD 等方案都是兼容的。但是也存在一些问题，首先就是发布过程中的 Double Deployment 资源的问题，因为它是先升级用户部署的 Deployment，再升级 Primary，所以在这过程中需要准备双倍的 Pod 资源。第二呢，针对一些自建的容器平台需要额外对接，因为它的实现思路是将用户部署资源都 copy 一份，且更改资源的名字以及 Label。PS：当年我也尝试过用两个Deployment 实现分批发布&lt;/p&gt;

&lt;h3 id=&quot;kruise-rollout&quot;&gt;Kruise Rollout&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/m-r3AQMbv2IPoAAJMhReZg&quot;&gt;Kruise Rollout: 让所有应用负载都能使用渐进式交付&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;在设计 Rollout 过程中有以下几个目标：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;无侵入性：对原生的 Workload 控制器以及用户定义的 Application Yaml 定义不进行任何修改，保证原生资源的干净、一致&lt;/li&gt;
  &lt;li&gt;可扩展性：通过可扩展的方式，支持 K8s Native Workload、自定义 Workload 以及 Nginx、Isito 等多种 Traffic 调度方式&lt;/li&gt;
  &lt;li&gt;易用性：对用户而言开箱即用，能够非常方便的与社区 Gitops 或自建 PaaS 结合使用&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;rollouts.kruise.io/v1alpha1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Rollout&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;strategy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;objectRef&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# 用于表明 Kruise Rollout 所作用的工作负载，例如：Deployment Name&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;workloadRef&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apps/v1&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Deployment, CloneSet, AdDaemonSet etc.&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Deployment&lt;/span&gt; 
        &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;echoserver&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;canary&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;       &lt;span class=&quot;c1&quot;&gt;# 定义了 Rollout 发布的过程&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# routing 5% traffics to the new version&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# Manual confirmation, release the back steps&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;pause&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{}&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# optional, The first step of released replicas. If not set, the default is to use 'weight', as shown above is 5%.&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;replicas&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;40&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# sleep 600s, release the back steps&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;pause&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;duration&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;600&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;60&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;pause&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;duration&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;600&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;weight&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;80&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;pause&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;duration&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;600&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# 最后一批无需配置&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;trafficRoutings&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;      &lt;span class=&quot;c1&quot;&gt;# 流量灰度所需要的资源 Name，例如：Service、Ingress 或 Gateway API&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# echoserver service name&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;echoserver&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# nginx ingress&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;nginx&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# echoserver ingress name&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;ingress&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;echoserver&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/kubernetes/openkruise_rollout.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Kruise Rollout 的工作机制&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;首先，用户基于容器平台做一次版本发布（一次发布从本质上讲就是将 K8s 资源 apply 到集群中）。&lt;/li&gt;
  &lt;li&gt;Kruise Rollout 包含一个 webhook 组件，它会拦截用户的发布请求，然后通过修改 workload strategy 的方式 Pause 住 workload 控制器的工作。&lt;/li&gt;
  &lt;li&gt;然后，就是根据用户的 Rollout 定义，动态的调整 workload 的参数，比如：partition，实现 workload 的分批发布。&lt;/li&gt;
  &lt;li&gt;等到批次发布完成后，又会调整 ingress、service 配置，将特定的流量导入到新版本。&lt;/li&gt;
  &lt;li&gt;最后，Kruise Rollout 还能够通过 prometheus 中的业务指标判断发布是否正常。比如说，对于一个 web 类 http 的服务，可以校验 http 状态码是否正常。&lt;/li&gt;
  &lt;li&gt;上面的过程，就完成了第一批次的灰度，后面的批次也是类似的。完整的 Rollout 过程结束后，kruise 会将 workload 等资源的配置恢复回来。 所以说，整个 Rollout 过程，是与现有工作负载能力的一种协同，它尽量复用工作负载的能力，又做到了非 Rollout 过程的零入侵。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;kubevela-workflow&quot;&gt;kubevela workflow&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/ypXynPXOGZ1iN-3CMrAmlQ&quot;&gt;为什么要用Kubevela Workflow？&lt;/a&gt;因为 KubeVela Workflow 在设计上有一个非常根本的区别：工作流中的步骤面向云原生 IaC 体系设计，支持抽象封装和复用，相当于你可以直接在步骤中调用自定义函数级别的原子能力，而&lt;strong&gt;不仅仅是下发容器&lt;/strong&gt;（前几个工具都只是分批灰度发布pod？）。在 KubeVela Workflow 中，每个步骤都有一个步骤类型，而每一种步骤类型，都会对应 WorkflowStepDefinition（工作流步骤定义）这个资源。你可以使用 CUE 语言（一种 IaC 语言，是 JSON 的超集） 来编写这个步骤定义，或者直接使用社区中已经定义好的步骤类型。&lt;/p&gt;

&lt;p&gt;你可以简单地将步骤类型定义理解为一个函数声明，每定义一个新的步骤类型，就是在定义一个新的功能函数。函数需要一些输入参数，步骤定义也是一样的。在步骤定义中，你可以通过 parameter 字段声明这个步骤定义需要的输入参数和类型。当工作流开始运行时，工作流控制器会使用用户传入的实际参数值，执行对应步骤定义中的 CUE 代码，就如同执行你的功能函数一样。有了这样一层步骤的抽象，就为步骤增添了极大的可能性。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;如果你希望自定义步骤类型，就如同编写一个新的功能函数一样，你可以在步骤定义中直接通过 import 来引用官方代码包，从而将其他原子能力沉淀到步骤中，包括 HTTP 调用，在多集群中下发，删除，列出资源（crd 的crud），条件等待，等等。这也意味着，通过这样一种可编程的步骤类型，你可以轻松对接任意系统&lt;/li&gt;
  &lt;li&gt;如果你只希望使用定义好的步骤，那么，就如同调用一个封装好的第三方功能函数一样，你只需要关心你的输入参数，并且使用对应的步骤类型就可以了。如，一个典型的构建镜像场景。首先，指定步骤类型为 build-push-image，接着，指定你的输入参数：构建镜像的代码来源与分支，构建后镜像名称以及推送到镜像仓库需要使用的秘钥信息。
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; apiVersion: core.oam.dev/v1alpha1
 kind: WorkflowRun
 metadata:
   name: build-push-image
   namespace: default
 spec:
   workflowSpec:
   steps:
     - name: build-push
       type: build-push-image
       properties:
         context:
           git: github.com/FogDong/simple-web-demo
           branch: main
         image: fogdong/simple-web-demo:v1
         credentials:
           image:
             name: image-secret
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;在这样一种架构下，步骤的抽象给步骤本身带来了无限可能性。当你需要在流程中新增一个节点时，&lt;strong&gt;你不再需要将业务代码进行“编译-构建-打包”后用 Pod 来执行逻辑&lt;/strong&gt;，只需要修改步骤定义中的配置代码，再加上工作流引擎本身的编排控制能力，就能够完成新功能的对接。&lt;strong&gt;在可扩展的基础之上，才能充分借助和发挥生态的力量&lt;/strong&gt;，加速产品的升级。在工作流中，每个步骤的步骤类型都是可复用的。这也意味着，当你开发新步骤类型时，你也在为下一次，下下次的新功能上线打下了基础。PS：有点Makefile target的感觉&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/kubevela/workflow/blob/main/examples/multiple-apps.md&quot;&gt;Control the delivery process of multiple applications&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;core.oam.dev/v1alpha1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;WorkflowRun&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apply-applications&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;default&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;annotations&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;workflowrun.oam.dev/debug&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;true&quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;workflowSpec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;check-app-exist&lt;/span&gt;     &lt;span class=&quot;c1&quot;&gt;## 查看 webservice-app&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;read-app&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;webservice-app&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apply-app1&lt;/span&gt;  
        &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apply-app&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;status[&quot;check-app-exist&quot;].message == &quot;Application not found&quot;&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# 如果 webservice-app 不存在  部署 webservice-app&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;core.oam.dev/v1beta1&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Application&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;webservice-app&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;express-server&lt;/span&gt;
                  &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;webservice&lt;/span&gt;
                  &lt;span class=&quot;na&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;crccheck/hello-world&lt;/span&gt;
                    &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8000&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;suspend&lt;/span&gt;       &lt;span class=&quot;c1&quot;&gt;# 暂停，除非手动操作 vela workflow resume apply-applications 恢复&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;suspend&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;24h&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apply-app2&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;apply-app&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;my-app&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;application&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;configMap&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;KubeVela Workflow 也提供了一个可视化的 Dashboard，运维人员只需要配置一次流水线模板，之后就可以通过触发执行，或者在每次触发执行时传入特定的运行时参数，来完成流程的自动化。&lt;/p&gt;

&lt;p&gt;虽然OAM定义了应用程序的组成，但在实际情况下，组成部分的交付过程仍然可能有很大的差异。例如，一个应用程序中的不同组件可能具有相互依赖或数据传递，其交付步骤必须按特定顺序执行。此外，交付过程有时还涉及除资源交付之外的更多操作，例如升级或通知。因此，一个可扩展的工作流被设计了出来，以满足 KubeVela 中流程定制的需求。与组件和特征一样，KubeVela 工作流程也利用 CUE 来定义工作流程步骤，提供了灵活性、可扩展性和可编程性。它可以被视为基础设施即代码（IaC）的另一种形式。与 KubeVela 中的组件和特征定义不同，&lt;strong&gt;工作流步骤定义不会将模板渲染成资源&lt;/strong&gt;。相反，它描述了在步骤中要执行的操作，该操作调用各种提供程序中的底层原子函数。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/kubernetes/kubevela_workflow.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;kubevela-workflow-源码分析&quot;&gt;kubevela workflow 源码分析&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;workflow
  /cmd
    /main.go
  /controllers
    /workflowrun_controller.go
  /pkg
    /cue          # cue相关处理逻辑
    /executor     # executor 执行引擎
    /tasks        # 内置或自定义 step
      /builtin
      /custom
      /template
      /discover.go
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;reconcile逻辑&quot;&gt;Reconcile逻辑&lt;/h3&gt;

&lt;p&gt;缘起 WorkflowRun 的Reconcile，先将 WorkflowRun 转为 WorkflowInstance，然后基于WorkflowInstances 生成taskRunners（一个Workflow
Step对应一个taskRunner），最后由executor 执行taskRunners。&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// workflow/controllers/workflowrun_controller.go&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkflowRunReconciler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Reconcile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;run&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkflowRun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ObjectKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;      &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Namespace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,},&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GenerateWorkflowInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;runners&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GenerateRunners&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logCtx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StepGeneratorOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;PackageDiscover&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PackageDiscover&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;          &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;New&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExecuteRunners&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logCtx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;runners&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  
  &lt;span class=&quot;n&quot;&gt;isUpdate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isUpdate&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Status&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Message&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Status&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Status&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Phase&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkflowStateSuspending&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkflowStateFailed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkflowStateTerminated&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkflowStateExecuting&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkflowStateSucceeded&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkflowStateSkipped&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{},&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;task-生成&quot;&gt;task 生成&lt;/h3&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// workflow/pkg/types/types.go&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;WorkflowInstance&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;WorkflowMeta&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;OwnerInfo&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;metav1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OwnerReference&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;Debug&lt;/span&gt;     &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;   &lt;span class=&quot;k&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;Mode&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkflowExecuteMode&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;Steps&lt;/span&gt;     &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkflowStep&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;Status&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;v1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkflowRunStatus&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// workflow/pkg/types/types.go&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TaskRunner&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;Pending&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;monitorContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wfCtx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wfContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;stepStatus&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StepStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StepStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wfContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TaskRunOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StepStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;一个workflow由多个step组成，每个step 有name/type/if/timeout/dependsOn/inputs/outputs/properties 等属性（对应 WorkflowStep struct），之后将  WorkflowStep 转为 TaskRunner，由executor 驱动执行。&lt;/p&gt;
&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// workflow/pkg/generator/generator.go&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GenerateRunners&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;monitorContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkflowInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;([]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TaskRunner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;taskDiscover&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tasks&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewTaskDiscover&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tasks&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TaskRunner&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;step&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;range&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Steps&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;opt&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TaskGeneratorOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generateTaskRunner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;step&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;taskDiscover&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;tasks&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tasks&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generateTaskRunner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkflowInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;step&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkflowStep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TaskRunner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;step&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkflowStepTypeStepGroup&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;genTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;taskDiscover&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetTaskGenerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;step&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;genTask&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;step&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;task&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;基于自定义cue模版生成task&quot;&gt;基于自定义cue模版生成task&lt;/h3&gt;

&lt;p&gt;示例demo yaml 中的 read-app/apply-app/suspend 都是一个task type，有些是builtin的，有些是自定义扩展实现的。buildin的比较简单，直接实现了TaskRunner 接口方法，初始化taskDiscover时直接写入，custom 则由 TaskLoader 负责管理&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// workflow/pkg/tasks/discover.go&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;taskDiscover&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;builtin&lt;/span&gt;            &lt;span class=&quot;k&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TaskGenerator&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;customTaskDiscover&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;custom&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TaskLoader&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NewTaskDiscover&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;monitorContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StepGeneratorOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TaskDiscover&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;taskDiscover&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;builtin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TaskGenerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkflowStepTypeSuspend&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;builtin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Suspend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkflowStepTypeStepGroup&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;builtin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StepGroup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;customTaskDiscover&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;custom&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewTaskLoader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TemplateLoader&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LoadTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PackageDiscover&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;td&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;taskDiscover&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GetTaskGenerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TaskGenerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;tg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ok&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;td&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;builtin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;          &lt;span class=&quot;c&quot;&gt;// 如果是自建的则直接返回&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ok&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;td&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;customTaskDiscover&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;   &lt;span class=&quot;c&quot;&gt;// 否则从custom 里寻找&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;tg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;td&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;customTaskDiscover&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetTaskGenerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Errorf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;can't find task generator: %s&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;对于自定义 task type，从 static 目录里扫描模版文件，如果还找不到，查询对应type 的WorkflowStepDefinition 中拿到cue 模版文件。&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;TaskLoader.GetTaskGenerator(ctx, name)  # 此处的name值为step.Type   
  templ, err := WorkflowStepLoader.LoadTemplate
    files, err := templateFS.ReadDir(templateDir)
    staticFilename := name + &quot;.cue&quot;
    for _, file := range files {      # 找到对应的cue模版文件
      if staticFilename == file.Name() {  
        fileName := fmt.Sprintf(&quot;%s/%s&quot;, templateDir, file.Name())
        content, err := templateFS.ReadFile(fileName)
        return string(content), err
      }
    }
    getDefinitionTemplate
      definition := &amp;amp;unstructured.Unstructured{}
	    definition.SetAPIVersion(&quot;core.oam.dev/v1beta1&quot;)
	    definition.SetKind(&quot;WorkflowStepDefinition&quot;)
      cli.Get(ctx, types.NamespacedName{Name: definitionName, Namespace: ns}, definition)
      d := new(def)
      runtime.DefaultUnstructuredConverter.FromUnstructured(definition.Object, d)
      d.Spec.Schematic.CUE.Template
  TaskLoader.makeTaskGenerator(templ)
    return func(...)(TaskRunner,error){
      paramsStr, err := GetParameterTemplate(wfStep)  # 从  WorkflowStep 获取用户参数
      tRunner := new(taskRunner)
		  tRunner.name = wfStep.Name
      tRunner.checkPending = ...
      tRunner.run =  ...
      return tRunner, nil
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;workflowSpec.step 有type 和properties 等参数，WorkflowStepDefinition 有对应type的cue template 文件，两相结合先把cue template中的参数替换成真实值，再根据cue 生成taskRunner 的Run方法。这里涉及到cue 包里的细节，还未深研究，其核心意图是：通过cue 代码（感觉也是dsl的一种）表述意图 + WorkflowStepDefinition 来支持自定义step type。PS： 估计是觉得通过代码方式扩展step type太慢了。&lt;/p&gt;
&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// workflow/pkg/tasks/custom/task.go&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wfContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TaskRunOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stepStatus&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StepStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operations&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rErr&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;basicVal&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;basicTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MakeBasicValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tracer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wfStep&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wfStatus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;paramsStr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PCtx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;taskv&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Value&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hook&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;range&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PreCheckHooks&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hook&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;range&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PreStartHooks&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;// refresh the basic template to get inputs value involved&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;basicTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;basicVal&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;taskv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strings&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([]&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;templ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;basicTemplate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ProcessScript&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TagFieldOrder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;doSteps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tracer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;taskv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;executor执行框架&quot;&gt;executor执行框架&lt;/h3&gt;

&lt;p&gt;先检查 tasks 是否都结束了（Done/Succeeded/Failed），结束则直接返回，否则新建 engine 执行。workflow 状态：executing/suspending/terminated/failed/succeeded。 There’re two modes of execution: StepByStep and DAG.&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;all the steps will be executed sequentially and the next step will be executed after the previous one succeeded. If a step is of type step-group, it can contain a series of sub-steps, all of which are executed together when the step group is executed.&lt;/li&gt;
  &lt;li&gt;DAG&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// workflow/pkg/executor/workflow.go&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;workflowExecutor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExecuteRunners&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;taskRunners&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TaskRunner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkflowRunPhase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;allTasksDone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;allTasksSucceeded&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;allDone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;taskRunners&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;checkWorkflowTerminated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;allTasksDone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isTerminatedManually&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkflowStateTerminated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkflowStateFailed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;checkWorkflowSuspended&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkflowStateSuspending&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;allTasksSucceeded&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkflowStateSucceeded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newEngine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wfCtx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;taskRunners&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dagMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;allTasksDone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;allTasksSucceeded&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;allDone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;taskRunners&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Terminated&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Suspend&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;wfContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CleanupMemoryStore&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;instance&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Namespace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkflowStateSuspending&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;allTasksSucceeded&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkflowStateSucceeded&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkflowStateExecuting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;engine 依次执行taskRunners ，并记录执行结果，每次从头开始遍历taskRunners，若已完成，则下一个。若未完成，先执行task的 Pending 方法，检查是否需要等待，若ok则执行task的Run 方法。task/step 的状态 WorkflowStepPhase：succeeded/failed/skipped/running/pending。&lt;/p&gt;
&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// workflow/pkg/executor/workflow.go&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;engine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;monitorContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;taskRunners&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TaskRunner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dag&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dag&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;runAsDAG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;taskRunners&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;taskRunners&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;checkFailedAfterRetries&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;setNextExecuteTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;engine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;monitorContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;taskRunners&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TaskRunner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dag&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;	&lt;span class=&quot;n&quot;&gt;wf&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;runner&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;range&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;taskRunners&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ok&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stepStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;runner&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()];&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ok&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsStepFinish&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Phase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Reason&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;
			&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pending&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;runner&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Pending&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wfCtx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stepStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pending&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;wfCtx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IncreaseCountValueInMemory&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ContextPrefixBackoffTimes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;updateStepStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dag&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;
			&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;generateRunOptions&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;findDependPhase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;taskRunners&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;runner&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wfCtx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;updateStepStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

		&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;failedAfterRetries&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;failedAfterRetries&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FailedAfterRetries&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;waiting&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;waiting&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Waiting&lt;/span&gt;
		&lt;span class=&quot;c&quot;&gt;// for the suspend step with duration, there's no need to increase the backoff time in reconcile when it's still running&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IsStepFinish&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Phase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Reason&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isWaitSuspendStep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handleBackoffTimes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wfCtx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;
			&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dag&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;
			&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;c&quot;&gt;// clear the backoff time when the step is finished&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handleBackoffTimes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wfCtx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;finishStep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dag&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;continue&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;needStop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;resume-逻辑&quot;&gt;resume 逻辑&lt;/h3&gt;

&lt;p&gt;用户触发执行&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vela workflow resume xx&lt;/code&gt;，将 处于running 状态的 suspend step 置为 succeed，之后更新 WorkflowRun 的status，触发WorkflowRun reconcile，进而触发 executor.ExecuteRunners 。&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// workflow/pkg/utils/operation.go&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wo&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;workflowRunOperator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Resume&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;run&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Status&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Terminated&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Errorf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;can not resume a terminated workflow&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Status&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Suspend&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;ResumeWorkflow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cli&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;writeOutputF&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Successfully resume workflow: %s&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ResumeWorkflow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cli&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;run&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkflowRun&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Status&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Suspend&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;steps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Status&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Steps&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;step&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;range&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;steps&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;step&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wfTypes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkflowStepTypeSuspend&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;step&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Phase&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkflowStepPhaseRunning&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Phase&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkflowStepPhaseSucceeded&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sub&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;range&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;step&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SubStepsStatus&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sub&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wfTypes&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkflowStepTypeSuspend&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sub&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Phase&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkflowStepPhaseRunning&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;n&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SubStepsStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Phase&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkflowStepPhaseSucceeded&lt;/span&gt;
			&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;retry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RetryOnConflict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;retry&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DefaultBackoff&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cli&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Patch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Merge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;go-workflow未完成&quot;&gt;go-workflow（未完成）&lt;/h2&gt;
</description>
      </item>
    
      <item>
        <title>容器和CPU那些事儿</title>
        <link>https://felix0080.github.io/2022/11/06/container_cpu.html</link>
        <guid isPermaLink="true">https://felix0080.github.io/2022/11/06/container_cpu.html</guid>
        <pubDate>Sun, 06 Nov 2022 00:00:00 +0000</pubDate>
        <description>&lt;h2 id=&quot;简介&quot;&gt;简介&lt;/h2&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#简介&quot; id=&quot;markdown-toc-简介&quot;&gt;简介&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#观察cpu-使用&quot; id=&quot;markdown-toc-观察cpu-使用&quot;&gt;观察cpu 使用&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#linux-视角&quot; id=&quot;markdown-toc-linux-视角&quot;&gt;linux 视角&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#docker-视角&quot; id=&quot;markdown-toc-docker-视角&quot;&gt;docker 视角&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#linux的cpu管理cfs&quot; id=&quot;markdown-toc-linux的cpu管理cfs&quot;&gt;Linux的CPU管理——CFS&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#cfs-基于虚拟运行时间的调度&quot; id=&quot;markdown-toc-cfs-基于虚拟运行时间的调度&quot;&gt;CFS 基于虚拟运行时间的调度&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#cfs-hard-limits&quot; id=&quot;markdown-toc-cfs-hard-limits&quot;&gt;CFS hard limits&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#容器-与-cfs&quot; id=&quot;markdown-toc-容器-与-cfs&quot;&gt;容器 与 CFS&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#kubernetes-与-cfs&quot; id=&quot;markdown-toc-kubernetes-与-cfs&quot;&gt;Kubernetes 与 CFS&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#request&quot; id=&quot;markdown-toc-request&quot;&gt;request&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#limit&quot; id=&quot;markdown-toc-limit&quot;&gt;limit&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#k8s-cpu-服务质量等级qos&quot; id=&quot;markdown-toc-k8s-cpu-服务质量等级qos&quot;&gt;k8s CPU 服务质量等级/Qos&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#降低高优任务的调度延迟&quot; id=&quot;markdown-toc-降低高优任务的调度延迟&quot;&gt;降低高优任务的调度延迟&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#k8s-cpu-qos-问题与解决&quot; id=&quot;markdown-toc-k8s-cpu-qos-问题与解决&quot;&gt;k8s CPU Qos 问题与解决&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#其它&quot; id=&quot;markdown-toc-其它&quot;&gt;其它&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;观察cpu-使用&quot;&gt;观察cpu 使用&lt;/h2&gt;

&lt;h3 id=&quot;linux-视角&quot;&gt;linux 视角&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/container/process_top.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/container/process_cpu.jpeg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;假设只有一个 CPU&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;一个用户程序开始运行了，就对应着第一个”us”框，”us”是”user”的缩写，代表 Linux 的用户态 CPU Usage。普通用户程序代码中，只要不是调用系统调用（System Call），这些代码的指令消耗的 CPU 就都属于”us”。&lt;/li&gt;
  &lt;li&gt;当用户程序代码中调用了系统调用，比如 read() 去读取一个文件，用户进程就会从用户态切换到内核态。内核态 read() 系统调用在读到真正 disk 上的文件前，会进行一些文件系统层的操作，这些代码指令的消耗就属于”sy”。”sy”是 “system”的缩写，代表内核态 CPU 使用。&lt;/li&gt;
  &lt;li&gt;接下来，这个 read() 系统调用会向 Linux 的 Block Layer 发出一个 I/O Request，触发一个真正的磁盘读取操作。这时进程一般会被置为 TASK_UNINTERRUPTIBLE。而 Linux 会把这段时间标示成”wa”，”wa”是”iowait”的缩写，代表等待 I/O 的时间，这里的 I/O 是指 Disk I/O。&lt;/li&gt;
  &lt;li&gt;当磁盘返回数据时，进程在内核态拿到数据，这里仍旧是内核态的 CPU 使用中的”sy”，然后，进程再从内核态切换回用户态，在用户态得到文件数据，”us”。进程在读取数据之后，没事可做就休眠了，”id”是”idle”的缩写，代表系统处于空闲状态。&lt;/li&gt;
  &lt;li&gt;如果这时这台机器在网络收到一个网络数据包，网卡就会发出一个中断（interrupt）。相应地，CPU 会响应中断，然后进入中断服务程序。CPU 就会进入”hi”，”hi”是”hardware irq”的缩写，代表 CPU 处理硬中断的开销。由于我们的中断服务处理需要关闭中断，所以这个硬中断的时间不能太长。&lt;/li&gt;
  &lt;li&gt;但是，发生中断后的工作是必须要完成的，如果这些工作比较耗时那怎么办呢？Linux 中有一个软中断的概念（softirq），它可以完成这些耗时比较长的工作。从网卡收到数据包的大部分工作，都是通过软中断来处理的。那么，CPU 就会进入到第八个框，”si”。这里”si”是”softirq”的缩写，代表 CPU 处理软中断的开销。&lt;strong&gt;无论是”hi”还是”si”，它们的 CPU 时间都不会计入进程的 CPU 时间。这是因为本身它们在处理的时候就不属于任何一个进程&lt;/strong&gt;。wa、hi、si，这些 I/O 或者中断相关的 CPU 使用，CPU Cgroup 不会去做限制&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;docker-视角&quot;&gt;docker 视角&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.docker.com/engine/reference/commandline/stats/&quot;&gt;docker stats&lt;/a&gt; returns a live data stream for running containers.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker stats&lt;/code&gt; 命令输出&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;CONTAINER ID        NAME                                         CPU %               MEM USAGE / LIMIT     MEM %               NET I/O             BLOCK I/O           PIDS
4aeb15578094        mesos-19ba2ecd-7a98-4e92-beed-c132b063578d   0.21%               376.4MiB / 1GiB       36.75%              3.65MB / 1.68MB     119kB / 90.1kB      174
77747f26dff4        mesos-e3d34892-8af6-4ab7-a649-0a4b424ccd04   0.31%               752.5MiB / 800MiB     94.06%              11.9MB / 3.06MB     86.1MB / 47MB       132
d64e482d2843        mesos-705b5dc6-7169-42e8-a143-6a7dc2e32600   0.18%               680.5MiB / 800MiB     85.06%              43.1MB / 17.1MB     194MB / 228MB       196
808a4bd888fb        mesos-65c9d5a6-3967-4a4a-9834-b83ae8c033be   1.81%               1.45GiB / 2GiB        72.50%              1.32GB / 1.83GB     8.36MB / 19.8MB     2392
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ol&gt;
  &lt;li&gt;CPU % 体现了 quota/period 值&lt;/li&gt;
  &lt;li&gt;MEM USAGE / LIMIT 反映了内存占用&lt;/li&gt;
  &lt;li&gt;NET I/O 反映了进出带宽&lt;/li&gt;
  &lt;li&gt;BLOCK I/O 反映了磁盘带宽，The amount of data the container has read to and written from block devices on the host ，貌似是一个累计值，但可以部分反映 项目对磁盘的写程度，有助于解决&lt;a href=&quot;http://qiankunli.github.io/2019/03/05/container_log.html&quot;&gt;容器狂打日志怎么办？&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;PID 反映了对应的进程号，也列出了进程id 与容器id的关系。根据pid 查询容器 id &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker stats --no-stream | grep 1169&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;具体到语言级，各个语言都有对应的分析工具，比如java 应用，可以进一步使用vjtop等工具分析进程内线程。&lt;/p&gt;

&lt;h2 id=&quot;linux的cpu管理cfs&quot;&gt;Linux的CPU管理——CFS&lt;/h2&gt;

&lt;p&gt;在 Linux 里面，进程大概可以分成两种：实时进程和 普通进程。每个 CPU 都有自己的 struct rq 结构（一个cpu一个运行队列），其用于描述在此 CPU 上所运行的所有进程，其包括一个实时进程队列 rt_rq 和一个 CFS 运行队列 cfs_rq，在调度时，调度器首先会先去实时进程队列找是否有实时进程需要运行，如果没有才会去 CFS 运行队列找是否有进程需要运行。&lt;/p&gt;

&lt;p&gt;cgroup 是 调度器 暴露给外界操作 的接口，对于 进程cpu 相关的资源配置 RT（realtime调度器） 和CFS 均有实现。本文主要讲  CFS，CFS 也是在不断发展的。&lt;/p&gt;

&lt;h3 id=&quot;cfs-基于虚拟运行时间的调度&quot;&gt;CFS 基于虚拟运行时间的调度&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://stackoverflow.com/questions/19181834/what-is-the-concept-of-vruntime-in-cfs/19193619&quot;&gt;What is the concept of vruntime in CFS&lt;/a&gt;vruntime is a measure of the “runtime” of the thread - the amount of time it has spent on the processor. The whole point of CFS is to be fair to all; hence, the algo kind of boils down to a simple thing: (among the tasks on a given runqueue) the task with the lowest vruntime is the task that most deserves to run, hence select it as ‘next’. CFS（完全公平调度器）是Linux内核2.6.23版本开始采用的进程调度器，具体的细节蛮复杂的，整体来说是保证每个进程运行的虚拟时间一致， &lt;strong&gt;每次选择vruntime 较少的进程来执行&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;另外两个细节&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;进程上下文切换会导致额外的 CPU 浪费。假如被选中的进程刚运行没多久，它的虚拟时间时间就比另一个进程小了。这时候难道要马上换另一个进程处理么？出于减少频繁切换进程所带来的成本考虑，Linux 会保证选择到的进程一个最短的运行时间，这个时间由 sched_min_granularity_ns 这个内核参数来控制。当然了，如果进程因为等待网络、磁盘等资源时主动放弃那另算。&lt;/li&gt;
  &lt;li&gt;在实践中可能确实有进程需要多分配一点运行时间。Linux 采用的做法在是上述绝对公平算法基础上再为进程引入一个权重。这个权重就是 Linux 进程的 nice 值，也就是我们平时 top 命令结果中看到的 ni 这一列。nice 范围为 -20（最高权重）到 19（最低权重）。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;vruntime就是根据权重、优先级（留给上层介入的配置）等将实际运行时间标准化。在内核中通过prio_to_weight数组进行nice值和权重的转换。&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prio_to_weight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
 &lt;span class=&quot;cm&quot;&gt;/* -20 */&lt;/span&gt;     &lt;span class=&quot;mi&quot;&gt;88761&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;     &lt;span class=&quot;mi&quot;&gt;71755&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;     &lt;span class=&quot;mi&quot;&gt;56483&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;     &lt;span class=&quot;mi&quot;&gt;46273&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;     &lt;span class=&quot;mi&quot;&gt;36291&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
 &lt;span class=&quot;cm&quot;&gt;/* -15 */&lt;/span&gt;     &lt;span class=&quot;mi&quot;&gt;29154&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;     &lt;span class=&quot;mi&quot;&gt;23254&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;     &lt;span class=&quot;mi&quot;&gt;18705&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;     &lt;span class=&quot;mi&quot;&gt;14949&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;     &lt;span class=&quot;mi&quot;&gt;11916&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
 &lt;span class=&quot;cm&quot;&gt;/* -10 */&lt;/span&gt;      &lt;span class=&quot;mi&quot;&gt;9548&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;      &lt;span class=&quot;mi&quot;&gt;7620&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;      &lt;span class=&quot;mi&quot;&gt;6100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;      &lt;span class=&quot;mi&quot;&gt;4904&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;      &lt;span class=&quot;mi&quot;&gt;3906&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
 &lt;span class=&quot;cm&quot;&gt;/*  -5 */&lt;/span&gt;      &lt;span class=&quot;mi&quot;&gt;3121&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;      &lt;span class=&quot;mi&quot;&gt;2501&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;      &lt;span class=&quot;mi&quot;&gt;1991&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;      &lt;span class=&quot;mi&quot;&gt;1586&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;      &lt;span class=&quot;mi&quot;&gt;1277&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
 &lt;span class=&quot;cm&quot;&gt;/*   0 */&lt;/span&gt;      &lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;       &lt;span class=&quot;mi&quot;&gt;820&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;       &lt;span class=&quot;mi&quot;&gt;655&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;       &lt;span class=&quot;mi&quot;&gt;526&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;       &lt;span class=&quot;mi&quot;&gt;423&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
 &lt;span class=&quot;cm&quot;&gt;/*   5 */&lt;/span&gt;       &lt;span class=&quot;mi&quot;&gt;335&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;       &lt;span class=&quot;mi&quot;&gt;272&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;       &lt;span class=&quot;mi&quot;&gt;215&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;       &lt;span class=&quot;mi&quot;&gt;172&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;       &lt;span class=&quot;mi&quot;&gt;137&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
 &lt;span class=&quot;cm&quot;&gt;/*  10 */&lt;/span&gt;       &lt;span class=&quot;mi&quot;&gt;110&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;        &lt;span class=&quot;mi&quot;&gt;87&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;        &lt;span class=&quot;mi&quot;&gt;70&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;        &lt;span class=&quot;mi&quot;&gt;56&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;        &lt;span class=&quot;mi&quot;&gt;45&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
 &lt;span class=&quot;cm&quot;&gt;/*  15 */&lt;/span&gt;        &lt;span class=&quot;mi&quot;&gt;36&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;        &lt;span class=&quot;mi&quot;&gt;29&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;        &lt;span class=&quot;mi&quot;&gt;23&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;        &lt;span class=&quot;mi&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;        &lt;span class=&quot;mi&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;NICE_0_LOAD = 1024&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;虚拟运行时间 vruntime += 实际运行时间 delta_exec * NICE_0_LOAD/ 权重&lt;/code&gt;&lt;/p&gt;

&lt;h3 id=&quot;cfs-hard-limits&quot;&gt;CFS hard limits&lt;/h3&gt;

&lt;p&gt;对一个进程配置 cpu-shares 并不能绝对限制 进程对cpu 的使用（如果机器很闲，进程的vruntime 虽然很大，但依然可以一直调度执行），也不能预估 进程使用了多少cpu 资源。这对于 桌面用户无所谓，但对于企业用户或者云厂商来说就很关键了。所以CFS 后续加入了 bandwith control。 将 进程的 cpu-period,cpu-quota 数据纳入到调度逻辑中。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;在操作系统里，cpu.cfs_period_us 的值一般是个固定值， Kubernetes 不会去修改它&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cpu.cfs_quota_us/cpu.cfs_period_us&lt;/code&gt; 的值与 top 命令看到的 进程 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%CPU&lt;/code&gt; 是一致的。 PS： 这就把原理与实践串起来了。&lt;/li&gt;
  &lt;li&gt;即使 cpu.cfs_quota_us 已经限制了进程 CPU 使用的绝对值，如果两个进程的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cpu.cfs_quota_us/cpu.cfs_period_us&lt;/code&gt; 和仍然大于了cpu 个数，则 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cpu.shares&lt;/code&gt; 可以限定两个进程使用cpu 时间的比例，&lt;strong&gt;当系统上 CPU 完全被占满的时候是有用的&lt;/strong&gt;。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;具体逻辑比较复杂，有兴趣可以看&lt;a href=&quot;https://storage.googleapis.com/pub-tools-public-publication-data/pdf/36669.pdf&quot;&gt;CPU bandwidth control for CFS&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/QYJycJCaxB42xdEo3qHHHA&quot;&gt;崩溃，K8s 使用 CPU Limit 后，服务响应变成龟速…&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/UdiQbpJZWQzRC2EFk3G-PQ&quot;&gt;让容器跑得更快：CPU Burst 技术实践&lt;/a&gt; Bandwidth Controller 适用于 CFS 任务，用 period 和 quota 管理 cgroup 的 CPU 时间消耗。若 cgroup 的 period 是 100ms quota 是 50ms，cgroup 的进程每 100ms 周期内最多使用 50ms CPU 时间。当 100ms 周期的 CPU 使用超过 50ms 时进程会被限流，cgroup 的 CPU 使用被限制到 50%。CPU 利用率是一段时间内 CPU 使用的平均，以较粗的粒度统计 CPU 的使用需求，CPU 利用率趋向稳定；当观察的粒度变细，CPU 使用的突发特征更明显。以 1s 粒度和 100ms 粒度同时观测容器负载运行，当观测粒度是 1s 时 CPU 利用率的秒级平均在 250% 左右，而在 Bandwidth Controller 工作的 100ms 级别观测 CPU 利用率的峰值已经突破 400% 。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/container/cpu_burst.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我们用 CPU Burst 技术来满足这种细粒度 CPU 突发需求，在传统的 CPU Bandwidth Controller quota 和 period 基础上引入 burst 的概念。当容器的 CPU 使用低于 quota 时，可用于突发的 burst 资源累积下来；当容器的 CPU 使用超过 quota，允许使用累积的 burst 资源。最终达到的效果是将容器&lt;strong&gt;更长时间的平均 CPU 消耗限制在 quota 范围内，允许短时间内的 CPU 使用超过其 quota&lt;/strong&gt;。如果用 Bandwidth Controller 算法来管理休假，假期管理的周期（period）是一年，一年里假期的额度是 quota ，有了 CPU Burst 技术之后今年修不完的假期可以放到以后来休了。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/KuZ71YyYIaf64uRDitJjnQ&quot;&gt;Linux 进程管理之CFS负载均衡&lt;/a&gt; 比想象的还要复杂。&lt;/p&gt;

&lt;h2 id=&quot;容器-与-cfs&quot;&gt;容器 与 CFS&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Linux 容器中所谓的核并不是真正的 CPU 核，而是转化成了执行时间的概念&lt;/strong&gt;。在容器进程调度的时候给其满足一定的 CPU 执行时间，而不是真正的分配逻辑核。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/sys/fs/cgroup/cpu&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/2lcX3-QBYFP5UnPw-b-Rvg&quot;&gt;Kubernetes中的CPU节流：事后分析&lt;/a&gt;几乎所有的容器编排器都依赖于内核控制组（cgroup）机制来管理资源约束。在容器编排器中设置硬CPU限制后，内核将使用完全公平调度器（CFS）Cgroup带宽控制来实施这些限制。CFS-Cgroup带宽控制机制使用两个设置来管理CPU分配：配额（quota）和周期（period）。当应用程序在给定时间段内使用完其分配的CPU配额时，此时它将受到CPU节流，直到下一个周期才能被调度。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://docs.docker.com/config/containers/resource_constraints/&quot;&gt;docker CPU文档&lt;/a&gt;By default, each container’s access to the host machine’s CPU cycles is unlimited. You can set various constraints to limit a given container’s access to the host machine’s CPU cycles. Most users use and configure the default CFS scheduler. In Docker 1.13 and higher, you can also configure the realtime scheduler.The CFS is the Linux kernel CPU scheduler for normal Linux processes. &lt;strong&gt;Several runtime flags allow you to configure the amount of access to CPU resources your container has&lt;/strong&gt;. When you use these settings, Docker modifies the settings for the container’s cgroup on the host machine.  默认情况，容器可以任意使用 host 上的cpu cycles，&lt;strong&gt;配的不是容器，配的是 CFS&lt;/strong&gt;，dockers仅仅是帮你设置了下参数而已。&lt;/p&gt;

&lt;table&gt;
  &lt;thead&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;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;–cpuset-cpus&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--cpuset-cpus=&quot;1,3&quot;&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;–cpu-shares&lt;/td&gt;
      &lt;td&gt;默认为1024&lt;/td&gt;
      &lt;td&gt;绝对值没有意义，只有和另一个进程对比起来才有意义&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;–cpu-period&lt;/td&gt;
      &lt;td&gt;100ms&lt;/td&gt;
      &lt;td&gt;Specify the CPU CFS scheduler period，和cpu-quota结合使用&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;–cpu-quota&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Impose a CPU CFS quota on the container，和cpu-period结合使用&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;–cpus&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
      &lt;td&gt;表示quota/period=1&lt;br /&gt;docker 1.13支持支持，替换cpu-period和cpu-quota&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;If you have 1 CPU, each of the following commands guarantees the container at most 50% of the CPU every second.&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Docker 1.13 and higher:&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker run -it --cpus=&quot;.5&quot; ubuntu /bin/bash&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Docker 1.12 and lower: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker run -it --cpu-period=100000 --cpu-quota=50000 ubuntu /bin/bash&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;shares值即CFS中每个进程的(准确的说是调度实体)权重(weight/load)，shares的大小决定了在一个CFS调度周期中，进程占用的比例，比如进程A的shares是1024，B的shares是512，假设调度周期为3秒，那么A将只用2秒，B将使用1秒&lt;/p&gt;

&lt;p&gt;现在的多核系统中每个核心都有自己的缓存，如果频繁的调度进程在不同的核心上执行势必会带来缓存失效等开销。–cpuset-cpus 可以让容器始终在一个或某几个 CPU 上运行。–cpuset-cpus 选项的一个缺点是必须指定 CPU 在操作系统中的编号，这对于动态调度的环境(无法预测容器会在哪些主机上运行，只能通过程序动态的检测系统中的 CPU 编号，并生成 docker run 命令)会带来一些不便。解决办法 &lt;a href=&quot;https://mp.weixin.qq.com/s/4qnbtwXi4TScEIYyfRm9Qw&quot;&gt;深入理解 Kubernetes CPU Manager&lt;/a&gt; &lt;a href=&quot;https://mp.weixin.qq.com/s/MPE92VARsJsNx6ewKYweOg&quot;&gt;Uber的20万容器实践：如何避免容器化环境中的 CPU 节流&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;kubernetes-与-cfs&quot;&gt;Kubernetes 与 CFS&lt;/h2&gt;

&lt;p&gt;假设&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cat /proc/cpuinfo| grep &quot;processor&quot;| wc -l&lt;/code&gt; 查看某个node 的逻辑core 数为48，使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl describe node xx&lt;/code&gt;查看node cpu情况&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Capacity:     # 节点的总资源
  ...
  cpu:                       48
Allocatable:  # 可以分配用来运行pod的
  ...
  cpu:                       47
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://medium.com/@betz.mark/understanding-resource-limits-in-kubernetes-cpu-time-9eff74d3161b&quot;&gt;Understanding resource limits in kubernetes: cpu time&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;memory&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;50Mi&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;cpu&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;50m&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;limits&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;memory&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;100Mi&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;cpu&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;100m&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;如果不明确标注单位，比如直接写 0.5，&lt;strong&gt;默认单位就是Core&lt;/strong&gt;，即 0.5 个处理器；当然也可以明确使用Millcores为单位，比如写成 500 m，同样也代表 0.5 个处理器，因为 Kubernetes 规定了1 Core = 1000 Millcores。而对于内存来说，它早已经有了广泛使用的计量单位，即 Bytes，如果设置中不明确标注单位，&lt;strong&gt;就会默认以 Bytes 计数&lt;/strong&gt;。为了实际设置的方便，Kubernetes 还支持以Ei、Pi、Ti、Gi、Mi、Ki，以及E、P、T、G、M、K为单位，这两者略微有一点儿差别。这里我就以Mi和M为例，它们分别是Mebibytes与Megabytes的缩写，前者表示 1024×1024 Bytes，后者表示 1000×1000 Bytes。&lt;/p&gt;

&lt;p&gt;cpu requests and cpu limits are implemented using two separate control systems.&lt;strong&gt;cpu request和limit 是两个cpu cgroup 子系统&lt;/strong&gt;&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Kubernetes&lt;/th&gt;
      &lt;th&gt;docker&lt;/th&gt;
      &lt;th&gt;cpu,cpuacct cgroup&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;request=50m&lt;/td&gt;
      &lt;td&gt;cpu-shares=51&lt;/td&gt;
      &lt;td&gt;cpu.shares&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;limit=100m&lt;/td&gt;
      &lt;td&gt;cpu-period=100000,cpu-quota=10000&lt;/td&gt;
      &lt;td&gt;cpu.cfs_period_us,cpu.cfs_quota_us&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;a href=&quot;https://cloud.tencent.com/developer/article/1402119&quot;&gt;深入理解 Kubernetes CPU Mangager&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;request&quot;&gt;request&lt;/h3&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ kubectl run limit-test --image=busybox --requests &quot;cpu=50m&quot; --command -- /bin/sh -c &quot;while true; do sleep 2; done&quot;
deployment.apps &quot;limit-test&quot; created
$ docker ps | grep busy | cut -d' ' -f1
f2321226620e
$ docker inspect f2321226620e --format ''
51
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Why 51, and not 50? The cpu control group and docker both divide a core into 1024 shares, whereas kubernetes divides it into 1000.&lt;/p&gt;

&lt;p&gt;Requests 使用的是 cpu shares 系统，cpu shares 将每个 CPU 核心划分为 1024 个时间片，并保证每个进程将获得固定比例份额的时间片。如果总共有 1024 个时间片，并且两个进程中的每一个都将 cpu.shares 设置为 512，那么它们将分别获得大约一半的 CPU 可用时间。但 cpu shares 系统无法精确控制 CPU 使用率的上限，也就是说如果一个进程没有使用它的这一份，其它进程是可以使用的。PS：核心是cfs每次挑选vruntime 最小的来执行，但某个进程如果io较多，就进不到就绪队列里，就只能等下一个调度周期了。&lt;/p&gt;

&lt;h3 id=&quot;limit&quot;&gt;limit&lt;/h3&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ kubectl run limit-test --image=busybox --requests &quot;cpu=50m&quot; --limits &quot;cpu=100m&quot; --command -- /bin/sh -c &quot;while true; do
sleep 2; done&quot;
deployment.apps &quot;limit-test&quot; created
$ kubectl get pods limit-test-5b4fb64549-qpd4n -o=jsonpath='{.spec.containers[0].resources}'
map[limits:map[cpu:100m] requests:map[cpu:50m]]
$ docker ps | grep busy | cut -d' ' -f1
f2321226620e
$ docker inspect 472abbce32a5 --format '  '
51 10000 100000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;cpu limits 会被带宽控制组设置为 cpu.cfs_period_us 和 cpu.cfs_quota_us 属性的值。&lt;/p&gt;

&lt;h3 id=&quot;k8s-cpu-服务质量等级qos&quot;&gt;k8s CPU 服务质量等级/Qos&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/bgoFj-aZo-RMh2hR5h0zrA&quot;&gt;cgroups 在 K8s 中的应用&lt;/a&gt;kubelet 作为 kubernetes 中的 node agent，所有 cgroup 的操作都由其内部的 containerManager 模块实现，containerManager 会通过 cgroup 将资源使用层层限制：node -&amp;gt; qos -&amp;gt; pod -&amp;gt; container。每一层都抽象出一种资源管理模型，通过这种方式提供了一种稳定的运行环境。如下图所示：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/container/kubelet_cgroup.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/Hp9clsqFe6hFghbnrAEyWA&quot;&gt;Kubelet 对 Pod 的服务质量管理&lt;/a&gt;kubelet 为不同类型的 pod 创建了不同的 cgroups，从而保证不同类型的 pod 获得的资源不同，尽量保证高优先级的服务质量，提升系统稳定性。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.cncf.io/blog/2020/06/10/kubernetes-resources-management-qos-quota-and-limitrangeb/&quot;&gt;Kubernetes Resources Management – QoS, Quota, and LimitRange&lt;/a&gt;A node can be overcommitted when it has pod scheduled that make no request, or when the sum of limits across all pods on that node exceeds the available machine capacity. In an &lt;strong&gt;overcommitted environment&lt;/strong&gt;, the pods on the node may attempt to use more compute resources than the ones available at any given point in time. &lt;strong&gt;When this occurs, the node must give priority to one container over another&lt;/strong&gt;. Containers that have the lowest priority are &lt;strong&gt;terminated/throttle&lt;/strong&gt; first. The entity used to make this decision is referred as the Quality of Service (QoS) Class.&lt;/p&gt;

&lt;p&gt;request 和 limit&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;if you set a limit but don’t set a request kubernetes will default the request to the limit（Kubernetes 会将 CPU 的 requests 设置为 与 limits 的值一样）. This can be fine if you have very good knowledge of how much cpu time your workload requires.&lt;/li&gt;
  &lt;li&gt;How about setting a request with no limit? In this case kubernetes is able to accurately schedule your pod, and the kernel will make sure it gets at least the number of shares asked for, but your process will not be prevented from using more than the amount of cpu requested, which will be stolen from other process’s cpu shares when available.&lt;/li&gt;
  &lt;li&gt;Setting neither a request nor a limit is the worst case scenario: the scheduler has no idea what the container needs, and the process’s use of cpu shares is unbounded, which may affect the node adversely.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;kubelet 为每个 Pod 设置 Cgroup 的目录结构：首先有一个一级目录叫 kubepod，所有 Pod 的 Cgroup 都会被挂到它下面， Burstable，BestEffort 这两种 Pod 没有直接挂在 kubepod 目录下，而是自己有一个原本是空白的没有值的二级目录。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;cgroup
  /kubepod
    /pod1   // Guaranted pod
    /pod2   // Guaranted pod
    /Burstable
      /pod3
      /pod4   
    /BestEffort
      /pod5
      /pod6  
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;降低高优任务的调度延迟&quot;&gt;降低高优任务的调度延迟&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/QpDuHCSTwwnYqSShHrG_tA&quot;&gt;阿里巴巴云原生混部系统 Koordinator 正式开源&lt;/a&gt; 代码已开源&lt;/p&gt;

&lt;p&gt;在线资源通常要给一个预留，预留的部分就是浪费的部分，所以要挖掘在线业务占用的资源给离线业务用，但是要注意：资源隔离，在线随时可以抢占离线。&lt;/p&gt;

&lt;p&gt;应用：&lt;strong&gt;CFS的抢占，其实是不那么暴力的抢占&lt;/strong&gt;。目前互联网企业都拥有海量的服务器，其中大部分只运行交互类延时敏感的在线业务，使CPU利用率非常低，造成资源的浪费（据统计全球服务器CPU利用率不足20%）。为提高服务器CPU的利用率，需要在运行在线业务的服务器上，混合部署一些高CPU消耗且延时不敏感的离线业务。为了使得在离线互相不影响，需要在在线业务CPU使用率低的时候，可以运行一些离线业务，而当在线业务CPU利用率高的时候，可以对离线业务快速抢占。如果在离线业务都使用不暴力的CFS调度器，现有的混部方案没办法做到及时抢占的。在同调度类优先级的进程，互相抢占的时候，需要满足两个条件。&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;第一个是抢占进程的虚拟时间要小于被抢占进程，&lt;/li&gt;
  &lt;li&gt;第二是被抢占进程已经运行的实际要大于最小运行时间。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;如果两个条件不能同时满足，就会导致无法抢占。基于这种考虑，开发了针对离线业务的新调度算法&lt;a href=&quot;https://github.com/Tencent/TencentOS-kernel&quot;&gt;bt&lt;/a&gt;，该算法可以保证在线业务优先运行。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/q-O2L-6_DMwhjCjGVAvmrg&quot;&gt;资源隔离技术之CPU隔离&lt;/a&gt; 建议先复习下linux 的抢占调度机制：每个CPU都有一个运行队列rq，每个rq会有一个cfs_rq运行队列，该结构包含一棵红黑树rb_tree，用来链接调度实体se，每次只能调度一个se到cpu上去运行。&lt;strong&gt;如果想要在任意时刻cfs_rq上的se运行时间都尽可能的接近，那么就需要不停地切换se上cpu运行&lt;/strong&gt;，但是频繁的切换会有开销，想要减小这种开销，就需要减少切换的次数。为了可以减小开销还能保证时间上的统一，内核便给cfs_rq的se进行排序，让他们按照时间顺序挂在rb_tree上，这样每次取红黑树最左边的se，就可以得到运行时间的最小的那个。但实际上，se又会有优先级的概念，不同优先级的se所分配到的cpu时间片是不一样的。内核便经过一系列公式的转换，可以得到一样的值，这个转换后的值称作虚拟运行时间vruntime，&lt;strong&gt;CFS实际上只需要保证每个任务运行的虚拟时间是相等的即可&lt;/strong&gt;。每次挑选se上cpu运行，当分配的时间片用完，就会将它再放回到cfs_rq中，挂在红黑树的适当位置。&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;内核CFS设置有两种最小时间粒度保护：sched_min_granularity_ns 和 sched_wakeup_granularity_ns，实际效果并不那么理想。在离线业务的混跑，当在线和离线任务分别调度到一个核上，相互抢执行时间，意味着&lt;strong&gt;离线一旦抢占，便可以持续运行一个最小粒度的时间sched_min_granularity_ns&lt;/strong&gt;，在线任务的唤醒延迟可能达sched_wakeup_granularity_ns，即在线业务的调度延迟可能会很大，导致在线业务的性能下降。另外，如果在离线业务跑到相互对应的一对HT上，还将面临超线程干扰的问题，虽然有core scheduling技术，但是其设计初衷并非为了混部，设计和实现开销较大，这些都将直接影响在线业务的性能。&lt;/li&gt;
  &lt;li&gt;当然也有可能会碰到运行过程中，时间片还未用完，但主动放弃运行的情况，如睡眠（TASK_INTERRUPTIBLE）或等待某种资源（TASK_UNINTERRUPTIBLE），这时就需要出列等待，进到“小黑屋”，直至相应事件发生才会再次放到红黑树中等待调度。等待后重新放入红黑树的se，如果休眠时间比较长，vruntime可能会非常小，便会迅速得到运行。为了避免其疯狂地执行，cfs_rq上会维护min_vruntime，如果新唤醒的vruntime(se) &amp;lt; vruntime (cfs_rq→min_vruntime)，会将se的vruntime修正至接近min_vruntime，这样就可以保证此类se优先执行，但是又不会疯狂执行。&lt;/li&gt;
  &lt;li&gt;调度器每次唤醒的是 vruntime 最小的task，task 一定获取cpu 除非主动放弃， 调度器会保证其至少可以执行sched_min_granularity_ns。这些都可能导致 高优任务 ready时因为cpu在执行低优任务而无法被立即执行。&lt;/li&gt;
  &lt;li&gt;group identity特性相对于upstream kernel的CFS设计，新增一棵低优先级红黑树，用于存放低优先级任务。能做到 当高优任务ready时，立即抢占低优任务，哪怕此时低优任务 尚未执行够 sched_min_granularity_ns。&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;k8s-cpu-qos-问题与解决&quot;&gt;k8s CPU Qos 问题与解决&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/M0iAujzSelESTITU7quo-g&quot;&gt;Kubernetes 中 CPU 调度管理的现状与限制&lt;/a&gt; 提了不少有意思的提案。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/12XFN2lPB3grS5FteaF__A&quot;&gt;百度混部实践：如何提高 Kubernetes 集群资源利用率？&lt;/a&gt; 是一篇很好的讲混部的文章
为什么原生 Kubernetes 没办法直接解决资源利用率的问题？&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;资源使用是动态的，而配额是静态限制。在线业务会根据其使用的峰值去预估 Quota（Request 和 Limit），配额申请之后就不能再修改，但资源用量却是动态的，白天和晚上的用量可能都不一样。&lt;/li&gt;
  &lt;li&gt;原生调度器并不感知真实资源的使用情况。对于 Burstable 这种想要超发的业务来说，无法做到合理的配置。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Kubernetes 原本的资源模型存在局限性（引入动态资源视图）。 我们可以基于原生的 QOS 体系做一些不修改原本语义的扩展行为，并且基于质量建立相应的定价体系，通过给出不同质量的资源供给 SLA，来对资源进行差异化定价，从而引导用户更加合理地使用资源。&lt;/p&gt;

&lt;p&gt;每个节点运行一个agent（单机引擎），agent根据 Guaranteed-Pod 的真实用量去给 Burstable/BestEffort 目录整体设置了一个值，这个值通过&lt;strong&gt;动态计算而来&lt;/strong&gt;。
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Burstable 资源使用 = 单机最大 CPU 用量 - Guaranteed容器用量 - Safety-Margin&lt;/code&gt;，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;BestEffort = 单机最大 CPU 用量 - Guaranteed容器用量 - Burstable容器用量 - Safety-Margin&lt;/code&gt;，比如一个pod request=limit=5，日常使用1，当pod 忙起来了，即申请的 Pod 现在要把自己借出去的这部分资源拿回来了，如何处理？此时会通过动态计算缩小 Burstable 和 BestEffort 的这两个框的值，达到一个压制的效果。当资源用量持续上涨时，如果 BestEffort 框整体 CPU 用量小于 1c ，单机引擎会把 BestEffort Pod 全部驱逐掉。当 Guaranteed-Pod 的用量还在持续上涨的时候，就会持续的压低 Burstable 整框 CPU 的 Quota，如果Burstable 框下只有一个 Pod，Request 是 1c，Limit 是 10c，那么单机引擎最低会将 Burstable 整框压制到 1c。换言之，对于 Request，就是说那些用户真实申请了 Quota 的资源，一定会得到得到供给；对于 Limit - Request 这部分资源，单机引擎和调度器会让它尽量能够得到供给；对于 BestEffort，也就是 No Limit 这部分资源，只要单机的波动存在，就存在被优先驱逐的风险. PS：pod的request /limit 是用户填的，不去调整，但是pod 的cgroup 都从属于k8s的某个qos cgroup，可以调整k8s 某个qos cgroup的限额。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/Cbck85WmivAW0mtMYdeEIw&quot;&gt;腾讯TencentOS 十年云原生的迭代演进之路&lt;/a&gt;通过将 Kubernetes Service QoS Class 与 TencentOS Priority 一一对应，在内核层原生感知优先级(Tencent Could Native Scheduler)，在底层提供强隔离机制(Cgroup Priority/CPU QoS/Memory QoS/IO QoS/Net QoS)，最大程度保证混部后业务的服务质量。而且这种优先级机制是贯穿在整个 cgroups 子系统中。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/AoOFF1RztZmC4vAamnsaOw&quot;&gt;谷歌每年节省上亿美金，资源利用率高达60%，用的技术有多厉害！&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/container/container_schedule.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;某些时候要对pod做绑核处理，或者是保护这个业务进程，或者是避免这个业务频繁切换干扰其它业务进程。尽量将同一个物理核上的逻辑核同时绑定给一个业务使用。否则，如果在线任务和混部任务分别跑在一个物理核的两个逻辑核上，在线任务还是有可能受到“noisy neighbor”干扰。&lt;/p&gt;

&lt;h2 id=&quot;其它&quot;&gt;其它&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/nKedQRFxmIgPBxtlIJasZw&quot;&gt;如何正确获取容器的CPU利用率？&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;如何正确地获取容器中的 cpu 利用率？&lt;/li&gt;
  &lt;li&gt;使用 lxcfs，将容器中的 /proc/stat 替换掉。这样 top 等命令就不再显示的是宿主机的 cpu 利用率了，而是容器的。&lt;/li&gt;
  &lt;li&gt;直接使用 cgroup 提供的伪文件来进行统计（libcontain），这些伪文件一般位于 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/sys/fs/cgroup/...&lt;/code&gt; 路径。kubelet 中集成的 cadvisor 就是采用上述方案来上报容器 cpu 利用率的打点信息的。cadvisor 具体访问 cgroup 目录是通过调用 libcontainer 获取的。&lt;/li&gt;
&lt;/ol&gt;
</description>
      </item>
    
      <item>
        <title>kubevela源码分析</title>
        <link>https://felix0080.github.io/2022/11/06/kubevela_source.html</link>
        <guid isPermaLink="true">https://felix0080.github.io/2022/11/06/kubevela_source.html</guid>
        <pubDate>Sun, 06 Nov 2022 00:00:00 +0000</pubDate>
        <description>&lt;h2 id=&quot;简介未完成&quot;&gt;简介（未完成）&lt;/h2&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#简介未完成&quot; id=&quot;markdown-toc-简介未完成&quot;&gt;简介（未完成）&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#使用&quot; id=&quot;markdown-toc-使用&quot;&gt;使用&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#源码结构&quot; id=&quot;markdown-toc-源码结构&quot;&gt;源码结构&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#启动&quot; id=&quot;markdown-toc-启动&quot;&gt;启动&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#引入workflow之前的处理流程&quot; id=&quot;markdown-toc-引入workflow之前的处理流程&quot;&gt;引入workflow之前的处理流程&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#根据-application-创建applicationconfiguration和component&quot; id=&quot;markdown-toc-根据-application-创建applicationconfiguration和component&quot;&gt;根据 Application 创建ApplicationConfiguration和Component&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#根据-applicationconfiguration和component-创建workload&quot; id=&quot;markdown-toc-根据-applicationconfiguration和component-创建workload&quot;&gt;根据 ApplicationConfiguration和Component 创建workload&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#引入workflow之后的处理流程&quot; id=&quot;markdown-toc-引入workflow之后的处理流程&quot;&gt;引入workflow之后的处理流程&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;使用&quot;&gt;使用&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://kubevela.io/docs/quick-start&quot;&gt;Deploy First Application&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;core.oam.dev/v1beta1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Application&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;first-vela-app&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;express-server&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;webservice&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;oamdev/hello-world&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
         &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8000&lt;/span&gt;
           &lt;span class=&quot;na&quot;&gt;expose&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;traits&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;scaler&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;replicas&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;policies&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;target-default&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;topology&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# The cluster with name local is installed the KubeVela.&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;clusters&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;local&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;default&quot;&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;target-prod&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;topology&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;clusters&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;local&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;# This namespace must be created before deploying.&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;namespace&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;prod&quot;&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;deploy-ha&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;override&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;webservice&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;traits&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;scaler&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                  &lt;span class=&quot;na&quot;&gt;replicas&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;workflow&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;deploy2default&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;deploy&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;policies&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;target-default&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;manual-approval&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;suspend&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;deploy2prod&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;deploy&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;policies&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;target-prod&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;deploy-ha&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vela def list&lt;/code&gt; 查看涉及到的 Definition，对于webservice，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl get ComponentDefinition -n vela-system webservice -o yaml&lt;/code&gt; 或者 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vela def get $defname&lt;/code&gt; 看下其内容&lt;/p&gt;

&lt;table&gt;
  &lt;tr&gt;
    &lt;th&gt;&lt;/th&gt;
    &lt;th&gt;Definition 种类&lt;/th&gt;
    &lt;th&gt;cue template内容&lt;/th&gt;
    &lt;th&gt;处理方式&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;webservice&lt;/td&gt;
    &lt;td&gt;ComponentDefinition&lt;/td&gt;
    &lt;td&gt;Deployment&lt;/td&gt;
    &lt;td&gt;转为 Deployment并分发&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;scaler&lt;/td&gt;
    &lt;td&gt;TraitDefinition&lt;/td&gt;
    &lt;td&gt;&lt;pre&gt;template: {
	parameter: {
		// +usage=Specify the number of workload
		replicas: *1 | int
	}
	// +patchStrategy=retainKeys
	patch: spec: replicas: parameter.replicas
}&lt;/pre&gt;&lt;/td&gt;
  &lt;td&gt;&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;topology&lt;/td&gt;
    &lt;td&gt;PolicyDefinition&lt;/td&gt;
    &lt;td&gt;&lt;/td&gt;
    &lt;td&gt;&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;override&lt;/td&gt;
    &lt;td&gt;PolicyDefinition&lt;/td&gt;
    &lt;td&gt;&lt;/td&gt;
    &lt;td&gt;&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;deploy&lt;/td&gt;
    &lt;td&gt;WorkflowStepDefinition&lt;/td&gt;
    &lt;td&gt;&lt;/td&gt;
    &lt;td&gt;&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;suspend&lt;/td&gt;
    &lt;td&gt;WorkflowStepDefinition&lt;/td&gt;
    &lt;td&gt;&lt;/td&gt;
    &lt;td&gt;&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;

&lt;h2 id=&quot;源码结构&quot;&gt;源码结构&lt;/h2&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;kubevela
  /cmd
    /core
  /pkg
    /controller
      /standard.oam.dev
        /v1alpha1
          /rollout
      /core.oam.dev
        /v1alpha2
          /application
            /application_controller.go
          /applicationconfiguration
          /core
            /components
            /policies
            /scopes
            /traits
            /workflow

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;启动&quot;&gt;启动&lt;/h2&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// kubevela/cmd/core/main.go&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;mgr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;restConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;oamv1alpha2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mgr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;controllerArgs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;mgr&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SetupSignalHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// kubevela/pkg/controller/core.oam.dev/v1alpha2/setup.go&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mgr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Manager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OAMSpecVer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;all&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;setup&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;range&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Manager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;application&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
      &lt;span class=&quot;n&quot;&gt;traitdefinition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
      &lt;span class=&quot;n&quot;&gt;componentdefinition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
      &lt;span class=&quot;n&quot;&gt;policydefinition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
      &lt;span class=&quot;n&quot;&gt;workflowstepdefinition&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;applicationconfiguration&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;n&quot;&gt;setup&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mgr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;minimal&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;v0.3&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;v0.2&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;xxDefinition 主要是管理 用户 定义的cue 模版，核心逻辑是 application 和  applicationconfiguration（在较新的版本去除了）。&lt;/p&gt;

&lt;h2 id=&quot;引入workflow之前的处理流程&quot;&gt;引入workflow之前的处理流程&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://developer.aliyun.com/article/783169&quot;&gt;源码解读：KubeVela 是如何将 appfile 转换为 K8s 特定资源对象的&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;起点：Application&lt;/li&gt;
  &lt;li&gt;中点：ApplicationConfiguration, Component&lt;/li&gt;
  &lt;li&gt;终点：Deployment, Service&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;根据-application-创建applicationconfiguration和component&quot;&gt;根据 Application 创建ApplicationConfiguration和Component&lt;/h3&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// kubevela/pkg/controller/core.oam.dev/v1alpha2/application/application_controller.go&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// Reconcile process app event&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Reconciler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Reconcile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v1beta1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ObjectKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;      &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Namespace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,},&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;appHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;      &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;applog&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;c&quot;&gt;// parse template&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;appParser&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;appfile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewApplicationParser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;appfile&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generatedAppfile&lt;/span&gt;

	&lt;span class=&quot;c&quot;&gt;// build template to applicationconfig &amp;amp; component&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;ac&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;appParser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GenerateApplicationConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;generatedAppfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Namespace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;appRev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ac&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{},&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;UpdateStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Application 提供 component/trait 的名字 找到cue 模版，加上 Application 配置的properties 拼凑ApplicationConfiguration（没找到create 逻辑）和Component 并创建。&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// kubevela/pkg/appfile/parser.go&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// GenerateApplicationConfiguration converts an appFile to applicationConfig &amp;amp; Components&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Parser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GenerateApplicationConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Appfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ns&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v1alpha2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApplicationConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v1alpha2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Component&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;appconfig&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v1alpha2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApplicationConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;components&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v1alpha2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Component&lt;/span&gt;
	
	&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;range&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Workloads&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;// 将 Application 转换为 ApplicationConfiguration 和 Component&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acComp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;generateComponentFromCUEModule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RevisionName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;components&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;appconfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Spec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Components&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;appconfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Spec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Components&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acComp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;appconfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// kubevela/pkg/controller/core.oam.dev/v1alpha2/application/apply.go&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// 在集群中创建 ApplicationConfiguration 和 Component &lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;h&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;appHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ac&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v1alpha2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApplicationConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;range&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comps&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;newComp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;comp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DeepCopy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
		&lt;span class=&quot;c&quot;&gt;// newComp will be updated and return the revision name instead of the component name&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;revisionName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;h&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;createOrUpdateComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newComp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;根据-applicationconfiguration和component-创建workload&quot;&gt;根据 ApplicationConfiguration和Component 创建workload&lt;/h3&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// kubevela/pkg/controller/core.oam.dev/v1alpha2/applicationconfiguration/applicationconfiguration.go&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// Reconcile an OAM ApplicationConfigurations by rendering and instantiating its Components and Traits.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OAMApplicationReconciler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Reconcile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reconcile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;reconcile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;ac&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v1alpha2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApplicationConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;// 获取 ApplicationConfiguration&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NamespacedName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ac&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;reconResult&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ACReconcile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ac&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reconResult&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// ACReconcile contains all the reconcile logic of an AC, it can be used by other controller&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;OAMApplicationReconciler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ACReconcile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ac&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v1alpha2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApplicationConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;log&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Logger&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reconcile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;// execute the prehooks&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;// 渲染&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;workloads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;depStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Render&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ac&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;applyOpts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApplyOption&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MustBeControllableBy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ac&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetUID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;applyOnceOnly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ac&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;applyOnceOnlyMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)}&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;// 创建 workload 和 traits 对应的 k8s 资源对象&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;workloads&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Apply&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ac&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Status&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Workloads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;workloads&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;applyOpts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reconcile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;RequeueAfter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;waitTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;渲染主要由 components 来完成，将 workload 形态从json 转换为 *unstructured.Unstructured，之后被部署到 k8s 。&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// kubevela/pkg/controller/core.oam.dev/v1alpha2/applicationconfiguration/render.go&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;components&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;c&quot;&gt;// indicate that if this is generated by application&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Reader&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;dm&lt;/span&gt;       &lt;span class=&quot;n&quot;&gt;discoverymapper&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DiscoveryMapper&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;ParameterResolver&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;workload&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ResourceRenderer&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;trait&lt;/span&gt;    &lt;span class=&quot;n&quot;&gt;ResourceRenderer&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Render&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ac&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v1alpha2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApplicationConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;([]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Workload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v1alpha2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DependencyStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;renderComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ac&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v1alpha2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApplicationConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Workload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;renderTrait&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ac&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v1alpha2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApplicationConfiguration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unstructured&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Unstructured&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v1alpha2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TraitDefinition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;有的trait是对workload的 patch，有的trait对应的独立的crd。通过trait.spec.workloadRefPath 建立trait与workload 之间的关系。
当trait 是patch时，kubevela 负责将patch 和 workload 做merge 然后apply 到k8s。&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;workloads,... := components.Render(ctx context.Context, ac *v1alpha2.ApplicationConfiguration) 
  for _, acc := range ac.Spec.Components {
    components.renderComponent
    renderWorkload  # 返回 unstructured.Unstructured
    for _, ct := range acc.Traits {
      components.renderTrait
        renderTrait
    }
    components.renderScope
  }
  for i, acc := range ac.Spec.Components {
    components.handleDependency(ctx, workloads[i], acc, dag, ac)
  }
workloads.Apply(...,workloads,...)
  for _, wl := range w {
    ApplyInputRef
    APIApplicator.Apply
    ApplyOutputRef
    for _, trait := range wl.Traits {
      ApplyInputRef
      APIApplicator.Apply   # 当trait 本身就对应一个crd时， 会直接apply
      ApplyOutputRef        # 当trait 是patch 类型时，会在这里通过 workloadRefPath 拿到ref object，对其进行json merge，之后apply
    }
    for _, s := range wl.Scopes {
			applyScope(ctx, wl, s, workloadRef)
		}
  }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;APIApplicator.Apply 更多是在 服务端模拟了 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubeclt apply -f&lt;/code&gt;的能力。&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// kubevela/pkg/utils/apply/apply.go&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// Apply applies new state to an object or create it if not exist&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;APIApplicator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Apply&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;desired&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;runtime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ao&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApplyOption&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;existing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;createOrGetExisting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;desired&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ao&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;existing&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;  &lt;span class=&quot;c&quot;&gt;// 说明是新建&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;// 如果是object已经存在，则发送patch&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;// threeWayMergePatch&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;patch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;patcher&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;patch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;existing&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;desired&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Wrapf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Patch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;desired&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;patch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;cannot patch object&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;引入workflow之后的处理流程&quot;&gt;引入workflow之后的处理流程&lt;/h2&gt;

&lt;p&gt;kubevela/workflow 是一个独立的工作流实现，有workflowInstance（对应一个工作流）/workflowStep/taskRunner（对应一个step）/executor等概念，将appFile 转为workflowInstance 即进入了workflow的工作流程。&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// kubevela/pkg/controller/core.oam.dev/v1alpha2/application/application_controller.go&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Reconciler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Reconcile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctrl&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v1beta1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ObjectKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;      &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Namespace&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Namespace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,},&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;c&quot;&gt;// 解析为appFile&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;appParser&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;appfile&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewApplicationParser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dm&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NewAppHandler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logCtx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;appParser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;appFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;appParser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GenerateAppFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logCtx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PrepareCurrentAppRevision&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logCtx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;appFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FinalizeAndApplyAppRevision&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logCtx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ApplyPolicies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logCtx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;appFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;// appFile 转为 workflowInstance&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;workflowInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;runners&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GenerateApplicationSteps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;logCtx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;appParser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;appFile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;currentAppRev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;// executor 执行 workflowInstance&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;New&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;workflowInstance&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;workflowState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;executor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExecuteRunners&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;authCtx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;runners&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;从 Application 解析为appFile，将部署计划拆分为 runners（一个runner即TaskRunner，包含一个Run方法） 由executor 驱动执行。&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// kubevela/workflow/pkg/executor/workflow.go&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;w&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;workflowExecutor&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ExecuteRunners&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;monitorContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;taskRunners&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TaskRunner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WorkflowRunPhase&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;newEngine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wfCtx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;w&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;taskRunners&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dagMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;// kubevela/workflow/pkg/executor/workflow.go&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;engine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;monitorContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;taskRunners&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TaskRunner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dag&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dag&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;runAsDAG&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;taskRunners&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;taskRunners&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;engine&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;monitorContext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;taskRunners&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;types&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;TaskRunner&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dag&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;runner&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;range&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;taskRunners&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;runner&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;wfCtx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;引入workflow之前，主要由applicationconfiguration controller 负责创建workload 及资源下发。引入workflow 之后，workflow 接管了这个活儿。if you only use components in the Application and do not declare a workflow, KubeVela will automatically create a default workflow for deploying the components when running the Application.&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Parser.GenerateAppFile(ctx context.Context, app *v1beta1.Application)
  GenerateAppFileFromApp(ctx context.Context, app *v1beta1.Application)
    appfile := Parser.newAppfile(appName, ns, app)
    for _, comp := range app.Spec.Components {
      wd, err := Parser.parseWorkload(ctx, comp)
      ...
    }
    Parser.parseWorkflowSteps(ctx, appfile)
      Parser.loadWorkflowToAppfile(ctx, af)
        af.WorkflowSteps, err = step.NewChainWorkflowStepGenerator(
		    &amp;amp;step.RefWorkflowStepGenerator{Client: af.WorkflowClient(p.client), Context: ctx},
		    &amp;amp;step.DeployWorkflowStepGenerator{},
		    &amp;amp;step.Deploy2EnvWorkflowStepGenerator{},
		    &amp;amp;step.ApplyComponentWorkflowStepGenerator{},
		    &amp;amp;step.DeployPreApproveWorkflowStepGenerator{},
	).Generate(af.app, af.WorkflowSteps)
      for _, workflowStep := range af.WorkflowSteps {
        parseWorkflowStep(ctx, af, workflowStep.Type)
      }
    ...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;核心是理解 type=deploy的step，其对应的 WorkflowStepDefinition 定义如下&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;import (
	&quot;vela/op&quot;
)

deploy: {
	alias: &quot;&quot;
	annotations: {}
	attributes: {}
	description: &quot;A powerful and unified deploy step for components multi-cluster delivery with policies.&quot;
	labels: {}
	type: &quot;workflow-step&quot;
}
template: {
	deploy: op.#Deploy &amp;amp; {
		policies:                 parameter.policies
		parallelism:              parameter.parallelism
		ignoreTerraformComponent: parameter.ignoreTerraformComponent
	}
	parameter: {
		//+usage=If set to false, the workflow will suspend automatically before this step, default to be true.
		auto: *true | bool
		//+usage=Declare the policies that used for this deployment. If not specified, the components will be deployed to the hub cluster.
		policies?: [...string]
		//+usage=Maximum number of concurrent delivered components.
		parallelism: *5 | int
		//+usage=If set false, this step will apply the components with the terraform workload.
		ignoreTerraformComponent: *true | bool
	}
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</description>
      </item>
    
      <item>
        <title>数据集管理fluid</title>
        <link>https://felix0080.github.io/2022/10/27/fluid.html</link>
        <guid isPermaLink="true">https://felix0080.github.io/2022/10/27/fluid.html</guid>
        <pubDate>Thu, 27 Oct 2022 00:00:00 +0000</pubDate>
        <description>&lt;h2 id=&quot;简介&quot;&gt;简介&lt;/h2&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#简介&quot; id=&quot;markdown-toc-简介&quot;&gt;简介&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#背景&quot; id=&quot;markdown-toc-背景&quot;&gt;背景&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#思路&quot; id=&quot;markdown-toc-思路&quot;&gt;思路&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#实现&quot; id=&quot;markdown-toc-实现&quot;&gt;实现&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#其它&quot; id=&quot;markdown-toc-其它&quot;&gt;其它&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fluid 项目当前主要关注数据集编排和应用编排这两个重要场景。&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;数据集编排可以&lt;strong&gt;将指定数据集的数据缓存到指定特性的 Kubernetes 节点&lt;/strong&gt;。PS：任务编排 是把任务调度到哪个node 上，数据编排即把 数据调度到哪个node上。
    &lt;ol&gt;
      &lt;li&gt;移动数据，先不考虑 Alluxio 这类组件，&lt;strong&gt;可以认为 fluid 支持将xx 文件 从 远程磁盘/oss/hdfs 移动 k8s 某个node 的xx 目录下&lt;/strong&gt;，不用的时候（比如删除dataset时）再把这个文件清理掉。&lt;/li&gt;
      &lt;li&gt;单论访问远端文件， 直接用 pvc 也可以，但性能不够，pvc 仅仅是把pod 跟远端存储搭个桥梁，访问接口统一了。所以，&lt;strong&gt;我们还是想 将文件挪到 k8s 集群本地来&lt;/strong&gt;，在k8s 集群（一般即计算集群）和 存储集群异地的时候尤其需要，数据移到了本地就得做 生命周期管理，且本地文件仍以pvc 方式提供给pod 访问。&lt;/li&gt;
      &lt;li&gt;为了效率，通常不会直接移动文件，且对于AI 等任务来说，k8s node磁盘也放不下训练数据，所以引入了Alluxio等组件。Fluid 实现了 pvc 接口，让 pod 内可以像使用本地磁盘一样无感知 Fluid 提供元数据和数据分布式分层缓存，以及高效文件检索功能（因为缓存了元数据）。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;应用编排将指定该应用调度到可以或已经存储了指定数据集的节点上。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;pvc 将各种存储融入到k8s ，fluid实际做的是把alluixo类似的分布式缓存系统，以统一的方式做到可以被k8s感知和调度；&lt;/p&gt;

&lt;h2 id=&quot;背景&quot;&gt;背景&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/yGc44Q0qseDG7zy0-PC8gg&quot;&gt;如何基于 Kubernetes 构建云原生 AI 平台&lt;/a&gt;在云上通过云原生架构运行 AI、大数据等任务，可以享受计算资源弹性的优势，但同时也会遇到，计算和存储分离架构带来的数据访问延迟和远程拉取数据带宽开销大的挑战。尤其在 GPU 深度学习训练场景中，迭代式的远程读取大量训练数据方法会严重拖慢 GPU 计算效率。另一方面，&lt;strong&gt;Kubernetes 只提供了异构存储服务接入和管理标准接口（CSI，Container Storage Interface），对应用如何在容器集群中使用和管理数据并没有定义&lt;/strong&gt;。在运行训练任务时，数据科学家需要能够管理数据集版本、控制访问权限、数据集预处理、加速异构数据读取等。但是在 Kubernetes 中还没有这样的标准方案，这是云原生容器社区缺失的重要能力之一。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/S-PBne1BErfGV4elNAajmQ&quot;&gt;Fluid 助力阿里云 Serverless 容器极致提速&lt;/a&gt;举例来说，如果我们想将 AI 推理服务应用部署在 Serverless 架构下，&lt;strong&gt;每个服务应用启动前必须将存放在外部存储系统的 AI 模型首先拉取到本地内存中&lt;/strong&gt;。考虑到近年来 AI 大模型已经成为业界主流，让我们假设这个 AI 模型的大小为 30GB，并且存储于 OSS 对象存储服务中，如果需要同时启动 100 个这样的 AI 推理服务应用，那么总共的数据读取量就是 3000GB。OSS 存储默认的数据访问限速是 10Gbps，这就意味着 100 个应用都需要等待 2400 秒（3000GB * 8 / 10Gbps）才可以真正开始对外提供服务。&lt;/p&gt;

&lt;p&gt;ACK 云原生 AI 套件对“&lt;strong&gt;计算任务使用数据的过程&lt;/strong&gt;”进行抽象，提出了弹性数据集 Dataset 的概念，并作为“first class citizen”在 Kubernetes 中实现。围绕弹性数据集 Dataset，ACK 创建了数据编排与加速系统 Fluid，来实现 Dataset 管理（CRUD 操作）、权限控制和访问加速等能力。Fluid 可以为每个 Dataset 配置缓存服务，既能在训练过程中&lt;strong&gt;将数据自动缓存在计算任务本地，供下轮迭代计算，也能将新的训练任务调度到已存在 Dataset 缓存数据的计算节点上运行&lt;/strong&gt;。再加上数据集预热、缓存容量监控和弹性伸缩，可以大大降低任务远程拉取数据的开销。Fluid 可以将多个不同类存储服务作为数据源（比如 OSS，HDFS）聚合到同一个 Dataset 中使用，还可以接入不同位置的存储服务实现混合云环境下的数据管理与访问加速。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/storage/fluid_dataset.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/GN7FBxOQYJdol6rEBQ2WSA&quot;&gt;重新定义容器化 Serverless 应用的数据访问&lt;/a&gt;&lt;a href=&quot;https://github.com/fluid-cloudnative/fluid&quot;&gt;Fluid&lt;/a&gt; is an open source Kubernetes-native Distributed Dataset Orchestrator and Accelerator for data-intensive applications, such as big data and AI applications.云原生环境与更早的大数据处理框架在设计理念和机制上存在天然分歧。深受Google三篇论文GFS、MapReduce、BigTable影响的Hadoop大数据生态，从诞生之初即信奉和实践“移动计算而不是数据”的理念。因此以Spark，Hive，MapReduce为代表的数据密集型计算框架及其应用为减少数据传输，其设计更多地考虑数据本地化架构。但随着时代的变迁，为兼顾资源扩展的灵活性与使用成本，计算和存储分离的架构在更新兴的云原生环境中大行其道。因此云原生环境里需要类似Fluid这样的一款组件来&lt;strong&gt;补充大数据框架拥抱云原生以后的数据本地性的缺失&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;计算和存储分离的模式使得以往我们认为非常特殊的服务可以被无状态化，可以像正常服务一样被纳入 devops 体系中，而基于 Fluid 的数据编排和加速系统，则是实践计算和存储分离的一个切口。&lt;/p&gt;

&lt;h2 id=&quot;思路&quot;&gt;思路&lt;/h2&gt;

&lt;p&gt;Fluid 把数据集的准备工作从整个流程里单独抽取了出来，利用底层的 K8s 分配需要的资源，例如缓存系统需要的内存和磁盘资源。资源分配出来后，可以选择性的发起元数据和数据的预热工作。等预热工作完成之后，再由 K8s 调度计算资源运行训练任务。训练任务可以选择是否亲和，所谓亲和是让缓存的节点和计算的节点是同一批节点，这个能带来什么样的好处待会儿会讲到。训练完成后，用户可以视情况而定决定何时回收 Fluid 占用的资源，这个过程和计算也是独立的。&lt;/p&gt;

&lt;p&gt;Fluid 的实质是利用计算集群的空闲资源（CPU，Memory，Disk）和特定场景的抽象假设简化问题&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;通过数据分流（Data Offloading）降低中心存储的压力；就近缓存（Tiered Locality Cache）和亲和性调度（Cache Locality Scheduling）提升数据访问性能；Fluid 会把需要访问的数据缓存到与应用 Pod 更近的分布式缓存系统（例如：JindoFS、JuiceFS、Alluxio 等）中，于是，与缓存系统位于同一 VPC 网络的应用 Pod，就能够以远比直接访问中心存储带宽更高的 VPC 内网带宽进行数据访问。&lt;/li&gt;
  &lt;li&gt;在计算资源高并发访问数据的时候，通过自动化扩容缓存集群提供弹性 IO 吞吐能力。由于对接的是分布式缓存系统，所以当单个缓存系统 Worker 节点提供带宽不足时，可将分布式缓存系统扩容，从而实现数据访问带宽的弹性伸缩，匹配 Serverless 场景下的计算资源弹性。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;实现&quot;&gt;实现&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/GsE5AQfHRQzSmFm8ACQgug&quot;&gt;Fluid 工作原理解析&lt;/a&gt;Fluid 的整个架构主要分为两个部分。&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Controller，包括 RuntimeController 及 DatasetController，分别管理 Runtime 和 Dataset 的生命周期，二者共同作用，以 helm chart 为基础，快速搭建出一套完整的分布式缓存系统，通常是 master + worker + fuse 的形式，向上提供服务。master 是缓存系统的核心组件，通常是一个 pod；worker 是组成缓存集群的组件，可以是多个 pod，可以设置个数；fuse 是提供 POSIX 接口服务的组件；&lt;/li&gt;
  &lt;li&gt;调度器，在有缓存的情况下，调度器会根据 worker 的节点信息，使得上层应用 pod 尽可能调度到有缓存的节点。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;整个过程如下，Dataset Controller 监听 Dataset，Runtime Controller 监听对应的 Runtime，当二者一致时，RuntimeController 会启动 Engine，Engine 创建出对应的 Chart（Runtime Controller 启动 JuiceFS 的环境的方法是启动一个 helm chart，其渲染的 values.yaml 以 ConfigMap 的形式保存在集群中），里面包含 Master、Worker、FUSE 组件。同时，Runtime Controller 会定期同步数据（如总数据量、当前使用数据量等）状态更新 Dataset 和 Runtime 的状态信息。&lt;/p&gt;

&lt;p&gt;下面以 JuiceFS 为例，搭建一套 Fluid 环境，搭建好后组件如下：&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ kubectl -n fluid-system get po
NAME                                         READY   STATUS    RESTARTS   AGE
csi-nodeplugin-fluid-fczdj                   2/2     Running   0          116s
csi-nodeplugin-fluid-g6gm8                   2/2     Running   0          117s
csi-nodeplugin-fluid-twr4m                   2/2     Running   0          116s
dataset-controller-5bc4bcb77d-844rz          1/1     Running   0          116s
fluid-webhook-7b4f48f647-s8c9w               1/1     Running   0          116s
juicefsruntime-controller-5d95878575-hj785   1/1     Running   0          116s
# JuiceFS 相对其他 Runtime 的特殊之处在于其没有 master 组件
jfsdemo-worker-0   1/1     Running   0              58m
jfsdemo-worker-1   1/1     Running   0              58m
# FUSE 组件
jfsdemo-fuse-pd9zq   1/1     Running   0              25s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;各个组件的作用：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;dataset-controller：管理 Dataset 的生命周期&lt;/li&gt;
  &lt;li&gt;juicefsruntime-controller：管理 JuiceFSRuntime 生命周期，并快速搭建 JuiceFS 环境；&lt;/li&gt;
  &lt;li&gt;fluid-webhook：实现 Fluid 应用的缓存调度工作；&lt;/li&gt;
  &lt;li&gt;csi-nodeplugin：实现各引擎的挂载路径与应用之间的连接工作；&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;在 Fluid 中，数据加速对应的是 DataLoad，也是一个 CRD，DatasetController 负责监听该资源，根据对应的 DataSet 启动 Job，执行数据预热操作。同时 Runtime Controller 向 Worker 同步缓存的数据信息，并更新 Dataset 的状态。&lt;/p&gt;

&lt;p&gt;大致理解：Dataset 表述了要访问哪些数据源文件，对pod 提供了pv和pvc ，对下封装了缓存、数据集调度等能力。&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;fluid 开发了对应的csi ，Dataset 和 Runtime Bound 之后，fluid 会自动创建与 Dataset 同名的pvc 和 pvc&lt;/li&gt;
  &lt;li&gt;pod 调度到一个node 上之后，因为pod 声明了和dataset 同名的pvc，fluid csi 在node 上准备一个目录（pv），走csi 流程挂到pod 里。&lt;/li&gt;
  &lt;li&gt;此外 在node上启动一个fuse pod（或者说，fuse pod 将挂载点暴露在宿主机上，然后 csi 将挂载点 bind 到应用 pod 里）。pod 里程序对这个目录的读写请求 都转给了fuse pod，fuse 将文件io 转为网络io 访问真实的runtime。PS：&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/storage/fluid_overview.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;其它&quot;&gt;其它&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/S-PBne1BErfGV4elNAajmQ&quot;&gt;Fluid 助力阿里云 Serverless 容器极致提速&lt;/a&gt; 展示了创建Dataset 访问oss 以及进行数据预热等操作。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/W80pcsY6eQiV7jMXzh4Ifw&quot;&gt;在混合云下，我们将Kubernetes与Fluid结合后性能提升了30%&lt;/a&gt;&lt;/p&gt;
</description>
      </item>
    
      <item>
        <title>应用管理平台kubevela</title>
        <link>https://felix0080.github.io/2022/10/23/kubevela.html</link>
        <guid isPermaLink="true">https://felix0080.github.io/2022/10/23/kubevela.html</guid>
        <pubDate>Sun, 23 Oct 2022 00:00:00 +0000</pubDate>
        <description>&lt;h2 id=&quot;简介&quot;&gt;简介&lt;/h2&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#简介&quot; id=&quot;markdown-toc-简介&quot;&gt;简介&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#为什么要用-kubevela&quot; id=&quot;markdown-toc-为什么要用-kubevela&quot;&gt;为什么要用 KubeVela？&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#使用&quot; id=&quot;markdown-toc-使用&quot;&gt;使用&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#设计&quot; id=&quot;markdown-toc-设计&quot;&gt;设计&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#从appfile-到-k8s-object&quot; id=&quot;markdown-toc-从appfile-到-k8s-object&quot;&gt;从appfile 到 k8s object&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#从k8s-object到-appfile&quot; id=&quot;markdown-toc-从k8s-object到-appfile&quot;&gt;从k8s object到 appfile&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#与helm对比&quot; id=&quot;markdown-toc-与helm对比&quot;&gt;与helm对比&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#将yaml拆分成模版与参数配置&quot; id=&quot;markdown-toc-将yaml拆分成模版与参数配置&quot;&gt;将yaml拆分成模版与参数/配置&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#应用模型对参数配置进行分类与规范&quot; id=&quot;markdown-toc-应用模型对参数配置进行分类与规范&quot;&gt;应用模型——对参数/配置进行分类与规范&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#appfile&quot; id=&quot;markdown-toc-appfile&quot;&gt;appfile&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#实现&quot; id=&quot;markdown-toc-实现&quot;&gt;实现&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#模块定义definition&quot; id=&quot;markdown-toc-模块定义definition&quot;&gt;模块定义（Definition）&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#自定义插件&quot; id=&quot;markdown-toc-自定义插件&quot;&gt;自定义插件&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#应用&quot; id=&quot;markdown-toc-应用&quot;&gt;应用&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;脉络：k8s yaml太多 ==&amp;gt; kustomize/helm（helm values.yaml太随意了） ==&amp;gt; 弄一个paas平台封装一下k8s==&amp;gt; 可扩展性（封装的标准化、可复用、IaC ） ==&amp;gt; OAM&lt;/p&gt;

&lt;h2 id=&quot;为什么要用-kubevela&quot;&gt;为什么要用 KubeVela？&lt;/h2&gt;

&lt;p&gt;我个人之前的感受：paas自己写个发布系统对接k8s开发下就行了，最多就是后来灰度发布一类，k8s deployment不好使，出现了openkruise也就够了，oam那一套概念有点多余（就像k8s 刚出的时候 pod 多个容器 有点多余，不如marathon简单）。如果大家直接对接 k8s 做paas，那么各个公司paas开发人员的努力都是发散的，一家一个样，能力无法沉淀。有了oam 在应用层面先提一套标准，然后kubevela 确定核心细节，大家以 kubevela 为核心 复用能力 并进行简单的差异化，那么就有可能 把paas的能力沉淀下来。从云厂商的视角来看，如果去卖一个paas 产品，如果没有kubevela 这套东西，根本就不可能商业化，&lt;strong&gt;case by case的为每个公司开发paas 产品，商业上根本走不通&lt;/strong&gt;。反过来说，我们如何去界定 不同公司paas平台差异和共通的部分，就需要抽象，以及将抽象具象/渲染为细节的机制。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://kubevela.io/zh/docs/&quot;&gt;为什么要用 KubeVela？&lt;/a&gt;云原生技术的发展趋势正在朝着利用 Kubernetes 作为公共抽象层来实现高度一致的、跨云、跨环境的的应用交付而不断迈进。然而，尽管 Kubernetes 在统一底层基础架构细节方面表现出色，它并没有在&lt;strong&gt;混合的分布式部署环境&lt;/strong&gt;之上提供应用层的软件交付模型和抽象。我们已经看到，这种缺乏统一上层抽象的软件交付过程，不仅降低了生产力、影响了用户体验，甚至还会导致生产中出现错误和故障。&lt;/p&gt;

&lt;p&gt;然而，为现代微服务应用的交付过程建模是一个高度碎片化且充满挑战的事情。到目前为止，绝大多数试图解决上述问题的技术方案，要么过于简单以致于无法覆盖实际生产使用中的问题，要么过于复杂难以落地使用。云原生带来的基础设施能力爆发式增长也决定了&lt;strong&gt;新一代的应用管理平台不能以硬编码的方式做能力的集成和 UI 的构建&lt;/strong&gt;，除了满足基础的功能和场景，平台本身的扩展能力成为了新时代应用管理平台的核心诉求。这就意味着平台不仅要简单易用，还要能够随着应用交付和管理的需求复杂度提升能够不断扩张，能够让开发者自助式的接入和使用，充分享受云原生生态的红利。&lt;/p&gt;

&lt;p&gt;这也是 KubeVela 出现的核心价值：它既能够简化面向混合环境（多集群/多云/混合云/分布式云）的应用交付过程；同时又足够灵活可以随时满足业务不断高速变化所带来的迭代压力。它本身是一个面向混合交付环境同时又高可扩展的应用交付引擎，满足平台构建者的扩展和自建需求；同时又附加了一系列开箱即用的扩展组件，能够让开发者自助式的开发、交付云原生应用。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://developer.aliyun.com/article/779485?spm=a2c6h.14164896.0.0.4f7875ffH4j9hq&quot;&gt;深入解读：KubeVela 与 PaaS 有何不同？&lt;/a&gt;&lt;strong&gt;kubevela 与 PaaS 最大的区别在可扩展性上&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;PaaS 的用户体验虽好，但却往往是不可扩展的。要支持一个新的能力，都必须对 PaaS 进行一轮开发，而且由于先前的一些假设和设计，甚至很可能需要大规模的重构。举个例子，我有一个 PaaS 系统，它所有的应用都是通过 Deployment 来执行的，那么这个 PaaS 的发布、扩容等功能，也都会直接按照 Deployment 来进行实现。而现在，用户提出了原地升级的诉求，需要让这个 PaaS 再支持 CloneSet，那整套系统很可能就得掀翻重来。再到运维能力这一侧，这个问题会更加严重，比如现在这个 PaaS 支持的是蓝绿发布策略，那么它跟流量管理、监控系统等依赖之间，都是需要大量交互和集成的。而现在我们要让 PaaS 支持一个新的策略叫做“金丝雀”发布，那么所有的这些交互和执行逻辑基本全得重重构一遍，工作量是巨大的。&lt;/li&gt;
  &lt;li&gt;相比之下，KubeVela 的目标从一开始就是利用整个 Kubernetes 生态作为自己的“插件中心”，并且“有意”把它的每一个内置能力都设计成独立的、可插拔的插件。这种高度可扩展的模型，背后其实有着精密的设计与实现。比如，KubeVela 如何确保某个完全独立的 Trait 一定能够绑定于某种 Workload Type？如何检查这些相互独立的 Trait 间是否存在冲突？这些挑战正是 Open Application Model（OAM）作为 KubeVela 模型层的起到的关键作用，一言以蔽之：OAM 是一个高度可扩展的应用定义与能力装配模型。&lt;/li&gt;
  &lt;li&gt;KubeVela 提倡的是一种面向未来的云原生平台架构，这种架构认为：
    &lt;ol&gt;
      &lt;li&gt;应用平台本身架构彻底模块化，其所有的能力都是可插拔的，而平台核心框架通过模型层提供标准化的能力封装与装配流程。&lt;/li&gt;
      &lt;li&gt;该流程能够无缝接入云原生生态中的任何应用管理能力，使得平台工程师完全专注于能力本身的研发和基于该模型的能力封装过程，使平台团队在为用户带来简单易用的平台层抽象的同时，快速、敏捷地响应用户千变万化的应用管理诉求。&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;不再配置漂移：除了扩展性和效率以外，许多围绕 IaC（Infrastructure-as-Code） 的工具都会引发生产环境和配置中心数据不一致的问题，业界称之为“配置漂移”，引起配置漂移的核心原因往往来自于生产环境的配置修改有多个来源、平台对配置的覆盖不完整等。KubeVela 通过一个 Application 对象涵盖了所有应用涉及的配置、并通过 Kubernetes 控制循环 来维护状态，并基于此始终面向终态维护配置的一致性、消除配置漂移的问题，且保留基于 IaC 模式的扩展性和灵活性。&lt;/p&gt;

&lt;h2 id=&quot;使用&quot;&gt;使用&lt;/h2&gt;

&lt;p&gt;KubeVela 的核心是将应用部署所需的所有组件和各项运维动作，描述为一个统一的、与基础设施无关的&lt;strong&gt;部署计划&lt;/strong&gt;，进而实现在混合环境中标准化和高效率的应用交付。每一个应用部署计划都由四个部分组成，分别是组件、运维能力、部署策略和工作流。其格式如下：&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;core.oam.dev/v1beta1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Application&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;name&amp;gt;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;# 定义一个应用包含的待交付制品（二进制、Docker 镜像、Helm Chart...）或云服务&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;component name&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;component type&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&amp;lt;parameter values&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;traits&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;# 随时绑定给待部署组件的、模块化、可拔插的运维能力，比如：副本数调整（手动、自动）、数据持久化、 设置网关策略、自动设置 DNS解析等。 &lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;trait type&amp;gt;&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;&amp;lt;traits parameter values&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;component name&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;component type&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&amp;lt;parameter values&amp;gt;&lt;/span&gt;    
  &lt;span class=&quot;na&quot;&gt;policies&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;     &lt;span class=&quot;c1&quot;&gt;# 定义指定应用交付过程中的策略，比如多集群部署的差异化配置、资源放置策略、安全组策略、防火墙规则、SLO 目标。有的 policy 需要配合 workflowStep 生效。&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;policy name&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;policy type&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;s&quot;&gt;&amp;lt;policy parameter values&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;workflow&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;     &lt;span class=&quot;c1&quot;&gt;# 工作流由多个步骤组成，允许用户自定义应用在某个环境的交付过程。典型的工作流步骤包括人工审核、数据传递、多集群发布、通知等。&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;step name&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;step type&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&amp;lt;step parameter values&amp;gt;&lt;/span&gt;   
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;oam 单从格式看，&lt;strong&gt;划分为components/traits/policies/workflow几个大类，每个类别包含 name/type/properties 字段&lt;/strong&gt;，使用时，更多是个性化指定properties。kebevela 作为中间层，将这些 运维参数 进行组装渲染，变成k8s的实际资源。&lt;/p&gt;

&lt;h2 id=&quot;设计&quot;&gt;设计&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://developer.aliyun.com/article/779485?spm=a2c6h.14164896.0.0.4f7875ffH4j9hq&quot;&gt;深入解读：KubeVela 与 PaaS 有何不同？&lt;/a&gt;在架构上，KubeVela 只有一个 controller （即ApplicationController）并且以插件的方式运行在 Kubernetes 之上，为 Kubernetes 带来了面向应用层的抽象，以及以此为基础的面向用户的使用界面，即Appfile。Appfile 乃至 KubeVela 运行机制背后的核心，则是其能力管理模型 Open Application Model (OAM) 。基于这个模型，KubeVela 为系统管理员提供了一套基于&lt;strong&gt;注册与自发现的能力装配流程&lt;/strong&gt;，来接入 Kubernetes 生态中的任意能力到 KubeVela 中，从而以“一套核心框架搭配不同能力”的方式，适配各种使用场景（比如 AI PaaS，数据库 PaaS 等等）。&lt;/p&gt;

&lt;p&gt;具体操作上，作为系统管理员或者平台开发者，上述能力装配流程允许他们把任意的 Kubernetes API 资源（含 CRD）以及对应的 Controller  作为“能力”一键注册到 KubeVela 中（即注册 XXDefinition），然后通过 CUE 模板语言将这些能力封装成用户可用的抽象（即成为 Appfile 中的一部分）。&lt;/p&gt;

&lt;h3 id=&quot;从appfile-到-k8s-object&quot;&gt;从appfile 到 k8s object&lt;/h3&gt;

&lt;p&gt;任何应用交付需求 在kubevela 内核中实际上都会被表达为 一个基于cue 语言的有向无环图DAG。而Application 对象实际上只是这个DAG 面向用户侧的一种UI而已：一旦提交，它就会被kubevela 渲染成上述cue 代码然后使用kubernetes 来执行这个DAG。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://developer.aliyun.com/article/783169&quot;&gt;源码解读：KubeVela 是如何将 appfile 转换为 K8s 特定资源对象的&lt;/a&gt; PS：基于kubevela引入workflow之前的版本&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/kubernetes/kubevela_model.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;从k8s-object到-appfile&quot;&gt;从k8s object到 appfile&lt;/h3&gt;

&lt;p&gt;接下来，我们就来 Demo 一下如何将 kubewatch 这个社区中的告警机制直接插入到 KubeVela 中作为一个告警 Trait 来使用：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;将平台能力注册为 OAM 对象，首先，你需要确定 CRD 所表示的能力是对应一个 Workload Type 还是 Trait？这里的区别在于 &lt;strong&gt;Workload Type 指的是如何运行你的代码。而 Trait 指的是如何运维、管理或者操作已经运行起来的代码实例&lt;/strong&gt;。而 KubeWatch 作为一种告警机制，自然作为 Trait 来使用的。这时候，我们就可以通过写一个 TraitDefinition yaml 来将它注册：&lt;/p&gt;

    &lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;core.oam.dev/v1alpha2&lt;/span&gt;
 &lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TraitDefinition&lt;/span&gt;
 &lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
   &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;kubewatch&lt;/span&gt;
   &lt;span class=&quot;na&quot;&gt;annotations&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; 
     &lt;span class=&quot;s&quot;&gt;definition.oam.dev/description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;watch&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;resource&quot;&lt;/span&gt;
   &lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
     &lt;span class=&quot;na&quot;&gt;appliesToWorkloads&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# trait 可以附加到指定的工作负载类型&lt;/span&gt;
       &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;*&quot;&lt;/span&gt;
     &lt;span class=&quot;na&quot;&gt;definitionRef&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
       &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;kubewatches.labs.bitnami.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;编写 CUE template 来封装对外暴露接口。上一步完成，KubeVela 就已经注册完毕在 KubeVela 平台中可用了。但接下来我们还需要将它暴露给用户使用，所以需要定义这个能力对外的使用接口。实际上，大多数社区能力虽然很强大，但对于最终用户来都比较复杂，学习和上手非常困难。所以在 KubeVela 中，它允许平台管理员&lt;strong&gt;对能力做进一步封装以便对用户暴露简单易用的使用接口&lt;/strong&gt;，在绝大多数场景下，这些使用接口往往只有几个参数就足够了。在能力封装这一步，KubeVela 选择了 CUE 模板语言，来连接用户界面和后端能力对象，并且天然就支持完全动态的模板绑定（即变更模板不需要重启或者重新部署系统）。下面就是 KubeWatch Trait 的模板例子：&lt;/p&gt;

    &lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;s&quot;&gt;...&lt;/span&gt;
 &lt;span class=&quot;s&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
   &lt;span class=&quot;na&quot;&gt;extension&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
     &lt;span class=&quot;na&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;|&lt;/span&gt;
       &lt;span class=&quot;s&quot;&gt;output: {&lt;/span&gt;
         &lt;span class=&quot;s&quot;&gt;apiVersion: &quot;labs.bitnami.com/v1alpha1&quot;&lt;/span&gt;
         &lt;span class=&quot;s&quot;&gt;kind: &quot;kubewatch&quot;&lt;/span&gt;
         &lt;span class=&quot;s&quot;&gt;spec: handler: webhook: url: parameter.webhook&lt;/span&gt;
       &lt;span class=&quot;s&quot;&gt;}&lt;/span&gt;
       &lt;span class=&quot;s&quot;&gt;parameter: {&lt;/span&gt;
         &lt;span class=&quot;s&quot;&gt;webhook: string&lt;/span&gt;
       &lt;span class=&quot;s&quot;&gt;}&lt;/span&gt;
      
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;将这个模板放到 Definition 文件中并 ` kubectl apply -f` 到 Kubernetes 中，KubeVela 就会自动识别和处理相关输入。这时候，用户就可以直接在 Appfile 中声明使用刚加进来的能力了，比如发送告警信息到指定的 Slack channel：&lt;/p&gt;

    &lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;testapp&lt;/span&gt;
 &lt;span class=&quot;na&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
   &lt;span class=&quot;na&quot;&gt;testsvc&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; 
     &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;webservice&lt;/span&gt;
     &lt;span class=&quot;na&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;crccheck/hello-world&lt;/span&gt;
     &lt;span class=&quot;na&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;8000&lt;/span&gt;
     &lt;span class=&quot;na&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; 
       &lt;span class=&quot;na&quot;&gt;domain&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;testsvc.example.com&lt;/span&gt;
     &lt;span class=&quot;na&quot;&gt;kubewatch&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
       &lt;span class=&quot;na&quot;&gt;webhook&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https://hooks.slack.com/&amp;lt;your-token&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;可以看到，这个 kubewatch 的配置是我们通过三方扩展进来的一个新的能力，通过 KubeVela 平台&lt;strong&gt;管理 Kubernetes 扩展能力&lt;/strong&gt;就是这么简单快速。有了 KubeVela，平台开发人员就可以简单快速地在 Kubernetes 上搭建起一个 PaaS，且能够将任何一个 Kubernetes 能力快速封装成面向最终用户的上层抽象。&lt;/p&gt;

&lt;h2 id=&quot;与helm对比&quot;&gt;与helm对比&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/frkBEwZSpNQstkwC6a4SUg&quot;&gt;基于 KubeVela 与 Kubernetes 打造“无限能力”的开放 PaaS&lt;/a&gt; 经典。&lt;/p&gt;

&lt;p&gt;当集群中存在数千个工作负载和相关资源时，操作人员很难识别逻辑关系，并根据其内部关系进行准确且符合业务需要的管理。&lt;/p&gt;

&lt;h3 id=&quot;将yaml拆分成模版与参数配置&quot;&gt;将yaml拆分成模版与参数/配置&lt;/h3&gt;

&lt;p&gt;Helm 的抽象能力是基于 Go 的 template，而 KubeVela 对于抽象的实现是则基于 DCL（DataConfiguration Language）。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/kubernetes/kubevela_cue.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;首先用户填抽象数据，接着通过 CUE 的模板注册在 KubeVela 的服务端，然后用户填的数据和模板直接合并，最后生成一个完整的 K8sYAML。这种过程看起来和 Helm 的 Go template 以及 Helm 的 Values 很像，但是 CUE 有很多强大的功能，比如：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;专注于操纵数据，而不是写代码&lt;/li&gt;
  &lt;li&gt;完全兼容 JSON&lt;/li&gt;
  &lt;li&gt;简单直观：Schema 和 Value 语法一致&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/kubernetes/kubevela_cue_to_object.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;以上方为例，首先定义 Workload， WorkloadDefinition 实际上就是一个模板，这个模板讲的是工作负载里一个 Deployment 模板，Deployment 下面是我们构建出来的参数 Parameter，它包含两个参数 Image 和 CMD；之后相当于把这个参数填到了 ③ 上面的工作负载中，它的类型 type=worker，也就是 ① 里面的 name=worker。&lt;/p&gt;

&lt;p&gt;同时还有一些抽离出来的参数，就是底下的 Deployment 里面，比如 Sidecar，把它抽出来单独使用变成一个 Trait，Trait 里面可以写一些内容如 NAME 或 Image。如果不加 Trait，单独使用 Worker 也是完全可以的。同时这个 Trait 也可以给到其他基于 Deployment 或带有 “spec：template:spec:containers” 这种数组模式的工作负载使用（PS：&lt;strong&gt;trait 原来是这么生效的&lt;/strong&gt;）。在 KubeVela 中，用户只要简单填写参数就会拿到这两个模板，然后在 KubeVela 中做 Merge，即 Patch 的合并，最后生成 Deployment。&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;helm&lt;/th&gt;
      &lt;th&gt;kubevela&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;模版存在形式&lt;/td&gt;
      &lt;td&gt;yaml文件&lt;/td&gt;
      &lt;td&gt;xxDefinition&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;参数/配置存在形式&lt;/td&gt;
      &lt;td&gt;yaml文件&lt;/td&gt;
      &lt;td&gt;Application，划分为components/traits/policies/workflow几个大类，&lt;br /&gt;每个类别包含 name/type/properties 字段&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;参数配置 + 模版如何翻译成k8s yaml&lt;/td&gt;
      &lt;td&gt;go template&lt;br /&gt;简单的参数替换&lt;/td&gt;
      &lt;td&gt;cue，kubevela 也支持helm&lt;br /&gt; 翻译由controller 进行，有时不单单是参数替换&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;部署&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;helm install -f values.yaml&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;kubectl apply -f application.yaml&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;能力复用&lt;/td&gt;
      &lt;td&gt;chart 应用商店&lt;/td&gt;
      &lt;td&gt;组件中心，应用市场&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;应用模型对参数配置进行分类与规范&quot;&gt;应用模型——对参数/配置进行分类与规范&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/kubernetes/helm_template.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Helm 大家比较熟悉，它可以把不同的 YAML 文件写成模板，模板里面能抠出来一些 Values，然后填写一些 Values 的信息。但是这里 Helm 有一个问题，就是组装完后 Helm 整体会成为一个黑盒，用户无法获得 Helm 里整体的状态。&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Helm 安装完后，它把这些抽象的能力变成K8s原始的资源，但这些资源是否安装成功，Helm 很难获得感知。&lt;/li&gt;
  &lt;li&gt;同时用户如果想做统一的能力，如要把 Rollout 抽出来的概念变成公共的功能给 WebService 与 Knative Revision 使用，这种情况在 Helm 中无法实现（rollout 参数表达了回滚的镜像、个数等等，但针对不同对象，比如 对deployment 和 statefulset 做回滚差异很大，或者说最后生成的k8s yaml 差别很大），包括后期做统一的监控、统一的发布管理、统一的日志管理、统一的扩缩容等，Helm 均无法实现。但是在 KubeVela 中，基于 OAM 模型提供的公共标准，就可以实现一些公共的能力。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;所以说，像&lt;strong&gt;状态回流&lt;/strong&gt;和公共能力抽象是HELM无法做到的两点，但用KubeVela可以很容易做到。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/kubernetes/oam_abstract.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;抽象是构建云原生应用平台的基础，抽象本质上分为这三种类型：转换抽象（一变一）、组合抽象（一变多）和拆分抽象（多变一），以及抽象后的状态回流。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;组合抽象，以一个网络访问的服务为例，底下由 Deployment 与 Service 组合构建而成，用户希望拿到工作负载 WebService，这样一个组合的抽象可以给用户提供服务。&lt;/li&gt;
  &lt;li&gt;拆分抽象，当我们在灰度发布时，K8s 生态经常会出现一些像 ArgoRollout 的发布能力，这些发布能力可能有个问题，就是把所有的概念全都糅杂在一起，有时用户在一开始使用时不关心的发布策略（如 Rollout）也在其中。“拆分抽像”的能力可以使用户在使用时把这些概念拆开来使用，在单独使用 Workload 部分时，应用也能正常运行，而不是说一定要填完 ArgoRollout。同时未来灰度发布时，用户如果希望有金丝雀的发布策略，KubeVela 也能将 Workload 与 Rollout 组装成 ArgoRollout。&lt;/li&gt;
  &lt;li&gt;转换抽象，在K8s原来的概念中，有的部分用户并不关心，如 Deployment 里的 labelSelectors。KubeVela 可以做一层转换，就像 Knative Revision，去掉多余的参数，封装出干净的模型。&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;appfile&quot;&gt;appfile&lt;/h3&gt;

&lt;p&gt;除了构建抽象，如何让用户使用也是一个非常关键的问题。在这里 KubeVela 给大家提供一个用户层的视角，对于不关心 K8s 细细的业务应用研发者， KubeVela 提供了 AppFile 的概念。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/kubernetes/kubevela_application.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如上图所示，Appfile 里会包含镜像的构建、镜像如何启动、端口是怎样的、资源有多少等信息。同时包含了一些 Trait 的参数，例如用户希望能够对外的访问提供一个域名、自动扩缩容的参数等，大家可以看到它是围绕应用展开的，没有一些多余的概念。同时它是一个文件，当用户在代码仓库里做统一变更时，体验效果非常好；同时它和应用环境没有关系，可以自动适配任意 K8s 集群与部署环境，并且具有很好的扩展能力。PS：其实单论把这个解析为 k8s yaml， helm 也可以，但是helm 仅仅是“翻译”为yaml，再进一步的工作比如状态回流等就很难做了。&lt;/p&gt;

&lt;h2 id=&quot;实现&quot;&gt;实现&lt;/h2&gt;

&lt;p&gt;KubeVela 本身主要由如下几个部分组成:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;核心控制器 为整个系统提供核心控制逻辑，完成诸如编排应用和工作流、修订版本快照、垃圾回收等等基础逻辑&lt;/li&gt;
  &lt;li&gt;模块化能力控制器 负责对 X-Definitions 对象进行注册和管理。&lt;/li&gt;
  &lt;li&gt;插件控制器 负责注册和管理 KubeVela 运行所需要的第三方插件，比如 VelaUX、 Flux、Terraform 组件等等。&lt;/li&gt;
  &lt;li&gt;UI 控制台和 CLI UI 控制台服务于希望开箱即用的开发者用户，CLI 适用于集成 KubeVela 和终端管理的用户。&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;模块定义definition&quot;&gt;模块定义（Definition）&lt;/h3&gt;

&lt;p&gt;每个应用程序可能包含多个原子功能单元，即 Component（组件），每个 Component 可以由多个 Trait 修饰。这些特性 Trait 不能独立工作。这些功能附加到组件并用作装饰。没有它，应用程序的主要目的不会改变，但会不完整。如果我们想定义一些与运行 Component 没有明确一对一关系的应用程序级别的行为或策略，需要更多的应用程序定义。截至目前，OAM 还没有对这部分建模提出决定性的解决方案。已经提出了包括 Scope、Policy、Workflow、Scope 在内的一些概念，用于定义应用程序的使用范围。Policy 用于描述常见的策略和行为。Workflow 侧重于应用程序的交付方面。&lt;/p&gt;

&lt;p&gt;KubeVela 将这些抽象基础建立在 ComponentDefinitions 和 TraitDefinitions 之上，&lt;strong&gt;以便更好地实现重用&lt;/strong&gt;。与直接使用 Helm Chart 对整个应用程序进行模板化相比，这为平台构建者提供了更精细的选择。这反过来又为平台工程师和业务研发人员之间的职责划分提供了更清晰的界限：平台工程师负责 X-Definitions，业务研发负责应用程序。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://kubevela.net/zh/docs/next/platform-engineers/oam/x-definition#%E6%A6%82%E8%BF%B0&quot;&gt;模块定义（Definition）&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Application 有很多声明“类型的字段”，如组件类型、运维特征类型、应用策略类型、工作流节点类型等，这些类型实际上就是 OAM 模型的模块定义（X-Definition），这些模块在 KubeVela 中全部都是基于 CUE 的可编程模块。当前 OAM 模型支持的模块定义（X-Definition）包括组件定义（ComponentDefinition），运维特征定义（TraitDefinition）、应用策略定义（PolicyDefinition），工作流节点定义（WorkflowStepDefinition）等。&lt;/p&gt;

&lt;p&gt;组件定义（ComponentDefinition）的设计目标是允许平台管理员将任何类型的可部署制品封装成待交付的“组件”。只要定义好之后，这种类型的组件就可以被用户在部署计划（Application）中引用、实例化并交付出去。&lt;/p&gt;

&lt;p&gt;Trait与 Component 的主要区别之一是，&lt;strong&gt;某些类型的 Trait 需要对 Component Definition 所抽象的资源进行修改&lt;/strong&gt;。例如，为了暴露端口，我们需要同时添加 Service 对象并在 Deployment 规范中暴露端口。&lt;strong&gt;CUE 本身并没有提供这样的方法，但是KubeVela 会以某种方式对 CUE 进行扩展来实现了这一功能&lt;/strong&gt;。除静态渲染外，KubeVela 的抽象层还有运行时感知能力。这意味着渲染逻辑可以根据运行环境进行更改，就像它当前运行的 Kubernetes 版本一样。当 KubeVela 用作跨集群的统一控制平面时，这一点尤其有用。&lt;/p&gt;

&lt;p&gt;如何定义运维能力之间的冲突关系与协作关系？TraitDefinition 中包含 conflictsWith 字段，用来描述该trait 与哪些trait 冲突。运维特征定义的格式和字段作用如下：&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;core.oam.dev/v1beta1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TraitDefinition&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;运维特征定义名称&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;annotations&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;definition.oam.dev/description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;功能描述说明&amp;gt;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;definition&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;运维能力对应的 Kubernetes 资源组&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;运维能力对应的 Kubernetes 资源类型&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;workloadRefPath&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;运维能力中对于工作负载对象的引用字段路径&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;podDisruptive&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;运维能力的参数更新会不会引起底层资源（pod）重启&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;manageWorkload&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;是否由该运维特征管理工作负载&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;skipRevisionAffect&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;该运维特征是否不计入版本变化的计算&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;appliesToWorkloads&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;运维特征能够适配的工作负载名称&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;conflictsWith&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;lt;与该运维特征冲突的其他运维特征名称&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;自定义插件&quot;&gt;自定义插件&lt;/h3&gt;

&lt;p&gt;如何基于 CUE 语言定义出功能强大的“能力模块”，然后把这些模块安装到 KubeVela 中？ &lt;a href=&quot;https://kubevela.net/zh/docs/next/platform-engineers/addon/intro&quot;&gt;自定义插件&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;应用&quot;&gt;应用&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/pTYK_WsdSwwiv7_Dzkxh_w&quot;&gt;基于 KubeVela 的机器学习实践&lt;/a&gt; 未细读，好神奇，初步看只是将 Application ==&amp;gt; TFJob&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/IbMTP5q-522oGpE1rRF1zw&quot;&gt;Kubevela 下的多集群应用&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/BomikFWWg4v-5HkvMoOnYw&quot;&gt;Kubevela 获得轻量级的云原生应用控制平面&lt;/a&gt; UI 都提供好了，支持多集群、workflow等。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/D7fgxHjwXj8URbfvOKbaPQ&quot;&gt;让应用交付和管理统一：KubeVela 亮点功能及核心技术回顾&lt;/a&gt;KubeVela 是一个面向现代化应用的交付与管理平台，它使得应用在面向混合、多云环境中的交付和运维变得更简单、高效和可靠。它有以下三个主要功能：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;基础设施无关。KubeVela 能够将你的云原生应用程序部署到各种目的地，如 Kubernetes 多集群、不同的云平台和边缘设备。&lt;/li&gt;
  &lt;li&gt;可编程。KubeVela 具有用于建模应用程序和交付过程的抽象层。这些抽象层允许用户使用可编程方式构建更高级别的可重用模块，用于应用程序交付，并在 KubeVela 系统中集成任意第三方项目（如 FluxCD、Crossplane、Istio、Prometheus）。&lt;/li&gt;
  &lt;li&gt;以应用为中心。KubeVela 面向业务应用开发专门设计，有丰富的工具生态，包括 CLI、UI、GitOps、可观测性等，为应用程序的交付和运维增加了大量开箱即用的功能。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/GxG5QDvnWWSx2B78BQUS7g&quot;&gt;KubeVela 1.7 版本解读：接管你的已有工作负载&lt;/a&gt; 未细读。&lt;/p&gt;

</description>
      </item>
    
      <item>
        <title>karmada支持crd</title>
        <link>https://felix0080.github.io/2022/10/21/karmada_resource_interpreter.html</link>
        <guid isPermaLink="true">https://felix0080.github.io/2022/10/21/karmada_resource_interpreter.html</guid>
        <pubDate>Fri, 21 Oct 2022 00:00:00 +0000</pubDate>
        <description>&lt;h2 id=&quot;简介&quot;&gt;简介&lt;/h2&gt;

&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#简介&quot; id=&quot;markdown-toc-简介&quot;&gt;简介&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#背景&quot; id=&quot;markdown-toc-背景&quot;&gt;背景&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#整体设计&quot; id=&quot;markdown-toc-整体设计&quot;&gt;整体设计&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#resource-interpreter-webhook-代码分析&quot; id=&quot;markdown-toc-resource-interpreter-webhook-代码分析&quot;&gt;Resource Interpreter Webhook 代码分析&lt;/a&gt;    &lt;ul&gt;
      &lt;li&gt;&lt;a href=&quot;#karmada-侧&quot; id=&quot;markdown-toc-karmada-侧&quot;&gt;karmada 侧&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;#interpreter-webhook-侧&quot; id=&quot;markdown-toc-interpreter-webhook-侧&quot;&gt;interpreter webhook 侧&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#可配置解释器&quot; id=&quot;markdown-toc-可配置解释器&quot;&gt;可配置解释器&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;背景&quot;&gt;背景&lt;/h2&gt;

&lt;p&gt;In the progress of propagating a resource from karmada-apiserver to member clusters, Karmada needs to know the resource definition. For Kubernetes native resources, Karmada knows how to parse them, but for custom resources defined by CRD, as lack of the knowledge of the resource structure, they can only be treated as normal resources. Therefore, the advanced scheduling algorithms cannot be used for them.The &lt;a href=&quot;https://karmada.io/docs/next/userguide/globalview/customizing-resource-interpreter/&quot;&gt;Resource Interpreter Framework&lt;/a&gt;is designed for interpreting resource structure.&lt;/p&gt;

&lt;p&gt;karmada 从 karmada-apiserver 向多个cluster 分发crd的过程中，一边是用户创建的、存在于karmada-apiserver 的crd，一边是运行在各个cluster 的crd，对于两边的crd 需要进行各种操作和状态同步。&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;比如使用GetReplicas 获取crd的副本数，计算完cluster1 应负责的crd 的replica1 之后，向cluster1 创建 replica=replica1 的crd。&lt;/li&gt;
  &lt;li&gt;假如deployment有10个pod，两个cluster 分别运行2/8 个pod，用户创建的deployment 的status 等字段 也应该能感知到真实情况。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;整体设计&quot;&gt;整体设计&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/DLDmWRmhM_gMVg1qGnj_fA&quot;&gt;Karmada 资源解释器&lt;/a&gt;资源解释器框架（Resource Interpreter Framework）是为解释资源结构而设计的，目前主要由下面 3 种解释器构成：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;内置解释器 (Built-in Interpreter)：用于解释常见的 Kubernetes 原生或知名扩展资源，由 Karmada 社区实现和维护，会内置到 Karmada 组件中。&lt;/li&gt;
  &lt;li&gt;自定义解释器（Customized Interpreter)：用于解释自定义资源或覆盖内置解释器。需要用户实现和维护 Interpreter Webhook，并且注册到 Karmada 中。&lt;/li&gt;
  &lt;li&gt;可配置解释器（Configurable Interpreter）：用于解释任意资源类型，并且支持用户通过编写 lua 脚本，来实现自定义资源对应的Interpreter Operations 方法或覆盖内置 Interpreter Operations 方法。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/kubernetes/karamada_resource_interpretor_framework.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;ResourceDetector 组件监听 用户创建的object，查找 object 匹配的 PropagationPolicy 并创建ResourceBinding。创建 ResourceBinding 时会执行 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ResourceInterpreter.GetReplicas(object)&lt;/code&gt; 拿到crd 的副本数 为集群层的调度提供依据。&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;// ResourceDetector is a resource watcher which watches all resources and reconcile the events.
controllermanager.Run ==&amp;gt; ResourceDetector.Reconcile
	object, err := d.GetUnstructuredObject(clusterWideKey)
	propagationPolicy, err := d.LookForMatchedPolicy(object, clusterWideKey)
	d.ApplyPolicy(object, clusterWideKey, propagationPolicy)
		binding, err := d.BuildResourceBinding(object, objectKey, policyLabels, policy.Spec.PropagateDeps)
			d.ResourceInterpreter.GetReplicas(object)
		controllerutil.CreateOrUpdate(context.TODO(), d.Client, bindingCopy...)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;resource-interpreter-webhook-代码分析&quot;&gt;Resource Interpreter Webhook 代码分析&lt;/h2&gt;

&lt;p&gt;resourceinterpreter 相关的目录结构&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;karmada
  /examples
    /customresourceinterpreter  # 示例一个名为workload的crd如何实现
      /apis
      /webhook
        /app
          /webhook.go
          /workloadwebhook.go
        /main.go
  /pkg
    /resourceinterpreter
	    /customizedInterpreter
        /configmanager          # 管理所有ResourceInterpreterWebhookConfiguration
        /webhook                # 对应 /pkg/webhook，提供了request 和 response 
        /customized.go          # ResourceInterpreter 的  CustomizedInterpreter 实现
	    /defaultInterpreter
	    /interpreter.go
    /webhook                    # 对自定义实现webhook 进行了简单的封装（比如请求的编解码等）， 自定义webhook时只需要实现一个子类即可。
      /interpreter
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;karmada-侧&quot;&gt;karmada 侧&lt;/h3&gt;

&lt;p&gt;在解释资源时，我们经常会通过调用 &lt;strong&gt;Interpreter Operations&lt;/strong&gt;，提取指定资源的多条信息。 Interpreter Operations 定义了解释器请求操作类型，资源解释器框架会为每个操作类型提供服务。&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;ResourceInterpreter函数&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;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;HookEnabled&lt;/td&gt;
      &lt;td&gt;检查是否有对应的 的ResourceInterpreterWebhookConfiguration&lt;/td&gt;
      &lt;td&gt;进行下列操作之前&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;GetReplicas&lt;/td&gt;
      &lt;td&gt;创建ResourceBinding 时获取karmada-apiserver  crd的replica&lt;/td&gt;
      &lt;td&gt;ResourceDetector.Reconcile ==&amp;gt; ApplyPolicy ==&amp;gt; BuildResourceBinding&lt;/td&gt;
      &lt;td&gt;仅对那些具有副本声明的资源类型是必需的，例如 Deployment 或类似的自定义资源。&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;ReviseReplica&lt;/td&gt;
      &lt;td&gt;分发到每个 cluster 时调整 cluster内crd的replica&lt;/td&gt;
      &lt;td&gt;ResourceBindingController.syncBinding ==&amp;gt; ensureWork&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;GetDependencies&lt;/td&gt;
      &lt;td&gt;比如对于  deployment 来说 configmap/secret/serviceaccount/pvc 即为 dependency&lt;/td&gt;
      &lt;td&gt;DependenciesDistributor.ReconcileResourceBinding&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Retain&lt;/td&gt;
      &lt;td&gt;Retain 表示 Karmada 请求 Webhook 保留想要的资源模板&lt;/td&gt;
      &lt;td&gt;objectWatcherImpl.retainClusterFields&lt;/td&gt;
      &lt;td&gt;例如一些资源被分发到成员集群以后，它们的模版将由其在成员集群中运行的控制器更新，那么对于这些类型的资源，Retain 是必须的&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;ReflectStatus&lt;/td&gt;
      &lt;td&gt;解析 各个cluster 下crd 的status 并汇总到所属work 的status下&lt;/td&gt;
      &lt;td&gt;WorkStatusController.RunWorkQueue ==&amp;gt; syncWorkStatus&lt;/td&gt;
      &lt;td&gt;仅对那些在特殊路径（不是&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.status&lt;/code&gt;）中定义其状态的资源类型是必需的。&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;AggregateStatus&lt;/td&gt;
      &lt;td&gt;将分发到各个cluster 的object 的状态（从worker.status）汇总并更新到 karmada-apiserver  crd status上&lt;/td&gt;
      &lt;td&gt;ResourceBindingController.syncBinding ==&amp;gt; AggregateResourceBindingWorkStatus + updateResourceStatus&lt;/td&gt;
      &lt;td&gt;只有那些需要将状态聚合到资源模板的资源类型是必须的。&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;InterpretDependency&lt;/td&gt;
      &lt;td&gt;指出特定对象的依赖关系&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;仅对那些具有依赖资源并期望传播依赖的资源类型是必需的，就像 Deployment 依赖于 ConfigMap/Secret&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Prune&lt;/td&gt;
      &lt;td&gt;指出特定对象如何将其资源模板打包到 Work&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;InterpretHealth&lt;/td&gt;
      &lt;td&gt;表示 Karmada 想要弄清楚特定对象的健康状态&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;仅对那些拥有并想要反映其健康状态的资源类型是必需的。&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/karmada-io/karmada/tree/master/docs/proposals/resource-interpreter-webhook&quot;&gt;Design Details&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/kubernetes/karmada_interpreter_operation.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;interpreter-webhook-侧&quot;&gt;interpreter webhook 侧&lt;/h3&gt;

&lt;p&gt;受到 Kubernetes Admission Webhook 的启发，Karmada 引入了 Resource Interpreter Webhook 方案。&lt;/p&gt;

&lt;p&gt;webhook 本质就是一个简单的 webserver，The webhook handles the ResourceInterpreterRequest request sent by the Karmada components (such as karmada-controller-manager), and sends back its decision as an ResourceInterpreterResponse.&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;// karmada/examples/customresourceinterpreter/webhook/app/webhook.go&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;controllerruntime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;hookManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;controllerruntime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;controllerruntime&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;Logger&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;         &lt;span class=&quot;n&quot;&gt;klog&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Background&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;Host&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;           &lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;BindAddress&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;Port&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;           &lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;SecurePort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;CertDir&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;opts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CertDir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;n&quot;&gt;LeaderElection&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;klog&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Info&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;registering webhooks to the webhook server&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;hookServer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hookManager&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetWebhookServer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;hookServer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Register&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/interpreter-workload&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interpreter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewWebhook&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;workloadInterpreter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{},&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interpreter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewDecoder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gclient&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NewSchema&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())))&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;hookServer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;WebhookMux&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/readyz/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StripPrefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/readyz/&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;healthz&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Handler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}))&lt;/span&gt;

	&lt;span class=&quot;c&quot;&gt;// blocks until the context is done.&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;hookManager&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;c&quot;&gt;// never reach here&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;nil&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;当收到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/interpreter-workload&lt;/code&gt; 请求，webhook.ServeHTTP ==&amp;gt; webhook.Handle ==&amp;gt; workloadInterpreter.Handle，之后根据请求的具体类型进行相应的处理。&lt;/p&gt;

&lt;div class=&quot;language-go highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;workloadInterpreter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Handle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ctx&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interpreter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interpreter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Response&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;workload&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;workloadv1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Workload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;err&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;decoder&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Decode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;workload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;klog&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Infof&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Explore workload(%s/%s) for request: %s&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;workload&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetNamespace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;workload&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;configv1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InterpreterOperationInterpretReplica&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;responseWithExploreReplica&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;workload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;configv1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InterpreterOperationReviseReplica&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;responseWithExploreReviseReplica&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;workload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;configv1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InterpreterOperationRetain&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;responseWithExploreRetaining&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;workload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;configv1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InterpreterOperationAggregateStatus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;responseWithExploreAggregateStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;workload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;configv1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InterpreterOperationInterpretHealth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;responseWithExploreInterpretHealth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;workload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;configv1alpha1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;InterpreterOperationInterpretStatus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;responseWithExploreInterpretStatus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;workload&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;interpreter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Errored&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;http&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;StatusBadRequest&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Errorf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;wrong request operation type: %s&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;req&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Operation&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
	&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;任何 访问这个 webhook 的 组件（在karmada 中主要是 controller ）都需要 知道 webserver 提供服务的url 地址等信息，这些信息 可以作为 请求组件的启动参数传入，也可以写入到crd（即 ResourceInterpreterWebhookConfiguration），请求组件直接解析 ResourceInterpreterWebhookConfiguration 即可，karmada 选择后者。以下列配置为例，当karmada resourceInterpreter 需要 获取 Workload 的副本数时，便去 解析 名为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;workloads.example.com&lt;/code&gt; 的ResourceInterpreterWebhookConfiguration 拿到 url，发出请求，访问用户自定义的crd 解析 replicas逻辑。&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;config.karmada.io/v1alpha1&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ResourceInterpreterWebhookConfiguration&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;examples&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;webhooks&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;workloads.example.com&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;rules&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;operations&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;InterpretReplica&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ReviseReplica&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Retain&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;AggregateStatus&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;InterpretHealth&quot;&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;InterpretStatus&quot;&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;apiGroups&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;workload.example.io&quot;&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;apiVersions&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;v1alpha1&quot;&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;kinds&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;Workload&quot;&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;clientConfig&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;https://:443/interpreter-workload&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;caBundle&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; 
    &lt;span class=&quot;na&quot;&gt;interpreterContextVersions&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;v1alpha1&quot;&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;timeoutSeconds&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;/public/upload/kubernetes/karamada_resource_interpretor_webhook_configuration.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;可配置解释器&quot;&gt;可配置解释器&lt;/h2&gt;

&lt;p&gt;以 Retain/retention 为例： 在实际使用 Karmada 的过程中我们发现，Karmada 会一直在监听成员集群中传播的资源，以确保资源始终处于期望状态。在很多情况下，运行在成员集群中的控制器会对资源进行更改，例如：Kubernetes 将为类型为 ClusterIP 的 Service 分配一个 clusterIP。Kubernetes 会在调度阶段为 Pod 分配一个 NodeName。当 Karmada 用户对资源模板进行更改时，Karmada 将针对成员集群更新资源，但在更新之前，Karmada 应保留成员集群控制器所做的更改。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;apiVersion: config.karmada.io/v1alpha1
kind: ResourceInterpreterCustomization
metadata:
  name: examples
spec:
  customizations:
    retention:
      luaScript: ``
  target:
    kind: Workload
    apiVersion: v1alpha1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;示例中我们为 v1alpha1 版本的 Workload 资源定义了一个 ResourceInterpreterCustomization 资源实例，并且通过自定义的 lua 脚本自定义了针对 Retention 请求的具体实现。当我们创建 PropagationPolicy 分发 v1alpha1 版本的 Workload 资源时，参与分发过程中的 Execution Controller 会发起 Retention 的 Interpreter Operations 请求，此时 Configurable Interpreter 根据我们创建的 ResourceInterpreterCustomization 找到与 Retention 请求对应的 lua 脚本，调用这些 lua 脚本从而实现对 Workload 资源的 Retention 请求的响应，当然 ResourceInterpreterCustomization 也支持自定义其他的 Interpreter Operations 请求的响应方法。PS：就像linux 支持ebpf 让我们进行一些简单的扩展一样，k8s 二次开发也开始设计一些机制 让用户通过脚本 来简单干预/扩展一下内部逻辑。&lt;/p&gt;
</description>
      </item>
    
      <item>
        <title>元宇宙</title>
        <link>https://felix0080.github.io/2022/09/23/metaverse.html</link>
        <guid isPermaLink="true">https://felix0080.github.io/2022/09/23/metaverse.html</guid>
        <pubDate>Fri, 23 Sep 2022 00:00:00 +0000</pubDate>
        <description>&lt;h2 id=&quot;简介&quot;&gt;简介&lt;/h2&gt;
&lt;ul id=&quot;markdown-toc&quot;&gt;
  &lt;li&gt;&lt;a href=&quot;#简介&quot; id=&quot;markdown-toc-简介&quot;&gt;简介&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;交互逐渐从单向变成双向，从抽象的信息变成具象的信息。&lt;/p&gt;

&lt;p&gt;通俗的理解，广播和报纸作为传播媒介，都是以文字内容作为主要支撑，很抽象，同时也很难能够和报纸背后的作者直接沟通，也就是我们所说的交互困难。同理，和一些小说里面的人物去交互沟通，也非常难，因为这都是单向的。但是现在有了交互技术，我们可能戴着一个 XR 的头盔，就可以在虚拟的世界里和任何一个物体、环境、人实现交互，这也就实现了双向的交互。&lt;/p&gt;
</description>
      </item>
    
  </channel>
</rss>
