<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Ekreke's Blog]]></title><description><![CDATA[Ekreke's Blog]]></description><link>https://blog.ekreke.cn</link><generator>RSS for Node</generator><lastBuildDate>Mon, 27 Apr 2026 13:43:45 GMT</lastBuildDate><atom:link href="https://blog.ekreke.cn/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Go 隐式接口与模板方法]]></title><description><![CDATA[前言
今天在使用testify框架写单元测试的时候有这样一个需求： 对于一个方法来说，可能会有很长的上下文链路数据。 按照正常的单元测试流程，这个时候我们需要按照接口的逻辑来事先mock好原始未处理的数据，并且定义最终想要的数据结果。定义好不同的test case 尽可能的覆盖到每一个if else，才可以通过后续的ci 流程。 对于一些特殊的case，我们需要一些特殊的操作：
测试前置处理-> 运行测试代码 -> 测试后处理

需要在测试前后对数据进行预处理，如：事先存入一些数据，测试后再删除...]]></description><link>https://blog.ekreke.cn/go</link><guid isPermaLink="true">https://blog.ekreke.cn/go</guid><category><![CDATA[golang]]></category><category><![CDATA[DesignPatterns]]></category><dc:creator><![CDATA[Ekreke]]></dc:creator><pubDate>Thu, 13 Nov 2025 08:04:26 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/q10VITrVYUM/upload/332f07a4d27d1cb217fed288f760ee5b.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-5ymn6kia">前言</h1>
<p>今天在使用testify框架写单元测试的时候有这样一个需求： 对于一个方法来说，可能会有很长的上下文链路数据。 按照正常的单元测试流程，这个时候我们需要按照接口的逻辑来事先mock好原始未处理的数据，并且定义最终想要的数据结果。定义好不同的test case 尽可能的覆盖到每一个if else，才可以通过后续的ci 流程。 对于一些特殊的case，我们需要一些特殊的操作：</p>
<pre><code class="lang-bash">测试前置处理-&gt; 运行测试代码 -&gt; 测试后处理
</code></pre>
<p>需要在测试前后对数据进行预处理，如：事先存入一些数据，测试后再删除这些数据。</p>
<p>这个时候按照官方文档，应该写一个afterEachTest，写在方法TearDownTest，并且绑定在testify默认创建的suit上。</p>
<pre><code class="lang-go"><span class="hljs-comment">// TearDownTest 在每个测试方法执行后调用，用于清理测试数据</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(ts *LogicsTestSuite)</span> <span class="hljs-title">TearDownTest</span><span class="hljs-params">()</span></span> {
    ts.afterEachTest()
}
</code></pre>
<p>这个时候就有奇怪的地方了，我并没有在我的测试方法中手动的运行这个TearDownTest，它究竟是如何运行的呢？</p>
<h1 id="heading-6zeu6aky5yig5p6q">问题分析</h1>
<p>其实key就在 <a target="_blank" href="https://github.com/stretchr/testify/blob/master/suite/suite.go">https://github.com/stretchr/testify/blob/master/suite/suite.go</a> 这里</p>
<p>简化的源代码如下:</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"reflect"</span>
    <span class="hljs-string">"strings"</span>
)

<span class="hljs-comment">// ====== 模拟 testify 的接口定义 ======</span>

<span class="hljs-comment">// 测试套件级别的接口</span>
<span class="hljs-keyword">type</span> SetupAllSuite <span class="hljs-keyword">interface</span> {
    SetupSuite()
}

<span class="hljs-keyword">type</span> TearDownAllSuite <span class="hljs-keyword">interface</span> {
    TearDownSuite()
}

<span class="hljs-comment">// 每个测试级别的接口</span>
<span class="hljs-keyword">type</span> SetupTestSuite <span class="hljs-keyword">interface</span> {
    SetupTest()
}

<span class="hljs-keyword">type</span> TearDownTestSuite <span class="hljs-keyword">interface</span> {
    TearDownTest()
}

<span class="hljs-comment">// ====== 模拟你的测试套件 ======</span>

<span class="hljs-keyword">type</span> MyTestSuite <span class="hljs-keyword">struct</span> {
    <span class="hljs-comment">// 注意：这里不需要显式嵌入任何东西</span>
    testData <span class="hljs-keyword">string</span>
}

<span class="hljs-comment">// 你实现了这些方法，就等于实现了对应的接口</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s *MyTestSuite)</span> <span class="hljs-title">SetupSuite</span><span class="hljs-params">()</span></span> {
    fmt.Println(<span class="hljs-string">"🏠 SetupSuite: 整个测试套件开始前的初始化"</span>)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s *MyTestSuite)</span> <span class="hljs-title">TearDownSuite</span><span class="hljs-params">()</span></span> {
    fmt.Println(<span class="hljs-string">"🏠 TearDownSuite: 整个测试套件结束后的清理"</span>)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s *MyTestSuite)</span> <span class="hljs-title">SetupTest</span><span class="hljs-params">()</span></span> {
    fmt.Println(<span class="hljs-string">"  ⚡ SetupTest: 每个测试开始前的准备"</span>)
    s.testData = <span class="hljs-string">"fresh data"</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s *MyTestSuite)</span> <span class="hljs-title">TearDownTest</span><span class="hljs-params">()</span></span> {
    fmt.Println(<span class="hljs-string">"  ⚡ TearDownTest: 每个测试结束后的清理"</span>)
    s.testData = <span class="hljs-string">""</span>
}

<span class="hljs-comment">// 测试方法（必须以 Test 开头）</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s *MyTestSuite)</span> <span class="hljs-title">TestMethod1</span><span class="hljs-params">()</span></span> {
    fmt.Println(<span class="hljs-string">"    ✅ TestMethod1 执行，测试数据:"</span>, s.testData)
}
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s *MyTestSuite)</span> <span class="hljs-title">TestMethod2</span><span class="hljs-params">()</span></span> {
    fmt.Println(<span class="hljs-string">"    ✅ TestMethod2 执行，测试数据:"</span>, s.testData)
}
<span class="hljs-comment">// ====== 模拟 testify 的 Run 函数 ======</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">RunTestSuite</span><span class="hljs-params">(suite <span class="hljs-keyword">interface</span>{})</span></span> {
    fmt.Println(<span class="hljs-string">"=== testify.Run() 开始执行 ==="</span>)
    <span class="hljs-comment">// 1. 套件级别的钩子</span>
    <span class="hljs-comment">// 注意：这里是 testify 框架在主动调用！</span>
    <span class="hljs-keyword">if</span> setupAllSuite, ok := suite.(SetupAllSuite); ok {
        setupAllSuite.SetupSuite()
    }
    <span class="hljs-comment">// 使用 defer 确保最后调用</span>
    <span class="hljs-keyword">if</span> tearDownAllSuite, ok := suite.(TearDownAllSuite); ok {
        <span class="hljs-keyword">defer</span> tearDownAllSuite.TearDownSuite()
    }
    <span class="hljs-comment">// 2. 通过反射找到所有测试方法</span>
    suiteType := reflect.TypeOf(suite)
    suiteValue := reflect.ValueOf(suite)

    <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; suiteType.NumMethod(); i++ {
        method := suiteType.Method(i)

        <span class="hljs-comment">// 查找以 Test 开头的方法</span>
        <span class="hljs-keyword">if</span> strings.HasPrefix(method.Name, <span class="hljs-string">"Test"</span>) {
            fmt.Printf(<span class="hljs-string">"\n--- 执行 %s ---\n"</span>, method.Name)
            <span class="hljs-comment">// 3. 每个测试的钩子调用</span>
            <span class="hljs-comment">// SetupTest - 测试前</span>
            <span class="hljs-keyword">if</span> setupTestSuite, ok := suite.(SetupTestSuite); ok {
                setupTestSuite.SetupTest()
            }
            <span class="hljs-comment">// 使用 defer 确保测试后调用</span>
            <span class="hljs-keyword">if</span> tearDownTestSuite, ok := suite.(TearDownTestSuite); ok {
                <span class="hljs-keyword">defer</span> tearDownTestSuite.TearDownTest()
            }
            <span class="hljs-comment">// 4. 执行实际的测试方法</span>
            method.Func.Call([]reflect.Value{suiteValue})
        }
    }
    fmt.Println(<span class="hljs-string">"\n=== testify.Run() 执行完成 ==="</span>)
}
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    suite := &amp;MyTestSuite{}
    RunTestSuite(suite)
}
</code></pre>
<p>这里采用了模版方法设计模式。 在之前定义了生命周期的相关接口和方法，在Run方法中会使用类型断言来查看是否已经实现了TearDownTest interface，如果实现了，就调用interface中定义的方法。 在go中，对于一个interface ，我们不需要显式的去implement定义它的实现，而是采用非侵入性接口（Implicit Interfaces）+ 结构匹配（Structural Typing）的一种ducking type 的设计方式。 在这里就可以体现这种设计的优势，如果我们使用Java这种需要显式impl的方式那么这里就会这样写：</p>
<p>生命周期定义：</p>
<pre><code class="lang-java"><span class="hljs-comment">// 定义多个接口</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">SetupAllSuite</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setupSuite</span><span class="hljs-params">()</span></span>;
}

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">TearDownAllSuite</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">tearDownSuite</span><span class="hljs-params">()</span></span>;
}

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">SetupTestSuite</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setupTest</span><span class="hljs-params">()</span></span>;
}

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">TearDownTestSuite</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">tearDownTest</span><span class="hljs-params">()</span></span>;
}
</code></pre>
<p>实现：</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyTestSuite</span> <span class="hljs-keyword">implements</span> 
        <span class="hljs-title">SetupAllSuite</span>, <span class="hljs-title">TearDownAllSuite</span>, 
        <span class="hljs-title">SetupTestSuite</span>, <span class="hljs-title">TearDownTestSuite</span> </span>{

    <span class="hljs-keyword">private</span> String testData;

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setupSuite</span><span class="hljs-params">()</span> </span>{
        System.out.println(<span class="hljs-string">"🏠 SetupSuite: 整个测试套件开始前的初始化"</span>);
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">tearDownSuite</span><span class="hljs-params">()</span> </span>{
        System.out.println(<span class="hljs-string">"🏠 TearDownSuite: 整个测试套件结束后的清理"</span>);
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setupTest</span><span class="hljs-params">()</span> </span>{
        System.out.println(<span class="hljs-string">"  ⚡ SetupTest: 每个测试开始前的准备"</span>);
        testData = <span class="hljs-string">"fresh data"</span>;
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">tearDownTest</span><span class="hljs-params">()</span> </span>{
        System.out.println(<span class="hljs-string">"  ⚡ TearDownTest: 每个测试结束后的清理"</span>);
        testData = <span class="hljs-string">""</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">testMethod1</span><span class="hljs-params">()</span> </span>{
        System.out.println(<span class="hljs-string">"    ✅ TestMethod1 执行，测试数据: "</span> + testData);
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">testMethod2</span><span class="hljs-params">()</span> </span>{
        System.out.println(<span class="hljs-string">"    ✅ TestMethod2 执行，测试数据: "</span> + testData);
    }
}
</code></pre>
<p>这样一比是不是就可以显著看出区别？</p>
<p>gpt 总结了一个表格如下：</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763020936709/28fbff9c-ab9e-40eb-b9a0-2f5d83837a1d.png" alt class="image--center mx-auto" /></p>
<h1 id="heading-5oc757ut">总结</h1>
<p>这种设计模式的好处在于，它能够在保持整体流程一致的前提下，允许不同的实现类根据自身需求灵活调整具体的执行细节。 通过将通用逻辑上移到抽象父类中，模板方法模式有效地减少了重复手动调用代码，从而提升系统的可维护性与可扩展性。 对于子类，我们只需关注自身差异化的部分，实现起来更加专注且清晰，同时也避免了因修改整体流程而带来的连锁影响。 这种模式让系统在结构上保持稳定，变化部分被局部化处理，变得类似积木一样“可加载”，使得逻辑更清晰和简洁。</p>
<h1 id="heading-reference">Reference</h1>
<p><a target="_blank" href="https://github.com/stretchr/testify">https://github.com/stretchr/testify</a> <a target="_blank" href="https://refactoringguru.cn/design-patterns/template-method">https://refactoringguru.cn/design-patterns/template-method</a></p>
]]></content:encoded></item><item><title><![CDATA[一次Nginx 403 的问题排查]]></title><description><![CDATA[前言&问题复现
参与了一个内部效率提升项目（边角料项目）后要发到内部的测试机器上。内部的测试机器上并没有配置集群，没有一个专门的ingress或者说是网关来处理请求分发。
并且这个测试机器属于多个部门，导致机器环境很复杂，一台物理机安装有多个nginx，有直接host安装的，也有在容器上运行的。
由于这是一个内部项目，没有必要专门部署一个minio，but 项目需要上传文件，所以就直接保存在server的目录下，简单配置了一下nginx的配置，配置如下:
    location /static...]]></description><link>https://blog.ekreke.cn/nginx-403</link><guid isPermaLink="true">https://blog.ekreke.cn/nginx-403</guid><category><![CDATA[nginx]]></category><dc:creator><![CDATA[Ekreke]]></dc:creator><pubDate>Sat, 18 Oct 2025 10:32:05 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/yLDabpoCL3s/upload/8d0023a5d77f5f8a4e69161e82fafc68.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-amp">前言&amp;问题复现</h1>
<p>参与了一个内部效率提升项目（边角料项目）后要发到内部的测试机器上。内部的测试机器上并没有配置集群，没有一个专门的ingress或者说是网关来处理请求分发。</p>
<p>并且这个测试机器属于多个部门，导致机器环境很复杂，一台物理机安装有多个nginx，有直接host安装的，也有在容器上运行的。</p>
<p>由于这是一个内部项目，没有必要专门部署一个minio，but 项目需要上传文件，所以就直接保存在server的目录下，简单配置了一下nginx的配置，配置如下:</p>
<pre><code class="lang-plaintext">    location /static/ {
        alias /data/www/nuwa/uploads/;
        # 禁用autoindex（避免目录被列出）
        autoindex off;
        # 设置缓存头（可选）
        expires 30d;
        # 确保允许访问
        satisfy any;
        allow all;
        access_log /var/log/nginx/access_xxx.log;
    }
</code></pre>
<p>配置完成并且重启nginx后，前端上传文件成功但是查询返回​​403 Forbidden 错误。</p>
<p>Nginx 返回 403 Forbidden 错误通常表示服务器（Nginx）拒绝了客户端访问所请求资源的请求。这通常是权限或配置方面的问题。</p>
<h1 id="heading-6lww5liq5byv6lev">走个弯路</h1>
<p>根据以往的经验，403 的问题通常是由于路径配置错误导致的，这里是使用的是alias，来快速过一下alias 和root 的区别。</p>
<p>区别：</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>特性</td><td><code>root</code> 指令</td><td><code>alias</code> 指令</td></tr>
</thead>
<tbody>
<tr>
<td>工作原理</td><td>追加 (Append)：将 URI 完整追加到指定路径后。</td><td>替换 (Substitution)：用 <code>alias</code> 路径替换 <code>location</code> 中匹配到的 URI 部分。</td></tr>
<tr>
<td>最终路径</td><td><code>root</code> 路径 + 完整 URI</td><td><code>alias</code> 路径 + URI 剩余部分（即匹配部分被替换掉）</td></tr>
<tr>
<td>使用范围</td><td>可以在 <code>server</code> 或 <code>location</code> 块中使用。</td><td>只能在 <code>location</code> 块中使用。</td></tr>
<tr>
<td>应用场景</td><td>物理目录结构与 URI 结构一致时。</td><td>物理目录结构与 URI 结构不一致时（路径重映射）。</td></tr>
<tr>
<td>斜杠建议</td><td>保持 <code>location</code> 路径与文件系统结构一致即可。</td><td>建议 <code>location</code> 和 <code>alias</code> 路径都以 <code>/</code> 结尾，以避免错误。</td></tr>
</tbody>
</table>
</div><p>来个🌰：</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>指令</td><td>NGINX 配置</td><td>用户请求</td><td>最终查找的物理路径</td></tr>
</thead>
<tbody>
<tr>
<td><code>root</code></td><td><code>location /static/ { root /var/www; }</code></td><td><code>/static/js/app.js</code></td><td><code>/var/www/static/js/app.js</code></td></tr>
<tr>
<td><code>alias</code></td><td><code>location /static/ { alias /data/files/; }</code></td><td><code>/static/js/app.js</code></td><td><code>/data/files/js/app.js</code></td></tr>
</tbody>
</table>
</div><p>经检查，路径配置正确，上传文件可以正常上传，但是尝试读取的时候会报403，这就很奇怪。</p>
<h1 id="heading-amp-1">权限&amp;用户问题排查</h1>
<p>在检查nginx日志的时候发现如下输出：</p>
<pre><code class="lang-plaintext">2025/07/10 17:50:53 [error] 51412#51412: *1 open() "/data/www/xxx/uploads/2025/07/10/39855bb0-bbbc-43bd-a053-860457276413.png" failed (13: Permission denied), client: 10.66.67.150, server: _,localhost, request: "GET /static/2025/07/10/39855bb0-bbbc-43bd-a053-860457276413.png HTTP/1.1", host: "10.4.20.4:28001"
</code></pre>
<p>这里的蛛丝马迹似乎在指明，这是一个由文件访问权限导致的异常。</p>
<p>当讨论到权限问题，用户以及角色问题就很明显是绕不开的话题了。</p>
<p>Nginx 在其运行中通常涉及到两个主要的用户角色，具体使用哪个非特权用户取决于操作系统发行版或安装方式了。</p>
<h2 id="heading-nginx">Nginx的用户种类</h2>
<h3 id="heading-1-nginx">1. Nginx 进程的运行用户（非特权用户）</h3>
<p>这是 Nginx 工作进程 (Worker Processes) 实际运行时所使用的用户，它是出于安全考虑而设置的。</p>
<p>这个用户拥有对网站文件进行读取（r）和对目录进行遍历（x）的权限。</p>
<p>根据不同的操作系统或安装方式，默认的非特权用户通常是以下之一：</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>用户名</td><td>常见于</td><td>描述</td></tr>
</thead>
<tbody>
<tr>
<td><code>www-data</code></td><td><strong>基于 Debian 的系统</strong>（如 Ubuntu）</td><td>这是 Ubuntu 等系统中最常见的 Web 服务器用户。</td></tr>
<tr>
<td><code>nginx</code></td><td><strong>基于 Red Hat 的系统</strong>（如 CentOS、RHEL、Fedora）和官方 Docker 镜像</td><td>许多较新的发行版和官方软件包倾向于使用专用的 <code>nginx</code> 用户。</td></tr>
<tr>
<td><code>nobody</code></td><td><strong>源码编译安装</strong>或某些极简的系统，以及 <strong>Nginx 官方文档中的默认值</strong></td><td>如果配置文件中没有显式设置 <code>user</code> 指令，Nginx 可能会回退到这个用户。</td></tr>
</tbody>
</table>
</div><p>这个用户是由 Nginx 配置文件 nginx.conf 中 user 指令 所决定的，通常位于文件顶部：</p>
<p>Nginx</p>
<pre><code class="lang-plaintext"># 示例：Debian/Ubuntu 上的默认配置
user www-data;
worker_processes auto;
</code></pre>
<p>如果看到 <code>user</code> 指令设置了哪个用户，那么 Nginx 的工作进程就会以这个用户的身份运行。</p>
<h3 id="heading-2-master">2. Master 进程用户（特权用户）</h3>
<p>Nginx 启动时，会有一个主进程 (Master Process) 负责读取配置文件、管理工作进程，以及绑定到端口（尤其是低于 1024 的端口，如 HTTP 的 80 端口和 HTTPS 的 443 端口）。</p>
<ul>
<li>这个主进程通常以 root 用户的身份运行。</li>
</ul>
<p>这是必要的，因为只有 root 用户才有权限绑定到 1024 以下的特权端口。一旦绑定成功，主进程就会创建非特权用户的工作进程来处理实际的客户端请求，以最大限度地保障安全性。</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>进程类型</td><td>运行用户</td><td>目的</td></tr>
</thead>
<tbody>
<tr>
<td>Master Process（主进程）</td><td>root</td><td>启动、绑定特权端口（如 80/443）、管理工作进程。</td></tr>
<tr>
<td>Worker Process（工作进程）</td><td>www-data、nginx 或 nobody</td><td>处理客户端请求、访问网站文件。出于安全考虑，使用非特权用户。</td></tr>
</tbody>
</table>
</div><h2 id="heading-5rex5ywl5p2d6zmq5o6s5pl">深入权限排查</h2>
<p>来检查nginx worker 进程的运行用户</p>
<pre><code class="lang-plaintext">commend: ps aux | grep nginx
</code></pre>
<p>可以看到worker process的角色是www-data，通过日志分析输出，基本可以确定问题出在文件权限上。</p>
<h3 id="heading-5pah5lu25yib5bu65zkm5p2d6zmq57un5om">文件创建和权限继承</h3>
<p>在案例中，文件是通过应用上传创建的：</p>
<pre><code class="lang-bash"><span class="hljs-comment"># 创建的目录结构</span>
/data/www/xxx/uploads/2025/07/10/xxx.png
</code></pre>
<p>查看文件权限：</p>
<pre><code class="lang-bash">-rw-r--r-- 1 root root 2 Jul 10 18:04 test.txt
</code></pre>
<p>问题就在这里：</p>
<ul>
<li><p>文件属主是 root</p>
</li>
<li><p>nginx worker 进程用户是 www-data</p>
</li>
<li><p>www-data 属于 other 用户组</p>
</li>
<li><p>虽然 other 有读权限（r--），但是nginx访问文件，是需要x权限</p>
</li>
</ul>
<h3 id="heading-linux">Linux目录权限的特殊性</h3>
<p>例如</p>
<pre><code class="lang-bash"><span class="hljs-comment"># 创建测试目录</span>
mkdir test_dir
chmod 644 test_dir  <span class="hljs-comment"># drw-r--r-- 有读权限但无执行权限</span>

<span class="hljs-comment"># 尝试访问</span>
<span class="hljs-built_in">cd</span> test_dir          <span class="hljs-comment"># 失败：Permission denied</span>
ls test_dir          <span class="hljs-comment"># 失败：Permission denied</span>
cat test_dir/file    <span class="hljs-comment"># 即使知道完整路径也失败</span>
</code></pre>
<p><strong>原因</strong>：目录的 x 权限控制用户能否：</p>
<ul>
<li><p>进入目录（cd）</p>
</li>
<li><p>访问目录内的文件和子目录</p>
</li>
<li><p>查看文件的元数据</p>
</li>
</ul>
<p>没有 x 权限，我们即使有 r 权限也无法访问。</p>
<h3 id="heading-5p2d6zmq6zo">权限链</h3>
<p>因此我们访问 /data/www/xxx/uploads/2025/07/10/xxx.png 需要：</p>
<pre><code class="lang-plaintext">/data     (x权限)
└── www   (x权限)
    └── xxx (x权限)
        └── uploads (x权限)
            └── 2025 (x权限)
                └── 07 (x权限)
                    └── 10 (x权限)
                        └── xxx.png (r权限)
</code></pre>
<p>任何一个环节缺少 x 权限，就不能正确的访问这个文件。</p>
<h1 id="heading-kirop6plhrpmlrnmoygqkg"><strong>解决方案</strong></h1>
<h2 id="heading-acl"><strong>使用ACL实现权限继承</strong></h2>
<p>ACL 的主要作用是允许你对文件或目录设置额外的、非标准的权限规则。我们默认的权限规则是通过UGO（user group other）和umask来进行控制的。</p>
<h3 id="heading-umask">umask</h3>
<p>在 Linux 中，umask 影响新创建文件权限的机制是固定的，并且遵循 文件基础权限 (666) 减去 umask 值的原则。umask 的值是一个八进制数字，用于屏蔽掉基础权限中的对应位。</p>
<p>Tips： 出于安全考虑，文件系统在创建文件时，默认不会赋予执行 (x) 权限，因此最大权限是 666，而不是 777。</p>
<p>规则：新文件/目录权限=文件/目录基础权限−umask</p>
<p>例如：</p>
<pre><code class="lang-plaintext">ekreke@ekrekes-MacBook-Air study % mkdir tmp
ekreke@ekrekes-MacBook-Air study % ls -alth
total 0
drwxr-xr-x@  6 ekreke  staff   192B Oct 18 17:43 .
drwxr-xr-x@  2 ekreke  staff    64B Oct 18 17:43 tmp
...
drwxr-xr-x@  7 ekreke  staff   224B Jun 13 13:19 ..
ekreke@ekrekes-MacBook-Air study % umask
022
</code></pre>
<p>但是这里会有一个问题，<strong>权限无法继承，umask 是一个全局或进程级的减法器，它与父目录当前设置的权限是无关的</strong>。无论父目录是 777 还是 700，新文件的权限都只取决于当前的 umask。</p>
<p>为了解决这个问题，我们可以使用ACL（Access Control Lists）<strong>用来在我们在父目录下创建新文件的时候，自动的分配文件访问所需要的权限</strong>，在此之前，我们需要设置一下目录的ugo权限。</p>
<pre><code class="lang-plaintext"># 递归修改所有权：将目录和所有内容的所有权交给Nginx运行用户
sudo chown -R www-data:www-data /data/www/nuwa/uploads

# 确保目录和父目录有执行权限
sudo chmod 775 /data/www/nuwa/uploads

# 确保所有父目录对Nginx work process 有执行权限
sudo chmod +x /data /data/www /data/www/nuwa
</code></pre>
<p>ACL设置:</p>
<pre><code class="lang-plaintext"># 1. 安装ACL工具（如果未安装）
sudo apt install acl

# 2. 设置默认ACL，让新创建的文件自动继承权限
sudo setfacl -R -d -m u::rwx,g::rx,o::rx /data/www/nuwa/uploads

# 3. 验证ACL设置
getfacl /data/www/nuwa/uploads
</code></pre>
<p>输出应该包含：</p>
<pre><code class="lang-plaintext">default:user::rwx
default:group::r-x
default:other::r-x
</code></pre>
<p>这样，给other分配了r，x作为 nginx的work process 就可以正常的访问静态资源了。</p>
<h1 id="heading-5li65lua5lmi5lya5yr55sf6lz5liq6zeu6aky77yf">为什么会发生这个问题？</h1>
<p>这个问题发生的核心原因是权限的错位。</p>
<ol>
<li><p>用户身份冲突： 负责上传文件的应用进程是root用户，和负责提供访问服务的 Nginx 工作进程（www-data）不是同一个用户。</p>
</li>
<li><p>权限级别降级： Nginx 进程 (www-data) 无法以文件的“所有者”或“所属组”身份访问，只能被归为“其他用户 (Other)”类别。</p>
</li>
<li><p>umask 限制： 文件在创建时，受应用程序当前运行环境的 umask 影响，默认权限被设置为 644 或更严格，屏蔽了“其他用户”所需的读 (r) 权限。</p>
</li>
<li><p>目录遍历受阻： 即使文件有读权限，从根目录到文件的路径链上，任意一级目录对 www-data 用户（“其他用户”）缺少执行 (x) 权限，也会导致 Nginx 无法定位和打开文件。</p>
</li>
<li><p>最重要的一点，我们绑定的静态资源路径是“/data“ 下 而不是 “/var”下。/var 目录是 Linux 文件系统层级标准（FHS）规定的用于存放经常变化文件的地方。默认的权限就是755，包含了O用户的r和x权限。所以说我们正常在/var 目录下绑定资源，很少会遇见这种问题。</p>
</li>
</ol>
<h3 id="heading-kirmnidkvbplrp7ot7uqkg"><strong>最佳实践</strong></h3>
<ol>
<li><p><strong>预创建目录并设置权限</strong></p>
<pre><code class="lang-plaintext"> # 部署时就设置好，var下是会有nginx所需的默认权限
 sudo mkdir -p /var/www/uploads
 sudo chown -R www-data:www-data /var/www/uploads
 sudo chmod 755 /var/www/uploads
</code></pre>
</li>
<li><p><strong>使用标准路径</strong></p>
<ol>
<li><p>对于nginx引用的内容，尽量使用 var/www 或 /usr/share/nginx/html</p>
</li>
<li><p>如果必须使用自定义路径，确保设置了正确的权限。</p>
</li>
</ol>
</li>
<li><p><strong>设置合理的umask</strong></p>
<pre><code class="lang-plaintext"> # 在应用启动脚本中设置
 umask 022  # 新文件权限 644，新目录权限 755
</code></pre>
</li>
</ol>
<h1 id="heading-reference">Reference</h1>
<p><a target="_blank" href="https://linux.vbird.org/linux_basic_train/centos8/unit10.php">https://linux.vbird.org/linux_basic_train/centos8/unit10.php</a></p>
]]></content:encoded></item><item><title><![CDATA[[Learn With Agent] JSX & React Components]]></title><description><![CDATA[前言
这是Learn With Agent 的第一篇博客，这个系列（不知道有没有后续了）是想通过agent辅助进行快速的学习（过概念）掌握一些相关的知识。之后再通过agent辅助开发，应该也能做出来个7788。对于AI还有很多思考，另起一个博客再说吧就。
因为网络和经费原因，这里使用的是CC+GLM 4.6 & Gemini 系列。🈚️广，文字为百分百人类手敲&传统CV大法。
JSX
JSX是React生态中占据很重要的部分，JSX是一种语法拓展，它可以允许用户在js中写类似html的标签结构...]]></description><link>https://blog.ekreke.cn/learn-with-agent-jsx-and-react-components</link><guid isPermaLink="true">https://blog.ekreke.cn/learn-with-agent-jsx-and-react-components</guid><category><![CDATA[React]]></category><dc:creator><![CDATA[Ekreke]]></dc:creator><pubDate>Thu, 09 Oct 2025 16:20:10 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/UYsBCu9RP3Y/upload/532d6d51855d3424a2ec49f01ddcf4c1.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-5ymn6kia">前言</h1>
<p>这是Learn With Agent 的第一篇博客，这个系列（不知道有没有后续了）是想通过agent辅助进行快速的学习（过概念）掌握一些相关的知识。之后再通过agent辅助开发，应该也能做出来个7788。对于AI还有很多思考，另起一个博客再说吧就。</p>
<p>因为网络和经费原因，这里使用的是CC+GLM 4.6 &amp; Gemini 系列。🈚️广，文字为百分百人类手敲&amp;传统CV大法。</p>
<h1 id="heading-jsx">JSX</h1>
<p>JSX是React生态中占据很重要的部分，JSX是一种语法拓展，它可以允许用户在js中写类似html的标签结构。 JSX还是一种描述UI的方式。对于react component，JSX像是镜像，而react component更像容器，它的作用就是将JSX渲染成真实的DOM。</p>
<pre><code class="lang-js"><span class="hljs-comment">// 1. 这是一个 React 组件（函数）</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Welcome</span>(<span class="hljs-params">props</span>) </span>{
  <span class="hljs-comment">// 2. 组件在它的 return 语句中使用了 JSX</span>
  <span class="hljs-keyword">return</span> (
    <span class="hljs-comment">// 这是 JSX，它描述了一个 &lt;h1&gt; 和一个 &lt;p&gt; 标签的 UI 结构</span>
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Hello, {props.name}!<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>这是 Welcome 组件的内容。<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
  <span class="hljs-comment">// 3. 这个 JSX 最终会被组件返回</span>
}
</code></pre>
<h1 id="heading-jsx-1">JSX 渲染机制</h1>
<h2 id="heading-bundlejs">bundle.js 文件</h2>
<p>JSX 本质上是 JavaScript 的一个扩展语法。浏览器或 Node.js 环境（在没有额外配置的情况下）并不能直接识别</p>
<p>这样的 JSX 标签。因此，它们必须被转换成普通的 JavaScript 代码。 如果是一个简单的在线demo，比如从cdn中直接引入babel，它会实时运行，并且把jsx转换为React.createElement调用。 在一个CICD流水线中，会通过webpack、vite等工具将所有jsx转换为浏览器可以识别的bundle.js 文件。这个bundle.js文件中已经不含任何 JSX 语法了。</p>
<h2 id="heading-ampdom">运行时渲染&amp;虚拟dom</h2>
<p>转译后的代码进入浏览器后，React库本身会接管整个渲染过程。 React.createElement 调用不会直接生成DOM元素，而是返回一个轻量级的JavaScript对象，我们称之为React元素（React Element）。 主要是因为浏览器操作真实dom会有昂贵的开销，所以说React引入了虚拟dom的概念。 虚拟dom是一个轻量级的JavaScript对象，它描述了真实dom的结构。当组件的状态发生变化时，React会重新渲染组件，并且对比前后两次渲染的虚拟dom差异，只更新必要的部分到真实dom中。它存在的唯一目的就是为了提高性能，尤其是在需要频繁更新UI的应用中。</p>
<h1 id="heading-components">Components</h1>
<h2 id="heading-react">react中组件的作用</h2>
<p>react中组件的主要作用就是封装ui以及交互逻辑。和后端的microservices很像，组件的主要作用除了用就是为了复用 :)</p>
<p>一个简单的组件的例子：</p>
<pre><code class="lang-js">    <span class="hljs-keyword">const</span> Avatar = <span class="hljs-function">(<span class="hljs-params">{ imageUrl, name }</span>) =&gt;</span> {
      <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"grandchild-component"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h5</span>&gt;</span>📸 头像组件 (组件)<span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
            <span class="hljs-attr">src</span>=<span class="hljs-string">{imageUrl</span> || `<span class="hljs-attr">https:</span>//<span class="hljs-attr">picsum.photos</span>/<span class="hljs-attr">seed</span>/${<span class="hljs-attr">name</span>}/<span class="hljs-attr">50</span>/<span class="hljs-attr">50.jpg</span>`}
            <span class="hljs-attr">alt</span>=<span class="hljs-string">{</span>`${<span class="hljs-attr">name</span>}的头像`}
            <span class="hljs-attr">style</span>=<span class="hljs-string">{{width:</span> '<span class="hljs-attr">50px</span>', <span class="hljs-attr">height:</span> '<span class="hljs-attr">50px</span>', <span class="hljs-attr">borderRadius:</span> '<span class="hljs-attr">50</span>%'}}
          /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
      );
    };
</code></pre>
<p>一个页面往往由多个组件构成，这也是前端工程化组件化的思想之一</p>
<pre><code class="lang-js">  <span class="hljs-keyword">const</span> App = <span class="hljs-function">() =&gt;</span> {       
    <span class="hljs-keyword">return</span> (           
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>                    
        <span class="hljs-tag">&lt;<span class="hljs-name">BasicJSXDemo</span> /&gt;</span>      // 独立的功能组件
        <span class="hljs-tag">&lt;<span class="hljs-name">ComparisonDemo</span> /&gt;</span>    // 独立的对比演示组件 
        <span class="hljs-tag">&lt;<span class="hljs-name">DynamicDataDemo</span> /&gt;</span>   // 独立的数据展示组件                                 
        <span class="hljs-tag">&lt;<span class="hljs-name">InteractiveDemo</span> /&gt;</span>   // 独立的交互演示组件                                 
      div&gt;                                                                       
    );                                                                           
  };</span>
</code></pre>
<h2 id="heading-54i25a2q57ue5lu2">父子组件</h2>
<p>父子组件顾名思义</p>
<p>来拿我们后端的内容做对比的话：</p>
<p>组件层级 ≈ 容器编排层级 (K8s: Pod → Service → Deployment)         </p>
<p>Props传递 ≈ 配置传递 (ConfigMap → Pod)                                                                                       </p>
<p>事件回调 ≈ 事件通知 (Webhook/Alert Manager)                                                                                </p>
<p>组件复用 ≈ 模板复用 (Terraform/Helm Chart)</p>
<h3 id="heading-54i25a2q57ue5lu255qe5a6e6zmf5bqu55so">父子组件的实际应用</h3>
<p>父子组件关系就像现实中的父子关系一样，父组件负责管理和协调，子组件负责具体的业务逻辑实现。</p>
<p>React中，数据是单向流动的，从父组件流向子组件，这个设计模式被称为"单向数据流"。这样可以避免数据管理的混乱，让组件之间的依赖关系更加清晰。</p>
<pre><code class="lang-js"><span class="hljs-comment">// 父组件</span>
<span class="hljs-keyword">const</span> UserProfile = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> [userName, setUserName] = useState(<span class="hljs-string">"张三"</span>);
  <span class="hljs-keyword">const</span> [userAge, setUserAge] = useState(<span class="hljs-number">25</span>);

  <span class="hljs-comment">// 父组件定义的事件处理函数</span>
  <span class="hljs-keyword">const</span> handleAgeChange = <span class="hljs-function">(<span class="hljs-params">newAge</span>) =&gt;</span> {
    setUserAge(newAge);
  };

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"parent-component"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>用户资料<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
      {/* 通过props将数据传递给子组件 */}
      <span class="hljs-tag">&lt;<span class="hljs-name">UserAvatar</span>
        <span class="hljs-attr">name</span>=<span class="hljs-string">{userName}</span>
        <span class="hljs-attr">age</span>=<span class="hljs-string">{userAge}</span>
        <span class="hljs-attr">onAgeChange</span>=<span class="hljs-string">{handleAgeChange}</span>  // 将回调函数传递给子组件
      /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
};

<span class="hljs-comment">// 子组件</span>
<span class="hljs-keyword">const</span> UserAvatar = <span class="hljs-function">(<span class="hljs-params">{ name, age, onAgeChange }</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> handleClick = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-comment">// 子组件调用父组件传递的回调函数</span>
    onAgeChange(age + <span class="hljs-number">1</span>);
  };

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"child-component"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h4</span>&gt;</span>头像：{name}<span class="hljs-tag">&lt;/<span class="hljs-name">h4</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>年龄：{age}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleClick}</span>&gt;</span>增加年龄<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
};
</code></pre>
<p>通过合理地使用父子组件的结构，可以构建出更加清晰、可维护的react project。</p>
]]></content:encoded></item><item><title><![CDATA[提示词以及常见优化技巧]]></title><description><![CDATA[prompt 种类

system : 系统提示词用于统一设定当前会话下ai的行为，例如设定ai的行为、语气、风格或限制。用户不会显式观察到系统提示词，但是会影响到用户和ai的后续对话。

user : 这是用户实际向ai提出的问题，这是对话的起点。

assistant: 这是ai对于用户问题的回答，通常也会被添加到上下文中，供下一次对话进行参考，所以也属于prompt的范畴。


prompt 格式
标准格式：
<Instruction>

问答格式：
<Question>?

零样本提示 ...]]></description><link>https://blog.ekreke.cn/5oq56s66kn5lul5yk5bi46keb5lyy5yyw5oqa5ben</link><guid isPermaLink="true">https://blog.ekreke.cn/5oq56s66kn5lul5yk5bi46keb5lyy5yyw5oqa5ben</guid><category><![CDATA[#PromptEngineering]]></category><dc:creator><![CDATA[Ekreke]]></dc:creator><pubDate>Thu, 14 Aug 2025 15:51:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/agUC-v_D1iI/upload/eaa0ff9ed43b6f04846e97d2316d65a5.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-prompt">prompt 种类</h1>
<ul>
<li><p>system : 系统提示词用于统一设定当前会话下ai的行为，例如设定ai的行为、语气、风格或限制。用户不会显式观察到系统提示词，但是会影响到用户和ai的后续对话。</p>
</li>
<li><p>user : 这是用户实际向ai提出的问题，这是对话的起点。</p>
</li>
<li><p>assistant: 这是ai对于用户问题的回答，通常也会被添加到上下文中，供下一次对话进行参考，所以也属于prompt的范畴。</p>
</li>
</ul>
<h1 id="heading-prompt-1">prompt 格式</h1>
<h2 id="heading-5qch5yeg5qc85byp77ya">标准格式：</h2>
<pre><code class="lang-go">&lt;Instruction&gt;
</code></pre>
<h2 id="heading-6zeu562u5qc85byp77ya">问答格式：</h2>
<pre><code class="lang-go">&lt;Question&gt;?
</code></pre>
<h2 id="heading-zero-shot-prompting">零样本提示 (Zero-shot Prompting):</h2>
<p>依赖于模型在训练时学到的知识来给出回答。</p>
<p>例子: 什么是提示工程？ 优点: 简单、直接。 局限: 对于复杂的任务，模型可能表现不佳。</p>
<h2 id="heading-few-shot-prompting">少样本提示 (Few-shot Prompting):</h2>
<p>先提供一些任务的示例，然后让模型完成下一个类似的任务。</p>
<p>例子:</p>
<pre><code class="lang-go">This is awesome! <span class="hljs-comment">// Positive</span>
This is bad! <span class="hljs-comment">// Negative</span>
Wow that movie was rad! <span class="hljs-comment">// Positive</span>
What a horrible show! <span class="hljs-comment">//</span>
</code></pre>
<p>前三个例子告诉模型，“awesome”和“rad”是正面情绪，“bad”是负面情绪。因此，模型可以推断出“horrible”也应被标记为负面情绪。 优点: 在许多情况下，能显著提高模型在特定任务上的表现。 局限: 需要花费更多精力来构建有效的示例，并且会占用更多的提示词（token）数量。</p>
<h3 id="heading-6aoo6zmp">风险</h3>
<p>在 Few-shot 场景下，如果正负样本比例失衡（如 8:2），模型倾向于预测占多数的标签。</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>风险点</td><td>快速缓解</td><td>进阶手段</td></tr>
</thead>
<tbody>
<tr>
<td>样本分布失衡</td><td>平衡正负样本数</td><td>数据增强、重采样</td></tr>
<tr>
<td>样本顺序偏见</td><td>随机排序</td><td>交叉验证、集成预测</td></tr>
<tr>
<td>事实错误/有害内容</td><td>提示中加入“请仔细核查事实”</td><td>内容审核、RLHF</td></tr>
</tbody>
</table>
</div><h1 id="heading-prompt-2">prompt 构成</h1>
<p>指令 Instruction: 希望模型执行的具体任务或操作 上下文 Context: 这是额外的外部信息或背景知识，可以引导模型给出更准确的回复。 输入信息 Input Data: 这是模型需要处理的具体数据或信息。 输出指示器 Output Indicator: 模型需要生成的输出类型或格式。</p>
<p>一个prompt并不需要完整的以上的四个部分，这根据具体的任务和模型的能力。</p>
<h1 id="heading-6yca55so5oq56s66kn6k66k6h">通用提示词设计</h1>
<h2 id="heading-5lu75yqh5oug6kej">任务拆解</h2>
<p>从简单开始：提示设计是一个反复试验的过程。建议从简单的提示开始，随着目标变得更明确，再逐步增加复杂性和上下文。 拆解任务：对于复杂的任务，可以将其分解成多个简单的子任务，以避免一开始就陷入复杂的提示设计。</p>
<h2 id="heading-5oyh5luk">指令</h2>
<p>使用明确的指令: 采用明确的指令,或者说希望达到的目标来告诉模型需要它完成的事。 指令放置与格式: 建议将指令放在提示的开头，并使用清晰的分隔符（如###）将其与上下文或输入数据分开。</p>
<h2 id="heading-5yeg56gu5ocn">准确性</h2>
<p>保持具体和详细：提供更多的描述，更多的详细细节. 提供例子：在提示中提供示例是获得所需格式输出的一种非常有效的方法。 关注上下文长度：在保持具体的同时，也要注意提示的长度限制，避免加入不必要的细节，确保所有细节都与手头的任务相关。</p>
<h2 id="heading-6yg5ywn5lin57k56gu55qe5op6lw">避免不精确的描述</h2>
<p>直接而精确：要直接、具体，避免含糊不清的描述。 关注TODO：不要告诉模型不要做什么（比如“不要问我的兴趣相关内容”），明确告诉它要做什么（比如“推荐全球热门电影”）。这能更有效地指导模型的行为，减少产生意外结果的可能性。</p>
<h1 id="heading-5oq56s66kn5lyy5yyw5oqa5ben">提示词优化技巧</h1>
<h2 id="heading-zero-shot-prompting-1">零样本提示词 (Zero-shot Prompting)</h2>
<p>常用技巧</p>
<ol>
<li><p>明确任务目标: 告诉模型需要完成的任务，如分类、生成文本、翻译等。</p>
</li>
<li><p>指定格式和输出要求: 如指定输出为JSON格式、限制输出长度等。</p>
</li>
<li><p>角色扮演: 如“你是一个专业的翻译”、“你是一个专业的客服”等。</p>
</li>
<li><p>逐步推理: 如“先理解用户的问题，然后根据理解生成回答”等。</p>
</li>
<li><p>限制范围: 如限制模型的回答范围，避免生成不相关的内容。</p>
</li>
</ol>
<h2 id="heading-few-shot-prompting-1">少样本提示 (Few-shot Prompting)</h2>
<p>常用技巧</p>
<ol>
<li><p>任务描述 + 示例结合: 先描述任务，然后提供一些示例，让模型根据示例完成任务。</p>
</li>
<li><p>示例涵盖边界情况: 提供一些边界情况的示例，帮助模型理解任务的复杂性和边界条件。</p>
</li>
<li><p>示例与目标风格一致: 确保示例与目标风格一致，避免生成与示例风格不同的内容,如我需要json形式的结果。</p>
</li>
<li><p>用分隔符清晰分隔示例与新任务: 用分隔符（如###）清晰地区分示例和新任务，避免混淆。</p>
</li>
</ol>
<h2 id="heading-cot-chain-of-thought">COT (Chain of Thought)</h2>
<p>COT通过让大模型显式生成“中间推理步骤”来提升其在多步推理任务上的准确性 主要变体</p>
<h3 id="heading-cot">零样本COT</h3>
<ol>
<li><p>添加COT提示: 在任务描述后添加“请一步一步思考”等提示，引导模型生成推理步骤。</p>
</li>
<li><p>要求示例中包含推理步骤: 提供的示例中包含推理步骤，帮助模型理解任务的复杂性。</p>
</li>
</ol>
<h3 id="heading-cot-1">自动COT</h3>
<ol>
<li><p>问题聚类（Question Clustering）：将数据集中的问题分成几个簇。</p>
</li>
<li><p>示例采样（Demonstration Sampling）：从每个簇中选择一个代表性问题，并使用零样本思维链提示为它生成推理链。该过程会采用一些简单的启发式规则，比如问题长度或推理步骤的数量，以确保示例的简洁和准确。</p>
</li>
</ol>
<p>示例： Question:</p>
<pre><code class="lang-go">如果一个玻璃杯从<span class="hljs-number">10</span>楼掉下去，会发生什么？逐步思考。
</code></pre>
<p>Answer:</p>
<pre><code class="lang-go">Let<span class="hljs-string">'s think step by step.
1. 玻璃是易碎材料。
2. 从10楼掉下会有很大速度和冲击力。
3. 玻璃承受不了这样的冲击。
4. 因此，它很可能会在落地时碎裂。
答案：它会碎掉。</span>
</code></pre>
<h2 id="heading-self-consistency">Self-Consistency（自一致性）</h2>
<p>常和 Chain-of-Thought（思维链提示）配合使用。 核心思想: 让模型多次独立推理同一个问题，然后选出现次数最多的答案作为最终结果。 技巧：</p>
<ol>
<li><p>生成多条推理路径，并且引入temperature</p>
</li>
<li><p>收集多个候选答案</p>
</li>
<li><p>投票选出最高频率的答案</p>
</li>
</ol>
<h2 id="heading-tot">思维树 (TOT)</h2>
<p>思维树（ToT）是一种用于解决复杂任务的提示工程框架，它通过模拟人类的树状思维过程，显著提升了大型语言模型（LLM）的推理和解决问题的能力。</p>
<p>不同于传统的“思维链（Chain-of-Thought, CoT）”那种线性的、一步步的思考方式，ToT 鼓励 AI 进行多路径、多步骤的探索。它将问题分解成多个中间步骤（即“思想”），并在每个步骤中生成多个可能的选项（“分支”），形成一个树状的思考结构。</p>
<p>prompt 结构：分解任务→ 生成和评估 → 结合搜索算法</p>
<p>示例</p>
<pre><code class="lang-go">你是一位经验丰富的旅行规划师。

你的任务是为客户规划一个从北京到上海的五天四夜旅行。


请你严格按照以下步骤进行思考和规划，最终给出详细的行程安排：


**第一步：任务分解**

首先，将这个复杂的旅行规划任务分解成几个主要的子任务或“思想”步骤。例如：

<span class="hljs-number">1.</span>  **交通规划**：如何规划北京到上海的往返高铁，以及上海市内的交通方式？

<span class="hljs-number">2.</span>  **住宿选择**：如何根据预算和兴趣，选择合适的住宿区域和酒店？

<span class="hljs-number">3.</span>  **行程安排**：如何将客户的兴趣（历史文化、现代艺术、本地美食）合理地分配到五天行程中？

<span class="hljs-number">4.</span>  **餐饮推荐**：如何为每一天安排符合客户喜好的美食体验？

<span class="hljs-number">5.</span>  **预算考量**：如何在整个过程中平衡中等偏高的预算？


**第二步：多路径思考与探索**

针对每个子任务，请思考至少两种不同的方案或“思想路径”，并简要说明其优劣。


**示例（住宿规划）：**

* **方案A（思想路径A）**：选择住在外滩附近。

    * **优势**：交通便利，景点集中，夜景美观。

    * **劣势**：酒店价格较高，游客较多，可能会有些嘈杂。

* **方案B（思想路径B）**：选择住在法租界区域（如徐汇区）。

    * **优势**：环境安静优雅，靠近现代艺术区，美食选择多样。

    * **劣势**：离外滩等核心景点稍远，部分地区交通便利性不如外滩。


请像这样，为每个子任务思考至少两种不同的方案。


**第三步：评估和选择**

在思考完所有方案后，请你对它们进行综合评估，并从这些“思想路径”中选择一个你认为最佳的组合方案。请解释你为什么选择这个方案。


**第四步：生成最终行程**

根据你选择的最佳方案，生成一个详细、结构化的五天四夜行程。行程应包括：

* 每天的行程主题。

* 具体的景点和活动安排。

* 推荐的餐饮和交通方式。


请按照这个逻辑，一步步地为我完成这个旅行规划。
</code></pre>
<h2 id="heading-generated-knowledge-prompting">Generated Knowledge Prompting（生成知识提示）</h2>
<p>是一种让大语言模型（LLM）先生成相关背景知识，再利用这些知识去回答问题的提示方法。</p>
<p>示例</p>
<h3 id="heading-step1">step1 生成相关知识</h3>
<pre><code class="lang-go">Q: 请列出联合国安理会常任理事国的相关背景知识。
A:
<span class="hljs-number">1.</span> 联合国安理会有<span class="hljs-number">5</span>个常任理事国。
<span class="hljs-number">2.</span> 他们拥有否决权。
<span class="hljs-number">3.</span> 成员是根据二战胜利国确定的。
<span class="hljs-number">4.</span> 成员包括：中国、法国、俄罗斯、英国、美国。
</code></pre>
<h3 id="heading-step2">step2 利用相关知识回答问题</h3>
<pre><code class="lang-go">Q: 哪些国家是联合国安理会常任理事国？
A: 中国、法国、俄罗斯、英国、美国。
</code></pre>
<p>适用场景</p>
<ol>
<li><p>开放域问答（历史、科学、地理等）</p>
</li>
<li><p>常识推理（需要多步背景知识）</p>
</li>
<li><p>学术类问题（先回忆理论再应用）</p>
</li>
<li><p>跨领域问题（整合多个知识片段）</p>
</li>
</ol>
<h1 id="heading-reference">Reference</h1>
<p>[1]What is chain of thought (CoT) prompting? <a target="_blank" href="https://www.ibm.com/think/topics/chain-of-thoughts">https://www.ibm.com/think/topics/chain-of-thoughts</a> [2]Grok Prompts : <a target="_blank" href="https://github.com/xai-org/grok-prompts">https://github.com/xai-org/grok-prompts</a></p>
<p>[3]Tree of Thoughts Implement : <a target="_blank" href="https://github.com/princeton-nlp/tree-of-thought-llm">https://github.com/princeton-nlp/tree-of-thought-llm</a></p>
]]></content:encoded></item><item><title><![CDATA[Http 长连接 & 短连接详解]]></title><description><![CDATA[网络连接基础
TCP/IP
TCP/IP，是几乎所有互联网通信的基石。HTTP、WebSocket 和 常见的RPC框架尽管功能各异，但都运行在应用层，并从根本上依赖传输层的 TCP（传输控制协议）来实现可靠的、面向连接的数据传输 。
TCP 的核心职责是确保数据包从发送端到接收端可靠、按序且无损地传输 。这包括序列号、确认、流量控制和拥塞控制等机制。在网络层，IP（互联网协议）负责网络路由和寻址，使数据能够跨越不同网络到达其目的地 。现代操作系统普遍内置并管理 TCP/IP 协议栈，从而为应用...]]></description><link>https://blog.ekreke.cn/http</link><guid isPermaLink="true">https://blog.ekreke.cn/http</guid><category><![CDATA[http]]></category><category><![CDATA[network]]></category><dc:creator><![CDATA[Ekreke]]></dc:creator><pubDate>Fri, 13 Jun 2025 09:25:18 GMT</pubDate><content:encoded><![CDATA[<h1 id="heading-572r57uc6le5o6l5z656ga">网络连接基础</h1>
<h2 id="heading-tcpip">TCP/IP</h2>
<p>TCP/IP，是几乎所有互联网通信的基石。HTTP、WebSocket 和 常见的RPC框架尽管功能各异，但都运行在应用层，并从根本上依赖传输层的 TCP（传输控制协议）来实现可靠的、面向连接的数据传输 。</p>
<p>TCP 的核心职责是确保数据包从发送端到接收端可靠、按序且无损地传输 。这包括序列号、确认、流量控制和拥塞控制等机制。在网络层，IP（互联网协议）负责网络路由和寻址，使数据能够跨越不同网络到达其目的地 。现代操作系统普遍内置并管理 TCP/IP 协议栈，从而为应用开发者抽象了大部分底层复杂性。</p>
<p>这种分层架构意味着高层协议继承了 TCP 的基本属性（可靠性、顺序性、面向连接），而无需重新实现这些功能，从而简化了应用开发。然而，这种抽象也意味着底层 TCP 层的性能特征和限制会直接影响所有应用层协议。例如，TCP 的连接建立开销（即“三次握手”）是每个新连接固有的固定成本，无论应用协议如何，都无法避免。TCP 的慢启动机制也会影响任何新连接的初始性能。</p>
<p>因此，对于不同场景，在应用层上会做出一些不同的trade off，由此产生了不同的解决方案。</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1749787026320/17dfedb9-eac6-43b5-94a0-e7f7cdec470f.png" alt class="image--center mx-auto" /></p>
<p>(图片来源：<a target="_blank" href="https://www.geeksforgeeks.org/tcp-3-way-handshake-process/">https://www.geeksforgeeks.org/tcp-3-way-handshake-process/</a>)</p>
<h1 id="heading-6zw6le5o6l">长连接</h1>
<h2 id="heading-5lul57un">介绍</h2>
<p>HTTP 协议的长连接和短连接，由于构建在7层，所以实质上是 TCP 协议的长连接和短连接。</p>
<p>HTTP长连接的请求数量限定是最多连续发送100个请求，超过限定将关闭这条连接。</p>
<p>与短连接相反，长连接（也称为持久连接或 Keep-Alive 连接）在客户端和服务器之间建立一个 TCP 连接后，该连接会保持一段时间，用于发送一系列连续的请求，而不会在每次请求-响应完成后立即关闭 。</p>
<p>长连接也带来了一定的挑战。服务器需要维持更多的客户端连接，即使这些连接处于空闲状态，也会持续消耗服务器资源 。这可能导致服务器维护过多连接，尤其是在客户端不主动关闭连接的情况下 。在重负载下，长连接也可能面临拒绝服务（DoS）攻击的风险 。为应对这些问题，服务器通常会配置<code>Keep-Alive</code> 超时时间，例如 Apache 2.0 默认设置为 15 秒，Apache 2.2 默认设置为 5 秒 。</p>
<p>当连接在设定的时间内没有任何活动时，服务器会发送探测报文段以检测客户端状态，从而识别和关闭半开放或已崩溃客户端的连接，以防止恶意连接影响后端服务 。</p>
<p>长连接的实现需要在性能提升和服务器资源消耗之间进行权衡。虽然长连接可以显著提高性能，但也增加了服务器端连接管理的复杂性。例如，需要实现保活机制来检测半开放连接，并设置合理的超时策略来释放空闲资源。如果客户端不主动关闭连接，服务器可能会保持过多连接，这需要服务器采取额外的控制机制，如限制每个客户端的最大长连接数，以防止恶意客户端耗尽整个后端服务资源。</p>
<p>因此，选择长连接需要仔细考虑其带来的运维复杂性，并实施相应的连接管理策略。</p>
<h1 id="heading-55t6le5o6l">短连接</h1>
<h2 id="heading-5lul57un-1">介绍</h2>
<p>在短连接模型中，客户端和服务器之间为每个独立的请求-响应周期建立一个新的 TCP 连接，并在完成通信后立即终止 。这个过程本质上涉及每次事务的 TCP“三次握手”用于连接建立和“四次挥手”用于连接拆除 。HTTP/1.0 默认采用这种行为。</p>
<p>短连接的优点在于其管理相对简单。从服务器的角度来看，所有现有连接都是活跃的，这可能消除了对复杂空闲连接管理机制的需求。此外，服务器在请求处理完成后立即释放资源，不会长时间占用空闲连接。</p>
<p>然而，短连接存在显著的缺点。最主要的问题是高昂的开销。对于频繁的客户端请求，重复建立和终止 TCP 连接会浪费大量时间（因为每次连接建立都需要消耗时间和资源）和带宽 。此外，TCP 连接的性能只有在被使用一段时间后（即成为“热连接”）才能得到改善，而短连接模式下，每次都创建新的“冷连接”会破坏 TCP 的这种性能提升能力，导致整体效率低下 。</p>
<h1 id="heading-5qgi5l6l">案例</h1>
<h2 id="heading-server">server</h2>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"io"</span>
    <span class="hljs-string">"io/ioutil"</span>
    <span class="hljs-string">"log"</span>
    <span class="hljs-string">"net/http"</span>
    <span class="hljs-string">"time"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-comment">// 创建一个自定义的 Transport，配置连接池参数</span>
    <span class="hljs-comment">// MaxIdleConnsPerHost: 每个主机的最大空闲连接数，用于长连接复用</span>
    <span class="hljs-comment">// IdleConnTimeout: 空闲连接的最大存活时间</span>
    tr := &amp;http.Transport{
        MaxIdleConns:        <span class="hljs-number">100</span>,             <span class="hljs-comment">// 所有主机的最大空闲连接数</span>
        MaxIdleConnsPerHost: <span class="hljs-number">10</span>,              <span class="hljs-comment">// 每个主机的最大空闲连接数</span>
        IdleConnTimeout:     <span class="hljs-number">90</span> * time.Second, <span class="hljs-comment">// 空闲连接的超时时间</span>
        DisableKeepAlives:   <span class="hljs-literal">false</span>,           <span class="hljs-comment">// 默认启用 Keep-Alive，这里显式设置为 false 确保启用</span>
    }

    <span class="hljs-comment">// 创建一个使用自定义 Transport 的 HTTP 客户端</span>
    client := &amp;http.Client{Transport: tr}

    <span class="hljs-comment">// 模拟多次请求以复用连接</span>
    <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">5</span>; i++ {
        resp, err := client.Get(<span class="hljs-string">"http://localhost:8080/hello"</span>)
        <span class="hljs-keyword">if</span> err!= <span class="hljs-literal">nil</span> {
            log.Printf(<span class="hljs-string">"Request %d failed: %v\n"</span>, i+<span class="hljs-number">1</span>, err)
            <span class="hljs-keyword">continue</span>
        }

        <span class="hljs-comment">// 确保读取并关闭响应体，以便连接可以返回到连接池中复用</span>
        _, err = io.Copy(ioutil.Discard, resp.Body)
        <span class="hljs-keyword">if</span> err!= <span class="hljs-literal">nil</span> {
            log.Printf(<span class="hljs-string">"Failed to discard response body for request %d: %v\n"</span>, i+<span class="hljs-number">1</span>, err)
        }
        resp.Body.Close()

        fmt.Printf(<span class="hljs-string">"Request %d successful, status: %s\n"</span>, i+<span class="hljs-number">1</span>, resp.Status)
        time.Sleep(<span class="hljs-number">500</span> * time.Millisecond) <span class="hljs-comment">// 稍作等待</span>
    }

    <span class="hljs-comment">// 客户端也可以配置为短连接，例如通过设置 DisableKeepAlives 为 true</span>
    <span class="hljs-comment">// shortConnClient := &amp;http.Client{</span>
    <span class="hljs-comment">//     Transport: &amp;http.Transport{</span>
    <span class="hljs-comment">//         DisableKeepAlives: true, // 禁用 Keep-Alive，强制短连接</span>
    <span class="hljs-comment">//     },</span>
    <span class="hljs-comment">// }</span>
    <span class="hljs-comment">// resp, err := shortConnClient.Get("http://localhost:8080/hello")</span>
    <span class="hljs-comment">//...</span>
}
</code></pre>
<h2 id="heading-client">client</h2>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"io"</span>
    <span class="hljs-string">"io/ioutil"</span>
    <span class="hljs-string">"log"</span>
    <span class="hljs-string">"net/http"</span>
    <span class="hljs-string">"time"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-comment">// 创建一个自定义的 Transport，配置连接池参数</span>
    <span class="hljs-comment">// MaxIdleConnsPerHost: 每个主机的最大空闲连接数，用于长连接复用</span>
    <span class="hljs-comment">// IdleConnTimeout: 空闲连接的最大存活时间</span>
    tr := &amp;http.Transport{
        MaxIdleConns:        <span class="hljs-number">100</span>,             <span class="hljs-comment">// 所有主机的最大空闲连接数</span>
        MaxIdleConnsPerHost: <span class="hljs-number">10</span>,              <span class="hljs-comment">// 每个主机的最大空闲连接数</span>
        IdleConnTimeout:     <span class="hljs-number">90</span> * time.Second, <span class="hljs-comment">// 空闲连接的超时时间</span>
        DisableKeepAlives:   <span class="hljs-literal">false</span>,           <span class="hljs-comment">// 默认启用 Keep-Alive，这里显式设置为 false 确保启用</span>
    }

    <span class="hljs-comment">// 创建一个使用自定义 Transport 的 HTTP 客户端</span>
    client := &amp;http.Client{Transport: tr}

    <span class="hljs-comment">// 模拟多次请求以复用连接</span>
    <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">5</span>; i++ {
        resp, err := client.Get(<span class="hljs-string">"http://localhost:8080/hello"</span>)
        <span class="hljs-keyword">if</span> err!= <span class="hljs-literal">nil</span> {
            log.Printf(<span class="hljs-string">"Request %d failed: %v\n"</span>, i+<span class="hljs-number">1</span>, err)
            <span class="hljs-keyword">continue</span>
        }

        <span class="hljs-comment">// 确保读取并关闭响应体，以便连接可以返回到连接池中复用</span>
        _, err = io.Copy(ioutil.Discard, resp.Body)
        <span class="hljs-keyword">if</span> err!= <span class="hljs-literal">nil</span> {
            log.Printf(<span class="hljs-string">"Failed to discard response body for request %d: %v\n"</span>, i+<span class="hljs-number">1</span>, err)
        }
        resp.Body.Close()

        fmt.Printf(<span class="hljs-string">"Request %d successful, status: %s\n"</span>, i+<span class="hljs-number">1</span>, resp.Status)
        time.Sleep(<span class="hljs-number">500</span> * time.Millisecond) <span class="hljs-comment">// 稍作等待</span>
    }

    <span class="hljs-comment">// 客户端也可以配置为短连接，例如通过设置 DisableKeepAlives 为 true</span>
    <span class="hljs-comment">// shortConnClient := &amp;http.Client{</span>
    <span class="hljs-comment">//     Transport: &amp;http.Transport{</span>
    <span class="hljs-comment">//         DisableKeepAlives: true, // 禁用 Keep-Alive，强制短连接</span>
    <span class="hljs-comment">//     },</span>
    <span class="hljs-comment">// }</span>
    <span class="hljs-comment">// resp, err := shortConnClient.Get("http://localhost:8080/hello")</span>
    <span class="hljs-comment">//...</span>
}
</code></pre>
<h1 id="heading-6zw6le5o6l6zmn57qn">长连接降级</h1>
<h2 id="heading-kirlrqlmilfnq6kvqflr7zoh7tplbov57mjqxlj5jkulrnn63ov57mjqxvvjoqkg"><strong>客户端侧导致长连接变为短连接：</strong></h2>
<ul>
<li><p><strong>明确要求关闭连接：</strong> 客户端在发送 HTTP 请求时，如果其请求头中明确包含 <code>Connection: close</code>，那么即使服务器支持长连接，客户端也会在收到响应后主动关闭 TCP 连接。这在某些特定场景下可能会发生，例如客户端判断不再需要与该服务器进行更多交互，或者需要强制刷新 DNS 等。</p>
</li>
<li><p><strong>客户端异常断开：</strong> 如果客户端由于网络问题、应用崩溃或用户强制关闭等原因导致 TCP 连接异常中断，那么正在使用的长连接自然也就断开了，未来的新请求就需要重新建立短连接（或者新的长连接）。</p>
</li>
<li><p><strong>客户端设置了较短的空闲超时：</strong> 客户端在管理其连接池时，可能会为长连接设置一个空闲超时时间。如果在这个时间内没有新的请求发送，客户端会主动关闭该连接，从而变为短连接行为。</p>
</li>
</ul>
<h2 id="heading-kirmni3liqhlmajkvqflr7zoh7tplbov57mjqxlj5jkulrnn63ov57mjquqkg"><strong>服务器侧导致长连接变为短连接</strong></h2>
<ul>
<li><p><strong>服务器设置了空闲超时 (Keep-Alive Timeout)：</strong> 这是最常见的情况。服务器为了避免持有过多的空闲连接（占用资源），会为长连接设置一个超时时间。如果在指定时间内，客户端没有发送新的请求，服务器会主动关闭该 TCP 连接。当客户端再次发送请求时，就必须重新建立连接，表现为短连接行为。</p>
</li>
<li><p><strong>服务器达到最大连接数限制：</strong> 如果服务器承载的连接数达到其配置的最大值，为了保证服务可用性，可能会主动关闭一些空闲或不活跃的长连接，为新的连接腾出空间。</p>
</li>
<li><p><strong>服务器资源耗尽或异常：</strong> 服务器在处理请求过程中出现内存不足、CPU 过高、服务崩溃等异常情况，可能会强制关闭所有活跃连接或拒绝新的长连接请求，迫使客户端回退到短连接模式（重新建立连接）。</p>
</li>
<li><p><strong>服务器明确要求关闭连接：</strong> 服务器在响应头中包含 <code>Connection: close</code>，这会告诉客户端在收到响应后关闭连接。例如，服务器可能在处理完一个特定的、不希望被复用的请求后（如某些管理操作），或在服务器即将关闭/重启前，发出这样的指令。</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Linux下网络管理常用工具]]></title><description><![CDATA[ip
Ip工具可以查看当台主机的一些基本信息。
场景

查看基本信息:查看网络接口、mtu等。

查看docker虚拟网络相关信息。


Ping
ping 是 Linux 下一个非常常用且基础的网络工具，用于测试网络连接的可达性。
场景

测试网络连通性: 判断本机是否能够到达目标主机。分别ping lo地址、本机对外地址、gateway地址、公网地址，可以一步一步的进行问题的排查。

测量网络性能: 查看RTT、丢包等信息，即数据包从发送到接收到回复所花费的时间。

域名解析验证: 当使用域...]]></description><link>https://blog.ekreke.cn/linux</link><guid isPermaLink="true">https://blog.ekreke.cn/linux</guid><category><![CDATA[Linux]]></category><category><![CDATA[network]]></category><dc:creator><![CDATA[Ekreke]]></dc:creator><pubDate>Fri, 11 Apr 2025 09:38:46 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/4Mw7nkQDByk/upload/edaef49e724548938bd79297d568c5f6.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-ip">ip</h1>
<p>Ip工具可以查看当台主机的一些基本信息。</p>
<h2 id="heading-5zy65pmv">场景</h2>
<ul>
<li><p><strong>查看基本信息:</strong>查看网络接口、mtu等。</p>
</li>
<li><p><strong>查看docker虚拟网络相关信息</strong>。</p>
</li>
</ul>
<h1 id="heading-ping">Ping</h1>
<p>ping 是 Linux 下一个非常常用且基础的网络工具，用于测试网络连接的可达性。</p>
<h2 id="heading-5zy65pmv-1">场景</h2>
<ul>
<li><p><strong>测试网络连通性:</strong> 判断本机是否能够到达目标主机。分别ping lo地址、本机对外地址、gateway地址、公网地址，可以一步一步的进行问题的排查。</p>
</li>
<li><p><strong>测量网络性能:</strong> 查看RTT、丢包等信息，即数据包从发送到接收到回复所花费的时间。</p>
</li>
<li><p><strong>域名解析验证:</strong> 当使用域名作为目标时，ping 命令会先解析域名为 IP 地址，可以间接验证 DNS 解析是否正常。</p>
</li>
<li><p><strong>压测:ping</strong> 可以使用-f 进行Flood ping，可以配合-s(设置package size)来进行简单的对目标主机的压测。</p>
</li>
</ul>
<h1 id="heading-dig">Dig</h1>
<p>Dig是 Linux 命令行下用于查询 DNS信息的强大工具。dig 允许用户直接与 DNS 服务器交互，查询各种 DNS 记录，并显示从服务器返回的详细信息。</p>
<h2 id="heading-5zy65pmv-2">场景</h2>
<ul>
<li><p><strong>检查域名是否能够被解析:</strong>这是dig最基本的功能，通过查询 A、AAAA、CNAME 等记录，可以确认域名是否在 DNS 系统中存在并指向正确的 IP 地址或别名。</p>
</li>
<li><p><strong>查看DNS解析信息:</strong>包括DNS解析时长、最大超时时间等。</p>
</li>
<li><p><strong>检查指定的 DNS 服务器是否正常工作:</strong>可以通过指定不同的 DNS 服务器进行查询，判断特定的 DNS 服务器是否响应查询或返回正确的结果。</p>
</li>
<li><p><strong>检查DNS 记录是否正确传播:</strong>在 DNS 记录更新后，可以使用 dig 查询不同的 DNS 服务器，检查新的记录是否已经在全球范围内生效。</p>
</li>
<li><p><strong>查看DNS解析过程:</strong>DNS 缓存问题可能导致用户无法访问某些网站。使用 dig 的 +trace 选项可以帮助跟踪 DNS 解析过程，查找可能的缓存问题。</p>
</li>
<li><p><strong>查看DNS策略是否生效:</strong>dig baidu.com的时候可以发现有两个ip地址，可以通过dig来查看DNS 配置策略状态。</p>
</li>
</ul>
<h1 id="heading-nmap">nmap</h1>
<p>namp是一个网络扫描工具，主要用于发现网络上的主机、探测开放的端口、识别运行的服务以及操作系统类型等。</p>
<h2 id="heading-5zy65pmv-3">场景</h2>
<ul>
<li><p><strong>端口扫描:</strong>扫描主机的端口，查看是否开放，可以指定全连接或半连接扫描。</p>
</li>
<li><p><strong>网段范围的主机探测:</strong>本质上是ping命令，但是可以指定范围，通过ip ranges来进行主机的存活探测。</p>
</li>
<li><p><strong>Flood Testing:</strong>基本原理同ping。</p>
</li>
<li><p><strong>暴力破解测试:</strong>不细说。</p>
</li>
<li><p><strong>检测远程主机OS信息(估测)：</strong>原理是根据一些网络协议信息进行os版本的推测。</p>
</li>
</ul>
<h1 id="heading-netstat-amp-ss">netstat &amp; ss</h1>
<h2 id="heading-netstat">netstat</h2>
<p>netstat 用于显示 Linux 系统上的网络连接、路由表、接口统计信息、伪装连接、多播成员关系等。它提供了一个实时的网络活动快照，对于监控网络状态、诊断网络问题以及了解系统上的网络配置非常有用。</p>
<h2 id="heading-ss">ss</h2>
<p>ss 是另一个用于显示 Linux 系统上的套接字统计信息的命令行工具。它被认为是 netstat 的现代替代品，能够显示更多的 TCP 和套接字信息。两个工具在功能上有一些重叠。</p>
<h2 id="heading-kirlnlrmma8qkg"><strong>场景</strong></h2>
<ul>
<li><p><strong>查看所有活动的网络连接:</strong>显示所有 TCP 和 UDP 连接，以及正在监听的端口;查找占用特定端口的服务。</p>
</li>
<li><p><strong>查看套接字相关信息:</strong>包括 TCP 窗口大小、拥塞控制算法等。</p>
</li>
</ul>
<h1 id="heading-traceroute">traceroute</h1>
<p>traceroute 是一个网络诊断工具，用于跟踪数据包从计算机到目标主机所经过的路由（路径）。</p>
<h2 id="heading-5zy65pmv-4">场景</h2>
<ul>
<li><p><strong>诊断网络连接问题:</strong>无法访问某个网站或服务时，traceroute 可以帮助确定问题出在本地网络、ISP 网络还是目标服务器的网络路径上。</p>
</li>
<li><p><strong>识别网络瓶颈:</strong>观察每个跃点的延迟，可以找到网络中可能导致速度缓慢的瓶颈节点。</p>
</li>
</ul>
<h1 id="heading-speedtest-cli">speedtest-cli</h1>
<p>一个简单的网络测试工具，可以快速获取最近的网络测试节点并且测试网速。</p>
<h1 id="heading-vnstat">Vnstat</h1>
<p>Vnstat 是一个轻量级的命令行网络流量监控工具。它在后台运行，记录网络接口的接收和发送数据量，并提供各种报告，记录网络的历史使用情况。</p>
<h2 id="heading-5zy65pmv-5">场景</h2>
<ul>
<li><strong>网卡流量监控:</strong>基于proc，针对网卡的流量监控，可以分时进行查询，还可以导出图片进行分析。</li>
</ul>
<h1 id="heading-nethogs">nethogs</h1>
<p>类似于 top 命令，但专注于按进程实时显示网络带宽使用情况。它可以让你看到哪个进程正在消耗最多的上传和下载带宽。缺点就是不能够查询一些内核级别的进程的通信信息。</p>
<h2 id="heading-5zy65pmv-6">场景</h2>
<ul>
<li><strong>网卡实时流量监控:</strong>针对网卡的实时流量监控，可以达到进程级别。</li>
</ul>
<h1 id="heading-iftop">iftop</h1>
<p>实时监控网络接口的带宽使用情况，但它按网络连接（主机对）显示流量，并尝试解析主机名。可以查看哪些主机之间正在进行大量的网络通信。</p>
<ul>
<li><strong>实时流量监控:</strong>针对主机级别的实时流量监控，和nethogs有点类似。</li>
</ul>
<h1 id="heading-wireshark">wireshark</h1>
<p>一个强大的网络分析可视化工具。</p>
<ul>
<li><p><strong>详细分析网络协议:</strong> 可以深入分析 TCP/IP 协议栈的各个层次，查看每个数据包的详细头部信息和数据内容。</p>
</li>
<li><p><strong>性能分析:</strong> 分析网络延迟、丢包、重传等性能指标，帮助优化网络性能。</p>
</li>
</ul>
<h1 id="heading-tcpdump">tcpdump</h1>
<p>一个命令行抓包工具。它可以捕获指定接口上的网络数据包，并将数据包信息输出到终端或保存到文件中。</p>
<ul>
<li><p><strong>作为数据源:</strong> tcpdump 的一个常用的用处就是将dump下的数据导入到wireshark中进行详细分析。tcpdump可以快速抓取数据包。</p>
</li>
<li><p><strong>数据分析:</strong>可以进行一些简单的数据分析，当然可视化方面还是wireshark比较好。</p>
</li>
</ul>
<h1 id="heading-bcc">bcc</h1>
<p>BCC 是一个围绕 eBPF 构建的强大工具包，里面有很多关于网络抓包的工具。</p>
<p>没有接触过，皓哥这里有详细的介绍：<a target="_blank" href="https://coolshell.cn/articles/22320.html">https://coolshell.cn/articles/22320.html</a></p>
<h1 id="heading-reference">Reference</h1>
<ul>
<li><p>Nmap:<a target="_blank" href="https://www.recordedfuture.com/threat-intelligence-101/tools-and-techniques/nmap-commands">https://www.recordedfuture.com/threat-intelligence-101/tools-and-techniques/nmap-command</a></p>
</li>
<li><p>traceroute:<a target="_blank" href="https://support.microsoft.com/en-us/topic/how-to-use-tracert-to-troubleshoot-tcp-ip-problems-in-windows-e643d72b-2f4f-cdd6-09a0-fd2989c7ca8e">https://support.microsoft.com/en-us/topic/how-to-use-tracert-to-troubleshoot-tcp-ip-problems-in-windows-e643d72b-2f4f-cdd6-09a0-fd2989c7ca8e</a></p>
</li>
<li><p>tcpdump:<a target="_blank" href="https://www.modb.pro/db/634018">https://www.modb.pro/db/634018</a></p>
</li>
<li><p>bcc:<a target="_blank" href="https://www.cnblogs.com/charlieroro/p/13265252.html#bcc%E5%8F%AF%E8%A7%82%E6%B5%8B%E6%80%A7">https://www.cnblogs.com/charlieroro/p/13265252.html#bcc%E5%8F%AF%E8%A7%82%E6%B5%8B%E6%80%A7</a></p>
</li>
<li><p>Vnstat:<a target="_blank" href="https://cgking.gitbook.io/linux/linux/vnstat-tong-ji-fu-wu-qi-wang-ka-liu-liang">https://cgking.gitbook.io/linux/linux/vnstat-tong-ji-fu-wu-qi-wang-ka-liu-liang</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Kubernetes集群搭建]]></title><description><![CDATA[Prerequisites
硬件资源
目前登录使用的是机房内的一台机器（192.168.40.50） 连接已经配好的虚拟机地址 虚拟机硬件资源如下： os版本：Ubuntu 24.04 LTS (GNU/Linux 6.8.0-31-generic x86_64) k8s版本：Kubernetes v1.30.6

将会使用kubeadm进行安装
安装前要求检测

一台兼容的 Linux 主机。Kubernetes 项目为基于 Debian 和 Red Hat 的 Linux 发行版以及一些不提...]]></description><link>https://blog.ekreke.cn/kubernetes</link><guid isPermaLink="true">https://blog.ekreke.cn/kubernetes</guid><category><![CDATA[Kubernetes]]></category><dc:creator><![CDATA[Ekreke]]></dc:creator><pubDate>Fri, 01 Nov 2024 10:16:20 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/jOqJbvo1P9g/upload/43050394ce1612626ee7ba7cc9699c4e.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-prerequisites">Prerequisites</h1>
<h2 id="heading-56gs5lu26lwe5rqq">硬件资源</h2>
<p>目前登录使用的是机房内的一台机器（192.168.40.50） 连接已经配好的虚拟机地址 虚拟机硬件资源如下： os版本：Ubuntu 24.04 LTS (GNU/Linux 6.8.0-31-generic x86_64) k8s版本：Kubernetes v1.30.6</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730453790805/f2db13d5-11a2-471a-8322-6022375cf017.png" alt class="image--center mx-auto" /></p>
<p>将会使用kubeadm进行安装</p>
<h2 id="heading-5a6j6kof5ymn6kab5rgc5qoa5rwl">安装前要求检测</h2>
<ul>
<li><p>一台兼容的 Linux 主机。Kubernetes 项目为基于 Debian 和 Red Hat 的 Linux 发行版以及一些不提供包管理器的发行版提供通用的指令。</p>
</li>
<li><p>每台机器 2 GB 或更多的 RAM（如果少于这个数字将会影响你应用的运行内存）。</p>
</li>
<li><p>控制平面机器需要 CPU 2 核心或更多。</p>
</li>
<li><p>集群中的所有机器的网络彼此均能相互连接（公网和内网都可以）。</p>
</li>
<li><p>节点之中不可以有重复的主机名、MAC 地址或 product_uuid。请参见<a target="_blank" href="https://kubernetes.io/zh-cn/docs/setup/production-environment/tools/kubeadm/install-kubeadm/#verify-mac-address">这里</a>了解更多详细信息。</p>
</li>
<li><p>开启机器上的某些端口。请参见<a target="_blank" href="https://kubernetes.io/zh-cn/docs/setup/production-environment/tools/kubeadm/install-kubeadm/#check-required-ports">这里</a>了解更多详细信息。</p>
</li>
<li><p>交换分区的配置。kubelet 的默认行为是在节点上检测到交换内存时无法启动。更多细节参阅<a target="_blank" href="https://kubernetes.io/zh-cn/docs/concepts/architecture/nodes/#swap-memory">交换内存管理</a>。</p>
</li>
<li><p>如果 kubelet 未被正确配置使用交换分区，则你<strong>必须</strong>禁用交换分区。例如，<code>sudo swapoff -a</code> 将暂时禁用交换分区。要使此更改在重启后保持不变，请确保在如 <code>/etc/fstab</code>、<code>systemd.swap</code> 等配置文件中禁用交换分区，具体取决于你的系统如何配置。</p>
</li>
</ul>
<h1 id="heading-install-steps">Install Steps</h1>
<h2 id="heading-6ywn572u5bf6kab5os5lu25lul5yk572r57uc5yc5pww">配置必要插件以及网络参数</h2>
<p>k8s 需要通过ipv4来进行不同pod之间的通信</p>
<pre><code class="lang-sh"><span class="hljs-comment"># 安装两个必要内核插件，overlay 和 br_netfilter</span>
cat &lt;&lt;EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
​
<span class="hljs-comment"># 动态加载这两个模块</span>
sudo modprobe overlay
sudo modprobe br_netfilter
​
<span class="hljs-comment"># 设置所需的 sysctl 参数，参数在重新启动后保持不变</span>
cat &lt;&lt;EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables  = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward                 = 1
EOF
​
<span class="hljs-comment"># 应用 sysctl 参数而不重新启动</span>
sudo sysctl --system
​
<span class="hljs-comment"># 使用以下命令验证 net.ipv4.ip_forward 是否设置为 1</span>
sudo sysctl net.ipv4.ip_forward
</code></pre>
<h2 id="heading-containerd-service-container-runtime">下载containerd service 选择 container runtime</h2>
<p>这里选择containerd而不是docker（kubernetes新版本已将默认容器编排切换为containerd）</p>
<pre><code class="lang-json">## <span class="hljs-number">1</span>、containerd
# 下载包
wget https:<span class="hljs-comment">//github.com/containerd/containerd/releases/download/v1.7.22/containerd-1.7.22-linux-amd64.tar.gz</span>
​
# 将下载的包解压到/usr/local下
tar Cxzvf /usr/local containerd<span class="hljs-number">-1.7</span><span class="hljs-number">.22</span>-linux-amd64.tar.gz

# 下载服务启动文件
wget -O /etc/systemd/system/containerd.service https:<span class="hljs-comment">//raw.githubusercontent.com/containerd/containerd/main/containerd.service</span>

# 文件内容如下，下载不下来直接复制
cat /etc/systemd/system/containerd.service
[Unit]
Description=containerd container runtime
Documentation=https:<span class="hljs-comment">//containerd.io</span>
After=network.target local-fs.target
[Service]
ExecStartPre=-/sbin/modprobe overlay
ExecStart=/usr/local/bin/containerd
Type=notify
Delegate=yes
KillMode=process
Restart=always
RestartSec=<span class="hljs-number">5</span>
# Having non-zero Limit*s causes performance problems due to accounting overhead
# in the kernel. We recommend using cgroups to do container-local accounting.
LimitNPROC=infinity
LimitCORE=infinity
# Comment TasksMax if your systemd version does not supports it.
# Only systemd <span class="hljs-number">226</span> and above support this version.
TasksMax=infinity
OOMScoreAdjust=<span class="hljs-number">-999</span>
[Install]
WantedBy=multi-user.target
# 启动containerd
systemctl daemon-reload
systemctl enable --now containerd
​
## <span class="hljs-number">2</span>、Installing runc
wget https:<span class="hljs-comment">//github.com/opencontainers/runc/releases/download/v1.2.0-rc.3/runc.amd64</span>
TODO:worker1
sudo install -m <span class="hljs-number">755</span> runc.amd64 /usr/local/sbin/runc
</code></pre>
<p>containerd也可以通过apt来下载：</p>
<pre><code class="lang-json">`sudo apt-get install containerd.io`
</code></pre>
<h2 id="heading-containerd-important">修改containerd配置文件 (!important)</h2>
<h3 id="heading-containerd">containerd 相关配置</h3>
<pre><code class="lang-json"># 创建containerd目录
sudo mkdir /etc/containerd
​
# 恢复默认配置文件
sudo containerd config default | sudo tee /etc/containerd/config.toml &gt; /dev/<span class="hljs-literal">null</span>

​
# 切换为国内源
sed -i 's/registry.k8s.io/registry.aliyuncs.com\/google_containers/' /etc/containerd/config.toml
​
# 修改SystemCgroup为<span class="hljs-literal">true</span>
[plugins.<span class="hljs-string">"io.containerd.grpc.v1.cri"</span>.containerd.runtimes.runc.options]
    SystemdCgroup = <span class="hljs-literal">true</span>
​
# 重启服务
sudo systemctl daemon-reload
sudo systemctl restart containerd
</code></pre>
<h3 id="heading-cgroup">设置cgroup</h3>
<pre><code class="lang-shell">sudo sed -i 's/SystemdCgroup = true/SystemdCgroup = false/' /etc/containerd/config.toml
</code></pre>
<h4 id="heading-config">为什么还需要在config中设置镜像源</h4>
<p>使用kubeadm init 并指定<code>image-repository</code> 后仍需要在 <code>containerd</code> 的 <code>config.toml</code> 文件中指定镜像源的原因，主要是由于这两个配置的生命周期不同：</p>
<ul>
<li><p>kubeadm init 配置镜像源仅用于初始化过程加速必要的相关组件：如 <code>kube-apiserver</code>、<code>kube-controller-manager</code>、<code>kube-scheduler</code> 等）</p>
</li>
<li><p><code>containerd</code> 自身的配置不受 <code>kubeadm</code> 影响 。Kubernetes 组件启动后，由 <code>containerd</code> 负责持续管理容器和镜像的生命周期。当 <code>containerd</code> 需要重新拉取镜像（例如镜像更新或节点重启后），它将遵循自己的 <code>config.toml</code> 配置文件，而不是 <code>kubeadm</code> 的参数。未在 <code>containerd</code> 中配置镜像源时，它会默认访问官方的 <a target="_blank" href="http://k8s.gcr.io"><code>k8s.gcr.io</code></a> 源，而这一源在中国大陆访问通常较慢或受限。</p>
</li>
</ul>
<p>设置完成后保存、重新加载containerd配置、重启containerd服务、验证配置是否生效</p>
<pre><code class="lang-shell">sudo systemctl daemon-reload
sudo systemctl restart containerd
sudo systemctl status containerd
</code></pre>
<h2 id="heading-kubeadm">下载kubeadm以及相关组件</h2>
<pre><code class="lang-bash">sudo apt-get update
<span class="hljs-comment"># apt-transport-https may be a dummy package; if so, you can skip that package</span>
sudo apt-get install -y apt-transport-https ca-certificates curl gpg

<span class="hljs-comment"># If the directory `/etc/apt/keyrings` does not exist, it should be created before the curl command, read the note below.</span>
<span class="hljs-comment"># sudo mkdir -p -m 755 /etc/apt/keyrings</span>
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg

<span class="hljs-comment"># This overwrites any existing configuration in /etc/apt/sources.list.d/kubernetes.list</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.30/deb/ /'</span> | sudo tee /etc/apt/sources.list.d/kubernetes.list

sudo apt-get update

sudo apt-get install -y kubelet kubeadm kubectl

sudo apt-mark hold kubelet kubeadm kubectl
</code></pre>
<h2 id="heading-master">初始化master节点</h2>
<h3 id="heading-5zg95luk6kgm6yoo572y">命令行部署</h3>
<p>国内如果不指定image源，在拉取sandbox的时候会出现异常 sudo kubeadm init --image-repository <a target="_blank" href="http://registry.aliyuncs.com/google_containers">registry.aliyuncs.com/google_containers</a> --pod-network-cidr=10.128.0.0/16</p>
<h3 id="heading-yaml">yaml部署（推荐）</h3>
<p>首先执行kubeadm config print init-defaults 输出kubeadm默认配置</p>
<p>之后需要在kubeadm-config.yaml这个文件中修改以下几个地方：</p>
<ol>
<li><p>hostname 修改name为自己的主机名称，上下文： nodeRegistration: criSocket: unix:///run/containerd/containerd.sock imagePullPolicy: IfNotPresent imagePullSerial: true name: k8s-master taints: null</p>
</li>
<li><p>podSubnet 追加podSubnet podSubnet: <a target="_blank" href="http://10.244.0.0/16">10.244.0.0/16</a> 上下文： networking: dnsDomain: <a target="_blank" href="http://cluster.local">cluster.local</a> serviceSubnet: <a target="_blank" href="http://10.96.0.0/12">10.96.0.0/12</a> podSubnet: <a target="_blank" href="http://10.244.0.0/16">10.244.0.0/16</a></p>
</li>
<li><p>KubeletConfiguration 文件最后追加 KubeletConfiguration apiVersion: <a target="_blank" href="http://kubelet.config.k8s.io/v1beta1">kubelet.config.k8s.io/v1beta1</a> kind: KubeletConfiguration cgroupDriver: systemd</p>
</li>
</ol>
<p>之后，可以根据yaml文件初始化master节点：kubeadm init --config=kubeadm-config.yaml</p>
<h2 id="heading-master-1">master初始化完成</h2>
<p>初始化完成后显示示例：</p>
<pre><code class="lang-json">Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

Alternatively, if you are the root user, you can run:

export KUBECONFIG=/etc/kubernetes/admin.conf

You should now deploy a pod network to the cluster.
Run <span class="hljs-string">"kubectl apply -f [podnetwork].yaml"</span> with one of the options listed at:
  https:<span class="hljs-comment">//kubernetes.io/docs/concepts/cluster-administration/addons/</span>

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join <span class="hljs-number">192.168</span><span class="hljs-number">.40</span><span class="hljs-number">.140</span>:<span class="hljs-number">6443</span> --token wssf4y.fikow8fj6w1f2oar \
        --discovery-token-ca-cert-hash sha256:f942ac3be4f63eff7ff780145bf2937bd10656e7a361daf2da002fe2ece6a76e
</code></pre>
<p>注意：<code>export KUBECONFIG=$HOME/.kube/config</code> 这里需要设置环境变量，当使用普通用户非root的时候，如果没有上述操作，可能会默认读取/etc下的config文件</p>
<h2 id="heading-flannel">初始化flannel网络插件</h2>
<p>下载配置文件</p>
<pre><code class="lang-yaml"><span class="hljs-string">wget</span> <span class="hljs-string">https://raw.githubusercontent.com/coreos/flannel/a70459be0084506e4ec919aa1c114638878db11b/Documentation/kube-flannel.yml</span>
</code></pre>
<p>如果连接失败可参考文件：</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Namespace</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">labels:</span>
    <span class="hljs-attr">k8s-app:</span> <span class="hljs-string">flannel</span>
    <span class="hljs-attr">pod-security.kubernetes.io/enforce:</span> <span class="hljs-string">privileged</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">kube-flannel</span>
<span class="hljs-meta">---</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">ServiceAccount</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">labels:</span>
    <span class="hljs-attr">k8s-app:</span> <span class="hljs-string">flannel</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">flannel</span>
  <span class="hljs-attr">namespace:</span> <span class="hljs-string">kube-flannel</span>
<span class="hljs-meta">---</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">rbac.authorization.k8s.io/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">ClusterRole</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">labels:</span>
    <span class="hljs-attr">k8s-app:</span> <span class="hljs-string">flannel</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">flannel</span>
<span class="hljs-attr">rules:</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">apiGroups:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">""</span>
  <span class="hljs-attr">resources:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">pods</span>
  <span class="hljs-attr">verbs:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">get</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">apiGroups:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">""</span>
  <span class="hljs-attr">resources:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">nodes</span>
  <span class="hljs-attr">verbs:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">get</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">list</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">watch</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">apiGroups:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">""</span>
  <span class="hljs-attr">resources:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">nodes/status</span>
  <span class="hljs-attr">verbs:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">patch</span>
<span class="hljs-meta">---</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">rbac.authorization.k8s.io/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">ClusterRoleBinding</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">labels:</span>
    <span class="hljs-attr">k8s-app:</span> <span class="hljs-string">flannel</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">flannel</span>
<span class="hljs-attr">roleRef:</span>
  <span class="hljs-attr">apiGroup:</span> <span class="hljs-string">rbac.authorization.k8s.io</span>
  <span class="hljs-attr">kind:</span> <span class="hljs-string">ClusterRole</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">flannel</span>
<span class="hljs-attr">subjects:</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">kind:</span> <span class="hljs-string">ServiceAccount</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">flannel</span>
  <span class="hljs-attr">namespace:</span> <span class="hljs-string">kube-flannel</span>
<span class="hljs-meta">---</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">data:</span>
  <span class="hljs-attr">cni-conf.json:</span> <span class="hljs-string">|
    {
      "name": "cbr0",
      "cniVersion": "0.3.1",
      "plugins": [
        {
          "type": "flannel",
          "delegate": {
            "hairpinMode": true,
            "isDefaultGateway": true
          }
        },
        {
          "type": "portmap",
          "capabilities": {
            "portMappings": true
          }
        }
      ]
    }
</span>  <span class="hljs-attr">net-conf.json:</span> <span class="hljs-string">|
    {
      "Network": "10.244.0.0/16",
      "EnableNFTables": false,
      "Backend": {
        "Type": "vxlan"
      }
    }
</span><span class="hljs-attr">kind:</span> <span class="hljs-string">ConfigMap</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">labels:</span>
    <span class="hljs-attr">app:</span> <span class="hljs-string">flannel</span>
    <span class="hljs-attr">k8s-app:</span> <span class="hljs-string">flannel</span>
    <span class="hljs-attr">tier:</span> <span class="hljs-string">node</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">kube-flannel-cfg</span>
  <span class="hljs-attr">namespace:</span> <span class="hljs-string">kube-flannel</span>
<span class="hljs-meta">---</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">DaemonSet</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">labels:</span>
    <span class="hljs-attr">app:</span> <span class="hljs-string">flannel</span>
    <span class="hljs-attr">k8s-app:</span> <span class="hljs-string">flannel</span>
    <span class="hljs-attr">tier:</span> <span class="hljs-string">node</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">kube-flannel-ds</span>
  <span class="hljs-attr">namespace:</span> <span class="hljs-string">kube-flannel</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">matchLabels:</span>
      <span class="hljs-attr">app:</span> <span class="hljs-string">flannel</span>
      <span class="hljs-attr">k8s-app:</span> <span class="hljs-string">flannel</span>
  <span class="hljs-attr">template:</span>
    <span class="hljs-attr">metadata:</span>
      <span class="hljs-attr">labels:</span>
        <span class="hljs-attr">app:</span> <span class="hljs-string">flannel</span>
        <span class="hljs-attr">k8s-app:</span> <span class="hljs-string">flannel</span>
        <span class="hljs-attr">tier:</span> <span class="hljs-string">node</span>
    <span class="hljs-attr">spec:</span>
      <span class="hljs-attr">affinity:</span>
        <span class="hljs-attr">nodeAffinity:</span>
          <span class="hljs-attr">requiredDuringSchedulingIgnoredDuringExecution:</span>
            <span class="hljs-attr">nodeSelectorTerms:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">matchExpressions:</span>
              <span class="hljs-bullet">-</span> <span class="hljs-attr">key:</span> <span class="hljs-string">kubernetes.io/os</span>
                <span class="hljs-attr">operator:</span> <span class="hljs-string">In</span>
                <span class="hljs-attr">values:</span>
                <span class="hljs-bullet">-</span> <span class="hljs-string">linux</span>
      <span class="hljs-attr">containers:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">args:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">--ip-masq</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">--kube-subnet-mgr</span>
        <span class="hljs-attr">command:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">/opt/bin/flanneld</span>
        <span class="hljs-attr">env:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">POD_NAME</span>
          <span class="hljs-attr">valueFrom:</span>
            <span class="hljs-attr">fieldRef:</span>
              <span class="hljs-attr">fieldPath:</span> <span class="hljs-string">metadata.name</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">POD_NAMESPACE</span>
          <span class="hljs-attr">valueFrom:</span>
            <span class="hljs-attr">fieldRef:</span>
              <span class="hljs-attr">fieldPath:</span> <span class="hljs-string">metadata.namespace</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">EVENT_QUEUE_DEPTH</span>
          <span class="hljs-attr">value:</span> <span class="hljs-string">"5000"</span>
        <span class="hljs-attr">image:</span> <span class="hljs-string">docker.io/flannel/flannel:v0.26.0</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">kube-flannel</span>
        <span class="hljs-attr">resources:</span>
          <span class="hljs-attr">requests:</span>
            <span class="hljs-attr">cpu:</span> <span class="hljs-string">100m</span>
            <span class="hljs-attr">memory:</span> <span class="hljs-string">50Mi</span>
        <span class="hljs-attr">securityContext:</span>
          <span class="hljs-attr">capabilities:</span>
            <span class="hljs-attr">add:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">NET_ADMIN</span>
            <span class="hljs-bullet">-</span> <span class="hljs-string">NET_RAW</span>
          <span class="hljs-attr">privileged:</span> <span class="hljs-literal">false</span>
        <span class="hljs-attr">volumeMounts:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/run/flannel</span>
          <span class="hljs-attr">name:</span> <span class="hljs-string">run</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/etc/kube-flannel/</span>
          <span class="hljs-attr">name:</span> <span class="hljs-string">flannel-cfg</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/run/xtables.lock</span>
          <span class="hljs-attr">name:</span> <span class="hljs-string">xtables-lock</span>
      <span class="hljs-attr">hostNetwork:</span> <span class="hljs-literal">true</span>
      <span class="hljs-attr">initContainers:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">args:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">-f</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">/flannel</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">/opt/cni/bin/flannel</span>
        <span class="hljs-attr">command:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">cp</span>
        <span class="hljs-attr">image:</span> <span class="hljs-string">docker.io/flannel/flannel-cni-plugin:v1.5.1-flannel2</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">install-cni-plugin</span>
        <span class="hljs-attr">volumeMounts:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/opt/cni/bin</span>
          <span class="hljs-attr">name:</span> <span class="hljs-string">cni-plugin</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">args:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">-f</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">/etc/kube-flannel/cni-conf.json</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">/etc/cni/net.d/10-flannel.conflist</span>
        <span class="hljs-attr">command:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">cp</span>
        <span class="hljs-attr">image:</span> <span class="hljs-string">docker.io/flannel/flannel:v0.26.0</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">install-cni</span>
        <span class="hljs-attr">volumeMounts:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/etc/cni/net.d</span>
          <span class="hljs-attr">name:</span> <span class="hljs-string">cni</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">mountPath:</span> <span class="hljs-string">/etc/kube-flannel/</span>
          <span class="hljs-attr">name:</span> <span class="hljs-string">flannel-cfg</span>
      <span class="hljs-attr">priorityClassName:</span> <span class="hljs-string">system-node-critical</span>
      <span class="hljs-attr">serviceAccountName:</span> <span class="hljs-string">flannel</span>
      <span class="hljs-attr">tolerations:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">effect:</span> <span class="hljs-string">NoSchedule</span>
        <span class="hljs-attr">operator:</span> <span class="hljs-string">Exists</span>
      <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">hostPath:</span>
          <span class="hljs-attr">path:</span> <span class="hljs-string">/run/flannel</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">run</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">hostPath:</span>
          <span class="hljs-attr">path:</span> <span class="hljs-string">/opt/cni/bin</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">cni-plugin</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">hostPath:</span>
          <span class="hljs-attr">path:</span> <span class="hljs-string">/etc/cni/net.d</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">cni</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">configMap:</span>
          <span class="hljs-attr">name:</span> <span class="hljs-string">kube-flannel-cfg</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">flannel-cfg</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">hostPath:</span>
          <span class="hljs-attr">path:</span> <span class="hljs-string">/run/xtables.lock</span>
          <span class="hljs-attr">type:</span> <span class="hljs-string">FileOrCreate</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">xtables-lock</span>
</code></pre>
<h3 id="heading-6k6572u6zwc5yop5rqq">设置镜像源</h3>
<pre><code class="lang-toml"><span class="hljs-section">[plugins."io.containerd.grpc.v1.cri".registry.mirrors]</span> 
<span class="hljs-section">[plugins."io.containerd.grpc.v1.cri".registry.mirrors."[docker.io]</span>(http://docker.io)"] 
<span class="hljs-attr">endpoint</span> = [<span class="hljs-string">"[https://docker.1panel.live(https://docker.1panel.live)"</span>,<span class="hljs-string">"[https://docker.agsv.top](https://docker.agsv.top)"</span>,<span class="hljs-string">"[https://docker.agsvpt.work](https://docker.agsvpt.work)"</span>] 
<span class="hljs-section">[plugins."io.containerd.grpc.v1.cri".registry.mirrors."[k8s.gcr.io]</span>(http://k8s.gcr.io)"]
<span class="hljs-attr">endpoint</span> = [<span class="hljs-string">"[registry.aliyuncs.com/google_containers](http://registry.aliyuncs.com/google_containers)"</span>]
</code></pre>
<p>目前境内一些好用的镜像源：</p>
<pre><code class="lang-json">https:<span class="hljs-comment">//docker.1panel.live ✅网友自建</span>
地址 https:<span class="hljs-comment">//docker.agsv.top  ✅网友自建</span>
备用 https:<span class="hljs-comment">//docker.agsvpt.work  ✅网友自建</span>
地址①:https:<span class="hljs-comment">//dockerpull.com ✅网友自建</span>
地址②:https:<span class="hljs-comment">//dockerproxy.cn ✅网友自建</span>
</code></pre>
<p>配置完成后进行应用 kubectl apply -f kube-flannel.yml</p>
<p>kubectl get pods --all-namespaces 可以检查看dns和flannel相关pod有没有正常启动，</p>
<h2 id="heading-5yid5ael5yyw5bel5l2c6iqc54k5">初始化工作节点</h2>
<p>工作节点前置任务完成后（安装containerd&amp;安装kubeadm）</p>
<pre><code class="lang-bash">kubeadm join &lt;master-ip&gt;:&lt;port&gt; --token abcdef.0123456789abcdef \
    --discovery-token-ca-cert-hash sha256:&lt;discovery-token-ca-cert-hash&gt;
</code></pre>
<p><strong>主节点的 IP 和端口</strong>：</p>
<p>主节点的 IP 地址可以通过在主节点上运行 <code>ip addr show</code> 或 <code>ifconfig</code> 命令来获取。</p>
<p>端口通常是 <code>6443</code>，这是 Kubernetes API 服务器默认使用的端口。</p>
<p><strong>加入令牌（Token）</strong>：</p>
<p>当你使用 <code>kubeadm init</code> 初始化主节点时，命令会输出一个加入令牌。这个令牌用于工作节点加入集群时的身份验证。</p>
<p>如果你丢失了这个令牌，可以通过以下命令在主节点上重新获取：</p>
<pre><code class="lang-bash">kubeadm token list
</code></pre>
<p>这将列出所有可用的令牌及其过期时间。你可以选择一个合适的令牌用于 <code>kubeadm join</code> 命令。</p>
<p><strong>发现令牌的 CA 证书哈希</strong>：</p>
<p>发现令牌的 CA 证书哈希是用于验证集群信息的证书的哈希值。这个哈希值也可以在主节点上通过以下命令获取：</p>
<pre><code class="lang-bash">kubeadm token create --print-join-command
</code></pre>
<p>这个命令会输出一个完整的 <code>kubeadm join</code> 命令，包括所需的 CA 证书哈希。你可以直接复制这个命令中的哈希值。</p>
<pre><code class="lang-bash"><span class="hljs-comment"># 可以在master节点生成新的加入令牌</span>
kubeadm token create --print-join-command
</code></pre>
<p>初始化完成后可以跳到主节点，kubectl get nodes 查看是否加入成功</p>
<p>工作节点不需要进行flannel配置，工作节点加入集群后会自动进行同步</p>
<h3 id="heading-5yid5ael5yyw6iqc54k55asx6lsl5zco5zue6yca5pa55rov">初始化节点失败后回退方法</h3>
<p>重置 Kubernetes 节点，使其恢复到初始化前的状态 sudo kubeadm reset -f</p>
<p>手动清除配置文件： sudo rm -rf /etc/kubernetes /var/lib/etcd /var/lib/kubelet /var/lib/dockershim /var/run/kubernetes</p>
<p>查看是否有端口占用，如果有kill一下 sudo lsof -i :10250</p>
]]></content:encoded></item><item><title><![CDATA[一些好用的ai工具]]></title><description><![CDATA[导航站：
Discover the Best AI Tools and Services - Your Ultimate AI Directory at AIPURE
设计相关
https://slea.ai logo生成
stable diffusion 图片处理
AI 搜索：
perplexity.ai
felo.ai/
kimi
翻译：
Immersive Translate - Translate Web & PDF 沉浸式翻译，强烈推荐
DeepL目前体感翻译效果最好的，好处就是可以不...]]></description><link>https://blog.ekreke.cn/ai</link><guid isPermaLink="true">https://blog.ekreke.cn/ai</guid><category><![CDATA[#ai-tools]]></category><dc:creator><![CDATA[Ekreke]]></dc:creator><pubDate>Sun, 27 Oct 2024 16:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/ectRUZRYrYk/upload/477125ffec1b7fda402c055c9554f2d7.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-5a86iiq56uz77ya">导航站：</h3>
<p><a target="_blank" href="https://aipure.ai/">Discover the Best AI Tools and Services - Your Ultimate AI Directory at AIPURE</a></p>
<h3 id="heading-6k66k6h55u45ywz">设计相关</h3>
<p><a target="_blank" href="https://slea.ai">https://slea.ai</a> logo生成</p>
<p>stable diffusion 图片处理</p>
<h3 id="heading-ai">AI 搜索：</h3>
<p><a target="_blank" href="http://perplexity.ai">perplexity.ai</a></p>
<p><a target="_blank" href="http://felo.ai/">felo.ai/</a></p>
<p><a target="_blank" href="https://kimi.moonshot.cn">kimi</a></p>
<h3 id="heading-5776kr77ya">翻译：</h3>
<p><a target="_blank" href="https://chromewebstore.google.com/detail/immersive-translate-trans/bpoadfkcbjbfhfodiogcnhhhpibjhbnh?utm_source=official%EF%BC%88GLM/SiliconCloud">Immersive Translate - Translate Web &amp; PDF</a> 沉浸式翻译，强烈推荐</p>
<p><a target="_blank" href="https://www.deepl.com/en/translator">DeepL</a>目前体感翻译效果最好的，好处就是可以不局限于web端</p>
<p><a target="_blank" href="http://podwise.ai">podwise.ai</a></p>
<h3 id="heading-llm-api">LLM API：</h3>
<p><a target="_blank" href="http://siliconflow.com">siliconflow.com</a></p>
<p><a target="_blank" href="http://openrouter.ai">openrouter.ai</a></p>
<p><a target="_blank" href="http://OpenRouter.ai">OpenRouter.ai</a></p>
<h3 id="heading-chatbot">Chatbot：</h3>
<p><a target="_blank" href="http://lobechat.com">lobechat.com</a></p>
<p><a target="_blank" href="http://poe.com">poe.com</a></p>
<p><a target="_blank" href="http://monica.im">monica.im</a></p>
<p><a target="_blank" href="https://chatgpt.com/">chatgpt</a></p>
<p><a target="_blank" href="https://claude.ai/">claude</a></p>
<h3 id="heading-ai-ide">AI IDE：</h3>
<p><a target="_blank" href="http://www.cursor.com">www.cursor.com</a></p>
<p><a target="_blank" href="https://v0.dev/">v0</a></p>
<h3 id="heading-kirovoxliqnpmixor7sqku8mg"><strong>辅助阅读</strong>：</h3>
<p><a target="_blank" href="http://notebooklm.google.com">notebooklm.google.com</a></p>
<h3 id="heading-ai-1">AI 应用开发：</h3>
<p><a target="_blank" href="http://dify.ai">dify.ai</a></p>
<p><a target="_blank" href="https://www.coze.com/">coze</a></p>
<h3 id="heading-6kt6zz55sf5oiq77ya">语音生成：</h3>
<p><a target="_blank" href="http://elevenlabs.io">elevenlabs.io</a></p>
<p><a target="_blank" href="http://fish.audio">fish.audio</a></p>
<p><a target="_blank" href="https://github.com/SWivid/F5-TTS">https://github.com/SWivid/F5-TTS</a></p>
<h3 id="heading-5lia5lqb5lmx5lid5ywr57of55qe">一些乱七八糟的</h3>
<p><a target="_blank" href="https://sunoprompt.com/zh">suno prompt 生成</a></p>
<p><a target="_blank" href="suno.com">suno</a></p>
<p><a target="_blank" href="http://napkin.ai">示例图生成</a></p>
<h3 id="heading-ppt">PPT:</h3>
<p><a target="_blank" href="https://gamma.app/signup?r=mz3b22zprlhdp5t">https://gamma.app/signup?r=mz3b22zprlhdp5t</a></p>
<hr />
]]></content:encoded></item><item><title><![CDATA[Visual Studio Code 配置delve断点调试]]></title><description><![CDATA[0.dlv & dap
Delve 是一个 Go 语言的调试器，由 Sam Boyer 和 Brian Powers 开发，并且是 Go 社区广泛使用的调试工具之一。它提供了一套丰富的调试功能，专门针对 Go 语言的特性设计，使得开发者可以更加高效地进行程序调试。默认在bin目录下。
DAP（Debug Adapter Protocol）是一个标准化的 JSON-RPC 协议，用于定义调试器和开发环境之间的通信，支持跨语言和跨平台的调试功能。 https://microsoft.github.i...]]></description><link>https://blog.ekreke.cn/visual-studio-code-delve</link><guid isPermaLink="true">https://blog.ekreke.cn/visual-studio-code-delve</guid><category><![CDATA[tools]]></category><dc:creator><![CDATA[Ekreke]]></dc:creator><pubDate>Wed, 14 Aug 2024 16:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/cckf4TsHAuw/upload/4ba71a6fb494d3d08ee9f799db3d5340.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-0dlv-amp-dap">0.dlv &amp; dap</h2>
<p>Delve 是一个 Go 语言的调试器，由 Sam Boyer 和 Brian Powers 开发，并且是 Go 社区广泛使用的调试工具之一。它提供了一套丰富的调试功能，专门针对 Go 语言的特性设计，使得开发者可以更加高效地进行程序调试。默认在bin目录下。</p>
<p>DAP（Debug Adapter Protocol）是一个标准化的 JSON-RPC 协议，用于定义调试器和开发环境之间的通信，支持跨语言和跨平台的调试功能。 <a target="_blank" href="https://microsoft.github.io/debug-adapter-protocol/overview">https://microsoft.github.io/debug-adapter-protocol/overview</a></p>
<h2 id="heading-1-visual-studio-code">1. Visual Studio Code 配置</h2>
<p>当在Visual Studio Code中安装go官方tools后，默认会使用dlv作为debugger，dap作为protocol进行本机调试</p>
<p>Visual Studio Code中根据launch.json中的配置信息进行debug 一个常见的配置信息如下</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"version"</span>: <span class="hljs-string">"0.2.0"</span>,
    <span class="hljs-attr">"configurations"</span>: [
        {
            <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Launch Package"</span>,
            <span class="hljs-attr">"type"</span>: <span class="hljs-string">"go"</span>,
            <span class="hljs-attr">"request"</span>: <span class="hljs-string">"launch"</span>,
            <span class="hljs-attr">"mode"</span>: <span class="hljs-string">"auto"</span>,
            <span class="hljs-attr">"program"</span>: <span class="hljs-string">"cmd/xxx/."</span>
        }
    ]
}
</code></pre>
<ul>
<li><p>version: Visual Studio Code 配置文件的具体版本</p>
</li>
<li><p>configurations：一个数组，可以通过json形式添加不同的config</p>
</li>
<li><p>type: 指定了调试会话使用的调试器类型。在这里，"go" 表示使用 Go 语言的调试器。</p>
</li>
<li><p>request: 定义了调试会话的请求类型。"launch" 表示启动一个新的程序进行调试。</p>
</li>
<li><p>mode: "auto",：指定了调试模式。"auto" 模式意味着调试器将自动选择是直接启动程序还是附加到已运行的程序上。</p>
</li>
<li><p>"program": "cmd/healthcare/."：定义了要启动和调试的程序的路径。这里 <code>"cmd/healthcare/."</code> 指的是项目中 <code>cmd/healthcare</code> 目录下的主程序入口文件。 详细配置字段以及流程： <a target="_blank" href="https://code.visualstudio.com/docs/editor/debugging">https://code.visualstudio.com/docs/editor/debugging</a></p>
</li>
</ul>
<h2 id="heading-2">2.注意事项</h2>
<p>go的包管理机制： <a target="_blank" href="https://go.dev/blog/using-go-modules">https://go.dev/blog/using-go-modules</a></p>
<p>Q: go run main.go的时候，go编译器会尝试自动编译和链接引入的import包，当使用wire时，wire会生成wire_gen.go ，如果在kratos框架中就会自动生成在 main包下，且wireApp为包可见级别。此时go run main.go会提示wireApp is undefined , 可能是go build 的时候，只build main.go 并没有寻找到wire生成的gen.go ， main.go 中也没有显式的import(存疑)，导致buid的过程中没有链接到wireApp S: 解决方法：build整个cmd项目目录下main package的所有go文件 go build .</p>
<p>同理，在Visual Studio Code配置 "program": "cmd/xxx/." 的时候，需要指定main package 所在的平级目录文件夹。不可cmd/xxx/main.go</p>
]]></content:encoded></item><item><title><![CDATA[个人知识管理体系总结]]></title><description><![CDATA[0.前言
这两天在小宇宙发现Ep 23. 个人知识管理体系系列 - 输入篇 - 捕蛇者说 (pythonhunter.org)有关知识管理体系以及如何学习的系列podcast。深有感触，这两年经历了一系列事情，我对学习这件事有了很多想法，遂整理一篇blog沉淀一下。
1. 一些影响
考研/高强度的网上冲浪/实习。 关于考研和实习这两件事我感觉可以挖个坑后面详细讲讲。先把重心放在知识管理这个主题上。
关于考研
虽说考研最后没有参加考试，但是对我影响还是很大的，有以下几个方面：能够真正沉下心输入&内...]]></description><link>https://blog.ekreke.cn/5liq5lq655l6kg566h55cg5l2t57o75oc757ut</link><guid isPermaLink="true">https://blog.ekreke.cn/5liq5lq655l6kg566h55cg5l2t57o75oc757ut</guid><category><![CDATA[tools]]></category><dc:creator><![CDATA[Ekreke]]></dc:creator><pubDate>Sun, 28 Jul 2024 16:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/xrVDYZRGdw4/upload/d950f72e6c7a3d4c2b41178c512a2a4e.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-0">0.前言</h1>
<p>这两天在小宇宙发现<a target="_blank" href="https://pythonhunter.org/episodes/ep23">Ep 23. 个人知识管理体系系列 - 输入篇 - 捕蛇者说 (</a><a target="_blank" href="http://pythonhunter.org">pythonhunter.org</a><a target="_blank" href="https://pythonhunter.org/episodes/ep23">)</a>有关知识管理体系以及如何学习的系列podcast。深有感触，这两年经历了一系列事情，我对学习这件事有了很多想法，遂整理一篇blog沉淀一下。</p>
<h1 id="heading-1">1. 一些影响</h1>
<p>考研/高强度的网上冲浪/实习。 关于考研和实习这两件事我感觉可以挖个坑后面详细讲讲。先把重心放在知识管理这个主题上。</p>
<h3 id="heading-5ywz5lqo6icd56cu">关于考研</h3>
<p>虽说考研最后没有参加考试，但是对我影响还是很大的，有以下几个方面：能够真正沉下心输入&amp;内化&amp;输出（一个完整的知识增长workflow流程）、计算机基础&amp;数学基础补全、对应试教育的厌恶++；</p>
<h3 id="heading-5ywz5lqo572r5lik5yay5rwq">关于网上冲浪</h3>
<p>起因是因为去年十月份在放弃考研之后。一段时间高强度网上冲浪，在知乎上刷到<a target="_blank" href="https://www.zhihu.com/people/L.M.Sherlock/answers/by_votes">Thoughts Memo (</a><a target="_blank" href="http://zhihu.com">zhihu.com</a><a target="_blank" href="https://www.zhihu.com/people/L.M.Sherlock/answers/by_votes">)</a>这位博主的文章，花了两天的时间通读了一遍。很有感触，并且开始使用anki对于碎片化记忆点进行review。</p>
<p>开始玩hoi4，和一些不太符合主流价值观的mod，有将近一个多月的时间是这样度过的：白天hoi4，夜晚刷Wikipedia相关历史词条。</p>
<p>开始接触podcast，最早听的就是beyondcode，印象比较深的还是郭宇的那集，应该正好是那段时间播出的。</p>
<p>开始接触stand-up comedy，特指台湾(STR network)。博恩夜夜秀 -&gt; 贺龙夜夜秀。个人：博恩、贺龙、龙龙、微笑丹尼。</p>
<p>这段时间对世界观塑造的影响比较大，对于生活、技术的理解、世界观、价值观的改变等等。扩展了信息源，有意识的开始使用英语摄取信息。开始学习德文。</p>
<h3 id="heading-5ywz5lqo5a6e5lmg">关于实习</h3>
<p><strong>"you don't know what you don't know"</strong></p>
<p>三月份实习总共拿了两个offer， 一个是深圳的一家不知名小游戏公司的go游戏sdk开发+运维 还有一个就是美团的软服；因为成都比较熟悉加上有学长在，而且毕竟是大厂，还是选择了美团。 对于知识管理方面主要是一些流程规范（短短两个字其实内涵很多，如何沉淀文档、如何写一份技术设计、如何写一份需求复盘、如何阅读prd都是值得参考的。这个之后我会放在[[美团经历]]中详细讲一下）以及一些resource。</p>
<h1 id="heading-2-why">2. 现状和Why</h1>
<p>按照流转状态分类的话和podcast标题一致，大致分为三个过程：输入/内化/输出。</p>
<h3 id="heading-6l6t5ywl">输入</h3>
<p>输入是很重要的一环，garbage in garbage out 。我一直坚信一个观点， 输入的质量决定输出的质量。</p>
<p>本文我主要想谈论的是一些&lt;非人相关的input&gt;。其实能够有一个很好的社交圈，如果能够有机会one one ，是更好的选择。</p>
<p>目前我的输入渠道：官方文档(如果有)=书&gt;博客&gt;podcast&gt;others。</p>
<p>官方文档永远是最权威的，且最具时效性，如版本更迭，大部分技术项目也都会有archive。书的话时效性就相对较低了，但整体连贯，逻辑结构强，一些细节会更加丰满且通俗易懂。博客的话质量就层次不齐了（比如我这个应该就不太行😂）。需要挑选一些共识度较高的食用。podcast同理，但因为缺少一定的视觉输入，还是差了点意思，一般也就通勤听听，大多是一些比较轻松的话题。</p>
<p>当然，屏蔽一些内容农场也是很有必要的前置任务。可以试一下ublocklist.<a target="_blank" href="https://coolshell.cn/articles/9308.html">“作环保的程序员，从不用百度开始” | 酷 壳 - CoolShell</a></p>
<h3 id="heading-5yaf5yyw">内化</h3>
<p>内化这个词在我的理解就是对input的消化和吸收。 内化的知识我觉得可以分为两种类型：逻辑型 &amp; 记忆型。其实也就是知识论中的先验和后验。两种不同的类型有不同解决方式。逻辑型的类似什么TCP需要三次握手？四次挥手？Why desgin？这种知识我觉得可以通过 梳理逻辑关系来加深理解。后验的一些东西类似单词拼写、一些command，就不如直接交给anki了。</p>
<h3 id="heading-6l6t5ye6">输出</h3>
<p>输出有很多方式：讲演、思维导图、文档、思维导图... 输出算是我的强项了，但是一些有逻辑性的文档输出还是差了一点。我一直觉得我的思维比较发散，需要在逻辑梳理这方面多加强一些。</p>
<h1 id="heading-3">3. 目前常用的一些知识管理工具</h1>
<p>Vscode + Vim插件 + Markdown  文本编辑器</p>
<p><a target="_blank" href="http://Draw.io">Draw.io</a>  绘图</p>
<p>Blog 目前是Typecho，有自己重新build的一个想法</p>
<p>Obsidian + Notion 笔记管理</p>
<p>Google Calendar 日程/Todo</p>
<p>Feedly  RSS订阅</p>
<p>Anki 碎片化记忆</p>
<p>uBlackulist 屏蔽内容农场</p>
]]></content:encoded></item><item><title><![CDATA[Hello World]]></title><description><![CDATA[0.前言
  2024年六月是一段特别的日子。离职、毕业、旅行，各种花花绿绿的计划排满了整个google calendar。在离职前后就有过想重新搭一个博客的想法，但因为各种乱七八糟的事情完成日期一直拖到了26号这样，虽说过程不是很难，但也踩了不少小坑。第一篇博客就用来记录一下这个新bolg plateform搭建时的心路历程吧。
1.需求分析
  虽说大学时正经的帖子没写过多少，但是搭建博客还是花了不少的功夫。wordpress、hexo、jekyll都上线把玩过。wordpress有点过于沉...]]></description><link>https://blog.ekreke.cn/hello-world</link><guid isPermaLink="true">https://blog.ekreke.cn/hello-world</guid><category><![CDATA[blog]]></category><dc:creator><![CDATA[Ekreke]]></dc:creator><pubDate>Sun, 23 Jun 2024 16:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/oqStl2L5oxI/upload/c6f0d08a51a89887bc53dcebd70147ec.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-0">0.前言</h1>
<p>  2024年六月是一段特别的日子。离职、毕业、旅行，各种花花绿绿的计划排满了整个google calendar。在离职前后就有过想重新搭一个博客的想法，但因为各种乱七八糟的事情完成日期一直拖到了26号这样，虽说过程不是很难，但也踩了不少小坑。第一篇博客就用来记录一下这个新bolg plateform搭建时的心路历程吧。</p>
<h1 id="heading-1">1.需求分析</h1>
<p>  虽说大学时正经的帖子没写过多少，但是搭建博客还是花了不少的功夫。wordpress、hexo、jekyll都上线把玩过。wordpress有点过于沉重了，不仅访问速度慢，并且似乎一直存在一些插件安全问题。</p>
<p>  hexo和jekyll这种静态的安全问题不太用考虑。大一的时候玩过hexo，那个时候还没有类似github pages这种东西，直接挂载服务器，配置一些新组件和修改新增博客的时候有点坐牢，之后也就不了了之了，记录还是多用语雀和notion。</p>
<p>  jekyll，大约是在大三的时候接触的，通过github action中的自动化脚本可以轻松的在push以后自动推送，配合vscode中vim用的也还是挺爽的，但是总觉得静态博客没有管理后台还是差了点意思。并且因为GFW&amp;&amp;国内政策的问题，搭建小站需要各种流程，很是繁琐。</p>
<p>  根据上面的一些问题大概总结了这次搭建博客平台的一些需求点：</p>
<ul>
<li><p>无需审核备案</p>
</li>
<li><p>流量安全</p>
</li>
<li><p>动态博客</p>
</li>
</ul>
<h1 id="heading-2">2.资源整理</h1>
<ul>
<li><p>阿里云 上海 2gb ram 2 core ecs</p>
</li>
<li><p>阿里云 ekreke.xyz</p>
</li>
<li><p>RackNerd Los Angeles 2gb ram 1 core VPS （新购入）</p>
</li>
<li><p>cloudflare account (cloudflare 下简称cf)</p>
</li>
<li><p>人生中宝贵的3天</p>
</li>
</ul>
<h1 id="heading-3">3.解决方案</h1>
<h2 id="heading-5peg6zya5a6h5qc45ash5qgi6zya5rgc5pa55qgi">无需审核备案需求方案</h2>
<p>  对于备案这种墙内问题，以前尝试通过端口转发+nginx反代隐藏真实80端口，但是不仅需要一台空闲的机器，而且还会有后续的审核风险，这次干脆一了百了选择墙外服务器来解决。(端口转发+反代规避备案教程类似：<a target="_blank" href="https://www.xiaoweigod.com/network/1566.html">https://www.xiaoweigod.com/network/1566.html</a>)</p>
<h2 id="heading-5rwb6yep5a6j5ywo6zya5rgc5pa55qgi">流量安全需求方案</h2>
<p>  流量安全主要针对的就是一些恶意流量攻击的问题。这个部分主要就外包给cf来做了，还是免费。cf的提供的功能非常全面，可以进行托管省时省力省心，唯一的缺点就是大陆GFW会对某些托管的ip进行封锁，拨测ping值感人。关于更多cf相关内容可以详见此podcast(<a target="_blank" href="https://www.xiaoyuzhoufm.com/episode/6645a7b9fbc01d7f367e6da3">https://www.xiaoyuzhoufm.com/episode/6645a7b9fbc01d7f367e6da3</a>)</p>
<h2 id="heading-5yqo5ocb5y2a5a6i5pa55qgi">动态博客方案</h2>
<p>  这部分无非就是套架子，都大同小异。以前自己也写过类似的博客系统小玩具，这次为了方便偷个懒还是直接套用了。google了一下 top 10 blog framework , 比较中意的就是ghost。不过实装过后会发现在大陆访问的时候会加载一些被墙的静态资源。并且会出现莫名其妙的http&amp;https混合内容问题。看了一下论坛issues，也并没有解决，遂弃。到达南京后在lvan chen的力推下整上了typecho。</p>
<h1 id="heading-4">4.搭建步骤</h1>
<ol>
<li><p>解析域名：因为以前的域名是阿里云购买，担心直接指海外vps不安全遂放在cf上进行托管。cf的托管方式也很有意思，不是直接进行域名转移，而是在阿里云上更改解析权威域名的dns服务器域名。之后在dns板块进行配置解析。</p>
</li>
<li><p>服务部署：此处可参考typecho官方文档。mysql直接docker run一下即可。</p>
</li>
<li><p>ssl证书/nginx配置：证书可以手动进行配置，参考(<a target="_blank" href="http://chenk.vip/index.php/archives/133/">http://chenk.vip/index.php/archives/133/</a>) 也可以使用certbot（<a target="_blank" href="https://certbot.eff.org/%EF%BC%89">https://certbot.eff.org/）</a> nginx配置一下服务的端口即可。</p>
</li>
</ol>
<h1 id="heading-5hello-world">5.Hello World!!!</h1>
<p>  在查ghost的issues的时候偶然看到这么一条评论：其实技术选型并不重要，博客最终要的还是<strong>内容</strong>。自认为还是一个想法挺多的人，但是说来惭愧大学四年并没有积累太多的文字。文字积累的最多的还是在公司内网的平台上，大多也是一些简短的总结和技术设计，随着离职也不复存在了。随着年龄的增长，越来越觉得记录是一个很有必要的事情。   粗浅的分析一下，人对于一件事的观点变更大概有以下三种模式：</p>
<ol>
<li><p>accept A</p>
</li>
<li><p>A -&gt; B</p>
</li>
<li><p>A -&gt; B.. -&gt; A</p>
</li>
</ol>
<p>  我并不觉得任何单一的观点具有可记录的意义。就像陈皓老师在一期podcast中说的一样，单个的点不是知识，串联起来的过程才是知识、经验，才是我们真正需要关注的东西。For example， 如果分别在10 ， 18 ， 22岁的时候分别问我大陆有没有言论自由，我觉得我会分别回答有、没有、有。一个类似ABA Problem的过程 :)（<a target="_blank" href="https://en.wikipedia.org/wiki/ABA_problem%EF%BC%89%E5%AE%9E%E9%99%85%E4%B8%8A%E5%88%B0%E5%BA%95%E6%9C%89%E6%88%96%E6%B2%A1%E6%9C%89%E5%B9%B6%E6%B2%A1%E6%9C%89%E4%BB%80%E4%B9%88%E5%8F%AF%E4%BB%A5%E8%AE%B0%E5%BD%95%E7%9A%84%E4%BB%B7%E5%80%BC%EF%BC%8C%E4%BD%86%E5%A6%82%E6%9E%9C%E7%BB%86%E7%A9%B6%E5%A6%82%E4%B8%8A%E6%AF%8F%E4%B8%80%E6%AE%B5%E6%97%B6%E9%97%B4%E7%A4%BE%E4%BC%9A%E7%9A%84%E5%8F%98%E9%9D%A9%EF%BC%8C%E4%B8%AA%E4%BA%BA%E7%9A%84%E7%BB%8F%E5%8E%86%E5%8E%86%E7%A8%8B%EF%BC%8C%E5%B0%B1%E8%83%BD%E6%89%BE%E5%88%B0%E4%B8%80%E4%B8%AA%E7%8A%B6%E6%80%81%E5%8F%98%E6%9B%B4%E7%9A%84why%E3%80%82%E8%BF%99%E6%98%AF%E5%B8%B8%E5%B8%B8%E8%A2%AB%E4%BA%BA%E6%89%80%E5%BF%BD%E8%A7%86%E7%9A%84%EF%BC%8C%E4%B9%9F%E6%98%AF%E6%88%91%E8%A7%89%E5%BE%97%E6%9C%80%E4%B8%BA%E5%80%BC%E5%BE%97%E5%92%8C%E5%BF%85%E8%A6%81%E8%AE%B0%E5%BD%95%E7%9A%84--experience%E3%80%82">https://en.wikipedia.org/wiki/ABA_problem）实际上到底有或没有并没有什么可以记录的价值，但如果细究如上每一段时间社会的变革，个人的经历历程，就能找到一个状态变更的why。这是常常被人所忽视的，也是我觉得最为值得和必要记录的--experience。</a>   有点扯远了。。anyway , 希望从现在开始能以每周一更的频率更新下去，记录一些有趣的事情。</p>
<p><strong>（全文完）</strong></p>
]]></content:encoded></item></channel></rss>