<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Hexo</title>
  
  
  <link href="http://example.com/atom.xml" rel="self"/>
  
  <link href="http://example.com/"/>
  <updated>2025-04-29T14:09:57.409Z</updated>
  <id>http://example.com/</id>
  
  <author>
    <name>John Doe</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>Notion获取教育会员</title>
    <link href="http://example.com/2025/04/29/Notion%E8%8E%B7%E5%8F%96%E6%95%99%E8%82%B2%E4%BC%9A%E5%91%98/"/>
    <id>http://example.com/2025/04/29/Notion%E8%8E%B7%E5%8F%96%E6%95%99%E8%82%B2%E4%BC%9A%E5%91%98/</id>
    <published>2025-04-29T13:09:54.000Z</published>
    <updated>2025-04-29T14:09:57.409Z</updated>
    
    <content type="html"><![CDATA[<p>Notion是一款强大的笔记和协作工具，提供了专门针对教育用户的免费Plus计划。如果你是学生或教育工作者，可以通过以下步骤申请Notion教育优惠，解锁Plus功能。</p><hr><h2 id="申请步骤"><a href="#申请步骤" class="headerlink" title="申请步骤"></a>申请步骤</h2><ol><li><p><strong>使用学校邮箱注册Notion账号</strong></p><p> 确保你的Notion账号绑定的是学校提供的教育邮箱，注册完成之后可申请教育版。</p></li><li><p><strong>确保工作区只有一个成员</strong></p><p> 如果你的工作区中有其他成员，请先移除，确保工作区仅包含你自己。</p></li><li><p><strong>检查当前计划是否为免费版</strong></p><p> 如果你已经订阅了付费计划，请先降级到免费版：</p><ul><li>进入 <strong>Settings → Plans</strong></li><li>点击 <strong>Downgrade</strong> 降级到免费计划。</li></ul></li><li><p><strong>申请教育优惠</strong></p><ul><li>进入 <strong>Settings → Plans</strong></li><li>点击 <strong>Get free education plan</strong> 按钮，系统会自动验证你的学校邮箱。</li><li>验证成功后，你的账号将自动升级到教育版Plus计划。</li></ul></li></ol><hr><p><img                       lazyload                     src="/images/loading.svg"                     data-src="https://tc.aptzone.cc/images/2025/03/01/202503012051515.png"                                     ></p><h2 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h2><ul><li>你的学校必须在 <a class="link"   href="https://www.whed.net/home.php" >World Higher Education Database (WHED)<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a> 中注册，才能享受教育优惠。</li><li>如果你的学校邮箱未被自动验证，可以联系Notion的支持团队进行人工审核。</li></ul><hr><p>通过以上步骤，你就可以轻松解锁Notion的教育版Plus功能，享受更多强大功能，提升学习和工作的效率！</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;Notion是一款强大的笔记和协作工具，提供了专门针对教育用户的免费Plus计划。如果你是学生或教育工作者，可以通过以下步骤申请Notion教育优惠，解锁Plus功能。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;申请步骤&quot;&gt;&lt;a href=&quot;#申请步骤&quot; class=&quot;header</summary>
      
    
    
    
    
    <category term="教育优惠" scheme="http://example.com/tags/%E6%95%99%E8%82%B2%E4%BC%98%E6%83%A0/"/>
    
  </entry>
  
  <entry>
    <title>三角套利的基本逻辑</title>
    <link href="http://example.com/2025/04/29/%E4%B8%89%E8%A7%92%E5%A5%97%E5%88%A9%E7%9A%84%E5%9F%BA%E6%9C%AC%E9%80%BB%E8%BE%91/"/>
    <id>http://example.com/2025/04/29/%E4%B8%89%E8%A7%92%E5%A5%97%E5%88%A9%E7%9A%84%E5%9F%BA%E6%9C%AC%E9%80%BB%E8%BE%91/</id>
    <published>2025-04-29T12:20:12.000Z</published>
    <updated>2025-04-29T13:09:12.399Z</updated>
    
    <content type="html"><![CDATA[<h2 id="什么是三角套利？"><a href="#什么是三角套利？" class="headerlink" title="什么是三角套利？"></a>什么是三角套利？</h2><p>三角套利是一种利用不同交易市场（或同一市场内不同交易对）之间短暂的价格差异来获取理论上无风险利润的策略。尤其在波动剧烈的市场中，这类价格不一致的情况可能更频繁出现，为敏锐的交易者提供了潜在的套利机会。</p><h2 id="如何判断与计算套利机会？"><a href="#如何判断与计算套利机会？" class="headerlink" title="如何判断与计算套利机会？"></a>如何判断与计算套利机会？</h2><p>三角套利的核心在于快速发现并计算三个交易对之间的汇率关系，判断是否存在一个闭环交易能让你用初始资金换回更多资金。以下是具体的判别式、计算方法以及一个交互式计算器，可以帮助你理解和模拟这一过程：</p><iframe src="https://zefanhu.github.io/crypto-arbitrage-tool/static/index.html" width="100%" height="650px" style="border: 1px solid #eee; display: block; margin: 20px auto;" title="三角套利的基本逻辑">    您的浏览器不支持 iframe，无法显示交互式图表。请尝试更换浏览器或设备。</iframe><p><em>（如果您无法看到上面的图表，请确保您的浏览器支持 iframe。）</em></p><h2 id="高波动市场下的策略优化"><a href="#高波动市场下的策略优化" class="headerlink" title="高波动市场下的策略优化"></a>高波动市场下的策略优化</h2><p>市场瞬息万变，尤其在高波动时期，简单的套利逻辑需要配合精密的策略优化才能有效执行：</p><ol><li><strong>高频监控是前提</strong>：套利窗口可能仅持续几秒甚至毫秒，依赖高频数据接口（如WebSocket）实时捕捉价格变动至关重要。</li><li><strong>动态调整阈值</strong>：<ul><li><strong>市场平稳时</strong>：可以接受较低的利润阈值（例如0.2%），捕捉更多微小机会。</li><li><strong>市场剧烈波动时</strong>：应提高阈值（例如0.5%或更高），以覆盖增加的滑点和执行风险。</li></ul></li><li><strong>智能管理流动性</strong>：<ul><li><strong>拆分订单</strong>：避免大额订单冲击市场价格，降低滑点。</li><li><strong>选择交易所</strong>：优先在交易深度好、流动性强的平台执行。</li><li><strong>设置滑点容忍度</strong>：预先设定可接受的最大滑点，超出则取消订单，避免意外损失。</li></ul></li></ol><hr><h2 id="不可忽视的风险管理"><a href="#不可忽视的风险管理" class="headerlink" title="不可忽视的风险管理"></a>不可忽视的风险管理</h2><p>即使是理论上的无风险套利，在实际操作中也存在风险：</p><ol><li><strong>滑点风险</strong>：高波动下，从看到价格到订单成交，价格可能已经发生不利变化。<ul><li><strong>对策</strong>：优先使用限价单锁定价格，而非市价单。</li><li><strong>监控</strong>：实时追踪订单簿变化和自身订单的执行状态。</li></ul></li><li><strong>执行风险</strong>：包括网络延迟、API响应慢、订单部分成交等，可能导致套利失败甚至亏损。</li><li><strong>资金分配</strong>：<ul><li><strong>分散风险</strong>：切忌将所有资金投入单次套利。</li><li><strong>动态调整</strong>：根据市场状况和套利机会的确定性调整投入比例。</li></ul></li></ol><p><strong>总结</strong>：三角套利听起来简单，但成功执行需要精密的计算、快速的响应、对市场细微变化的洞察以及严格的风险控制。尤其是在当前多变的市场环境中，持续优化策略和风控措施是保持盈利的关键。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;什么是三角套利？&quot;&gt;&lt;a href=&quot;#什么是三角套利？&quot; class=&quot;headerlink&quot; title=&quot;什么是三角套利？&quot;&gt;&lt;/a&gt;什么是三角套利？&lt;/h2&gt;&lt;p&gt;三角套利是一种利用不同交易市场（或同一市场内不同交易对）之间短暂的价格差异来获取理论上无风险</summary>
      
    
    
    
    
    <category term="投资理财" scheme="http://example.com/tags/%E6%8A%95%E8%B5%84%E7%90%86%E8%B4%A2/"/>
    
  </entry>
  
  <entry>
    <title>配置本地MCP服务器及调用RapidAPI代码执行服务</title>
    <link href="http://example.com/2025/04/29/%E9%85%8D%E7%BD%AE%E6%9C%AC%E5%9C%B0MCP%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%8F%8A%E8%B0%83%E7%94%A8RapidAPI%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C%E6%9C%8D%E5%8A%A1/"/>
    <id>http://example.com/2025/04/29/%E9%85%8D%E7%BD%AE%E6%9C%AC%E5%9C%B0MCP%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%8F%8A%E8%B0%83%E7%94%A8RapidAPI%E4%BB%A3%E7%A0%81%E6%89%A7%E8%A1%8C%E6%9C%8D%E5%8A%A1/</id>
    <published>2025-04-29T12:06:12.000Z</published>
    <updated>2025-04-29T12:09:03.855Z</updated>
    
    <content type="html"><![CDATA[<h1 id="配置本地MCP服务器及调用RapidAPI代码执行服务"><a href="#配置本地MCP服务器及调用RapidAPI代码执行服务" class="headerlink" title="配置本地MCP服务器及调用RapidAPI代码执行服务"></a>配置本地MCP服务器及调用RapidAPI代码执行服务</h1><p>在现代AI应用中，我们常常希望模型能够调用外部工具或服务来完成特定任务，例如执行代码、查询数据库或调用第三方API。模型上下文协议（Model Context Protocol, MCP）提供了一种标准化的方式来实现这种交互。</p><p>本教程将指导您如何一步步在本地Windows环境中配置一个基于Node.js的MCP服务器，该服务器能够连接到RapidAPI上的代码执行服务（具体为 Judge0 CE API）。同时，我们还将介绍如何在（假设的）客户端应用 Cherry Studio 中配置此服务器，从而让AI能够通过函数调用（Function Call）来执行代码片段。</p><h2 id="第一部分：搭建本地-MCP-服务器"><a href="#第一部分：搭建本地-MCP-服务器" class="headerlink" title="第一部分：搭建本地 MCP 服务器"></a>第一部分：搭建本地 MCP 服务器</h2><h3 id="1-创建项目目录并初始化"><a href="#1-创建项目目录并初始化" class="headerlink" title="1. 创建项目目录并初始化"></a>1. 创建项目目录并初始化</h3><p>首先，我们需要在您的 Windows 电脑上创建一个项目文件夹，并初始化 Node.js 项目。</p><p><strong>命令 (PowerShell):</strong></p><div class="code-container" data-rel="Powershell"><figure class="iseeu highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 切换到 H 盘（如果需要）</span></span><br><span class="line"><span class="built_in">H</span>:</span><br><span class="line"><span class="comment"># 创建项目目录（如果目录已存在，此命令会提示，可以忽略）</span></span><br><span class="line">mkdir mcp<span class="literal">-rapidapi-server</span></span><br><span class="line"><span class="comment"># 进入项目目录</span></span><br><span class="line"><span class="built_in">cd</span> mcp<span class="literal">-rapidapi-server</span></span><br><span class="line"><span class="comment"># 初始化 npm 项目，生成默认 package.json</span></span><br><span class="line">npm init <span class="literal">-y</span></span><br></pre></td></tr></table></figure></div><p><strong>说明:</strong></p><ul><li><code>mkdir</code>: 创建新目录。</li><li><code>cd</code>: 切换当前目录。</li><li><code>npm init -y</code>: 快速生成一个 <code>package.json</code> 文件，用于管理项目依赖和脚本。</li></ul><h3 id="2-安装依赖"><a href="#2-安装依赖" class="headerlink" title="2. 安装依赖"></a>2. 安装依赖</h3><p>接下来，我们需要安装运行 MCP 服务器所必需的依赖库。</p><p><strong>命令 (PowerShell):</strong></p><div class="code-container" data-rel="Powershell"><figure class="iseeu highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install @modelcontextprotocol/sdk zod node<span class="literal">-fetch</span></span><br></pre></td></tr></table></figure></div><p><strong>说明:</strong></p><ul><li><code>@modelcontextprotocol/sdk</code>: MCP 协议的官方 Node.js SDK，用于创建 MCP 服务器。</li><li><code>zod</code>: 一个强大的 TypeScript&#x2F;JavaScript 模式验证库，用于验证工具的输入参数。</li><li><code>node-fetch</code>: 允许在 Node.js 环境中像浏览器一样使用 <code>Workspace</code> API 来发送 HTTP 请求。我们用它来调用 RapidAPI。</li></ul><h3 id="3-配置-package-json"><a href="#3-配置-package-json" class="headerlink" title="3. 配置 package.json"></a>3. 配置 <code>package.json</code></h3><p>为了确保能使用 ES 模块语法（如 <code>import</code>&#x2F;<code>export</code>）并方便地启动服务器，我们需要对 <code>package.json</code> 文件进行一些修改。</p><p><strong>编辑 <code>H:\mcp-rapidapi-server\package.json</code> 文件，内容如下:</strong></p><div class="code-container" data-rel="Json"><figure class="iseeu highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;name&quot;</span><span class="punctuation">:</span> <span class="string">&quot;mcp-rapidapi-server&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;version&quot;</span><span class="punctuation">:</span> <span class="string">&quot;1.0.0&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;main&quot;</span><span class="punctuation">:</span> <span class="string">&quot;server.js&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;module&quot;</span><span class="punctuation">,</span>  <span class="comment">// 启用 ES 模块</span></span><br><span class="line">  <span class="attr">&quot;scripts&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;start&quot;</span><span class="punctuation">:</span> <span class="string">&quot;node server.js&quot;</span><span class="punctuation">,</span> <span class="comment">// 定义启动脚本</span></span><br><span class="line">    <span class="attr">&quot;test&quot;</span><span class="punctuation">:</span> <span class="string">&quot;echo \&quot;Error: no test specified\&quot; &amp;&amp; exit 1&quot;</span></span><br><span class="line">  <span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;dependencies&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="comment">// 依赖库及其版本（实际安装版本可能因 ^ 而略有不同）</span></span><br><span class="line">    <span class="attr">&quot;@modelcontextprotocol/sdk&quot;</span><span class="punctuation">:</span> <span class="string">&quot;^1.0.0&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;node-fetch&quot;</span><span class="punctuation">:</span> <span class="string">&quot;^3.0.0&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;zod&quot;</span><span class="punctuation">:</span> <span class="string">&quot;^3.0.0&quot;</span></span><br><span class="line">  <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure></div><p><strong>说明:</strong></p><ul><li><code>&quot;type&quot;: &quot;module&quot;</code>: 这是启用 ES 模块的关键设置。</li><li><code>&quot;start&quot;: &quot;node server.js&quot;</code>: 允许我们稍后通过 <code>npm start</code> 命令来启动服务器。</li></ul><h3 id="4-创建并编写-server-js"><a href="#4-创建并编写-server-js" class="headerlink" title="4. 创建并编写 server.js"></a>4. 创建并编写 <code>server.js</code></h3><p>现在，核心的服务器逻辑来了。在 <code>H:\mcp-rapidapi-server</code> 目录下创建一个名为 <code>server.js</code> 的文件，并将以下代码粘贴进去。</p><p><strong>代码 (<code>server.js</code>):</strong></p><div class="code-container" data-rel="Javascript"><figure class="iseeu highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">McpServer</span> &#125; <span class="keyword">from</span> <span class="string">&quot;@modelcontextprotocol/sdk/server/mcp.js&quot;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">StdioServerTransport</span> &#125; <span class="keyword">from</span> <span class="string">&quot;@modelcontextprotocol/sdk/server/stdio.js&quot;</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; z &#125; <span class="keyword">from</span> <span class="string">&quot;zod&quot;</span>;</span><br><span class="line"><span class="keyword">import</span> fetch <span class="keyword">from</span> <span class="string">&quot;node-fetch&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// --- 辅助函数和常量 ---</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 支持的语言及其在 Judge0 API 中的 ID</span></span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">LANGUAGE_IDS</span> = &#123;</span><br><span class="line">  <span class="string">&quot;c&quot;</span>: <span class="number">50</span>,</span><br><span class="line">  <span class="string">&quot;cpp&quot;</span>: <span class="number">54</span>,</span><br><span class="line">  <span class="string">&quot;go&quot;</span>: <span class="number">95</span>,</span><br><span class="line">  <span class="string">&quot;java&quot;</span>: <span class="number">91</span>,</span><br><span class="line">  <span class="string">&quot;javascript&quot;</span>: <span class="number">93</span>,</span><br><span class="line">  <span class="string">&quot;python&quot;</span>: <span class="number">92</span>,</span><br><span class="line">  <span class="string">&quot;ruby&quot;</span>: <span class="number">72</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 语言别名，增加容错性</span></span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">LANGUAGE_ALIASES</span> = &#123;</span><br><span class="line">  <span class="string">&quot;c++&quot;</span>: <span class="string">&quot;cpp&quot;</span>,</span><br><span class="line">  <span class="string">&quot;golang&quot;</span>: <span class="string">&quot;go&quot;</span>,</span><br><span class="line">  <span class="string">&quot;js&quot;</span>: <span class="string">&quot;javascript&quot;</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 根据语言名称获取对应的 ID</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">getLanguageId</span>(<span class="params">language</span>) &#123;</span><br><span class="line">  <span class="keyword">let</span> l = language.<span class="title function_">toLowerCase</span>();</span><br><span class="line">  <span class="keyword">return</span> <span class="variable constant_">LANGUAGE_IDS</span>[<span class="variable constant_">LANGUAGE_ALIASES</span>[l] || l] || <span class="number">0</span>; <span class="comment">// 不支持则返回 0</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Base64 编码函数 (用于 Judge0 API)</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">encode</span>(<span class="params">str</span>) &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="title class_">Buffer</span>.<span class="title function_">from</span>(<span class="built_in">unescape</span>(<span class="built_in">encodeURIComponent</span>(str || <span class="string">&quot;&quot;</span>))).<span class="title function_">toString</span>(<span class="string">&#x27;base64&#x27;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Base64 解码函数 (用于 Judge0 API 返回结果)</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">decode</span>(<span class="params">bytes</span>) &#123;</span><br><span class="line">  <span class="keyword">const</span> escaped = <span class="built_in">escape</span>(<span class="title class_">Buffer</span>.<span class="title function_">from</span>(bytes || <span class="string">&quot;&quot;</span>, <span class="string">&#x27;base64&#x27;</span>).<span class="title function_">toString</span>());</span><br><span class="line">  <span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">decodeURIComponent</span>(escaped);</span><br><span class="line">  &#125; <span class="keyword">catch</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">unescape</span>(escaped); <span class="comment">// Fallback for potential decoding errors</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// --- MCP 服务器设置 ---</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 创建 MCP 服务器实例</span></span><br><span class="line"><span class="keyword">const</span> server = <span class="keyword">new</span> <span class="title class_">McpServer</span>(&#123;</span><br><span class="line">  <span class="attr">name</span>: <span class="string">&quot;rapidapi-code-execution&quot;</span>,</span><br><span class="line">  <span class="attr">version</span>: <span class="string">&quot;1.0.0&quot;</span>,</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// --- 工具定义与注册 ---</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义代码执行工具的处理函数</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">codeExecutionHandler</span> = <span class="keyword">async</span> (<span class="params">&#123; source_code, language &#125;</span>) =&gt; &#123;</span><br><span class="line">  <span class="comment">// 从环境变量获取 RapidAPI 密钥</span></span><br><span class="line">  <span class="keyword">const</span> rapidApiKey = process.<span class="property">env</span>.<span class="property">RAPIDAPI_KEY</span>;</span><br><span class="line">  <span class="keyword">if</span> (!rapidApiKey) &#123;</span><br><span class="line">      <span class="keyword">return</span> &#123; <span class="attr">content</span>: [&#123; <span class="attr">type</span>: <span class="string">&quot;text&quot;</span>, <span class="attr">text</span>: <span class="string">&quot;Error: RAPIDAPI_KEY environment variable not set.&quot;</span> &#125;] &#125;;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">const</span> languageId = <span class="title function_">getLanguageId</span>(language);</span><br><span class="line">  <span class="keyword">if</span> (languageId === <span class="number">0</span>) &#123;</span><br><span class="line">    <span class="comment">// 如果语言不受支持，返回错误信息</span></span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">      <span class="attr">content</span>: [</span><br><span class="line">        &#123;</span><br><span class="line">          <span class="attr">type</span>: <span class="string">&quot;text&quot;</span>,</span><br><span class="line">          <span class="attr">text</span>: <span class="string">`Unsupported language: <span class="subst">$&#123;language&#125;</span>`</span>,</span><br><span class="line">        &#125;,</span><br><span class="line">      ],</span><br><span class="line">    &#125;;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 准备请求 RapidAPI 的 Headers 和 Body</span></span><br><span class="line">  <span class="keyword">const</span> requestHeaders = &#123;</span><br><span class="line">    <span class="string">&quot;x-rapidapi-key&quot;</span>: rapidApiKey,</span><br><span class="line">    <span class="string">&quot;x-rapidapi-host&quot;</span>: <span class="string">&quot;judge0-ce.p.rapidapi.com&quot;</span>, <span class="comment">// 明确 Host</span></span><br><span class="line">    <span class="string">&quot;Content-Type&quot;</span>: <span class="string">&quot;application/json&quot;</span>,</span><br><span class="line">  &#125;;</span><br><span class="line">  <span class="keyword">const</span> requestData = &#123;</span><br><span class="line">    <span class="attr">language_id</span>: languageId,</span><br><span class="line">    <span class="attr">source_code</span>: <span class="title function_">encode</span>(source_code), <span class="comment">// 源代码需要 Base64 编码</span></span><br><span class="line">    <span class="attr">redirect_stderr_to_stdout</span>: <span class="literal">true</span>, <span class="comment">// 将标准错误重定向到标准输出</span></span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="comment">// 发送 POST 请求到 Judge0 API</span></span><br><span class="line">    <span class="keyword">const</span> response = <span class="keyword">await</span> <span class="title function_">fetch</span>(</span><br><span class="line">      <span class="string">&quot;[https://judge0-ce.p.rapidapi.com/submissions?base64_encoded=true&amp;wait=true](https://judge0-ce.p.rapidapi.com/submissions?base64_encoded=true&amp;wait=true)&quot;</span>, <span class="comment">// 使用 base64 并等待执行结果</span></span><br><span class="line">      &#123;</span><br><span class="line">        <span class="attr">method</span>: <span class="string">&quot;POST&quot;</span>,</span><br><span class="line">        <span class="attr">headers</span>: requestHeaders,</span><br><span class="line">        <span class="attr">body</span>: <span class="title class_">JSON</span>.<span class="title function_">stringify</span>(requestData),</span><br><span class="line">      &#125;</span><br><span class="line">    );</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 处理网络或 API 错误</span></span><br><span class="line">    <span class="keyword">if</span> (!response.<span class="property">ok</span>) &#123;</span><br><span class="line">      <span class="keyword">const</span> errorText = <span class="keyword">await</span> response.<span class="title function_">text</span>();</span><br><span class="line">      <span class="variable language_">console</span>.<span class="title function_">error</span>(<span class="string">&quot;RapidAPI Error:&quot;</span>, response.<span class="property">status</span>, errorText);</span><br><span class="line">      <span class="keyword">return</span> &#123;</span><br><span class="line">        <span class="attr">content</span>: [</span><br><span class="line">          &#123;</span><br><span class="line">            <span class="attr">type</span>: <span class="string">&quot;text&quot;</span>,</span><br><span class="line">            <span class="attr">text</span>: <span class="string">`API request failed with status <span class="subst">$&#123;response.status&#125;</span>: <span class="subst">$&#123;errorText&#125;</span>`</span>,</span><br><span class="line">          &#125;,</span><br><span class="line">        ],</span><br><span class="line">      &#125;;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 解析返回的 JSON 数据</span></span><br><span class="line">    <span class="keyword">const</span> responseData = <span class="keyword">await</span> response.<span class="title function_">json</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 组合编译输出和标准输出作为最终结果</span></span><br><span class="line">    <span class="keyword">const</span> compileOutput = <span class="title function_">decode</span>(responseData[<span class="string">&quot;compile_output&quot;</span>]);</span><br><span class="line">    <span class="keyword">const</span> stdoutOutput = <span class="title function_">decode</span>(responseData[<span class="string">&quot;stdout&quot;</span>]);</span><br><span class="line">    <span class="keyword">const</span> stderrOutput = <span class="title function_">decode</span>(responseData[<span class="string">&quot;stderr&quot;</span>]); <span class="comment">// 也解码 stderr 看看</span></span><br><span class="line">    <span class="keyword">const</span> message = responseData[<span class="string">&quot;message&quot;</span>] ? <span class="title function_">decode</span>(responseData[<span class="string">&quot;message&quot;</span>]) : <span class="string">&quot;&quot;</span>; <span class="comment">// 解码 message</span></span><br><span class="line">    <span class="keyword">const</span> statusDesc = responseData.<span class="property">status</span>?.<span class="property">description</span> || <span class="string">&quot;&quot;</span>; <span class="comment">// 获取状态描述</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">let</span> resultText = <span class="string">&quot;&quot;</span>;</span><br><span class="line">    <span class="keyword">if</span> (compileOutput) resultText += <span class="string">`Compile Output:\n<span class="subst">$&#123;compileOutput&#125;</span>\n`</span>;</span><br><span class="line">    <span class="keyword">if</span> (stdoutOutput) resultText += <span class="string">`Output:\n<span class="subst">$&#123;stdoutOutput&#125;</span>\n`</span>;</span><br><span class="line">    <span class="keyword">if</span> (stderrOutput) resultText += <span class="string">`Error Output:\n<span class="subst">$&#123;stderrOutput&#125;</span>\n`</span>;</span><br><span class="line">    <span class="keyword">if</span> (message) resultText += <span class="string">`Message:\n<span class="subst">$&#123;message&#125;</span>\n`</span>;</span><br><span class="line">    <span class="keyword">if</span> (statusDesc) resultText += <span class="string">`Status: <span class="subst">$&#123;statusDesc&#125;</span>\n`</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">      <span class="attr">content</span>: [</span><br><span class="line">        &#123;</span><br><span class="line">          <span class="attr">type</span>: <span class="string">&quot;text&quot;</span>,</span><br><span class="line">          <span class="attr">text</span>: resultText.<span class="title function_">trim</span>() || <span class="string">&quot;Execution finished with no output.&quot;</span>, <span class="comment">// 如果完全没输出，给个提示</span></span><br><span class="line">        &#125;,</span><br><span class="line">      ],</span><br><span class="line">    &#125;;</span><br><span class="line">  &#125; <span class="keyword">catch</span> (error) &#123;</span><br><span class="line">    <span class="comment">// 捕获请求过程中的异常</span></span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">error</span>(<span class="string">&quot;Fetch Error:&quot;</span>, error);</span><br><span class="line">    <span class="keyword">return</span> &#123;</span><br><span class="line">      <span class="attr">content</span>: [</span><br><span class="line">        &#123;</span><br><span class="line">          <span class="attr">type</span>: <span class="string">&quot;text&quot;</span>,</span><br><span class="line">          <span class="attr">text</span>: <span class="string">`Error during API call: <span class="subst">$&#123;error.message&#125;</span>`</span>,</span><br><span class="line">        &#125;,</span><br><span class="line">      ],</span><br><span class="line">    &#125;;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 向 MCP 服务器注册 &#x27;code_execution&#x27; 工具</span></span><br><span class="line">server.<span class="title function_">tool</span>(</span><br><span class="line">  <span class="string">&quot;code_execution&quot;</span>, <span class="comment">// 工具名称，AI 调用时使用</span></span><br><span class="line">  <span class="string">&quot;Run code in various programming languages using RapidAPI Judge0&quot;</span>, <span class="comment">// 工具描述</span></span><br><span class="line">  &#123;</span><br><span class="line">    <span class="comment">// 定义输入参数及其类型和描述 (使用 Zod)</span></span><br><span class="line">    <span class="attr">source_code</span>: z.<span class="title function_">string</span>().<span class="title function_">describe</span>(<span class="string">&quot;The source code snippet to execute.&quot;</span>),</span><br><span class="line">    <span class="attr">language</span>: z</span><br><span class="line">      .<span class="title function_">enum</span>([<span class="string">&quot;c&quot;</span>, <span class="string">&quot;cpp&quot;</span>, <span class="string">&quot;go&quot;</span>, <span class="string">&quot;java&quot;</span>, <span class="string">&quot;javascript&quot;</span>, <span class="string">&quot;python&quot;</span>, <span class="string">&quot;ruby&quot;</span>]) <span class="comment">// 支持的语言列表</span></span><br><span class="line">      .<span class="title function_">describe</span>(<span class="string">&quot;The programming language of the source code.&quot;</span>),</span><br><span class="line">  &#125;,</span><br><span class="line">  codeExecutionHandler <span class="comment">// 指定处理函数</span></span><br><span class="line">);</span><br><span class="line"></span><br><span class="line"><span class="comment">// --- 测试代码 (已注释掉，仅用于开发调试) ---</span></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">if (process.env.TEST_MODE) &#123;</span></span><br><span class="line"><span class="comment">  const testInput = &#123;</span></span><br><span class="line"><span class="comment">    source_code: &quot;print(&#x27;Hello from MCP test!&#x27;)&quot;,</span></span><br><span class="line"><span class="comment">    language: &quot;python&quot;</span></span><br><span class="line"><span class="comment">  &#125;;</span></span><br><span class="line"><span class="comment">  console.log(&quot;Running in TEST_MODE...&quot;);</span></span><br><span class="line"><span class="comment">  codeExecutionHandler(testInput).then(result =&gt; &#123;</span></span><br><span class="line"><span class="comment">    console.log(&quot;Test result:&quot;, JSON.stringify(result, null, 2));</span></span><br><span class="line"><span class="comment">    process.exit(0); // 测试成功后退出</span></span><br><span class="line"><span class="comment">  &#125;).catch(error =&gt; &#123;</span></span><br><span class="line"><span class="comment">    console.error(&quot;Test error:&quot;, error);</span></span><br><span class="line"><span class="comment">    process.exit(1); // 测试失败后退出</span></span><br><span class="line"><span class="comment">  &#125;);</span></span><br><span class="line"><span class="comment">&#125;</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// --- 服务器主运行逻辑 ---</span></span><br><span class="line"><span class="keyword">async</span> <span class="keyword">function</span> <span class="title function_">main</span>(<span class="params"></span>) &#123;</span><br><span class="line">  <span class="comment">// 如果不是在测试模式下，则正常启动服务器</span></span><br><span class="line">  <span class="keyword">if</span> (!process.<span class="property">env</span>.<span class="property">TEST_MODE</span>) &#123;</span><br><span class="line">    <span class="keyword">const</span> transport = <span class="keyword">new</span> <span class="title class_">StdioServerTransport</span>(); <span class="comment">// 使用标准输入输出进行通信</span></span><br><span class="line">    <span class="keyword">await</span> server.<span class="title function_">connect</span>(transport);</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">error</span>(<span class="string">&quot;RapidAPI Code Execution MCP Server running on stdio...&quot;</span>); <span class="comment">// 使用 console.error 避免干扰 stdout</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 启动服务器并捕获潜在错误</span></span><br><span class="line"><span class="title function_">main</span>().<span class="title function_">catch</span>(<span class="function">(<span class="params">error</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">error</span>(<span class="string">&quot;Fatal error initializing server:&quot;</span>, error);</span><br><span class="line">  process.<span class="title function_">exit</span>(<span class="number">1</span>);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></div><p><strong>说明:</strong></p><ul><li><strong>模块导入</strong>: 代码首先导入了所需的 SDK 组件、<code>zod</code> 和 <code>node-fetch</code>。</li><li><strong>辅助函数</strong>: 包含处理语言 ID、别名以及 Base64 编解码的函数。</li><li><strong>服务器实例化</strong>: 创建了一个 <code>McpServer</code> 实例。</li><li><strong>工具处理函数 (<code>codeExecutionHandler</code>)</strong>: 这是核心逻辑所在，负责接收参数、调用 RapidAPI、处理响应和错误，并返回符合 MCP 格式的结果。<strong>注意：</strong> 已添加对 <code>RAPIDAPI_KEY</code> 环境变量是否设置的检查。同时改进了错误处理和结果组合。</li><li><strong>工具注册 (<code>server.tool</code>)</strong>: 将 <code>codeExecutionHandler</code> 函数注册为名为 <code>code_execution</code> 的工具，并定义了输入参数的模式（使用 <code>zod</code>）。</li><li><strong>测试代码</strong>: 这部分代码（现在已被注释掉）用于在开发时快速测试 <code>codeExecutionHandler</code> 函数是否能正常工作。</li><li><strong>主函数 (<code>main</code>)</strong>: 使用 <code>StdioServerTransport</code> 启动服务器，监听标准输入输出流，等待客户端（如 Cherry Studio）连接。</li></ul><h3 id="5-测试服务器-可选，但推荐首次运行时执行"><a href="#5-测试服务器-可选，但推荐首次运行时执行" class="headerlink" title="5. 测试服务器 (可选，但推荐首次运行时执行)"></a>5. 测试服务器 (可选，但推荐首次运行时执行)</h3><p>在您确认代码无误后，可以通过设置环境变量并运行脚本来测试服务器是否能独立调用 RapidAPI。</p><p><strong>命令 (PowerShell):</strong></p><div class="code-container" data-rel="Powershell"><figure class="iseeu highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 确保在项目目录下: H:\mcp-rapidapi-server</span></span><br><span class="line"><span class="built_in">cd</span> <span class="built_in">H</span>:\mcp<span class="literal">-rapidapi-server</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置 RapidAPI 密钥 (请替换为你的真实密钥)</span></span><br><span class="line"><span class="variable">$env:RAPIDAPI_KEY</span>=<span class="string">&quot;YOUR_RAPIDAPI_KEY_HERE&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置测试模式环境变量</span></span><br><span class="line"><span class="variable">$env:TEST_MODE</span>=<span class="string">&quot;1&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 运行服务器脚本 (在测试模式下)</span></span><br><span class="line">node server.js</span><br><span class="line"></span><br><span class="line"><span class="comment"># 清理环境变量 (可选)</span></span><br><span class="line"><span class="comment"># Remove-Item Env:\RAPIDAPI_KEY</span></span><br><span class="line"><span class="comment"># Remove-Item Env:\TEST_MODE</span></span><br></pre></td></tr></table></figure></div><p><strong>预期输出 (类似这样):</strong></p><div class="code-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">Running in TEST_MODE...</span><br><span class="line">Test result: &#123;</span><br><span class="line">  &quot;content&quot;: [</span><br><span class="line">    &#123;</span><br><span class="line">      &quot;type&quot;: &quot;text&quot;,</span><br><span class="line">      &quot;text&quot;: &quot;Output:\nHello from MCP test!&quot;</span><br><span class="line">    &#125;</span><br><span class="line">  ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><p><strong>说明:</strong></p><ul><li><strong>环境变量</strong>: 在运行前必须设置 <code>RAPIDAPI_KEY</code> 和 <code>TEST_MODE=1</code>。</li><li><strong>成功标志</strong>: 看到类似 “Test result: …” 的输出并且程序正常退出，表示 <code>server.js</code> 中的 <code>codeExecutionHandler</code> 函数可以成功调用 RapidAPI。</li><li><strong>失败排查</strong>: 如果出错，请检查：<ul><li><code>RAPIDAPI_KEY</code> 是否正确且有效。</li><li>网络连接是否正常。</li><li>依赖是否都已正确安装 (<code>npm install</code>)。</li><li><code>server.js</code> 代码是否有语法错误。</li></ul></li></ul><h3 id="6-准备正式运行"><a href="#6-准备正式运行" class="headerlink" title="6. 准备正式运行"></a>6. 准备正式运行</h3><p>测试成功后，服务器脚本就已经准备好了。您可以移除或保持注释掉 <code>server.js</code> 中的测试代码块。确保在后续步骤中<strong>不设置</strong> <code>TEST_MODE</code> 环境变量，以便服务器能正常启动并等待客户端连接。</p><hr><h2 id="第二部分：在-Cherry-Studio-中配置-MCP-服务器"><a href="#第二部分：在-Cherry-Studio-中配置-MCP-服务器" class="headerlink" title="第二部分：在 Cherry Studio 中配置 MCP 服务器"></a>第二部分：在 Cherry Studio 中配置 MCP 服务器</h2><p>服务器端已经就绪，现在我们需要告诉 Cherry Studio 如何找到并运行这个服务器。以下是在 Cherry Studio 图形界面中进行配置的步骤（基于通用配置项推断）。</p><h3 id="配置步骤"><a href="#配置步骤" class="headerlink" title="配置步骤"></a>配置步骤</h3><h4 id="1-打开MCP服务器配置窗口"><a href="#1-打开MCP服务器配置窗口" class="headerlink" title="1. 打开MCP服务器配置窗口"></a>1. 打开MCP服务器配置窗口</h4><ul><li>启动 Cherry Studio 应用程序。</li><li>在菜单栏或设置界面中找到与 <strong>MCP 服务器</strong>、<strong>外部工具</strong> 或类似功能相关的配置选项。</li><li>点击 <strong>“添加服务器”</strong>、<strong>“新建配置”</strong> 或类似按钮，打开用于添加新 MCP 服务器的配置窗口。</li></ul><h4 id="2-填写配置字段"><a href="#2-填写配置字段" class="headerlink" title="2. 填写配置字段"></a>2. 填写配置字段</h4><p>在弹出的配置窗口中，仔细填写以下信息：</p><ul><li><strong>名称 (Name)</strong>：<ul><li><strong>值:</strong> <code>@modelcontextprotocol/server-rapidapi-code-execution</code></li><li><strong>说明:</strong> 给这个服务器配置起一个唯一的、易于识别的名称。</li></ul></li><li><strong>描述 (Description)</strong>：<ul><li><strong>值:</strong> <code>MCP server for executing code via RapidAPI Judge0 API</code></li><li><strong>说明:</strong> 简要描述这个服务器的功能。</li></ul></li><li><strong>类型 (Type)</strong>：<ul><li><strong>选项:</strong> <code>STDIO</code> (或 <code>Standard Input/Output</code>)</li><li><strong>说明:</strong> 选择此类型，因为我们的服务器是通过标准输入&#x2F;输出来与 Cherry Studio 通信的。</li></ul></li><li><strong>命令 (Command)</strong>：<ul><li><strong>值:</strong> <code>node</code></li><li><strong>说明:</strong> 指定用于启动服务器进程的可执行文件。这里是 Node.js 运行时。</li></ul></li><li><strong>参数 (Arguments)</strong>：<ul><li><strong>值:</strong> <code>H:\mcp-rapidapi-server\server.js</code></li><li><strong>说明:</strong> 提供 <code>server.js</code> 脚本的 <strong>完整绝对路径</strong>。<strong>请务必根据您的实际文件位置修改此路径。</strong> 确保路径分隔符对于 Windows 是反斜杠 <code>\</code>。</li></ul></li><li><strong>环境变量 (Environment Variables)</strong>：<ul><li><strong>值:</strong> <code>RAPIDAPI_KEY=YOUR_RAPIDAPI_KEY_HERE</code></li><li><strong>说明:</strong> 在这里设置服务器运行时所需的环境变量。<strong>将 <code>YOUR_RAPIDAPI_KEY_HERE</code> 替换为您自己的有效 RapidAPI 密钥。</strong> 如果有多个环境变量，通常可以换行或按特定格式添加。</li></ul></li><li><strong>启用 (Usage &#x2F; Enable)</strong>：<ul><li><strong>状态:</strong> <strong>开 (On)</strong> &#x2F; <strong>启用 (Enabled)</strong></li><li><strong>说明:</strong> 确保此开关处于打开状态，以激活这个服务器配置。</li></ul></li></ul><h4 id="3-保存配置"><a href="#3-保存配置" class="headerlink" title="3. 保存配置"></a>3. 保存配置</h4><ul><li>填写完所有字段后，点击窗口中的 <strong>“确定”</strong>、<strong>“保存”</strong> 或 <strong>“应用”</strong> 按钮。</li></ul><h4 id="4-重启-Cherry-Studio-推荐"><a href="#4-重启-Cherry-Studio-推荐" class="headerlink" title="4. 重启 Cherry Studio (推荐)"></a>4. 重启 Cherry Studio (推荐)</h4><ul><li>为了确保新的 MCP 服务器配置被完全加载和识别，建议关闭并重新启动 Cherry Studio 应用程序。</li></ul><h4 id="5-测试配置"><a href="#5-测试配置" class="headerlink" title="5. 测试配置"></a>5. 测试配置</h4><ul><li><p>重启后，尝试在 Cherry Studio 中向 AI 发出需要执行代码的指令。例如：</p><blockquote><p>“请使用 python 运行代码: <code>print(1 + 2 * 3)</code>“</p></blockquote></li><li><p>观察 AI 的响应。如果配置成功，AI 应该能够识别出这是一个需要使用 <code>code_execution</code> 工具的任务，然后调用您本地运行的 MCP 服务器。服务器执行代码（通过 RapidAPI）后，将结果返回给 AI，最终显示给您。</p></li><li><p><strong>预期结果 (示例):</strong></p>  <div class="code-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Output:</span><br><span class="line">7</span><br></pre></td></tr></table></figure></div></li></ul><h3 id="配置注意事项"><a href="#配置注意事项" class="headerlink" title="配置注意事项"></a>配置注意事项</h3><ul><li><strong>路径精确性</strong>: <code>参数 (Arguments)</code> 中填写的 <code>server.js</code> 路径必须是 <strong>绝对路径</strong> 且完全正确，Cherry Studio 必须有权限访问该路径。</li><li><strong>API 密钥</strong>: <code>环境变量</code> 中的 <code>RAPIDAPI_KEY</code> 必须是您自己有效且未过期的 RapidAPI 密钥。</li><li><strong>服务器运行</strong>: Cherry Studio 在需要调用工具时，会根据您的配置尝试启动 <code>node H:\mcp-rapidapi-server\server.js</code> 命令。确保 Node.js 已正确安装并可在系统路径中找到。</li><li><strong>防火墙&#x2F;安全软件</strong>: 某些情况下，防火墙或安全软件可能会阻止 Cherry Studio 启动本地进程或进行网络通信，需要相应配置允许。</li></ul><h3 id="故障排除"><a href="#故障排除" class="headerlink" title="故障排除"></a>故障排除</h3><p>如果在测试配置时遇到问题（例如 AI 无法调用工具，或调用后报错），可以尝试以下步骤排查：</p><ol><li><p><strong>检查 Cherry Studio 日志</strong>: 查找与 MCP 服务器启动或通信相关的错误信息。</p></li><li><p><strong>手动运行命令</strong>: 在 PowerShell 或 CMD 中，手动执行 Cherry Studio 配置中的完整命令（包括环境变量设置），例如：</p> <div class="code-container" data-rel="Powershell"><figure class="iseeu highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">$env:RAPIDAPI_KEY</span>=<span class="string">&quot;YOUR_RAPIDAPI_KEY_HERE&quot;</span></span><br><span class="line">node <span class="built_in">H</span>:\mcp<span class="literal">-rapidapi-server</span>\server.js</span><br></pre></td></tr></table></figure></div><p> 观察是否有错误输出，或者服务器是否能正常启动并打印 <code>...running on stdio...</code> 信息。</p></li><li><p><strong>检查路径和密钥</strong>: 再次确认 <code>server.js</code> 的路径和 RapidAPI 密钥是否无误。</p></li><li><p><strong>确认工具名称</strong>: 确保 AI 模型知道要调用的工具名称是 <code>code_execution</code>（与 <code>server.js</code> 中 <code>server.tool()</code> 的第一个参数一致）。</p></li></ol><hr><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>恭喜！通过以上步骤，您已经成功：</p><ol><li>在本地搭建了一个 Node.js MCP 服务器，它能够代理对 RapidAPI 代码执行服务的调用。</li><li>在 Cherry Studio 中配置了这个本地服务器，使得 AI 可以通过 MCP 协议调用该服务器来执行代码。</li></ol><p>这为您提供了一个强大的基础，您可以基于此扩展更多自定义工具，进一步增强您的 AI 应用能力。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;配置本地MCP服务器及调用RapidAPI代码执行服务&quot;&gt;&lt;a href=&quot;#配置本地MCP服务器及调用RapidAPI代码执行服务&quot; class=&quot;headerlink&quot; title=&quot;配置本地MCP服务器及调用RapidAPI代码执行服务&quot;&gt;&lt;/a&gt;配置本地M</summary>
      
    
    
    
    
    <category term="MCP" scheme="http://example.com/tags/MCP/"/>
    
  </entry>
  
  <entry>
    <title>深圳市分行业单位从业人员工资增长图</title>
    <link href="http://example.com/2025/04/29/%E6%B7%B1%E5%9C%B3%E5%B8%82%E5%88%86%E8%A1%8C%E4%B8%9A%E5%8D%95%E4%BD%8D%E4%BB%8E%E4%B8%9A%E4%BA%BA%E5%91%98%E5%B7%A5%E8%B5%84%E5%A2%9E%E9%95%BF%E5%9B%BE/"/>
    <id>http://example.com/2025/04/29/%E6%B7%B1%E5%9C%B3%E5%B8%82%E5%88%86%E8%A1%8C%E4%B8%9A%E5%8D%95%E4%BD%8D%E4%BB%8E%E4%B8%9A%E4%BA%BA%E5%91%98%E5%B7%A5%E8%B5%84%E5%A2%9E%E9%95%BF%E5%9B%BE/</id>
    <published>2025-04-29T11:37:00.000Z</published>
    <updated>2025-04-29T12:05:27.060Z</updated>
    
    <content type="html"><![CDATA[<h1 id="深圳哪些行业最“吸金”？一图看懂2013-2023年各行业工资变迁"><a href="#深圳哪些行业最“吸金”？一图看懂2013-2023年各行业工资变迁" class="headerlink" title="深圳哪些行业最“吸金”？一图看懂2013-2023年各行业工资变迁"></a>深圳哪些行业最“吸金”？一图看懂2013-2023年各行业工资变迁</h1><p>深圳，作为中国改革开放的前沿阵地和经济发展的活力引擎，其就业市场的薪酬水平一直是大家关注的焦点。不同行业的“钱景”如何？哪些行业在过去十年中增长迅猛？了解这些信息，无论是对于求职者规划职业生涯，还是对于观察城市经济结构演变，都具有重要的意义。</p><p>本篇博客将通过一个交互式图表，直观展示深圳市在 <strong>2013年至2023年</strong> 这十一年间，主要行业单位从业人员年平均工资（城镇非私营单位）的变化情况。</p><h2 id="交互式图表：深圳各行业年平均工资趋势-2013-2023"><a href="#交互式图表：深圳各行业年平均工资趋势-2013-2023" class="headerlink" title="交互式图表：深圳各行业年平均工资趋势 (2013-2023)"></a>交互式图表：深圳各行业年平均工资趋势 (2013-2023)</h2><p><strong>重要操作说明：</strong></p><ul><li><strong>将鼠标悬停</strong> 在图表的线条或数据点上，可以查看具体年份、具体行业的年平均工资，以及该年度的<strong>同比增长率</strong>。</li><li><strong>点击图例</strong> 中的行业名称，可以隐藏或显示对应行业的曲线，方便进行对比分析。</li></ul><iframe src="https://zefanhu.github.io/szpjgz/" width="100%" height="650px" style="border: 1px solid #eee; display: block; margin: 20px auto;" title="深圳各行业年平均工资趋势 (2013-2023)">    您的浏览器不支持 iframe，无法显示交互式图表。请尝试更换浏览器或设备。</iframe><p><em>（如果您无法看到上面的图表，请确保您的浏览器支持 iframe。）</em></p><h2 id="图表解读与观察"><a href="#图表解读与观察" class="headerlink" title="图表解读与观察"></a>图表解读与观察</h2><p>通过上面的交互式图表，我们可以观察到以下几个显著特点：</p><ol><li><p><strong>头部梯队：高薪行业格局</strong></p><ul><li><strong>金融业</strong> 长期以来稳居深圳市薪酬榜首，并且在 2023 年达到了惊人的 41.9 万元年平均工资，显示出强大的“吸金”能力。</li><li><strong>采矿业</strong> 虽然从业人员规模相对较小，但其薪酬水平一直处于较高位置，2023 年也达到了 34.6 万元。</li><li><strong>信息传输、软件和信息技术服务业</strong>（通常指 IT 行业）作为深圳的支柱产业之一，薪酬水平同样表现亮眼，位列前三甲，2023 年为 28.1 万元。</li></ul></li><li><p><strong>中坚力量：稳步增长的行业</strong></p><ul><li><strong>卫生和社会工作</strong>、<strong>电力、热力、燃气及水生产和供应业</strong>、<strong>科学研究和技术服务业</strong> 以及 <strong>教育</strong> 等行业，在过去十年中也展现出显著的薪酬增长，年平均工资普遍超过 20 万元大关（截至 2023 年）。这些行业通常与公共服务、科研创新和人才培养密切相关，其薪酬增长也反映了城市发展的投入方向。</li></ul></li><li><p><strong>追赶者与基础行业</strong></p><ul><li><strong>制造业</strong> 作为深圳的传统优势产业，虽然从业人员众多，但整体平均薪酬水平（2023 年约 14.2 万元）相较于头部行业有明显差距，但近年来也保持着稳健的增长。</li><li><strong>交通运输、仓储和邮政业</strong>、<strong>批发和零售业</strong>、<strong>租赁和商务服务业</strong> 等服务性行业，薪酬水平处于中等偏后位置。</li><li><strong>建筑业</strong>、<strong>居民服务、修理和其他服务业</strong> 以及 <strong>住宿和餐饮业</strong> 等基础性服务业和劳动密集型行业，其年平均工资相对较低。</li></ul></li><li><p><strong>增长趋势与波动</strong></p><ul><li><strong>整体趋势</strong>：绝大多数行业的年平均工资在 2013 年至 2023 年间都呈现出明显的上升趋势。</li><li><strong>增长率差异</strong>：不同行业的增长速度差异显著。鼠标悬停在数据点上可以看到具体的年同比增长率，部分行业在特定年份可能出现较大幅度的增长或回调（例如 2022-2023 年部分行业增速放缓甚至略有下降）。</li></ul></li></ol><h2 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h2><p>这张图表为我们提供了一个观察深圳过去十一年经济结构和劳动力市场变化的窗口。行业的薪酬水平不仅是经济发展的“晴雨表”，也直接关系到人才的流向和城市的竞争力。</p><p>金融、IT 等高薪行业持续领跑，体现了深圳在这些领域的优势地位。同时，科教文卫、公共事业等行业的稳步发展，也反映了城市综合服务能力的提升。当然，不同行业间的薪酬差距依然是需要关注的问题。</p><p>希望这份数据和图表能为您提供有价值的参考信息。</p><hr>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;深圳哪些行业最“吸金”？一图看懂2013-2023年各行业工资变迁&quot;&gt;&lt;a href=&quot;#深圳哪些行业最“吸金”？一图看懂2013-2023年各行业工资变迁&quot; class=&quot;headerlink&quot; title=&quot;深圳哪些行业最“吸金”？一图看懂2013-2023年</summary>
      
    
    
    
    
    <category term="经济" scheme="http://example.com/tags/%E7%BB%8F%E6%B5%8E/"/>
    
  </entry>
  
  <entry>
    <title>使用 Amazon Bedrock Stability 生成图片并通过 API 提供服务</title>
    <link href="http://example.com/2024/11/17/%E4%BD%BF%E7%94%A8-Amazon-Bedrock-Stability-%E7%94%9F%E6%88%90%E5%9B%BE%E7%89%87%E5%B9%B6%E9%80%9A%E8%BF%87-API-%E6%8F%90%E4%BE%9B%E6%9C%8D%E5%8A%A1/"/>
    <id>http://example.com/2024/11/17/%E4%BD%BF%E7%94%A8-Amazon-Bedrock-Stability-%E7%94%9F%E6%88%90%E5%9B%BE%E7%89%87%E5%B9%B6%E9%80%9A%E8%BF%87-API-%E6%8F%90%E4%BE%9B%E6%9C%8D%E5%8A%A1/</id>
    <published>2024-11-17T04:49:11.000Z</published>
    <updated>2024-11-17T04:58:06.042Z</updated>
    
    <content type="html"><![CDATA[<p>Amazon Bedrock 可以画图，我正好需要在网站上调用他来画图，所以就尝试在VPS上搭建了一个代理，提供API和KEY，输入prompt，将图片保存在VPS上，并返回图片的url。我用的就是GCP 300那个免费的美国VPS。</p><p>这里将介绍如何使用 Amazon Bedrock 的Stability生成图片，并通过 API 提供服务。下面是我的操作步骤，应该还可以优化和简化，我的步骤包括：</p><ol><li>申请访问 Amazon Bedrock 基础模型</li><li>配置 AWS IAM 权限</li><li>搭建 Flask API 服务</li><li>使用 systemd 管理服务和 Nginx 反向代理</li><li>测试 API</li></ol><h3 id="1-申请访问-Amazon-Bedrock-基础模型"><a href="#1-申请访问-Amazon-Bedrock-基础模型" class="headerlink" title="1. 申请访问 Amazon Bedrock 基础模型"></a>1. 申请访问 Amazon Bedrock 基础模型</h3><p>首先，你需要申请访问 Amazon Bedrock 的基础模型。你可以在 AWS 管理控制台中找到与 <strong>Bedrock</strong> 相关的服务。确保你选择了适合你需求的区域，例如支持 Stability AI 的区域。</p><h3 id="2-配置-AWS-IAM-权限"><a href="#2-配置-AWS-IAM-权限" class="headerlink" title="2. 配置 AWS IAM 权限"></a>2. 配置 AWS IAM 权限</h3><p>在 AWS IAM 中创建一个策略，授予 <code>InvokeModel</code> 和 <code>ListFoundationModels</code> 操作权限，允许访问 Amazon Bedrock 服务。具体步骤如下：</p><ol><li>登录 AWS 管理控制台。</li><li>打开 IAM 控制台，创建一个新的 IAM 策略。</li><li>在策略中，选择 <code>Bedrock</code> 服务，授予 <code>InvokeModel</code> 和 <code>ListFoundationModels</code> 操作权限。</li><li>配置资源 ARN（Resource ARN）为你需要访问的模型的 ARN，或选择任何资源。</li><li>将策略与 IAM 用户或角色关联。</li></ol><h3 id="3-安装-AWS-CLI-和-Boto3-SDK"><a href="#3-安装-AWS-CLI-和-Boto3-SDK" class="headerlink" title="3. 安装 AWS CLI 和 Boto3 SDK"></a>3. 安装 AWS CLI 和 Boto3 SDK</h3><p>在你的 VPS 上，你需要安装 AWS CLI 和 Boto3，以便与 Amazon Bedrock 交互。</p><p><strong>安装 AWS CLI 教程：</strong></p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">https://docs.aws.amazon.com/zh_cn/cli/latest/userguide/getting-started-install.html</span><br></pre></td></tr></table></figure></div><p>配置 AWS CLI：</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">aws configure</span><br></pre></td></tr></table></figure></div><p>输入以下信息：</p><ul><li>AWS Access Key ID:</li><li>AWS Secret Access Key:</li><li>Default region name: 选择你的区域，例如 <code>us-east-1</code></li><li>Default output format: <code>json</code></li></ul><p><strong>安装 Boto3 SDK：</strong></p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install boto3</span><br></pre></td></tr></table></figure></div><h3 id="4-搭建-Flask-API-服务"><a href="#4-搭建-Flask-API-服务" class="headerlink" title="4. 搭建 Flask API 服务"></a>4. 搭建 Flask API 服务</h3><p>我们使用 Flask 来创建一个 API 服务，用户可以通过 POST 请求生成图片，并返回图片的 URL。</p><p>以下是完整的 Flask 代码：</p><div class="code-container" data-rel="Python"><figure class="iseeu highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> flask <span class="keyword">import</span> Flask, request, jsonify, send_from_directory</span><br><span class="line"><span class="keyword">from</span> flask_cors <span class="keyword">import</span> CORS</span><br><span class="line"><span class="keyword">import</span> base64</span><br><span class="line"><span class="keyword">import</span> boto3</span><br><span class="line"><span class="keyword">import</span> json</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">from</span> datetime <span class="keyword">import</span> datetime</span><br><span class="line"></span><br><span class="line">app = Flask(__name__)</span><br><span class="line">CORS(app)</span><br><span class="line"></span><br><span class="line">client = boto3.client(<span class="string">&quot;bedrock-runtime&quot;</span>, region_name=<span class="string">&quot;us-east-1&quot;</span>)</span><br><span class="line">model_id = <span class="string">&quot;stability.stable-diffusion-xl-v1&quot;</span></span><br><span class="line"></span><br><span class="line">output_dir = <span class="string">&quot;/root/output&quot;</span></span><br><span class="line"><span class="keyword">if</span> <span class="keyword">not</span> os.path.exists(output_dir):</span><br><span class="line">    os.makedirs(output_dir)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 配置API密钥</span></span><br><span class="line">API_KEY = <span class="string">&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">generate_image_from_bedrock</span>(<span class="params">prompt</span>):</span><br><span class="line">    native_request = &#123;</span><br><span class="line">        <span class="string">&quot;text_prompts&quot;</span>: [&#123;<span class="string">&quot;text&quot;</span>: prompt, <span class="string">&quot;weight&quot;</span>: <span class="number">1</span>&#125;],</span><br><span class="line">        <span class="string">&quot;cfg_scale&quot;</span>: <span class="number">10</span>,</span><br><span class="line">        <span class="string">&quot;steps&quot;</span>: <span class="number">50</span>,</span><br><span class="line">        <span class="string">&quot;seed&quot;</span>: <span class="number">0</span>,</span><br><span class="line">        <span class="string">&quot;width&quot;</span>: <span class="number">1024</span>,</span><br><span class="line">        <span class="string">&quot;height&quot;</span>: <span class="number">1024</span>,</span><br><span class="line">        <span class="string">&quot;samples&quot;</span>: <span class="number">1</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        response = client.invoke_model(</span><br><span class="line">            modelId=model_id,</span><br><span class="line">            body=json.dumps(native_request)</span><br><span class="line">        )</span><br><span class="line">        model_response = json.loads(response[<span class="string">&quot;body&quot;</span>].read())</span><br><span class="line">        base64_image_data = model_response[<span class="string">&quot;artifacts&quot;</span>][<span class="number">0</span>][<span class="string">&quot;base64&quot;</span>]</span><br><span class="line">        <span class="keyword">return</span> base64_image_data</span><br><span class="line">    <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">        <span class="keyword">raise</span> Exception(<span class="string">f&quot;Error invoking Bedrock model: <span class="subst">&#123;<span class="built_in">str</span>(e)&#125;</span>&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">verify_api_key</span>(<span class="params">api_key</span>):</span><br><span class="line">    <span class="keyword">if</span> api_key != API_KEY:</span><br><span class="line">        <span class="keyword">raise</span> PermissionError(<span class="string">&quot;Invalid API Key. Access Denied.&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">save_image_and_get_url</span>(<span class="params">base64_image</span>):</span><br><span class="line">    timestamp = datetime.now().strftime(<span class="string">&quot;%Y%m%d_%H%M%S&quot;</span>)</span><br><span class="line">    filename = <span class="string">f&quot;image_<span class="subst">&#123;timestamp&#125;</span>.png&quot;</span></span><br><span class="line">    filepath = os.path.join(output_dir, filename)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">with</span> <span class="built_in">open</span>(filepath, <span class="string">&quot;wb&quot;</span>) <span class="keyword">as</span> image_file:</span><br><span class="line">        image_file.write(base64.b64decode(base64_image))</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="string">f&quot;/images/<span class="subst">&#123;filename&#125;</span>&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">&#x27;/generate-image&#x27;</span>, methods=[<span class="string">&#x27;POST&#x27;</span>]</span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">generate_image</span>():</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        data = request.json</span><br><span class="line">        prompt = data.get(<span class="string">&quot;prompt&quot;</span>)</span><br><span class="line">        api_key = request.headers.get(<span class="string">&quot;Authorization&quot;</span>)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> api_key <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">            <span class="keyword">return</span> jsonify(&#123;<span class="string">&quot;error&quot;</span>: <span class="string">&quot;API Key is required&quot;</span>&#125;), <span class="number">400</span></span><br><span class="line"></span><br><span class="line">        verify_api_key(api_key)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> prompt:</span><br><span class="line">            <span class="keyword">return</span> jsonify(&#123;<span class="string">&quot;error&quot;</span>: <span class="string">&quot;Prompt is required&quot;</span>&#125;), <span class="number">400</span></span><br><span class="line"></span><br><span class="line">        base64_image = generate_image_from_bedrock(prompt)</span><br><span class="line">        image_url = save_image_and_get_url(base64_image)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> jsonify(&#123;<span class="string">&quot;imageUrl&quot;</span>: image_url&#125;), <span class="number">200</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">except</span> PermissionError <span class="keyword">as</span> e:</span><br><span class="line">        <span class="keyword">return</span> jsonify(&#123;<span class="string">&quot;error&quot;</span>: <span class="built_in">str</span>(e)&#125;), <span class="number">403</span></span><br><span class="line">    <span class="keyword">except</span> KeyError <span class="keyword">as</span> e:</span><br><span class="line">        <span class="keyword">return</span> jsonify(&#123;<span class="string">&quot;error&quot;</span>: <span class="string">f&quot;Missing key: <span class="subst">&#123;<span class="built_in">str</span>(e)&#125;</span>&quot;</span>&#125;), <span class="number">400</span></span><br><span class="line">    <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">        <span class="keyword">return</span> jsonify(&#123;<span class="string">&quot;error&quot;</span>: <span class="string">f&quot;Internal Server Error: <span class="subst">&#123;<span class="built_in">str</span>(e)&#125;</span>&quot;</span>&#125;), <span class="number">500</span></span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route(<span class="params"><span class="string">&#x27;/images/&lt;path:filename&gt;&#x27;</span></span>)</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">serve_image</span>(<span class="params">filename</span>):</span><br><span class="line">    <span class="keyword">return</span> send_from_directory(output_dir, filename)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&#x27;__main__&#x27;</span>:</span><br><span class="line">    app.run(host=<span class="string">&quot;0.0.0.0&quot;</span>, port=<span class="number">5000</span>, debug=<span class="literal">False</span>)</span><br></pre></td></tr></table></figure></div><h4 id="代码说明"><a href="#代码说明" class="headerlink" title="代码说明"></a>代码说明</h4><ul><li><code>generate_image_from_bedrock</code>：调用 Amazon Bedrock 生成图像。</li><li><code>verify_api_key</code>：验证请求中的 API 密钥。</li><li><code>save_image_and_get_url</code>：将生成的图像保存到本地，并返回图像的 URL。</li></ul><h3 id="5-使用-systemd-和-Nginx-配置反向代理"><a href="#5-使用-systemd-和-Nginx-配置反向代理" class="headerlink" title="5. 使用 systemd 和 Nginx 配置反向代理"></a>5. 使用 systemd 和 Nginx 配置反向代理</h3><p>为了将 API 服务以后台进程运行，并通过 HTTPS 访问，我们需要使用 systemd 和 Nginx 配置。</p><h4 id="配置-systemd-服务"><a href="#配置-systemd-服务" class="headerlink" title="配置 systemd 服务"></a>配置 systemd 服务</h4><p>创建 systemd 服务文件：</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> nano /etc/systemd/system/flaskapp.service</span><br></pre></td></tr></table></figure></div><p>将以下内容粘贴到文件中：</p><div class="code-container" data-rel="Ini"><figure class="iseeu highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">[Unit]</span></span><br><span class="line"><span class="attr">Description</span>=Flask web application for image generation</span><br><span class="line"><span class="attr">After</span>=network.target</span><br><span class="line"></span><br><span class="line"><span class="section">[Service]</span></span><br><span class="line"><span class="attr">ExecStart</span>=/usr/bin/python3 /root/img-web.py</span><br><span class="line"><span class="attr">WorkingDirectory</span>=/root</span><br><span class="line"><span class="attr">StandardOutput</span>=journal</span><br><span class="line"><span class="attr">StandardError</span>=journal</span><br><span class="line"><span class="attr">Restart</span>=always</span><br><span class="line"><span class="attr">User</span>=root</span><br><span class="line"><span class="attr">Group</span>=root</span><br><span class="line"></span><br><span class="line"><span class="section">[Install]</span></span><br><span class="line"><span class="attr">WantedBy</span>=multi-user.target</span><br></pre></td></tr></table></figure></div><p>注意：</p><ul><li>我们将 <code>ExecStart</code> 设置为 <code>/usr/bin/python3 /root/img-web.py</code>，这里使用了你提供的 img-web.py 的路径。</li><li><code>WorkingDirectory</code> 设置为 <code>/root</code>，因为你的脚本在 root 目录下。</li><li><code>User</code> 和 <code>Group</code> 都设置为 <code>root</code>，因为你是在 root 用户下操作的。</li></ul><p>保存并关闭文件（在 nano 编辑器中，按 Ctrl+X，然后按 Y，最后按 Enter）。</p><p>重新加载 systemd 配置：</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> systemctl daemon-reload</span><br></pre></td></tr></table></figure></div><p>启用服务，使其在系统启动时自动启动：</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> systemctl <span class="built_in">enable</span> flaskapp.service</span><br></pre></td></tr></table></figure></div><p>启动服务：</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> systemctl start flaskapp.service</span><br></pre></td></tr></table></figure></div><p>检查服务状态：</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> systemctl status flaskapp.service</span><br></pre></td></tr></table></figure></div><h4 id="配置-Nginx-反向代理"><a href="#配置-Nginx-反向代理" class="headerlink" title="配置 Nginx 反向代理"></a>配置 Nginx 反向代理</h4><p>参考我的这篇博客</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">https://xblog.aptzone.cc/2024/01/26/docker%E4%BB%A5%E5%8F%8Adocker-compose%E7%9A%84%E4%B8%80%E4%BA%9B%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4/</span><br></pre></td></tr></table></figure></div><h3 id="6-测试-API"><a href="#6-测试-API" class="headerlink" title="6. 测试 API"></a>6. 测试 API</h3><p>使用 <code>curl</code> 命令进行测试，发送 POST 请求并查看返回的图像 URL：</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">curl -X POST http://your-domain.com/generate-image \</span><br><span class="line">    -H <span class="string">&quot;Authorization: YOUR_API_KEY&quot;</span> \</span><br><span class="line">    -H <span class="string">&quot;Content-Type: application/json&quot;</span> \</span><br><span class="line">    -d <span class="string">&#x27;&#123;&quot;prompt&quot;: &quot;a futuristic city skyline at sunset&quot;&#125;&#x27;</span></span><br></pre></td></tr></table></figure></div><p>如果一切顺利，返回的 JSON 会包含图片的 URL。</p><hr><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>通过本教程，你可以成功使用 Amazon Bedrock 创建图片，并通过 Flask API 提供服务。你还学会了如何使用 systemd 管理 Flask 服务，并通过 Nginx 配置反向代理，提供一个可以在线访问的 API。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;Amazon Bedrock 可以画图，我正好需要在网站上调用他来画图，所以就尝试在VPS上搭建了一个代理，提供API和KEY，输入prompt，将图片保存在VPS上，并返回图片的url。我用的就是GCP 300那个免费的美国VPS。&lt;/p&gt;
&lt;p&gt;这里将介绍如何使用 Am</summary>
      
    
    
    
    
    <category term="Amazon" scheme="http://example.com/tags/Amazon/"/>
    
    <category term="Bedrock" scheme="http://example.com/tags/Bedrock/"/>
    
  </entry>
  
  <entry>
    <title>基于知识图谱的电影问答系统</title>
    <link href="http://example.com/2024/10/10/%E5%9F%BA%E4%BA%8E%E7%9F%A5%E8%AF%86%E5%9B%BE%E8%B0%B1%E7%9A%84%E7%94%B5%E5%BD%B1%E9%97%AE%E7%AD%94%E7%B3%BB%E7%BB%9F/"/>
    <id>http://example.com/2024/10/10/%E5%9F%BA%E4%BA%8E%E7%9F%A5%E8%AF%86%E5%9B%BE%E8%B0%B1%E7%9A%84%E7%94%B5%E5%BD%B1%E9%97%AE%E7%AD%94%E7%B3%BB%E7%BB%9F/</id>
    <published>2024-10-10T13:24:25.000Z</published>
    <updated>2024-11-17T05:02:00.366Z</updated>
    
    <content type="html"><![CDATA[<p>This message is used to verify that this feed (feedId:68211402508590080) belongs to me (userId:67521536888445952). Join me in enjoying the next generation information browser <a class="link"   href="https://follow.is/" >https://follow.is<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a>.</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="https://tc.aptzone.cc/images/2024/10/10/bcf5b76e6af0fe8cb081133c4fe8024e.png"                      alt="Movie QA System"                ></p><p><strong>代码地址</strong>：<a class="link"   href="https://github.com/ZefanHu/KGQA" >https://github.com/ZefanHu/KGQA<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;This message is used to verify that this feed (feedId:68211402508590080) belongs to me (userId:67521536888445952). Join me in enjoying th</summary>
      
    
    
    
    
    <category term="知识图谱" scheme="http://example.com/tags/%E7%9F%A5%E8%AF%86%E5%9B%BE%E8%B0%B1/"/>
    
  </entry>
  
  <entry>
    <title>通过 Git Hook 自动部署 Hexo 博客到 1Panel 站点</title>
    <link href="http://example.com/2024/10/10/%E5%9C%A81panel%E9%9D%A2%E6%9D%BF%E5%BF%AB%E9%80%9F%E9%83%A8%E7%BD%B2hexo%E5%8D%9A%E5%AE%A2/"/>
    <id>http://example.com/2024/10/10/%E5%9C%A81panel%E9%9D%A2%E6%9D%BF%E5%BF%AB%E9%80%9F%E9%83%A8%E7%BD%B2hexo%E5%8D%9A%E5%AE%A2/</id>
    <published>2024-10-10T12:38:46.000Z</published>
    <updated>2025-04-29T11:07:24.135Z</updated>
    
    <content type="html"><![CDATA[<p>本文将介绍如何配置服务器端的 Git 仓库和钩子 (Hook)，以实现在本地执行 <code>hexo deploy</code> 命令时，自动将 Hexo 生成的静态文件部署到由 1Panel 面板管理的网站目录中。这种方法避免了手动上传文件的繁琐过程，实现了类似 CI&#x2F;CD 的自动化部署流程。</p><h2 id="前提条件"><a href="#前提条件" class="headerlink" title="前提条件"></a>前提条件</h2><p>在开始之前，请确保你已满足以下条件：</p><ol><li><p><strong>服务器</strong>:</p><ul><li>安装了 1Panel 面板的 Linux 服务器。</li><li>拥有服务器的 SSH 访问权限（本文示例将使用 <code>root</code> 用户，你可以根据实际情况替换为其他有权限的用户）。</li><li>服务器上已安装 <code>Git</code>。</li></ul></li><li><p><strong>1Panel 配置</strong>:</p><ul><li>已通过 1Panel 创建了一个网站（例如 <code>blog.yourdomain.com</code>），并记下该网站的 <strong>根目录</strong>（Document Root）。通常在 1Panel 中，路径类似于 <code>/opt/1panel/apps/openresty/openresty/www/sites/你的网站目录/index</code>。<strong>请务必替换为你的实际路径</strong>。</li></ul></li><li><p><strong>本地环境</strong>:</p><ul><li><p>你的电脑上已安装并配置好 Hexo 博客环境。</p></li><li><p>你的电脑上已安装 <code>Git</code>。</p></li><li><p>已安装 Hexo Git 部署插件：</p>  <div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install hexo-deployer-git --save</span><br></pre></td></tr></table></figure></div></li></ul></li><li><p><strong>SSH 密钥认证 (关键)</strong>:</p><ul><li>你需要在本地电脑生成 SSH 密钥对（如果还没有的话）。</li><li>必须将你的本地电脑的 <strong>SSH 公钥</strong> 添加到服务器上对应用户（如 <code>root</code>）的 <code>~/.ssh/authorized_keys</code> 文件中。这样可以确保 <code>hexo deploy</code> 时 Git PUSH 操作无需输入密码即可完成。</li></ul></li></ol><h2 id="配置步骤"><a href="#配置步骤" class="headerlink" title="配置步骤"></a>配置步骤</h2><h3 id="步骤一：在服务器上安装-Git"><a href="#步骤一：在服务器上安装-Git" class="headerlink" title="步骤一：在服务器上安装 Git"></a>步骤一：在服务器上安装 Git</h3><p>如果你的服务器尚未安装 Git，请根据你的 Linux 发行版执行相应命令：</p><ul><li><p><strong>Debian&#x2F;Ubuntu</strong>:</p>  <div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt-get update</span><br><span class="line"><span class="built_in">sudo</span> apt-get install git -y</span><br></pre></td></tr></table></figure></div></li><li><p><strong>Fedora&#x2F;RedHat&#x2F;CentOS</strong>:</p>  <div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> yum install git -y</span><br></pre></td></tr></table></figure></div></li></ul><h3 id="步骤二：在服务器上创建-Git-裸仓库-Bare-Repository"><a href="#步骤二：在服务器上创建-Git-裸仓库-Bare-Repository" class="headerlink" title="步骤二：在服务器上创建 Git 裸仓库 (Bare Repository)"></a>步骤二：在服务器上创建 Git 裸仓库 (Bare Repository)</h3><p>我们需要在服务器上创建一个 Git “裸仓库”。裸仓库不包含工作目录（即你看不到项目文件），只存储 Git 的版本历史和对象数据，非常适合作为中心仓库或中转仓库。</p><ol><li><p><strong>创建仓库目录</strong>:<br> 选择一个路径来存放你的 Git 仓库。例如，我们选择放在 <code>/root/git/</code> 目录下（你可以自定义路径）。</p> <div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 创建目录 (如果父目录不存在，也一并创建)</span></span><br><span class="line"><span class="built_in">mkdir</span> -p /root/git/ </span><br></pre></td></tr></table></figure></div></li><li><p><strong>修改目录权限 (可选但推荐)</strong>:<br> 如果你不是一直使用 <code>root</code> 用户，或者希望更精细地控制权限，可以修改目录所有者和权限。如果使用 <code>root</code>，此步通常可以跳过。</p> <div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 将目录所有者改为当前用户 (如果用 root 执行，则为 root)</span></span><br><span class="line"><span class="comment"># chown -R $USER:$USER /root/git/ </span></span><br><span class="line"><span class="comment"># 设置权限 (所有者完全控制，同组用户读取执行，其他用户读取执行)</span></span><br><span class="line"><span class="comment"># chmod -R 755 /root/git/</span></span><br></pre></td></tr></table></figure></div></li><li><p><strong>初始化裸仓库</strong>:<br> 进入你选择的目录，并初始化一个裸仓库。我们将仓库命名为 <code>blog.git</code> (你也可以自定义)。</p> <div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> /root/git</span><br><span class="line">git init --bare blog.git </span><br></pre></td></tr></table></figure></div><p> 执行后，你会在 <code>/root/git/</code> 目录下看到一个名为 <code>blog.git</code> 的文件夹。</p></li></ol><h3 id="步骤三：在服务器上配置-Git-钩子-post-receive"><a href="#步骤三：在服务器上配置-Git-钩子-post-receive" class="headerlink" title="步骤三：在服务器上配置 Git 钩子 (post-receive)"></a>步骤三：在服务器上配置 Git 钩子 (post-receive)</h3><p>Git 钩子是在 Git 操作过程中的特定时间点自动执行的脚本。<code>post-receive</code> 钩子在服务器成功接收到推送 (push) 后执行。我们将利用这个钩子将推送过来的 Hexo 文件检出 (checkout) 到 1Panel 的网站根目录。</p><ol><li><p><strong>创建钩子文件</strong>:<br> 进入裸仓库的 <code>hooks</code> 目录，并创建一个名为 <code>post-receive</code> 的文件。</p> <div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim /root/git/blog.git/hooks/post-receive</span><br></pre></td></tr></table></figure></div></li><li><p><strong>编辑钩子内容</strong>:<br> 在 <code>vim</code> 编辑器中，按 <code>i</code> 进入插入模式，然后粘贴以下脚本内容：</p> <div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/bash</span></span><br><span class="line"><span class="comment"># 指定 Hexo 网站文件存放的目录 (!!!!务必替换为你的 1Panel 网站实际根目录!!!!)</span></span><br><span class="line">WORK_TREE=<span class="string">&quot;/opt/1panel/apps/openresty/openresty/www/sites/blog/index&quot;</span> </span><br><span class="line"><span class="comment"># 指定 Git 裸仓库的路径 (!!!!务必替换为你的裸仓库实际路径!!!!)</span></span><br><span class="line">GIT_DIR=<span class="string">&quot;/root/git/blog.git&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 执行 Git checkout 命令，强制将内容检出到工作目录</span></span><br><span class="line">git --work-tree=<span class="variable">$&#123;WORK_TREE&#125;</span> --git-dir=<span class="variable">$&#123;GIT_DIR&#125;</span> checkout -f</span><br><span class="line"></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;Hexo blog deployed to <span class="variable">$&#123;WORK_TREE&#125;</span>&quot;</span> </span><br></pre></td></tr></table></figure></div><p> <strong>重要说明</strong>:</p><ul><li><code>--work-tree</code>: <strong>必须</strong> 修改为你在 1Panel 中创建的那个网站的 <strong>实际根目录路径</strong>。</li><li><code>--git-dir</code>: <strong>必须</strong> 修改为你刚刚创建的 Git <strong>裸仓库的路径</strong> (<code>blog.git</code>)。</li><li><code>checkout -f</code>: <code>-f</code> 参数表示强制检出，会覆盖目标目录中已有的文件。</li></ul></li><li><p><strong>保存并退出 Vim</strong>:<br> 按下 <code>ESC</code> 键退出插入模式，然后输入 <code>:wq!</code> 并按回车键，强制保存并退出 Vim。</p></li><li><p><strong>赋予钩子执行权限</strong>:<br> 新创建的钩子文件默认没有执行权限，需要添加：</p> <div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">chmod</span> +x /root/git/blog.git/hooks/post-receive</span><br></pre></td></tr></table></figure></div></li></ol><h3 id="步骤四：修改本地-Hexo-配置"><a href="#步骤四：修改本地-Hexo-配置" class="headerlink" title="步骤四：修改本地 Hexo 配置"></a>步骤四：修改本地 Hexo 配置</h3><p>现在，配置你本地 Hexo 博客的 <code>_config.yml</code> 文件，告诉它如何部署。</p><ol><li><p>打开你本地 Hexo 项目根目录下的 <code>_config.yml</code> 文件。</p></li><li><p>找到 <code>deploy</code> 部分（如果不存在，请添加）。</p></li><li><p>修改或添加 <code>git</code> 部署配置，如下所示：</p> <div class="code-container" data-rel="Yaml"><figure class="iseeu highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">deploy:</span></span><br><span class="line">  <span class="attr">type:</span> <span class="string">git</span></span><br><span class="line">  <span class="attr">repo:</span> <span class="string">ssh://root@YOUR_SERVER_IP:22/root/git/blog.git</span> <span class="comment"># !!! 修改 IP 和路径 !!!</span></span><br><span class="line">  <span class="attr">branch:</span> <span class="string">main</span> <span class="comment"># 或者 master，取决于你希望推送的分支</span></span><br><span class="line">  <span class="comment"># message: &quot;Site updated: &#123;&#123; now(&#x27;YYYY-MM-DD HH:mm:ss&#x27;) &#125;&#125;&quot; # 可选的提交信息</span></span><br></pre></td></tr></table></figure></div><p> <strong>请务必修改</strong>:</p><ul><li><code>root</code>: 替换为你配置了 SSH 免密登录的服务器用户名。</li><li><code>YOUR_SERVER_IP</code>: 替换为你的服务器公网 IP 地址或域名。</li><li><code>22</code>: 如果你的 SSH 端口不是默认的 22，请修改。</li><li><code>/root/git/blog.git</code>: 替换为你在服务器上创建的裸仓库的 <strong>绝对路径</strong>。</li><li><code>branch</code>: 确保这个分支名 (<code>main</code> 或 <code>master</code>) 与你的 <code>post-receive</code> 钩子期望检出的分支一致（默认情况下 <code>checkout -f</code> 会检出仓库的 <code>HEAD</code> 指向的分支，通常是 <code>master</code> 或 <code>main</code>）。</li></ul></li></ol><h3 id="步骤五：部署你的-Hexo-博客"><a href="#步骤五：部署你的-Hexo-博客" class="headerlink" title="步骤五：部署你的 Hexo 博客"></a>步骤五：部署你的 Hexo 博客</h3><p>一切配置完成后，部署就非常简单了：</p><ol><li><p>在本地 Hexo 项目目录下，执行标准的生成和部署命令：</p> <div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo clean &amp;&amp; hexo generate &amp;&amp; hexo deploy</span><br></pre></td></tr></table></figure></div><p> 或者直接：</p> <div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hexo d </span><br></pre></td></tr></table></figure></div></li><li><p><strong>工作流程</strong>:</p><ul><li><code>hexo g</code> 生成静态文件到 <code>public</code> 目录。</li><li><code>hexo d</code> 使用 <code>hexo-deployer-git</code> 插件：<ul><li>将 <code>public</code> 目录的内容提交到一个临时 Git 分支。</li><li>通过 SSH 将这个分支强制推送到你配置的服务器裸仓库 (<code>ssh://root@YOUR_SERVER_IP:22/root/git/blog.git</code>)。</li><li>服务器上的 Git 仓库接收到推送后，自动触发 <code>post-receive</code> 钩子脚本。</li><li>钩子脚本执行 <code>git checkout -f</code>，将最新的网站文件强制检出（部署）到 1Panel 网站的根目录 (<code>/opt/1panel/apps/openresty/openresty/www/sites/blog/index</code>)。</li></ul></li></ul></li><li><p><strong>验证</strong>:<br> 部署命令执行成功后，稍等片刻，然后访问你的博客域名 (<code>http://blog.yourdomain.com</code> 或 <code>https://blog.yourdomain.com</code>)，应该就能看到更新后的内容了。</p></li></ol><h3 id="故障排除提示"><a href="#故障排除提示" class="headerlink" title="故障排除提示"></a>故障排除提示</h3><ul><li><strong>权限问题</strong>: 检查服务器上 Git 仓库目录、网站根目录以及钩子文件的权限是否正确。钩子脚本需要有权限写入网站根目录。</li><li><strong>SSH 密钥问题</strong>: 确保本地 SSH 公钥已正确添加到服务器的 <code>authorized_keys</code> 文件中，并且本地 SSH 私钥可用。尝试手动 <code>ssh root@YOUR_SERVER_IP</code> 看是否能免密登录。</li><li><strong>路径错误</strong>: 仔细核对 <code>post-receive</code> 脚本中的 <code>--work-tree</code> 和 <code>--git-dir</code> 路径，以及本地 <code>_config.yml</code> 中的 <code>repo</code> 路径是否完全正确。</li><li><strong>钩子未执行</strong>: 检查钩子文件是否有执行权限 (<code>chmod +x</code>)，文件名是否精确为 <code>post-receive</code> (没有扩展名)。</li><li><strong>查看日志</strong>: 如果部署失败，可以尝试在服务器上查看 SSH 或 Git 相关日志，或者在钩子脚本中添加一些 <code>echo</code> 输出到日志文件来调试。</li></ul><p>通过以上配置，你就实现了一个高效、自动化的 Hexo 博客部署流程！</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;本文将介绍如何配置服务器端的 Git 仓库和钩子 (Hook)，以实现在本地执行 &lt;code&gt;hexo deploy&lt;/code&gt; 命令时，自动将 Hexo 生成的静态文件部署到由 1Panel 面板管理的网站目录中。这种方法避免了手动上传文件的繁琐过程，实现了类似 CI&amp;</summary>
      
    
    
    
    
    <category term="linux" scheme="http://example.com/tags/linux/"/>
    
    <category term="hexo" scheme="http://example.com/tags/hexo/"/>
    
    <category term="git" scheme="http://example.com/tags/git/"/>
    
    <category term="1Panel" scheme="http://example.com/tags/1Panel/"/>
    
    <category term="自动化部署" scheme="http://example.com/tags/%E8%87%AA%E5%8A%A8%E5%8C%96%E9%83%A8%E7%BD%B2/"/>
    
  </entry>
  
  <entry>
    <title>SearchEngine</title>
    <link href="http://example.com/2024/09/26/SearchEngine/"/>
    <id>http://example.com/2024/09/26/SearchEngine/</id>
    <published>2024-09-26T07:24:24.000Z</published>
    <updated>2024-11-17T05:00:31.145Z</updated>
    
    <content type="html"><![CDATA[<h2 id="开发环境"><a href="#开发环境" class="headerlink" title="开发环境"></a>开发环境</h2><ul><li><strong>操作系统</strong>: Ubuntu 22.04</li><li><strong>编程语言</strong>: C++11</li><li><strong>构建工具</strong>: Make</li><li><strong>数据库</strong>: Redis</li></ul><h2 id="技术栈"><a href="#技术栈" class="headerlink" title="技术栈"></a>技术栈</h2><ul><li><strong>中文分词库</strong>: cppjieba</li><li><strong>文本去重</strong>: simhash</li><li><strong>计算机词语权重</strong>: TF-IDF</li><li><strong>倒排索引</strong>: 用于快速检索文档</li><li><strong>余弦相似度算法</strong>: 用于计算查询和文档的相似度</li></ul><h2 id="主要模块说明"><a href="#主要模块说明" class="headerlink" title="主要模块说明"></a>主要模块说明</h2><ol><li><strong>DictProducer</strong>: 负责创建词典{Word Frequency}和索引文件{Word idx1 idx2 …}</li><li><strong>PageLibPreprocessor</strong>: 负责网页库的预处理,包括网页去重和倒排索引的生成。</li><li><strong>Dictionary</strong>: 管理词典和索引,提供词频查询等功能。</li><li><strong>WebSearch</strong>: 实现网页搜索的核心逻辑,包括查询分词、文档检索和相似度计算。</li><li><strong>KeyRecommend</strong>: 实现关键词推荐功能,使用最小编辑距离算法。</li><li><strong>NetServer</strong>: 基于 Wfrest 的 HTTP 服务器,处理前端请求。</li><li><strong>Configuration</strong>: 管理配置信息,实现单例模式。</li></ol><p>本项目的关键字推荐和网页搜索模块是单独的，在离线阶段完成了词典 词典索引 去重后的网页库 网页偏移库 倒排索引库<br>建立倒排索引是本项目的核心也是难点，目的是将单词或记录作为索引，将文档 ID 作为记录，<br>这样便可以方便地通过单词或记录查找到其所在的文档。<br>TF : Term Frequency, 某个词在文章中出现的次数；<br>DF: Document Frequency, 某个词在所有文章中出现的次数，即包含该词语的文档数量；<br>IDF: Inverse Document Frequency, 逆文档频率，表示该词对于该篇文章的重要性的一个系数，<br>其计算公式为：IDF &#x3D; log2(N&#x2F;(DF+1))，其中 N 表示文档的总数或网页库的文档数，<br>最后，词语的权重 w 则为：w &#x3D; TF * IDF</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;开发环境&quot;&gt;&lt;a href=&quot;#开发环境&quot; class=&quot;headerlink&quot; title=&quot;开发环境&quot;&gt;&lt;/a&gt;开发环境&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;操作系统&lt;/strong&gt;: Ubuntu 22.04&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;编程语</summary>
      
    
    
    
    
    <category term="C++" scheme="http://example.com/tags/C/"/>
    
  </entry>
  
  <entry>
    <title>使用 FRP 配置内网穿透访问内部服务</title>
    <link href="http://example.com/2024/08/11/%E5%86%85%E7%BD%91%E7%A9%BF%E9%80%8F%E9%85%8D%E7%BD%AE/"/>
    <id>http://example.com/2024/08/11/%E5%86%85%E7%BD%91%E7%A9%BF%E9%80%8F%E9%85%8D%E7%BD%AE/</id>
    <published>2024-08-11T04:07:39.000Z</published>
    <updated>2025-04-29T11:06:40.575Z</updated>
    
    <content type="html"><![CDATA[<p>本文将详细介绍如何利用流行的内网穿透工具 <a class="link"   href="https://github.com/fatedier/frp" >FRP (Fast Reverse Proxy)<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a>，在拥有公网 IP 的服务器（我们称之为“控制面板服务器”）和位于内网的服务器（“目标服务器”）之间建立连接，从而实现通过公网域名访问部署在目标服务器内部的服务。本文将以轻量级导航站 <code>van-nav</code> 作为内部服务示例。</p><h2 id="前提条件"><a href="#前提条件" class="headerlink" title="前提条件"></a>前提条件</h2><p>在开始之前，请确保你满足以下条件：</p><ul><li><strong>两台 Linux 服务器</strong> (本文以两台 Ubuntu 22.04 为例)：<ul><li>一台拥有公网 IP 的服务器，作为 <strong>控制面板服务器</strong> (运行 <code>frps</code>)。示例 IP：<code>18.138.249.128</code></li><li>一台位于内网或防火墙后的服务器，作为 <strong>目标服务器</strong> (运行 <code>frpc</code> 和内部服务)。示例 IP：<code>35.212.231.21</code> (这是公网IP，但假设其上的服务需要穿透)</li></ul></li><li><strong>目标服务器上运行的服务</strong>：需要一个通过 HTTP 协议访问的服务。本文以 Docker 容器运行的 <code>van-nav</code> 为例，监听在特定端口。也可以是直接运行的二进制程序。</li><li><strong>域名</strong>：一个你拥有管理权限的域名，需要将其解析到 <strong>控制面板服务器</strong> 的公网 IP。本文示例域名：<code>nat1.status.aptzone.cc</code>。</li><li><strong>FRP 程序</strong>：从 <a class="link"   href="https://github.com/fatedier/frp/releases" >FRP GitHub Releases<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a> 下载适合你服务器架构（如 amd64, arm64）的最新版本。压缩包内同时包含服务端程序 <code>frps</code> 和客户端程序 <code>frpc</code>。</li></ul><h2 id="配置步骤"><a href="#配置步骤" class="headerlink" title="配置步骤"></a>配置步骤</h2><p>接下来，我们将分步完成 FRP 的配置和运行。</p><h3 id="1-确定内网服务地址和端口"><a href="#1-确定内网服务地址和端口" class="headerlink" title="1. 确定内网服务地址和端口"></a>1. 确定内网服务地址和端口</h3><p>首先，你需要知道 <strong>目标服务器</strong> 上内部服务监听的 IP 地址和端口，以便 <code>frpc</code> 可以将流量转发给它。</p><ul><li><p><strong>确定 IP 地址</strong>：</p><ul><li><p>如果服务和 <code>frpc</code> 运行在同一台目标服务器上，通常可以使用 <code>127.0.0.1</code> (localhost)。</p></li><li><p>如果服务运行在 Docker 容器中，你可能需要使用 Docker 容器的内部 IP，或者更常见的是，使用目标服务器上可以访问该容器映射端口的网络接口 IP (例如，物理网卡 IP 或 Docker 网桥 IP)。</p></li><li><p>你可以使用 <code>ip addr show</code> 命令查看目标服务器的网络接口和 IP 地址：</p><pre><code><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ip addr show</span><br></pre></td></tr></table></figure></div>输出示例：<div class="code-container" data-rel="Text"><figure class="iseeu highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">1: lo: &lt;LOOPBACK,UP,LOWER_UP&gt; mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000</span><br><span class="line">    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00</span><br><span class="line">    inet 127.0.0.1/8 scope host lo</span><br><span class="line">       valid_lft forever preferred_lft forever</span><br><span class="line">    inet6 ::1/128 scope host </span><br><span class="line">       valid_lft forever preferred_lft forever</span><br><span class="line">2: ens4: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1460 qdisc mq state UP group default qlen 1000</span><br><span class="line">    link/ether 42:01:0a:8a:00:04 brd ff:ff:ff:ff:ff:ff</span><br><span class="line">    inet 10.138.0.4/32 metric 100 scope global dynamic ens4</span><br><span class="line">       valid_lft 2138sec preferred_lft 2138sec</span><br><span class="line">    inet6 fe80::4001:aff:fe8a:4/64 scope link </span><br><span class="line">       valid_lft forever preferred_lft forever</span><br><span class="line">3: docker0: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc noqueue state UP group default </span><br><span class="line">    link/ether 02:42:5c:de:e0:39 brd ff:ff:ff:ff:ff:ff</span><br><span class="line">    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0</span><br><span class="line">       valid_lft forever preferred_lft forever</span><br><span class="line">    inet6 fe80::42:5cff:fede:e039/64 scope link </span><br><span class="line">       valid_lft forever preferred_lft forever</span><br></pre></td></tr></table></figure></div>你需要记下目标服务器上 `frpc` 可以访问到 `van-nav` 服务的那个 IP 地址。在本例中，我们将使用 `ens4` 接口的 IP `10.138.0.4`。</code></pre></li></ul></li><li><p><strong>确定端口</strong>：确认 <code>van-nav</code> 服务实际监听的端口号。</p></li></ul><h3 id="2-运行内部服务-以-van-nav-Docker-为例"><a href="#2-运行内部服务-以-van-nav-Docker-为例" class="headerlink" title="2. 运行内部服务 (以 van-nav Docker 为例)"></a>2. 运行内部服务 (以 van-nav Docker 为例)</h3><p>在 <strong>目标服务器</strong> 上启动你的内部服务。以 <code>van-nav</code> Docker 容器为例：</p><div class="code-container" data-rel="Sh"><figure class="iseeu highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 确保你在一个合适的目录下执行，因为 -v $(pwd):/app/data 会映射当前目录</span></span><br><span class="line">docker run -d --name van-nav --restart always -p 8080:6412 -v $(<span class="built_in">pwd</span>):/app/data mereith/van-nav:latest</span><br></pre></td></tr></table></figure></div><ul><li><strong>命令解释</strong>：<ul><li><code>docker run -d</code>: 在后台运行容器。</li><li><code>--name van-nav</code>: 给容器命名为 <code>van-nav</code>。</li><li><code>--restart always</code>: 容器退出时自动重启。</li><li><code>-p 8080:6412</code>: 将容器内部的 <code>6412</code> 端口映射到 <strong>目标服务器</strong> 的 <code>8080</code> 端口。这意味着现在可以通过 <code>目标服务器IP:8080</code> (即 <code>10.138.0.4:8080</code>) 访问 <code>van-nav</code>。</li><li><code>-v $(pwd):/app/data</code>: 将主机当前目录映射到容器内的 <code>/app/data</code> 目录，用于持久化数据。</li><li><code>mereith/van-nav:latest</code>: 使用的 Docker 镜像。</li></ul></li></ul><blockquote><p><strong>替代方案</strong>：你也可以不使用 Docker，直接下载 <code>van-nav</code> 的二进制文件运行。具体步骤请参考其官方文档。关键是确保服务在目标服务器的某个 IP 和端口上运行。</p></blockquote><h3 id="3-配置-FRP-服务端-frps"><a href="#3-配置-FRP-服务端-frps" class="headerlink" title="3. 配置 FRP 服务端 (frps)"></a>3. 配置 FRP 服务端 (frps)</h3><p>在 <strong>控制面板服务器</strong> (公网服务器) 上，创建并编辑 <code>frps</code> 的配置文件。通常命名为 <code>frps.toml</code> (或 <code>frps.ini</code>，取决于你下载的版本和偏好，新版本推荐 TOML)。</p><p>将下载的 <code>frps</code> 可执行文件和配置文件（例如 <code>frps.toml</code>）放在服务器的某个目录下（例如 <code>/root/frp/</code>）。</p><p>编辑 <code>frps.toml</code> 文件：</p><div class="code-container" data-rel="Toml"><figure class="iseeu highlight toml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># /root/frp/frps.toml 内容示例</span></span><br><span class="line"><span class="attr">bindPort</span> = <span class="number">7000</span>         <span class="comment"># frps 与 frpc 通信的端口</span></span><br><span class="line"><span class="attr">vhostHttpPort</span> = <span class="number">8080</span>    <span class="comment"># frps 监听 HTTP 请求的端口 (用于域名转发)</span></span><br><span class="line"><span class="comment"># 你还可以添加 token 进行身份验证等，提高安全性，例如:</span></span><br><span class="line"><span class="comment"># auth.token = &quot;your_secure_token&quot; </span></span><br></pre></td></tr></table></figure></div><ul><li><code>bindPort</code>: <code>frps</code> 等待 <code>frpc</code> 连接的端口，需要确保防火墙已放行此端口 (TCP)。</li><li><code>vhostHttpPort</code>: 当 <code>frpc</code> 配置了 <code>type = http</code> 时，<code>frps</code> 在此端口监听来自公网的 HTTP 请求，并通过 <code>customDomains</code> 进行转发。确保防火墙放行此端口 (TCP)。</li></ul><h3 id="4-配置-FRP-客户端-frpc"><a href="#4-配置-FRP-客户端-frpc" class="headerlink" title="4. 配置 FRP 客户端 (frpc)"></a>4. 配置 FRP 客户端 (frpc)</h3><p>在 <strong>目标服务器</strong> (内网服务器) 上，创建并编辑 <code>frpc</code> 的配置文件。通常命名为 <code>frpc.toml</code> (或 <code>frpc.ini</code>)。</p><p>将下载的 <code>frpc</code> 可执行文件和配置文件（例如 <code>frpc.toml</code>）放在服务器的某个目录下（例如 <code>/root/frp/</code>）。</p><p>编辑 <code>frpc.toml</code> 文件：</p><div class="code-container" data-rel="Toml"><figure class="iseeu highlight toml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># /root/frp/frpc.toml 内容示例</span></span><br><span class="line"><span class="attr">serverAddr</span> = <span class="string">&quot;18.138.249.128&quot;</span>  <span class="comment"># 指向你的控制面板服务器的公网 IP</span></span><br><span class="line"><span class="attr">serverPort</span> = <span class="number">7000</span>             <span class="comment"># 必须与 frps.toml 中的 bindPort 一致</span></span><br><span class="line"><span class="comment"># 如果 frps 设置了 token，这里也需要配置:</span></span><br><span class="line"><span class="comment"># auth.token = &quot;your_secure_token&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="section">[[proxies]]</span></span><br><span class="line"><span class="attr">name</span> = <span class="string">&quot;van-nav-http&quot;</span>           <span class="comment"># 代理规则的名字，自定义，方便识别</span></span><br><span class="line"><span class="attr">type</span> = <span class="string">&quot;http&quot;</span>                   <span class="comment"># 代理类型为 HTTP</span></span><br><span class="line"><span class="attr">localIP</span> = <span class="string">&quot;10.138.0.4&quot;</span>          <span class="comment"># 目标服务器上 van-nav 服务的 IP (第一步确定的)</span></span><br><span class="line"><span class="attr">localPort</span> = <span class="number">8080</span>                <span class="comment"># 目标服务器上 van-nav 服务监听的端口 (第二步映射的)</span></span><br><span class="line"><span class="attr">customDomains</span> = [<span class="string">&quot;nat1.status.aptzone.cc&quot;</span>] <span class="comment"># 希望通过访问的域名 (需解析到控制面板服务器IP)</span></span><br></pre></td></tr></table></figure></div><ul><li><code>serverAddr</code>: 你的 <strong>控制面板服务器</strong> 的公网 IP 地址。</li><li><code>serverPort</code>: 连接 <code>frps</code> 的 <code>bindPort</code>。</li><li><code>[[proxies]]</code>: 定义一个具体的代理规则。</li><li><code>name</code>: 代理规则的唯一标识。</li><li><code>type = &quot;http&quot;</code>: 表明这是一个 HTTP 类型的代理。<code>frps</code> 会使用 <code>vhostHttpPort</code> 来处理这类代理的请求。</li><li><code>localIP</code>: 目标服务器上内部服务监听的 IP 地址。</li><li><code>localPort</code>: 目标服务器上内部服务监听的端口。</li><li><code>customDomains</code>: 指定将哪些域名的请求转发到这个 <code>localIP:localPort</code>。这个域名必须已经解析到你的 <strong>控制面板服务器</strong> 的公网 IP (<code>18.138.249.128</code>)。</li></ul><h3 id="5-使用-systemd-运行-frp"><a href="#5-使用-systemd-运行-frp" class="headerlink" title="5. 使用 systemd 运行 frp"></a>5. 使用 systemd 运行 frp</h3><p>为了让 <code>frps</code> 和 <code>frpc</code> 在后台稳定运行并在服务器重启后自动启动，推荐使用 <code>systemd</code> 进行管理。</p><p><strong>注意</strong>：以下步骤假设 <code>frps</code>、<code>frpc</code> 及其配置文件位于 <code>/root/frp/</code> 目录下。请根据你的实际路径修改 <code>ExecStart</code> 中的路径。</p><h4 id="配置-frps-的-systemd-服务-在控制面板服务器上"><a href="#配置-frps-的-systemd-服务-在控制面板服务器上" class="headerlink" title="配置 frps 的 systemd 服务 (在控制面板服务器上)"></a>配置 <code>frps</code> 的 <code>systemd</code> 服务 (在控制面板服务器上)</h4><ol><li><p>创建 <code>systemd</code> 服务单元文件：</p> <div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> vim /etc/systemd/system/frps.service</span><br></pre></td></tr></table></figure></div></li><li><p>在文件中添加以下内容：</p> <div class="code-container" data-rel="Ini"><figure class="iseeu highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">[Unit]</span></span><br><span class="line"><span class="attr">Description</span>=FRP Server Service</span><br><span class="line"><span class="attr">After</span>=network.target</span><br><span class="line"></span><br><span class="line"><span class="section">[Service]</span></span><br><span class="line"><span class="attr">Type</span>=simple</span><br><span class="line"><span class="attr">User</span>=root  <span class="comment"># 或者一个专门运行 frps 的非 root 用户</span></span><br><span class="line"><span class="attr">Restart</span>=<span class="literal">on</span>-failure</span><br><span class="line"><span class="attr">ExecStart</span>=/root/frp/frps -c /root/frp/frps.toml <span class="comment"># 确保路径正确</span></span><br><span class="line"></span><br><span class="line"><span class="section">[Install]</span></span><br><span class="line"><span class="attr">WantedBy</span>=multi-user.target</span><br></pre></td></tr></table></figure></div></li><li><p>保存并退出编辑器，然后重新加载 <code>systemd</code> 配置：</p> <div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> systemctl daemon-reload</span><br></pre></td></tr></table></figure></div></li><li><p>启动并设置开机自启 <code>frps</code> 服务：</p> <div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> systemctl start frps</span><br><span class="line"><span class="built_in">sudo</span> systemctl <span class="built_in">enable</span> frps</span><br></pre></td></tr></table></figure></div></li></ol><h4 id="配置-frpc-的-systemd-服务-在目标服务器上"><a href="#配置-frpc-的-systemd-服务-在目标服务器上" class="headerlink" title="配置 frpc 的 systemd 服务 (在目标服务器上)"></a>配置 <code>frpc</code> 的 <code>systemd</code> 服务 (在目标服务器上)</h4><ol><li><p>创建 <code>systemd</code> 服务单元文件：</p> <div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> vim /etc/systemd/system/frpc.service</span><br></pre></td></tr></table></figure></div></li><li><p>在文件中添加以下内容：</p> <div class="code-container" data-rel="Ini"><figure class="iseeu highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">[Unit]</span></span><br><span class="line"><span class="attr">Description</span>=FRP Client Service</span><br><span class="line"><span class="attr">After</span>=network.target</span><br><span class="line"></span><br><span class="line"><span class="section">[Service]</span></span><br><span class="line"><span class="attr">Type</span>=simple</span><br><span class="line"><span class="attr">User</span>=root  <span class="comment"># 或者一个专门运行 frpc 的非 root 用户</span></span><br><span class="line"><span class="attr">Restart</span>=<span class="literal">on</span>-failure</span><br><span class="line"><span class="attr">ExecStart</span>=/root/frp/frpc -c /root/frp/frpc.toml <span class="comment"># 确保路径正确</span></span><br><span class="line"></span><br><span class="line"><span class="section">[Install]</span></span><br><span class="line"><span class="attr">WantedBy</span>=multi-user.target</span><br></pre></td></tr></table></figure></div></li><li><p>保存并退出编辑器，然后重新加载 <code>systemd</code> 配置：</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> systemctl daemon-reload</span><br></pre></td></tr></table></figure></div></li><li><p>启动并设置开机自启 <code>frpc</code> 服务：</p> <div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> systemctl start frpc</span><br><span class="line"><span class="built_in">sudo</span> systemctl <span class="built_in">enable</span> frpc</span><br></pre></td></tr></table></figure></div></li></ol><h4 id="检查服务状态"><a href="#检查服务状态" class="headerlink" title="检查服务状态"></a>检查服务状态</h4><p>你可以使用以下命令检查 <code>frps</code> 和 <code>frpc</code> 服务的运行状态和日志：</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 在控制面板服务器上检查 frps</span></span><br><span class="line"><span class="built_in">sudo</span> systemctl status frps</span><br><span class="line">journalctl -u frps -f --no-pager <span class="comment"># 查看实时日志</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 在目标服务器上检查 frpc</span></span><br><span class="line"><span class="built_in">sudo</span> systemctl status frpc</span><br><span class="line">journalctl -u frpc -f --no-pager <span class="comment"># 查看实时日志</span></span><br></pre></td></tr></table></figure></div><p>如果一切顺利，<code>frpc</code> 应该已经成功连接到 <code>frps</code>。此时，理论上，发送到 <strong>控制面板服务器</strong> <code>18.138.249.128</code> 的 <code>8080</code> 端口（<code>vhostHttpPort</code>）且 <code>Host</code> 头为 <code>nat1.status.aptzone.cc</code> 的 HTTP 请求，应该会被转发到 <strong>目标服务器</strong> 的 <code>10.138.0.4:8080</code>。</p><p>你可以尝试直接访问 <code>http://18.138.249.128:8080</code> 并在请求中指定 Host 头，或者更常见地，通过下一步配置的反向代理来访问。</p><h3 id="6-配置反向代理-以-Nginx-Proxy-Manager-为例"><a href="#6-配置反向代理-以-Nginx-Proxy-Manager-为例" class="headerlink" title="6. 配置反向代理 (以 Nginx Proxy Manager 为例)"></a>6. 配置反向代理 (以 Nginx Proxy Manager 为例)</h3><p>通常我们不希望用户通过 IP 和端口号（如 <code>:8080</code>）访问服务，而是直接通过标准的 HTTP (80) 或 HTTPS (443) 端口访问域名。这就需要一个反向代理服务器（如 Nginx、Caddy 或 Nginx Proxy Manager）部署在 <strong>控制面板服务器</strong> 上。</p><p>如果你正在使用 Nginx Proxy Manager (NPM)，请在 NPM 中添加一个新的代理主机 (Proxy Host)：</p><ul><li><strong>Domain Names</strong>: <code>nat1.status.aptzone.cc</code> (你在 <code>frpc.toml</code> 中配置的 <code>customDomains</code>)</li><li><strong>Scheme</strong>: <code>http</code></li><li><strong>Forward Hostname &#x2F; IP</strong>: <code>18.138.249.128</code> (或者 <code>127.0.0.1</code>，因为 NPM 和 <code>frps</code> 都在这台控制面板服务器上)</li><li><strong>Forward Port</strong>: <code>8080</code> (你在 <code>frps.toml</code> 中配置的 <code>vhostHttpPort</code>)</li><li><strong>(可选但推荐)</strong> 在 <code>SSL</code> 选项卡中为此域名申请并启用 SSL 证书 (例如 Let’s Encrypt)，实现 HTTPS 访问。</li></ul><p><strong>工作流程解释：</strong></p><ol><li>用户浏览器访问 <code>https://nat1.status.aptzone.cc</code> (假设配置了 SSL)。</li><li>DNS 将域名解析到控制面板服务器的 IP <code>18.138.249.128</code>。</li><li>请求到达控制面板服务器的 443 端口，被 Nginx Proxy Manager 接收。</li><li>NPM 根据域名 <code>nat1.status.aptzone.cc</code> 找到对应的代理规则。</li><li>NPM 将请求 (解密后变为 HTTP) 转发给本机 (<code>127.0.0.1</code> 或 <code>18.138.249.128</code>) 的 <code>8080</code> 端口。</li><li><code>frps</code> 在 <code>8080</code> 端口 (<code>vhostHttpPort</code>) 监听到这个请求。</li><li><code>frps</code> 检查请求的 <code>Host</code> 头 (<code>nat1.status.aptzone.cc</code>)，找到与之关联的 <code>frpc</code> 客户端连接。</li><li><code>frps</code> 通过 <code>bindPort</code> (<code>7000</code>) 建立的隧道，将请求转发给目标服务器上对应的 <code>frpc</code> 实例。</li><li><code>frpc</code> 收到请求，将其转发给 <code>localIP:localPort</code>，即 <code>10.138.0.4:8080</code> 上的 <code>van-nav</code> 服务。</li><li><code>van-nav</code> 处理请求并将响应原路返回。</li></ol><p>完成以上所有步骤后，你就应该能通过访问 <code>http://nat1.status.aptzone.cc</code> 或 <code>https://nat1.status.aptzone.cc</code> (如果配置了 SSL) 来访问部署在内网目标服务器上的 <code>van-nav</code> 服务了。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;本文将详细介绍如何利用流行的内网穿透工具 &lt;a class=&quot;link&quot;   href=&quot;https://github.com/fatedier/frp&quot; &gt;FRP (Fast Reverse Proxy)&lt;i class=&quot;fa-solid fa-arrow-up-rig</summary>
      
    
    
    
    
    <category term="linux" scheme="http://example.com/tags/linux/"/>
    
    <category term="frp" scheme="http://example.com/tags/frp/"/>
    
    <category term="内网穿透" scheme="http://example.com/tags/%E5%86%85%E7%BD%91%E7%A9%BF%E9%80%8F/"/>
    
  </entry>
  
  <entry>
    <title>在 Heroku 上部署 Docker 容器 (以 One-API 为例)</title>
    <link href="http://example.com/2024/07/21/%E5%9C%A8Heroku%E4%B8%8A%E9%83%A8%E7%BD%B2Docker%E5%AE%B9%E5%99%A8/"/>
    <id>http://example.com/2024/07/21/%E5%9C%A8Heroku%E4%B8%8A%E9%83%A8%E7%BD%B2Docker%E5%AE%B9%E5%99%A8/</id>
    <published>2024-07-21T05:29:03.000Z</published>
    <updated>2025-04-29T11:17:00.780Z</updated>
    
    <content type="html"><![CDATA[<p>近期，我发现之前使用 Vercel 搭建的 RSSHub 服务版本过时，尝试更新部署新版本时却在 Vercel 上持续遇到故障。在寻找替代方案的过程中，我了解到可以通过 GitHub 学生包等途径获取 Heroku 的使用额度（例如当时发现的 $312 赠金），并且 Heroku 平台原生支持运行 Docker 容器，这为部署各种应用提供了极大的灵活性。</p><h2 id="Heroku-部署方式概览"><a href="#Heroku-部署方式概览" class="headerlink" title="Heroku 部署方式概览"></a>Heroku 部署方式概览</h2><p>Heroku 主要提供三种部署应用的方式：</p><ol><li><strong>Heroku Git</strong>: 通过 Heroku 提供的 Git 远程仓库部署代码，Heroku 使用 Buildpacks 自动构建和运行应用。</li><li><strong>GitHub</strong>: 直接连接 GitHub 仓库，实现自动或手动部署。</li><li><strong>Container Registry</strong>: 将构建好的 Docker 镜像推送到 Heroku 的容器注册表进行部署。</li></ol><p>本文将重点介绍第三种方法：<strong>使用 Heroku Container Registry 部署预先构建好的 Docker 镜像</strong>。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="https://tc.aptzone.cc/images/2024/09/01/202407211309768.md.png"                      alt="Heroku部署选项"                ></p><p>下面，我将以部署 <code>One-API</code>（一个 API 管理和分发项目）为例，详细说明在 Heroku 上部署 Docker 容器的具体步骤。</p><h2 id="环境与准备"><a href="#环境与准备" class="headerlink" title="环境与准备"></a>环境与准备</h2><ul><li><strong>操作系统</strong>: 示例使用 Ubuntu 22.04，但步骤适用于其他支持 Docker 和 Heroku CLI 的系统。</li><li><strong>必要工具</strong>:<ul><li>Docker (已安装并运行)</li><li>Heroku CLI (Heroku 命令行工具)</li></ul></li><li><strong>Heroku 账号</strong>: 你需要一个 Heroku 注册账号。</li><li><strong>待部署的 Docker 镜像</strong>:<ul><li>你需要一个已经构建好的、可以在本地成功运行的 Docker 镜像。本文以本地已有的 <code>calciumion/new-api:latest</code> 镜像为例。</li><li><strong>关键要求</strong>: 该 Docker 镜像启动的应用 <strong>必须</strong> 监听由 Heroku 运行时动态注入的 <code>$PORT</code> 环境变量所指定的端口号。大多数 Web 框架能自动识别或可以配置为使用此环境变量。同时，建议在 Dockerfile 中使用 <code>EXPOSE $PORT</code> 或具体的端口号（如果你的应用固定监听某个端口，但监听 <code>$PORT</code> 是最推荐的做法）。</li></ul></li></ul><p><strong>我的本地 Ubuntu 环境，可以看到 <code>calciumion/new-api</code> 镜像已存在：</strong></p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="https://tc.aptzone.cc/images/2024/09/01/202407211311630.md.png"                      alt="Ubuntu环境"                ></p><h2 id="部署步骤"><a href="#部署步骤" class="headerlink" title="部署步骤"></a>部署步骤</h2><h3 id="1-安装-Heroku-CLI"><a href="#1-安装-Heroku-CLI" class="headerlink" title="1. 安装 Heroku CLI"></a>1. 安装 Heroku CLI</h3><p>Heroku CLI 是与 Heroku 平台交互的主要工具。</p><ul><li><p>对于 Debian&#x2F;Ubuntu 系统，可以使用官方脚本安装：</p>  <div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">curl [https://cli-assets.heroku.com/install-ubuntu.sh](https://cli-assets.heroku.com/install-ubuntu.sh) | sh</span><br></pre></td></tr></table></figure></div></li><li><p>对于 macOS, Windows 或其他 Linux 发行版，请参考 <a class="link"   href="https://devcenter.heroku.com/articles/heroku-cli" >Heroku 官方文档<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a> 进行安装。</p></li></ul><p>安装完成后，可以通过 <code>heroku --version</code> 验证安装。</p><h3 id="2-登录-Heroku"><a href="#2-登录-Heroku" class="headerlink" title="2. 登录 Heroku"></a>2. 登录 Heroku</h3><p>在终端执行登录命令，它会打开浏览器引导你完成登录授权过程：</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">heroku login</span><br></pre></td></tr></table></figure></div><h3 id="3-创建-Heroku-应用"><a href="#3-创建-Heroku-应用" class="headerlink" title="3. 创建 Heroku 应用"></a>3. 创建 Heroku 应用</h3><p>在部署之前，你需要在 Heroku 上创建一个应用。应用名称 (<code>&lt;your-app-name&gt;</code>) 在 Heroku 全平台上必须是唯一的。</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 将 &lt;your-app-name&gt; 替换为你想要的应用名称，例如 newapi0819</span></span><br><span class="line">heroku create newapi0819</span><br></pre></td></tr></table></figure></div><p>成功后，Heroku 会返回应用的访问 URL 和 Git 仓库 URL。</p><h3 id="4-设置应用堆栈为-Container-关键步骤"><a href="#4-设置应用堆栈为-Container-关键步骤" class="headerlink" title="4. 设置应用堆栈为 Container (关键步骤)"></a>4. 设置应用堆栈为 Container (关键步骤)</h3><p>默认情况下，Heroku 应用使用 Buildpack 堆栈来构建源代码。因为我们要部署的是预构建的 Docker 镜像，所以需要将应用的堆栈（Stack）明确设置为 <code>container</code>。</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 将 newapi0819 替换为你的应用名称</span></span><br><span class="line">heroku stack:<span class="built_in">set</span> container -a newapi0819</span><br></pre></td></tr></table></figure></div><h3 id="5-登录-Heroku-Container-Registry"><a href="#5-登录-Heroku-Container-Registry" class="headerlink" title="5. 登录 Heroku Container Registry"></a>5. 登录 Heroku Container Registry</h3><p>你需要登录到 Heroku 自己的 Docker 镜像注册表，才能推送镜像：</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">heroku container:login</span><br></pre></td></tr></table></figure></div><h3 id="6-标记-Tag-本地-Docker-镜像"><a href="#6-标记-Tag-本地-Docker-镜像" class="headerlink" title="6. 标记 (Tag) 本地 Docker 镜像"></a>6. 标记 (Tag) 本地 Docker 镜像</h3><p>将你本地准备好的 Docker 镜像，按照 Heroku Container Registry 的格式进行标记。格式为：<code>registry.heroku.com/&lt;app-name&gt;/&lt;process-type&gt;</code>。</p><ul><li><code>&lt;app-name&gt;</code>: 你在第 3 步创建的应用名称 (例如 <code>newapi0819</code>)。</li><li><code>&lt;process-type&gt;</code>: 定义这个容器扮演的角色。对于处理 Web 请求的应用，通常使用 <code>web</code>。其他类型如 <code>worker</code> 用于后台任务。</li></ul><p>假设你的本地镜像是 <code>calciumion/new-api:latest</code>，应用名为 <code>newapi0819</code>，进程类型为 <code>web</code>：</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker tag calciumion/new-api:latest [registry.heroku.com/newapi0819/web](https://registry.heroku.com/newapi0819/web)</span><br></pre></td></tr></table></figure></div><h3 id="7-推送-Docker-镜像到-Heroku"><a href="#7-推送-Docker-镜像到-Heroku" class="headerlink" title="7. 推送 Docker 镜像到 Heroku"></a>7. 推送 Docker 镜像到 Heroku</h3><p>将标记好的镜像推送到 Heroku Container Registry：</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker push [registry.heroku.com/newapi0819/web](https://registry.heroku.com/newapi0819/web)</span><br></pre></td></tr></table></figure></div><p>推送过程可能需要一些时间，具体取决于镜像大小和网络速度。</p><h3 id="8-发布-Release-镜像到应用"><a href="#8-发布-Release-镜像到应用" class="headerlink" title="8. 发布 (Release) 镜像到应用"></a>8. 发布 (Release) 镜像到应用</h3><p>推送完成后，你需要执行 <code>release</code> 命令，告诉 Heroku 使用这个新推送的镜像来运行你的应用。</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 发布 web 进程类型的容器</span></span><br><span class="line">heroku container:release web -a newapi0819</span><br></pre></td></tr></table></figure></div><p>Heroku 会拉取镜像并在新的 Dyno（Heroku 的容器实例）中启动它。</p><h3 id="9-验证部署"><a href="#9-验证部署" class="headerlink" title="9. 验证部署"></a>9. 验证部署</h3><p>部署完成后，可以通过以下方式验证：</p><ul><li><p><strong>查看实时日志</strong>:</p>  <div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">heroku logs --<span class="built_in">tail</span> -a newapi0819</span><br></pre></td></tr></table></figure></div><p>  观察是否有错误信息，确认应用是否成功启动并监听在 <code>$PORT</code> 上。</p></li><li><p><strong>打开应用 URL</strong>:</p>  <div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">heroku open -a newapi0819</span><br></pre></td></tr></table></figure></div><p>  或者直接在浏览器中访问 <code>https://newapi0819.herokuapp.com</code> (将 <code>newapi0819</code> 替换为你的应用名)。</p></li></ul><h2 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h2><ol><li><strong>环境变量</strong>: 如果你的 Docker 应用依赖环境变量（例如数据库连接字符串、API 密钥等），你需要在 Heroku 应用的设置 (Settings) -&gt; “Config Vars” 部分进行配置。Heroku 会将这些 Config Vars 作为环境变量注入到运行的容器中。<strong>不要</strong> 将敏感信息硬编码在 Docker 镜像里。</li><li><strong>短暂文件系统与数据持久化</strong>: Heroku 的 Dyno 文件系统是 <strong>短暂的 (Ephemeral)</strong>。这意味着每次 Dyno 重启（至少每天发生一次，或在部署、配置更改、平台维护、崩溃后），所有写入容器本地文件系统的更改都会丢失。如果你的应用需要持久化存储数据（如数据库内容、用户上传的文件等），<strong>必须</strong> 使用 Heroku Add-ons（例如 Heroku Postgres, Heroku Redis）或外部的云数据库&#x2F;云存储服务（如 AWS RDS, AWS S3）。</li><li><strong>监听 <code>$PORT</code></strong>: 再次强调，部署到 Heroku 的 Web 应用 <strong>必须</strong> 监听 <code>$PORT</code> 环境变量指定的端口。Heroku 的路由层会将外部的 80&#x2F;443 端口请求路由到你的容器内部监听的这个动态端口上。</li><li><strong>Heroku 赠金&#x2F;额度</strong>: 如果你通过 GitHub 学生包或其他途径获得了 Heroku 额度，请注意其使用规则。例如，文中提到的 $13&#x2F;月使用上限和 24 个月有效期。Heroku 的定价和优惠政策可能会变化，请参考 Heroku 官方文档获取最新信息。免费套餐通常也有一定的限制（如 Dyno 休眠机制）。</li></ol><p>通过以上步骤，你应该能够成功地将你的 Docker 容器化应用部署到 Heroku 平台上。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;近期，我发现之前使用 Vercel 搭建的 RSSHub 服务版本过时，尝试更新部署新版本时却在 Vercel 上持续遇到故障。在寻找替代方案的过程中，我了解到可以通过 GitHub 学生包等途径获取 Heroku 的使用额度（例如当时发现的 $312 赠金），并且 Her</summary>
      
    
    
    
    
    <category term="Linux" scheme="http://example.com/tags/Linux/"/>
    
    <category term="Docker" scheme="http://example.com/tags/Docker/"/>
    
    <category term="Heroku" scheme="http://example.com/tags/Heroku/"/>
    
    <category term="部署" scheme="http://example.com/tags/%E9%83%A8%E7%BD%B2/"/>
    
  </entry>
  
  <entry>
    <title>CloudDiskServer</title>
    <link href="http://example.com/2024/06/30/CloudDiskServer/"/>
    <id>http://example.com/2024/06/30/CloudDiskServer/</id>
    <published>2024-06-30T14:00:22.000Z</published>
    <updated>2024-11-17T05:01:41.379Z</updated>
    
    <content type="html"><![CDATA[<h2 id="开发环境"><a href="#开发环境" class="headerlink" title="开发环境"></a>开发环境</h2><ul><li><strong>操作系统</strong>: Ubuntu 22.04</li><li><strong>编程语言</strong>: C++11</li><li><strong>构建工具</strong>: Make</li><li><strong>数据库</strong>: MySQL 8.0</li><li><strong>容器化平台</strong>: Docker</li></ul><h2 id="技术栈"><a href="#技术栈" class="headerlink" title="技术栈"></a>技术栈</h2><ul><li><strong>消息队列</strong>: RabbitMQ（使用 <code>SimpleAmqpClient</code> 库，Docker 部署）</li><li><strong>对象存储服务</strong>: 阿里云对象存储（Alibaba Cloud OSS）</li><li><strong>服务注册中心</strong>: Consul（使用 <code>ppconsul</code> 库）</li><li><strong>Web框架</strong>: wfrest（C++ 异步 Web 框架）</li><li><strong>RPC机制</strong>: sRPC（基于 Workflow 框架）</li></ul><hr><h2 id="安装与配置指南"><a href="#安装与配置指南" class="headerlink" title="安装与配置指南"></a>安装与配置指南</h2><p>为了顺利运行本项目，请确保安装和配置必要的环境和依赖，并根据需要调整配置文件中的相关参数，以适应具体环境和需求。</p><h3 id="1-安装-ppconsul"><a href="#1-安装-ppconsul" class="headerlink" title="1. 安装 ppconsul"></a>1. 安装 ppconsul</h3><p><strong>ppconsul</strong> 是 Consul 的 C++ 客户端库。</p><h4 id="1-1-安装必要的依赖"><a href="#1-1-安装必要的依赖" class="headerlink" title="1.1 安装必要的依赖"></a>1.1 安装必要的依赖</h4><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt-get update &amp;&amp; <span class="built_in">sudo</span> apt-get install cmake libcurl4-openssl-dev libboost-all-dev</span><br></pre></td></tr></table></figure></div><h4 id="1-2-下载并构建-ppconsul"><a href="#1-2-下载并构建-ppconsul" class="headerlink" title="1.2 下载并构建 ppconsul"></a>1.2 下载并构建 ppconsul</h4><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git <span class="built_in">clone</span> https://github.com/oliora/ppconsul.git &amp;&amp; <span class="built_in">cd</span> ppconsul &amp;&amp; <span class="built_in">mkdir</span> build &amp;&amp; <span class="built_in">cd</span> build &amp;&amp; cmake .. &amp;&amp; make &amp;&amp; <span class="built_in">sudo</span> make install &amp;&amp; <span class="built_in">sudo</span> ldconfig</span><br></pre></td></tr></table></figure></div><hr><h3 id="2-Consul-部署与使用"><a href="#2-Consul-部署与使用" class="headerlink" title="2. Consul 部署与使用"></a>2. Consul 部署与使用</h3><p>按照以下步骤部署和配置 Consul：</p><h4 id="2-1-启动-Consul-服务器容器"><a href="#2-1-启动-Consul-服务器容器" class="headerlink" title="2.1 启动 Consul 服务器容器"></a>2.1 启动 Consul 服务器容器</h4><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> docker pull consul:1.15.3 &amp;&amp; <span class="built_in">sudo</span> docker run --hostname consulsvr1 --name consul_node_1 -d -p 8500:8500 -p 8301:8301 -p 8302:8302 -p 8600:8600 consul:1.15.3 agent -server -bootstrap-expect 2 -ui -<span class="built_in">bind</span>=0.0.0.0 -client=0.0.0.0</span><br></pre></td></tr></table></figure></div><h4 id="2-2-获取分配的-IP-地址"><a href="#2-2-获取分配的-IP-地址" class="headerlink" title="2.2 获取分配的 IP 地址"></a>2.2 获取分配的 IP 地址</h4><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> docker inspect --format <span class="string">&#x27;&#123;&#123;.NetworkSettings.IPAddress&#125;&#125;&#x27;</span> consul_node_1</span><br></pre></td></tr></table></figure></div><h4 id="2-3-启动其它两个-Consul-节点并加入集群"><a href="#2-3-启动其它两个-Consul-节点并加入集群" class="headerlink" title="2.3 启动其它两个 Consul 节点并加入集群"></a>2.3 启动其它两个 Consul 节点并加入集群</h4><p>确保将上述命令中的 <code>&lt;IP_ADDRESS&gt;</code> 替换为实际的 IP 地址。</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> docker run --hostname consulsvr2 --name consul_node_2 -d -p 8501:8500 consul:1.15.3 agent -server -ui -<span class="built_in">bind</span>=0.0.0.0 -client=0.0.0.0 -<span class="built_in">join</span> &lt;IP_ADDRESS&gt; &amp;&amp; <span class="built_in">sudo</span> docker run --hostname consulsvr3 --name consul_node_3 -d -p 8502:8500 consul:1.15.3 agent -server -ui -<span class="built_in">bind</span>=0.0.0.0 -client=0.0.0.0 -<span class="built_in">join</span> &lt;IP_ADDRESS&gt;</span><br></pre></td></tr></table></figure></div><h4 id="2-4-查看集群状态"><a href="#2-4-查看集群状态" class="headerlink" title="2.4 查看集群状态"></a>2.4 查看集群状态</h4><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> docker <span class="built_in">exec</span> -t consul_node_1 consul members</span><br></pre></td></tr></table></figure></div><h4 id="2-5-测试-Consul-UI-和-API"><a href="#2-5-测试-Consul-UI-和-API" class="headerlink" title="2.5 测试 Consul UI 和 API"></a>2.5 测试 Consul UI 和 API</h4><p>启动并测试后，您可以通过以下 URL 访问 Consul 服务：</p><ul><li><strong>UI</strong>: <a href="http://<Your_IP_Address>:8500/ui/dc1/services">http:&#x2F;&#x2F;<Your_IP_Address>:8500&#x2F;ui&#x2F;dc1&#x2F;services</a></li><li><strong>API</strong>: <a href="http://<Your_IP_Address>:8500/v1/agent/services">http:&#x2F;&#x2F;<Your_IP_Address>:8500&#x2F;v1&#x2F;agent&#x2F;services</a></li></ul><hr><h3 id="3-安装阿里云OSS-SDK并设置OSS环境变量"><a href="#3-安装阿里云OSS-SDK并设置OSS环境变量" class="headerlink" title="3. 安装阿里云OSS SDK并设置OSS环境变量"></a>3. 安装阿里云OSS SDK并设置OSS环境变量</h3><h4 id="3-1-安装必要的依赖"><a href="#3-1-安装必要的依赖" class="headerlink" title="3.1 安装必要的依赖"></a>3.1 安装必要的依赖</h4><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt-get install libssl-dev libcurl4-gnutls-dev</span><br></pre></td></tr></table></figure></div><h4 id="3-2-设置环境变量"><a href="#3-2-设置环境变量" class="headerlink" title="3.2 设置环境变量"></a>3.2 设置环境变量</h4><p>编辑 <code>~/.bashrc</code> 文件：</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nano ~/.bashrc</span><br></pre></td></tr></table></figure></div><p>在文件末尾添加以下内容：</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">export</span> OSS_ACCESS_KEY_ID=<span class="string">&quot;your-access-key-id&quot;</span></span><br><span class="line"><span class="built_in">export</span> OSS_ACCESS_KEY_SECRET=<span class="string">&quot;your-access-key-secret&quot;</span></span><br></pre></td></tr></table></figure></div><p>保存并关闭文件后，使其生效：</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">source</span> ~/.bashrc</span><br></pre></td></tr></table></figure></div><h4 id="3-3-修改-oss-hpp-文件配置"><a href="#3-3-修改-oss-hpp-文件配置" class="headerlink" title="3.3 修改 oss.hpp 文件配置"></a>3.3 修改 <code>oss.hpp</code> 文件配置</h4><p>确保在 <code>oss.hpp</code> 文件中正确配置了阿里云OSS的相关信息，如 <code>Endpoint</code> 和 <code>BucketName</code>。</p><hr><h3 id="4-安装并配置-Nginx"><a href="#4-安装并配置-Nginx" class="headerlink" title="4. 安装并配置 Nginx"></a>4. 安装并配置 Nginx</h3><h4 id="4-1-安装-Nginx"><a href="#4-1-安装-Nginx" class="headerlink" title="4.1 安装 Nginx"></a>4.1 安装 Nginx</h4><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt install nginx &amp;&amp; <span class="built_in">sudo</span> systemctl status nginx</span><br></pre></td></tr></table></figure></div><h4 id="4-2-配置-Nginx"><a href="#4-2-配置-Nginx" class="headerlink" title="4.2 配置 Nginx"></a>4.2 配置 Nginx</h4><p>编辑 Nginx 配置文件：</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> vim /etc/nginx/nginx.conf</span><br></pre></td></tr></table></figure></div><p>将配置文件修改为以下内容：</p><div class="code-container" data-rel="Nginx"><figure class="iseeu highlight nginx"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">user</span> root;</span><br><span class="line"><span class="attribute">worker_processes</span>  <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"><span class="section">events</span> &#123;</span><br><span class="line">    <span class="attribute">worker_connections</span>  <span class="number">1024</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="section">http</span> &#123;</span><br><span class="line">    <span class="comment">#include       mime.types;</span></span><br><span class="line">    <span class="attribute">default_type</span>  application/octet-stream;</span><br><span class="line"></span><br><span class="line">    <span class="attribute">log_format</span>  main  <span class="string">&#x27;<span class="variable">$remote_addr</span> - <span class="variable">$remote_user</span> [<span class="variable">$time_local</span>] &quot;<span class="variable">$request</span>&quot; &#x27;</span></span><br><span class="line">                      <span class="string">&#x27;<span class="variable">$status</span> <span class="variable">$body_bytes_sent</span> &quot;<span class="variable">$http_referer</span>&quot; &#x27;</span></span><br><span class="line">                      <span class="string">&#x27;&quot;<span class="variable">$http_user_agent</span>&quot; &quot;<span class="variable">$http_x_forwarded_for</span>&quot;&#x27;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="attribute">sendfile</span>        <span class="literal">on</span>;</span><br><span class="line">    <span class="attribute">keepalive_timeout</span>  <span class="number">65</span>;</span><br><span class="line"></span><br><span class="line">    <span class="section">server</span> &#123;</span><br><span class="line">        <span class="attribute">listen</span>       <span class="number">8868</span>;</span><br><span class="line">        <span class="attribute">server_name</span>  localhost;</span><br><span class="line"></span><br><span class="line">        <span class="section">location</span> / &#123;</span><br><span class="line">            <span class="comment"># 将路径修改为服务器上传下载文件保存地址(pwd)</span></span><br><span class="line">            <span class="attribute">root</span> /root/myproject/tmp;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><h4 id="4-3-检查配置文件语法是否正确"><a href="#4-3-检查配置文件语法是否正确" class="headerlink" title="4.3 检查配置文件语法是否正确"></a>4.3 检查配置文件语法是否正确</h4><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> nginx -t</span><br></pre></td></tr></table></figure></div><p>如果输出显示配置文件语法正确：</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">nginx: the configuration file /etc/nginx/nginx.conf syntax is ok</span><br><span class="line">nginx: configuration file /etc/nginx/nginx.conf <span class="built_in">test</span> is successful</span><br></pre></td></tr></table></figure></div><h4 id="4-4-重新加载或重启-Nginx-服务"><a href="#4-4-重新加载或重启-Nginx-服务" class="headerlink" title="4.4 重新加载或重启 Nginx 服务"></a>4.4 重新加载或重启 Nginx 服务</h4><p>重新加载配置：</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> systemctl reload nginx</span><br></pre></td></tr></table></figure></div><p>如果需要，您可以选择重启 Nginx 服务：</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> systemctl restart nginx</span><br></pre></td></tr></table></figure></div><hr><h3 id="5-MySQL-安装与配置"><a href="#5-MySQL-安装与配置" class="headerlink" title="5. MySQL 安装与配置"></a>5. MySQL 安装与配置</h3><h4 id="5-1-安装-MySQL"><a href="#5-1-安装-MySQL" class="headerlink" title="5.1 安装 MySQL"></a>5.1 安装 MySQL</h4><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt install -y mysql-server-8.0 libmysqlclient-dev</span><br></pre></td></tr></table></figure></div><p>安装完成后，MySQL 服务会自动启动。</p><h4 id="5-2-检查-MySQL-状态"><a href="#5-2-检查-MySQL-状态" class="headerlink" title="5.2 检查 MySQL 状态"></a>5.2 检查 MySQL 状态</h4><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> systemctl status mysql</span><br></pre></td></tr></table></figure></div><h4 id="5-3-设置-MySQL-root-用户密码"><a href="#5-3-设置-MySQL-root-用户密码" class="headerlink" title="5.3 设置 MySQL root 用户密码"></a>5.3 设置 MySQL root 用户密码</h4><p>由于安装时没有设置密码，使用以下命令登录并设置密码：</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> mysql</span><br></pre></td></tr></table></figure></div><p>在 MySQL 控制台中执行以下 SQL 语句，将 root 用户的密码设置为 <code>1234</code>：</p><div class="code-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">ALTER USER &#x27;root&#x27;@&#x27;localhost&#x27; IDENTIFIED WITH mysql_native_password BY &#x27;1234&#x27;;</span><br><span class="line">UPDATE mysql.user SET host=&#x27;%&#x27; WHERE user=&#x27;root&#x27;;</span><br><span class="line">FLUSH PRIVILEGES;</span><br></pre></td></tr></table></figure></div><h4 id="5-4-重新登录-MySQL"><a href="#5-4-重新登录-MySQL" class="headerlink" title="5.4 重新登录 MySQL"></a>5.4 重新登录 MySQL</h4><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql -uroot -p</span><br></pre></td></tr></table></figure></div><p>输入密码 <code>1234</code> 进行登录。</p><h4 id="5-5-打开-MySQL-配置文件"><a href="#5-5-打开-MySQL-配置文件" class="headerlink" title="5.5 打开 MySQL 配置文件"></a>5.5 打开 MySQL 配置文件</h4><p>MySQL 的 <code>bind-address</code> 通常位于配置文件 <code>mysqld.cnf</code> 中。</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> nano /etc/mysql/mysql.conf.d/mysqld.cnf</span><br></pre></td></tr></table></figure></div><p><strong>注意</strong>：配置文件的路径可能因系统或 MySQL 版本而异。如果上面的路径不存在，可以尝试以下路径之一：</p><ul><li><code>/etc/mysql/my.cnf</code></li><li><code>/etc/my.cnf</code></li></ul><h4 id="5-6-修改-bind-address-的值"><a href="#5-6-修改-bind-address-的值" class="headerlink" title="5.6 修改 bind-address 的值"></a>5.6 修改 <code>bind-address</code> 的值</h4><p>在打开的配置文件中，查找以下行：</p><div class="code-container" data-rel="Ini"><figure class="iseeu highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">bind-address</span> = <span class="number">127.0</span>.<span class="number">0.1</span></span><br></pre></td></tr></table></figure></div><p>根据您的需求修改 <code>bind-address</code> 的值：</p><ul><li><p><strong>允许仅本地访问</strong>（默认设置）：</p><div class="code-container" data-rel="Ini"><figure class="iseeu highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">bind-address</span> = <span class="number">127.0</span>.<span class="number">0.1</span></span><br></pre></td></tr></table></figure></div></li><li><p><strong>允许所有网络接口访问</strong>：</p><div class="code-container" data-rel="Ini"><figure class="iseeu highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">bind-address</span> = <span class="number">0.0</span>.<span class="number">0.0</span></span><br></pre></td></tr></table></figure></div></li></ul><h4 id="5-7-重启-MySQL-服务以应用更改"><a href="#5-7-重启-MySQL-服务以应用更改" class="headerlink" title="5.7 重启 MySQL 服务以应用更改"></a>5.7 重启 MySQL 服务以应用更改</h4><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> systemctl restart mysql</span><br></pre></td></tr></table></figure></div><h4 id="5-8-验证-MySQL-是否绑定到新的地址"><a href="#5-8-验证-MySQL-是否绑定到新的地址" class="headerlink" title="5.8 验证 MySQL 是否绑定到新的地址"></a>5.8 验证 MySQL 是否绑定到新的地址</h4><p>您可以使用 <code>ss</code> 或 <code>netstat</code> 命令来验证 MySQL 是否正确绑定到指定的地址和端口（默认为 3306）。</p><p>使用 <code>ss</code>：</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> ss -tuln | grep 3306</span><br></pre></td></tr></table></figure></div><h4 id="5-9-导入数据库"><a href="#5-9-导入数据库" class="headerlink" title="5.9 导入数据库"></a>5.9 导入数据库</h4><p>运行 <code>tbl_sql.sql</code> 文件以导入数据库：</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql -uroot -p your_database &lt; tbl_sql.sql</span><br></pre></td></tr></table></figure></div><hr><h3 id="6-安装并配置-RabbitMQ"><a href="#6-安装并配置-RabbitMQ" class="headerlink" title="6. 安装并配置 RabbitMQ"></a>6. 安装并配置 RabbitMQ</h3><h4 id="6-1-启动-RabbitMQ-容器"><a href="#6-1-启动-RabbitMQ-容器" class="headerlink" title="6.1 启动 RabbitMQ 容器"></a>6.1 启动 RabbitMQ 容器</h4><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> docker run -d --hostname rabbitsvr --name rabbit -p 5672:5672 -p 15672:15672 -p 25672:25672 -v /data/rabbitmq:/var/lib/rabbitmq rabbitmq:management</span><br></pre></td></tr></table></figure></div><h4 id="6-2-访问-RabbitMQ-管理界面"><a href="#6-2-访问-RabbitMQ-管理界面" class="headerlink" title="6.2 访问 RabbitMQ 管理界面"></a>6.2 访问 RabbitMQ 管理界面</h4><p>打开浏览器访问 <a href="http://<Your_IP_Address>:15672">http:&#x2F;&#x2F;<Your_IP_Address>:15672</a> 进行管理和配置。</p><h4 id="6-3-根据-amqp-hpp-完成配置"><a href="#6-3-根据-amqp-hpp-完成配置" class="headerlink" title="6.3 根据 amqp.hpp 完成配置"></a>6.3 根据 <code>amqp.hpp</code> 完成配置</h4><p>确保根据项目需求配置 RabbitMQ 的队列和交换机，具体配置可参考 <code>amqp.hpp</code> 文件中的实现。</p><hr><h3 id="7-安装-AMQP-的-C-客户端-SDK"><a href="#7-安装-AMQP-的-C-客户端-SDK" class="headerlink" title="7. 安装 AMQP 的 C++ 客户端 SDK"></a>7. 安装 AMQP 的 C++ 客户端 SDK</h3><h4 id="7-1-下载源码包"><a href="#7-1-下载源码包" class="headerlink" title="7.1 下载源码包"></a>7.1 下载源码包</h4><p>请确保已下载以下源码包：</p><ul><li><code>rabbitmq-c-0.11.0.tar.gz</code></li><li><code>SimpleAmqpClient-2.5.1.tar.gz</code></li></ul><h4 id="7-2-安装依赖"><a href="#7-2-安装依赖" class="headerlink" title="7.2 安装依赖"></a>7.2 安装依赖</h4><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt install libboost-dev libboost-system-dev libboost-chrono-dev</span><br></pre></td></tr></table></figure></div><h4 id="7-3-构建并安装"><a href="#7-3-构建并安装" class="headerlink" title="7.3 构建并安装"></a>7.3 构建并安装</h4><h5 id="对于-rabbitmq-c"><a href="#对于-rabbitmq-c" class="headerlink" title="对于 rabbitmq-c"></a>对于 <code>rabbitmq-c</code></h5><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tar -xzf rabbitmq-c-0.11.0.tar.gz &amp;&amp; <span class="built_in">cd</span> rabbitmq-c-0.11.0 &amp;&amp; <span class="built_in">mkdir</span> build &amp;&amp; <span class="built_in">cd</span> build &amp;&amp; cmake .. &amp;&amp; make &amp;&amp; <span class="built_in">sudo</span> make install &amp;&amp; <span class="built_in">sudo</span> ldconfig</span><br></pre></td></tr></table></figure></div><h5 id="对于-SimpleAmqpClient"><a href="#对于-SimpleAmqpClient" class="headerlink" title="对于 SimpleAmqpClient"></a>对于 <code>SimpleAmqpClient</code></h5><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tar -xzf SimpleAmqpClient-2.5.1.tar.gz &amp;&amp; <span class="built_in">cd</span> SimpleAmqpClient-2.5.1 &amp;&amp; <span class="built_in">mkdir</span> build &amp;&amp; <span class="built_in">cd</span> build &amp;&amp; cmake .. &amp;&amp; make &amp;&amp; <span class="built_in">sudo</span> make install &amp;&amp; <span class="built_in">sudo</span> ldconfig</span><br></pre></td></tr></table></figure></div><hr><h3 id="8-安装-Workflow"><a href="#8-安装-Workflow" class="headerlink" title="8. 安装 Workflow"></a>8. 安装 Workflow</h3><h4 id="8-1-下载源码包"><a href="#8-1-下载源码包" class="headerlink" title="8.1 下载源码包"></a>8.1 下载源码包</h4><p>请确保已下载 <code>workflow-0.11.3.tar.gz</code>。</p><h4 id="8-2-安装依赖并构建"><a href="#8-2-安装依赖并构建" class="headerlink" title="8.2 安装依赖并构建"></a>8.2 安装依赖并构建</h4><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt install cmake &amp;&amp; tar -xzf workflow-0.11.3.tar.gz &amp;&amp; <span class="built_in">cd</span> workflow-0.11.3 &amp;&amp; <span class="built_in">mkdir</span> build &amp;&amp; <span class="built_in">cd</span> build &amp;&amp; cmake .. &amp;&amp; make &amp;&amp; <span class="built_in">sudo</span> make install &amp;&amp; <span class="built_in">sudo</span> ldconfig</span><br></pre></td></tr></table></figure></div><hr><h3 id="9-安装-wfrest"><a href="#9-安装-wfrest" class="headerlink" title="9. 安装 wfrest"></a>9. 安装 wfrest</h3><h4 id="9-1-下载源码包"><a href="#9-1-下载源码包" class="headerlink" title="9.1 下载源码包"></a>9.1 下载源码包</h4><p>请确保已下载 <code>wfrest-0.9.6.tar.gz</code>。</p><h4 id="9-2-构建并安装"><a href="#9-2-构建并安装" class="headerlink" title="9.2 构建并安装"></a>9.2 构建并安装</h4><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tar -xzf wfrest-0.9.6.tar.gz &amp;&amp; <span class="built_in">cd</span> wfrest-0.9.6 &amp;&amp; <span class="built_in">mkdir</span> build &amp;&amp; <span class="built_in">cd</span> build &amp;&amp; cmake .. &amp;&amp; make &amp;&amp; <span class="built_in">sudo</span> make install &amp;&amp; <span class="built_in">sudo</span> ldconfig</span><br></pre></td></tr></table></figure></div><hr><h3 id="10-安装-Protobuf"><a href="#10-安装-Protobuf" class="headerlink" title="10. 安装 Protobuf"></a>10. 安装 Protobuf</h3><h4 id="10-1-下载源码包"><a href="#10-1-下载源码包" class="headerlink" title="10.1 下载源码包"></a>10.1 下载源码包</h4><p>请确保已下载 <code>protobuf-3.20.1.tar.gz</code>。</p><h4 id="10-2-构建并安装"><a href="#10-2-构建并安装" class="headerlink" title="10.2 构建并安装"></a>10.2 构建并安装</h4><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tar -xzf protobuf-3.20.1.tar.gz &amp;&amp; <span class="built_in">cd</span> protobuf-3.20.1 &amp;&amp; <span class="built_in">sudo</span> apt-get install autoconf automake libtool curl make g++ unzip &amp;&amp; ./autogen.sh &amp;&amp; ./configure &amp;&amp; make &amp;&amp; <span class="built_in">sudo</span> make install &amp;&amp; <span class="built_in">sudo</span> ldconfig</span><br></pre></td></tr></table></figure></div><h4 id="10-3-验证Protobuf"><a href="#10-3-验证Protobuf" class="headerlink" title="10.3 验证Protobuf"></a>10.3 验证Protobuf</h4><p>执行protoc，如果出现操作指南说明安装成功了。</p><hr><h3 id="11-安装-srpc"><a href="#11-安装-srpc" class="headerlink" title="11. 安装 srpc"></a>11. 安装 srpc</h3><h4 id="11-1-下载源码包"><a href="#11-1-下载源码包" class="headerlink" title="11.1 下载源码包"></a>11.1 下载源码包</h4><p>请确保已下载 <code>srpc-0.10.2.tar.gz</code>。</p><h4 id="11-2-安装依赖并构建"><a href="#11-2-安装依赖并构建" class="headerlink" title="11.2 安装依赖并构建"></a>11.2 安装依赖并构建</h4><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt install liblz4-dev libsnappy-dev &amp;&amp; tar -xzf srpc-0.10.2.tar.gz &amp;&amp; <span class="built_in">cd</span> srpc-0.10.2 &amp;&amp; <span class="built_in">mkdir</span> build &amp;&amp; <span class="built_in">cd</span> build &amp;&amp; cmake .. &amp;&amp; make &amp;&amp; <span class="built_in">sudo</span> make install &amp;&amp; <span class="built_in">sudo</span> ldconfig</span><br></pre></td></tr></table></figure></div><hr><h2 id="常见问题"><a href="#常见问题" class="headerlink" title="常见问题"></a>常见问题</h2><p><strong>Q1: 为什么需要使用 <code>nlohmann::json</code>，而不是 wfrest 自带的 <code>Json</code> 类？</strong></p><p><strong>A1:</strong> 项目中选择使用 <code>nlohmann::json</code>，是因为 wfrest 自带的 <code>Json</code> 类在处理嵌套 JSON 结构时存在一定的限制，导致JSON构造失败。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;开发环境&quot;&gt;&lt;a href=&quot;#开发环境&quot; class=&quot;headerlink&quot; title=&quot;开发环境&quot;&gt;&lt;/a&gt;开发环境&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;操作系统&lt;/strong&gt;: Ubuntu 22.04&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;编程语</summary>
      
    
    
    
    
    <category term="C++" scheme="http://example.com/tags/C/"/>
    
  </entry>
  
  <entry>
    <title>C语言归并排序</title>
    <link href="http://example.com/2024/05/22/C%E8%AF%AD%E8%A8%80%E5%BD%92%E5%B9%B6%E6%8E%92%E5%BA%8F/"/>
    <id>http://example.com/2024/05/22/C%E8%AF%AD%E8%A8%80%E5%BD%92%E5%B9%B6%E6%8E%92%E5%BA%8F/</id>
    <published>2024-05-21T16:30:07.000Z</published>
    <updated>2024-09-01T16:02:46.134Z</updated>
    
    <content type="html"><![CDATA[<p>归并排序是一种分治法的排序算法，它将数组分成两个子数组，分别排序后再合并。其时间复杂度为O(n log n)，适合处理大规模数据的排序。</p><h3 id="图解递归过程以及复杂度"><a href="#图解递归过程以及复杂度" class="headerlink" title="图解递归过程以及复杂度"></a>图解递归过程以及复杂度</h3><p><img                       lazyload                     src="/images/loading.svg"                     data-src="https://tc.aptzone.cc/images/2024/09/01/202405220029122.md.png"                      alt="归并排序过程分析"                ></p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="https://tc.aptzone.cc/images/2024/09/01/202405220032749.md.png"                      alt="归并排序复杂度分析"                ></p><h3 id="代码实现"><a href="#代码实现" class="headerlink" title="代码实现"></a>代码实现</h3><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> SIZE(a) (sizeof(a)/sizeof(a[0]))</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> N 7</span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">print_array</span><span class="params">(<span class="type">int</span> arr[], <span class="type">int</span> n)</span> &#123;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; n; i++) &#123;</span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">&quot;%d &quot;</span>, arr[i]);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">&quot;\n&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">merge_sort</span><span class="params">(<span class="type">int</span> arrp[], <span class="type">int</span> n)</span>;</span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">void</span>)</span> &#123;</span><br><span class="line"></span><br><span class="line"> <span class="type">int</span> arr[N] = &#123; <span class="number">38</span>,<span class="number">27</span>,<span class="number">43</span>,<span class="number">3</span>,<span class="number">9</span>,<span class="number">82</span>,<span class="number">10</span> &#125;;</span><br><span class="line"> print_array(arr, N);</span><br><span class="line"> merge_sort(arr,N);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">int</span> tmp[N];</span><br><span class="line"><span class="type">void</span> <span class="title function_">merge</span><span class="params">(<span class="type">int</span> arr[], <span class="type">int</span> left, <span class="type">int</span> mid, <span class="type">int</span> right)</span> &#123;</span><br><span class="line"> <span class="type">int</span> i = left, j = mid + <span class="number">1</span>, k = left;</span><br><span class="line"> <span class="keyword">while</span> (i &lt;= mid &amp;&amp; j &lt;= right)</span><br><span class="line"> &#123;</span><br><span class="line">  <span class="keyword">if</span> (arr[i] &lt;= arr[j])</span><br><span class="line">  &#123;</span><br><span class="line">   tmp[k++] = arr[i++];</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">else</span></span><br><span class="line">  &#123;</span><br><span class="line">   tmp[k++] = arr[j++];</span><br><span class="line">  &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">while</span> (i &lt;= mid)</span><br><span class="line"> &#123;</span><br><span class="line">  tmp[k++] = arr[i++];</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">while</span> (j &lt;= right)</span><br><span class="line"> &#123;</span><br><span class="line">  tmp[k++] = arr[j++];</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> k = left; k &lt;= right; k++) &#123;</span><br><span class="line">  arr[k] = tmp[k];</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">void</span> <span class="title function_">m_sort</span><span class="params">(<span class="type">int</span> arr[], <span class="type">int</span> left, <span class="type">int</span> right)</span> &#123;</span><br><span class="line"> <span class="keyword">if</span> (left &gt;= right)</span><br><span class="line"> &#123;</span><br><span class="line">  <span class="keyword">return</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="type">int</span> mid = left + (right - left &gt;&gt; <span class="number">1</span>);</span><br><span class="line"> m_sort(arr, left, mid);</span><br><span class="line"> m_sort(arr, mid + <span class="number">1</span>, right);</span><br><span class="line"> merge(arr, left,mid, right);</span><br><span class="line"></span><br><span class="line"> print_array(arr, N);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">merge_sort</span><span class="params">(<span class="type">int</span> arr[], <span class="type">int</span> n)</span> &#123;</span><br><span class="line"> m_sort(arr, <span class="number">0</span>, n - <span class="number">1</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><h3 id="代码运行结果"><a href="#代码运行结果" class="headerlink" title="代码运行结果"></a>代码运行结果</h3><p>初始数组为：</p><div class="code-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">38 27 43 3 9 82 10</span><br></pre></td></tr></table></figure></div><p>排序过程中的数组变化：</p><div class="code-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">38 27 43 3 9 82 10</span><br><span class="line">27 38 43 3 9 82 10</span><br><span class="line">27 38 3 43 9 82 10</span><br><span class="line">3 27 38 43 9 82 10</span><br><span class="line">3 27 38 43 9 82 10</span><br><span class="line">3 27 38 43 9 10 82</span><br><span class="line">3 9 10 27 38 43 82</span><br></pre></td></tr></table></figure></div>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;归并排序是一种分治法的排序算法，它将数组分成两个子数组，分别排序后再合并。其时间复杂度为O(n log n)，适合处理大规模数据的排序。&lt;/p&gt;
&lt;h3 id=&quot;图解递归过程以及复杂度&quot;&gt;&lt;a href=&quot;#图解递归过程以及复杂度&quot; class=&quot;headerlink&quot; t</summary>
      
    
    
    
    
    <category term="C语言" scheme="http://example.com/tags/C%E8%AF%AD%E8%A8%80/"/>
    
  </entry>
  
  <entry>
    <title>去重排序</title>
    <link href="http://example.com/2024/05/06/%E5%8E%BB%E9%87%8D%E6%8E%92%E5%BA%8F/"/>
    <id>http://example.com/2024/05/06/%E5%8E%BB%E9%87%8D%E6%8E%92%E5%BA%8F/</id>
    <published>2024-05-06T15:59:54.000Z</published>
    <updated>2024-11-17T04:58:48.769Z</updated>
    
    <content type="html"><![CDATA[<h3 id="字符串去重并排序"><a href="#字符串去重并排序" class="headerlink" title="字符串去重并排序"></a>字符串去重并排序</h3><p>给定一个只包含小写字母的字符串，目标是编写一个函数，将字符串中的字符去重并按字典序排序。</p><h4 id="初始方法"><a href="#初始方法" class="headerlink" title="初始方法"></a>初始方法</h4><p>使用布尔数组作为哈希表来记录字符是否出现过，然后使用<code>qsort</code>函数对结果进行排序。</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdbool.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">remove_duplicate_and_sort</span><span class="params">(<span class="type">char</span>* str)</span> &#123;</span><br><span class="line">    <span class="type">bool</span> hash[<span class="number">26</span>] = &#123; <span class="number">0</span> &#125;;</span><br><span class="line">    <span class="type">int</span> index = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; str[i]; i++) &#123;</span><br><span class="line">        <span class="keyword">if</span> (!hash[str[i] - <span class="string">&#x27;a&#x27;</span>]) &#123;</span><br><span class="line">            hash[str[i] - <span class="string">&#x27;a&#x27;</span>] = <span class="literal">true</span>;</span><br><span class="line">            str[index++] = str[i];</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    str[index] = <span class="string">&#x27;\0&#x27;</span>;</span><br><span class="line">    qsort(str, index, <span class="keyword">sizeof</span>(<span class="type">char</span>), (<span class="type">int</span>(*)(<span class="type">const</span> <span class="type">void</span>*, <span class="type">const</span> <span class="type">void</span>*)) <span class="built_in">strcmp</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">void</span>)</span> &#123;</span><br><span class="line">    <span class="type">char</span> str[<span class="number">100</span>];</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;请输入一个字符串：\n&quot;</span>);</span><br><span class="line">    <span class="built_in">scanf</span>(<span class="string">&quot;%s&quot;</span>, str);</span><br><span class="line">    remove_duplicate_and_sort(str);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;处理后的字符串是：%s\n&quot;</span>, str);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><h4 id="优化方法：使用位图"><a href="#优化方法：使用位图" class="headerlink" title="优化方法：使用位图"></a>优化方法：使用位图</h4><p>通过引入位图（BitMap）的概念，可以进一步提高算法的效率。位图使用一个位来标记某个元素对应的状态，相比布尔数组更节省空间。</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;BitMap.h&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">remove_duplicate_and_sort</span><span class="params">(<span class="type">char</span>* str)</span> &#123;</span><br><span class="line">    BitMap* bm = bitmap_create(<span class="number">26</span>);</span><br><span class="line">    <span class="type">char</span>* p = str;</span><br><span class="line">    <span class="keyword">while</span> (*p) &#123;</span><br><span class="line">        bitmap_set(bm, *p - <span class="string">&#x27;a&#x27;</span>);</span><br><span class="line">        p++;</span><br><span class="line">    &#125;</span><br><span class="line">    p = str;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; bm-&gt;bits; i++) &#123;</span><br><span class="line">        <span class="keyword">if</span> (bitmap_isset(bm, i)) &#123;</span><br><span class="line">            *p++ = <span class="string">&#x27;a&#x27;</span> + i;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    *p = <span class="string">&#x27;\0&#x27;</span>;</span><br><span class="line">    bitmap_destroy(bm);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">void</span>)</span> &#123;</span><br><span class="line">    <span class="type">char</span> str[] = <span class="string">&quot;bcabc&quot;</span>;</span><br><span class="line">    remove_duplicate_and_sort(str);</span><br><span class="line">    <span class="built_in">puts</span>(str); <span class="comment">// 输出处理后的字符串</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><p>这种方法相比使用布尔数组，可以显著减少内存的使用量，尤其是在处理大量数据时更为显著。通过位操作实现的位图，其性能也比使用数组进行标记要高效。</p><hr><h3 id="补充"><a href="#补充" class="headerlink" title="补充"></a>补充</h3><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//BitMap.h</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdbool.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdint.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="type">uint32_t</span> Word;   <span class="comment">// uint32_t: 1.大小确定 (32bits);  2.无符号整数</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> &#123;</span></span><br><span class="line">    <span class="type">uint32_t</span>* <span class="built_in">array</span>;</span><br><span class="line">    <span class="type">size_t</span>    bits;</span><br><span class="line">&#125; BitMap;</span><br><span class="line"></span><br><span class="line">BitMap* <span class="title function_">bitmap_create</span><span class="params">(<span class="type">size_t</span> bits)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">bitmap_destroy</span><span class="params">(BitMap* bm)</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">bitmap_set</span><span class="params">(BitMap* bm, <span class="type">size_t</span> n)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">bitmap_unset</span><span class="params">(BitMap* bm, <span class="type">size_t</span> n)</span>;</span><br><span class="line"><span class="type">bool</span> <span class="title function_">bitmap_isset</span><span class="params">(BitMap* bm, <span class="type">size_t</span> n)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">bitmap_clear</span><span class="params">(BitMap* bm)</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">grow_capacity</span><span class="params">(BitMap* bm, <span class="type">size_t</span> bits)</span></span><br></pre></td></tr></table></figure></div><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// BitMap.c</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;BitMap.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BITS_PER_WORD 32</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BITMAP_SHIFT   5 <span class="comment">// 2^5 = 32</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BITMAP_MASK 0x1F</span></span><br><span class="line"><span class="comment">// 存储bits位，需要多少个word</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> BITMAP_SIZE(bits) ((bits + BITS_PER_WORD - 1) &gt;&gt; BITMAP_SHIFT)</span></span><br><span class="line"></span><br><span class="line">BitMap* <span class="title function_">bitmap_create</span><span class="params">(<span class="type">size_t</span> bits)</span> &#123;</span><br><span class="line"> BitMap* bm = <span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(BitMap));</span><br><span class="line"> bm-&gt;<span class="built_in">array</span> = <span class="built_in">calloc</span>(BITMAP_SIZE(bits), BITS_PER_WORD &gt;&gt; <span class="number">3</span>);</span><br><span class="line"> bm-&gt;bits = bits;</span><br><span class="line"> <span class="keyword">return</span> bm;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">bitmap_set</span><span class="params">(BitMap* bm, <span class="type">size_t</span> n)</span> &#123;</span><br><span class="line"> <span class="keyword">if</span> (n &gt;= bm-&gt;bits) &#123;</span><br><span class="line">  <span class="keyword">if</span> (BITMAP_SIZE(n + <span class="number">1</span>) &gt; BITMAP_SIZE(bm-&gt;bits)) &#123;</span><br><span class="line">   <span class="comment">// 扩容</span></span><br><span class="line">   grow_capacity(bm, n + <span class="number">1</span>);</span><br><span class="line">  &#125;</span><br><span class="line">  bm-&gt;bits = n + <span class="number">1</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="comment">// 设置第n位 </span></span><br><span class="line"> <span class="comment">// 如何表示第n位 (word, offset)</span></span><br><span class="line"> <span class="type">size_t</span> word = n &gt;&gt; BITMAP_SHIFT;</span><br><span class="line"> <span class="type">size_t</span> offset = n &amp; BITMAP_MASK; <span class="comment">// n%32</span></span><br><span class="line"> bm-&gt;<span class="built_in">array</span>[word] |= (<span class="number">0x1</span> &lt;&lt; offset);</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">void</span> <span class="title function_">bitmap_unset</span><span class="params">(BitMap* bm, <span class="type">size_t</span> n)</span> &#123;</span><br><span class="line"> <span class="keyword">if</span> (n &gt; bm-&gt;bits)</span><br><span class="line"> &#123;</span><br><span class="line">  <span class="keyword">return</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="type">size_t</span> word = n &gt;&gt; BITMAP_SHIFT;</span><br><span class="line"> <span class="type">size_t</span> offset = n &amp; BITMAP_MASK; <span class="comment">// n%32</span></span><br><span class="line"> bm-&gt;<span class="built_in">array</span>[word] &amp;= ~(<span class="number">0x1</span> &lt;&lt; offset);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">bool</span> <span class="title function_">bitmap_isset</span><span class="params">(BitMap* bm, <span class="type">size_t</span> n)</span> &#123;</span><br><span class="line"> <span class="keyword">if</span> (n &gt; bm-&gt;bits)</span><br><span class="line"> &#123;</span><br><span class="line">  <span class="keyword">return</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="type">size_t</span> word = n &gt;&gt; BITMAP_SHIFT;</span><br><span class="line"> <span class="type">size_t</span> offset = n &amp; BITMAP_MASK; <span class="comment">// n%32</span></span><br><span class="line"> bm-&gt;<span class="built_in">array</span>[word] &amp;= (<span class="number">0x1</span> &lt;&lt; offset);</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">void</span> <span class="title function_">bitmap_clear</span><span class="params">(BitMap* bm)</span> &#123;</span><br><span class="line"> <span class="built_in">memset</span>(bm-&gt;<span class="built_in">array</span>, <span class="number">0</span>, BITMAP_SIZE(bm-&gt;bits) * <span class="keyword">sizeof</span>(Word));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">bitmap_destroy</span><span class="params">(BitMap* bm)</span> &#123;</span><br><span class="line"> <span class="built_in">free</span>(bm-&gt;<span class="built_in">array</span>);</span><br><span class="line"> <span class="built_in">free</span>(bm);</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">void</span> <span class="title function_">grow_capacity</span><span class="params">(BitMap* bm, <span class="type">size_t</span> bits)</span></span><br><span class="line">&#123;</span><br><span class="line"> Word* new_array = <span class="built_in">realloc</span>(bm-&gt;<span class="built_in">array</span>, BITMAP_SIZE(bits) * <span class="keyword">sizeof</span>(Word));<span class="comment">//字*字节/字 == 字节数</span></span><br><span class="line"> <span class="keyword">if</span> (!new_array)</span><br><span class="line"> &#123;</span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">&quot;error realloc in grow_capacity\n&quot;</span>);</span><br><span class="line">  <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line"> &#125;</span><br><span class="line"> bm-&gt;<span class="built_in">array</span> = new_array;</span><br><span class="line"> <span class="type">int</span> bytes = (BITMAP_SIZE(bits) - BITMAP_SIZE(bm-&gt;bits)) * <span class="keyword">sizeof</span>(Word);</span><br><span class="line"> <span class="built_in">memset</span>(bm-&gt;<span class="built_in">array</span>+BITMAP_SIZE(bm-&gt;bits), <span class="number">0</span>, bytes);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div>]]></content>
    
    
      
      
    <summary type="html">&lt;h3 id=&quot;字符串去重并排序&quot;&gt;&lt;a href=&quot;#字符串去重并排序&quot; class=&quot;headerlink&quot; title=&quot;字符串去重并排序&quot;&gt;&lt;/a&gt;字符串去重并排序&lt;/h3&gt;&lt;p&gt;给定一个只包含小写字母的字符串，目标是编写一个函数，将字符串中的字符去重并按字典序排序。&lt;/</summary>
      
    
    
    
    <category term="每日一题" scheme="http://example.com/categories/%E6%AF%8F%E6%97%A5%E4%B8%80%E9%A2%98/"/>
    
    
    <category term="C语言" scheme="http://example.com/tags/C%E8%AF%AD%E8%A8%80/"/>
    
  </entry>
  
  <entry>
    <title>C语言中的动态数组</title>
    <link href="http://example.com/2024/05/05/C%E8%AF%AD%E8%A8%80%E4%B8%AD%E7%9A%84%E5%8A%A8%E6%80%81%E6%95%B0%E7%BB%84/"/>
    <id>http://example.com/2024/05/05/C%E8%AF%AD%E8%A8%80%E4%B8%AD%E7%9A%84%E5%8A%A8%E6%80%81%E6%95%B0%E7%BB%84/</id>
    <published>2024-05-04T16:36:29.000Z</published>
    <updated>2024-09-01T16:12:56.585Z</updated>
    
    <content type="html"><![CDATA[<h1 id="动态数组与其在C语言中的实现"><a href="#动态数组与其在C语言中的实现" class="headerlink" title="动态数组与其在C语言中的实现"></a>动态数组与其在C语言中的实现</h1><p>在C语言中，数组是一片连续的内存空间，这片内存空间被划分为大小相等的小空间。在介绍动态数组之前，让我们先简单回顾一下数组的基本特性。</p><h2 id="数组的优点"><a href="#数组的优点" class="headerlink" title="数组的优点"></a>数组的优点</h2><ul><li><strong>访问速度快</strong>：可以通过下标索引直接访问元素，时间复杂度为O(1)。</li><li><strong>内存连续</strong>：有利于局部性原理，高速缓存命中率高。</li></ul><h2 id="数组的缺点"><a href="#数组的缺点" class="headerlink" title="数组的缺点"></a>数组的缺点</h2><ul><li><strong>大小固定</strong>：数组大小必须在声明时确定，之后无法改变。若数组空间不够，需要重新定义更大的数组。</li><li><strong>空间静态分配</strong>：若申请了较大的数组但实际使用较少，会造成内存浪费。</li><li><strong>插入和删除效率低</strong>：需要移动大量元素，时间复杂度为O(n)。</li></ul><p>为了克服数组的上述缺点，我们引入了动态内存分配的概念。</p><h2 id="为什么需要动态内存分配？"><a href="#为什么需要动态内存分配？" class="headerlink" title="为什么需要动态内存分配？"></a>为什么需要动态内存分配？</h2><ul><li>栈空间有限, 不适合存放大数据。</li><li>栈帧大小在编译期间确定, 栈空间不能存放动态大小的数据。</li><li>每个线程都有自己的栈, 不适合存放多线程共享的数据。</li></ul><p>常见的内存分配函数有malloc, calloc, realloc, free。接下来我们将通过一个实例来展示如何在C语言中实现动态数组。</p><h2 id="C语言实现动态数组"><a href="#C语言实现动态数组" class="headerlink" title="C语言实现动态数组"></a>C语言实现动态数组</h2><p>首先，我们需要定义一个动态数组的结构体和相关操作。这些操作包括创建数组、添加元素、删除元素、增长容量以及销毁数组。</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// vector.h</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="type">int</span> E;</span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span> &#123;</span></span><br><span class="line"> E* elements;</span><br><span class="line"> <span class="type">int</span> size;</span><br><span class="line"> <span class="type">int</span> capacity;</span><br><span class="line">&#125; Vector;</span><br><span class="line"></span><br><span class="line">Vector* <span class="title function_">vector_create</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line">Vector* <span class="title function_">vector_create_calloc</span><span class="params">(<span class="type">void</span>)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">push_back</span><span class="params">(Vector* v, E val)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">push_front</span><span class="params">(Vector* v, E val)</span>;</span><br><span class="line">E <span class="title function_">pop_back</span><span class="params">(Vector* v)</span>;</span><br><span class="line">E <span class="title function_">pop_front</span><span class="params">(Vector* v)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">grow_capacity</span><span class="params">(Vector* v)</span>;</span><br><span class="line"><span class="type">void</span> <span class="title function_">vector_destory</span><span class="params">(Vector* v)</span>;</span><br></pre></td></tr></table></figure></div><p>接下来，我们实现这些函数。</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// vector.c</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;vector.h&quot;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> DEFAULT_CAPACITY 4</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> PREALLOC_MAX 1024</span></span><br><span class="line">Vector* <span class="title function_">vector_create</span><span class="params">(<span class="type">void</span>)</span> &#123;</span><br><span class="line"> Vector* v = <span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(Vector));</span><br><span class="line"> <span class="keyword">if</span> (!v)</span><br><span class="line"> &#123;</span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">&quot;Error: malloc fail in vector_create\n&quot;</span>);</span><br><span class="line">  <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line"> &#125;</span><br><span class="line"> E* p = <span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(E) * DEFAULT_CAPACITY);</span><br><span class="line"> <span class="keyword">if</span> (!p)</span><br><span class="line"> &#123;</span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">&quot;Error: malloc fail in vector_create\n&quot;</span>);</span><br><span class="line">  <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line"> &#125;</span><br><span class="line"> v-&gt;elements = p;</span><br><span class="line"> v-&gt;capacity = DEFAULT_CAPACITY;</span><br><span class="line"> v-&gt;size = <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Vector* <span class="title function_">vector_create_calloc</span><span class="params">(<span class="type">void</span>)</span> &#123;</span><br><span class="line"> Vector* v = <span class="built_in">calloc</span>(<span class="number">1</span>, <span class="keyword">sizeof</span>(Vector));</span><br><span class="line"> <span class="keyword">if</span> (v == <span class="literal">NULL</span>) &#123;</span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">&quot;Error: calloc failed\n&quot;</span>);</span><br><span class="line">  <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line"> &#125;</span><br><span class="line"> v-&gt;elements = <span class="built_in">calloc</span>(DEFAULT_CAPACITY, <span class="keyword">sizeof</span>(E));</span><br><span class="line"> <span class="keyword">if</span> (v-&gt;elements == <span class="literal">NULL</span>) &#123;</span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">&quot;Error: calloc failed\n&quot;</span>);</span><br><span class="line">  <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line"> &#125;</span><br><span class="line"> v-&gt;capacity = DEFAULT_CAPACITY;</span><br><span class="line"> v-&gt;size = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">return</span> v;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">grow_capacity</span><span class="params">(Vector* v)</span> &#123;</span><br><span class="line"> <span class="type">int</span> new_capacity = v-&gt;capacity &lt; PREALLOC_MAX ?</span><br><span class="line">  v-&gt;capacity &lt;&lt; <span class="number">1</span> : v-&gt;capacity + PREALLOC_MAX;</span><br><span class="line"> E* p = <span class="built_in">realloc</span>(v-&gt;elements, new_capacity * <span class="keyword">sizeof</span>(E));</span><br><span class="line"> <span class="keyword">if</span> (!p)</span><br><span class="line"> &#123;</span><br><span class="line">  <span class="built_in">printf</span>(<span class="string">&quot;ERROR: grow_capacity&quot;</span>);</span><br><span class="line">  <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line"> &#125;</span><br><span class="line"> v-&gt;elements = p;</span><br><span class="line"> v-&gt;capacity = new_capacity;</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">void</span> <span class="title function_">push_back</span><span class="params">(Vector* v, E val)</span> &#123;</span><br><span class="line"> <span class="keyword">if</span> (v-&gt;capacity == v-&gt;size)</span><br><span class="line"> &#123;</span><br><span class="line">  grow_capacity(v);</span><br><span class="line"> &#125;</span><br><span class="line"> v-&gt;elements[v-&gt;size++] = val;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 在数组最前面添加元素，所有元素依次后移</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">push_front</span><span class="params">(Vector* v, E val)</span> &#123;</span><br><span class="line"> <span class="keyword">if</span> (v-&gt;capacity == v-&gt;size)</span><br><span class="line"> &#123;</span><br><span class="line">  grow_capacity(v);</span><br><span class="line"> &#125;</span><br><span class="line"> v-&gt;size++;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = v-&gt;size - <span class="number">2</span>; i &gt; <span class="number">0</span>; i--)</span><br><span class="line"> &#123;</span><br><span class="line">  v-&gt;elements[i + <span class="number">1</span>] = v-&gt;elements[i];</span><br><span class="line"> &#125;</span><br><span class="line"> v-&gt;elements[<span class="number">0</span>] = val;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 删除最后一个元素, 并把最后一个元素返回</span></span><br><span class="line">E <span class="title function_">pop_back</span><span class="params">(Vector* v)</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> v-&gt;elements[v-&gt;size--];</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 删除第一个元素，并把第一个元素返回</span></span><br><span class="line">E <span class="title function_">pop_front</span><span class="params">(Vector* v)</span> &#123;</span><br><span class="line"> <span class="type">int</span> t = v-&gt;elements[<span class="number">0</span>];</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; v-&gt;size - <span class="number">1</span>; i++)</span><br><span class="line"> &#123;</span><br><span class="line">  v-&gt;elements[i] = v-&gt;elements[i + <span class="number">1</span>];</span><br><span class="line"> &#125;</span><br><span class="line"> v-&gt;size--;</span><br><span class="line"> <span class="keyword">return</span> t;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">vector_destory</span><span class="params">(Vector* v)</span> &#123;</span><br><span class="line"> <span class="built_in">free</span>(v-&gt;elements);</span><br><span class="line"> <span class="built_in">free</span>(v);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><p>现在，我们可以在主函数中测试我们的动态数组了。</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// main.c</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&quot;vector.h&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">void</span> <span class="title function_">print_vector</span><span class="params">(Vector* v)</span> &#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;向量大小: %d, 容量: %d\n&quot;</span>, v-&gt;size, v-&gt;capacity);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;元素: &quot;</span>);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; v-&gt;size; i++) &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;%d &quot;</span>, v-&gt;elements[i]);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;\n&quot;</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">(<span class="type">void</span>)</span> &#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;使用malloc创建向量:\n&quot;</span>);</span><br><span class="line">    Vector* v1 = vector_create();</span><br><span class="line">    push_back(v1, <span class="number">1</span>);</span><br><span class="line">    push_back(v1, <span class="number">2</span>);</span><br><span class="line">    push_back(v1, <span class="number">3</span>);</span><br><span class="line">    print_vector(v1);</span><br><span class="line"></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;使用realloc扩容向量:\n&quot;</span>);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">4</span>; i &lt;= <span class="number">10</span>; i++) &#123;</span><br><span class="line">        push_back(v1, i);</span><br><span class="line">    &#125;</span><br><span class="line">    print_vector(v1);</span><br><span class="line"></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;使用calloc创建向量:\n&quot;</span>);</span><br><span class="line">    Vector* v2 = vector_create_calloc();</span><br><span class="line">    push_back(v2, <span class="number">1</span>);</span><br><span class="line">    push_back(v2, <span class="number">2</span>);</span><br><span class="line">    push_back(v2, <span class="number">3</span>);</span><br><span class="line">    push_back(v2, <span class="number">4</span>);</span><br><span class="line">    push_back(v2, <span class="number">5</span>);</span><br><span class="line">    print_vector(v2);</span><br><span class="line"></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;使用free销毁向量\n&quot;</span>);</span><br><span class="line">    vector_destory(v1);</span><br><span class="line">    vector_destory(v2);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><p>运行上述代码，我们得到以下输出结果：</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="https://tc.aptzone.cc/images/2024/09/02/202409020004954.png"                      alt="image-20240505002040537"                ></p><p>下面我们来一个简单的总结，并讲两点注意事项：</p><ol><li><code>malloc</code>函数分配的内存块包含未初始化的垃圁数据，而<code>calloc</code>函数分配的内存块会被自动初始化为0。</li><li><code>malloc</code>函数只接受一个参数，即要分配的字节数。而<code>calloc</code>函数接受两个参数，一个是要分配的元素的数量，另一个是每个元素的大小。这使得<code>calloc</code>更适合用于数组的分配。</li><li>由于<code>calloc</code>会初始化分配的内存，所以它可能比<code>malloc</code>稍微慢一些，尤其是在分配大量内存时。</li></ol><p><strong>注意事项</strong></p><ol><li>use after free</li><li>double free</li></ol>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;动态数组与其在C语言中的实现&quot;&gt;&lt;a href=&quot;#动态数组与其在C语言中的实现&quot; class=&quot;headerlink&quot; title=&quot;动态数组与其在C语言中的实现&quot;&gt;&lt;/a&gt;动态数组与其在C语言中的实现&lt;/h1&gt;&lt;p&gt;在C语言中，数组是一片连续的内存空间，这片内</summary>
      
    
    
    
    
    <category term="C语言" scheme="http://example.com/tags/C%E8%AF%AD%E8%A8%80/"/>
    
  </entry>
  
  <entry>
    <title>C语言中的字符串库函数</title>
    <link href="http://example.com/2024/05/04/C%E8%AF%AD%E8%A8%80%E4%B8%AD%E7%9A%84%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%BA%93%E5%87%BD%E6%95%B0/"/>
    <id>http://example.com/2024/05/04/C%E8%AF%AD%E8%A8%80%E4%B8%AD%E7%9A%84%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%BA%93%E5%87%BD%E6%95%B0/</id>
    <published>2024-05-03T16:24:53.000Z</published>
    <updated>2024-09-01T16:10:01.017Z</updated>
    
    <content type="html"><![CDATA[<h1 id="C语言字符串库函数"><a href="#C语言字符串库函数" class="headerlink" title="C语言字符串库函数"></a>C语言字符串库函数</h1><p>在本文中，我们将探讨一些常用的 C 语言字符串库函数，包括 <code>strlen()</code>, <code>strcpy()</code>, <code>strncpy()</code>, <code>strcat()</code>, <code>strncat()</code> 和 <code>strcmp()</code>。对于每个函数，我们将展示其用法，并提供一个自定义的实现。</p><h2 id="1-strlen"><a href="#1-strlen" class="headerlink" title="1. strlen()"></a>1. strlen()</h2><p><code>strlen()</code>函数接受一个字符串作为参数，并返回字符串的长度。请注意，它不计算结尾的 <code>\0</code>。</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">strlen</span>(<span class="string">&quot;ABC&quot;</span>) = <span class="number">3</span></span><br><span class="line"><span class="built_in">strlen</span>(<span class="string">&quot;&quot;</span>) = <span class="number">0</span></span><br></pre></td></tr></table></figure></div><p>下面是一个常用的计算字符串长度的方法：</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">char</span>* p = s;</span><br><span class="line"><span class="keyword">while</span>(*p)&#123;  <span class="comment">//搜索字符串的末尾，即`\0`</span></span><br><span class="line"> p++;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">return</span> p - s;</span><br></pre></td></tr></table></figure></div><h2 id="2-strcpy-strncpy"><a href="#2-strcpy-strncpy" class="headerlink" title="2. strcpy() &amp; strncpy()"></a>2. strcpy() &amp; strncpy()</h2><p>接下来我们来看 <code>strcpy()</code> 和 <code>strncpy()</code> 函数。这两个函数用于复制字符串。</p><p>首先是 <code>strcpy()</code> 函数的使用：</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">char</span> s1[MAXLINE];</span><br><span class="line"><span class="built_in">strcpy</span>(s1, <span class="string">&quot;Hello World.&quot;</span>);</span><br><span class="line">s1[MAXLINE - <span class="number">1</span>] = <span class="string">&#x27;\0&#x27;</span>;</span><br></pre></td></tr></table></figure></div><p><img                       lazyload                     src="/images/loading.svg"                     data-src="https://tc.aptzone.cc/images/2024/09/01/image-20240503234505992.png"                      alt="image-20240503234505992"                ></p><p>然后是 <code>strncpy()</code> 函数的使用：</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">char</span> s2[MAXLINE];</span><br><span class="line"><span class="built_in">strncpy</span>(s2, <span class="string">&quot;Hello world&quot;</span>, MAXLINE - <span class="number">1</span>);</span><br><span class="line">s2[MAXLINE - <span class="number">1</span>] = <span class="string">&#x27;\0&#x27;</span>;</span><br></pre></td></tr></table></figure></div><p><img                       lazyload                     src="/images/loading.svg"                     data-src="https://tc.aptzone.cc/images/2024/09/01/image-20240503234553962.png"                      alt="image-20240503234553962"                ></p><p>下面是 <code>strcpy()</code> 和 <code>strncpy()</code> 的自定义实现：</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">char</span>* <span class="title function_">my_strcpy</span><span class="params">(<span class="type">char</span>* s1, <span class="type">const</span> <span class="type">char</span>* s2)</span> &#123;</span><br><span class="line"> <span class="type">char</span>* p = s1;</span><br><span class="line">    <span class="keyword">while</span>(*s1++ = *s2++)</span><br><span class="line">        ;</span><br><span class="line">    <span class="keyword">return</span> p;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">char</span>* <span class="title function_">my_strncpy</span><span class="params">(<span class="type">char</span>* s1, <span class="type">const</span> <span class="type">char</span>* s2, <span class="type">int</span> count)</span> &#123;</span><br><span class="line">    <span class="type">char</span>* p = s1;</span><br><span class="line">    <span class="keyword">while</span>((count-- &gt; <span class="number">0</span>) &amp;&amp; (*s1++ = *s2++))</span><br><span class="line">        ;</span><br><span class="line">    <span class="keyword">return</span> p;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><h2 id="3-strcat-strncat"><a href="#3-strcat-strncat" class="headerlink" title="3. strcat() &amp; strncat()"></a>3. strcat() &amp; strncat()</h2><p>接下来我们来看 <code>strcat()</code> 和 <code>strncat()</code> 函数。这两个函数用于连接字符串。</p><p>首先是 <code>strcat()</code> 函数的使用：</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">char</span> s1[MAXLINE] = <span class="string">&quot;Hello &quot;</span>;</span><br><span class="line"><span class="built_in">strcat</span>(s1, <span class="string">&quot;world\n&quot;</span>);</span><br></pre></td></tr></table></figure></div><p>然后是 <code>strncat()</code> 函数的使用：</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">char</span> s2[MAXLINE] = <span class="string">&quot;Hello &quot;</span>;</span><br><span class="line"><span class="built_in">strncat</span>(s2, <span class="string">&quot;world\n&quot;</span>, MAXLINE - <span class="built_in">strlen</span>(s2) - <span class="number">1</span>);</span><br></pre></td></tr></table></figure></div><p>下面是 <code>strcat()</code> 和 <code>strncat()</code> 的自定义实现：</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">char</span>* <span class="title function_">my_strcat</span><span class="params">(<span class="type">char</span>* s1, <span class="type">const</span> <span class="type">char</span>* s2)</span>&#123;</span><br><span class="line"> <span class="type">char</span>* p = s1;</span><br><span class="line">    <span class="keyword">while</span>(*s1)&#123;</span><br><span class="line">        s1++;</span><br><span class="line">    &#125; <span class="comment">//s1 == &#x27;\0&#x27;</span></span><br><span class="line">    <span class="keyword">while</span>(*s1++ = *s2++)</span><br><span class="line">        ;</span><br><span class="line">    <span class="keyword">return</span> p;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="type">char</span>* <span class="title function_">my_strncat</span><span class="params">(<span class="type">char</span>* s1, <span class="type">const</span> <span class="type">char</span>* s2, <span class="type">size_t</span> count)</span>&#123;</span><br><span class="line"> <span class="type">char</span>* p = s1;</span><br><span class="line">    <span class="keyword">while</span>(*s1)&#123;</span><br><span class="line">        s1++;</span><br><span class="line">    &#125; <span class="comment">//s1 == &#x27;\0&#x27;</span></span><br><span class="line">    <span class="keyword">while</span>((count--) &amp;&amp; (*s1++ = *s2++))</span><br><span class="line">        ;</span><br><span class="line">    <span class="keyword">return</span> p;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><h2 id="4-strcmp"><a href="#4-strcmp" class="headerlink" title="4. strcmp()"></a>4. strcmp()</h2><p>最后，我们来看 <code>strcmp()</code> 函数。这个函数用于比较两个字符串。</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;strcmp(%s, %s) = %d\n&quot;</span>, <span class="string">&quot;ABCDef&quot;</span>, <span class="string">&quot;ABCDe&quot;</span>, <span class="built_in">strcmp</span>(<span class="string">&quot;ABCDef&quot;</span>, <span class="string">&quot;ABCDe&quot;</span>));</span><br><span class="line"><span class="comment">// strcmp(ABCDef, ABCDe) = 1</span></span><br></pre></td></tr></table></figure></div><p>下面是 <code>strcmp()</code> 的自定义实现：</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">my_strcmp</span><span class="params">(<span class="type">const</span> <span class="type">char</span>* s1, <span class="type">const</span> <span class="type">char</span>* s2)</span>&#123;</span><br><span class="line">    <span class="keyword">while</span>(*s1 &amp;&amp; *s2)&#123;</span><br><span class="line">        <span class="keyword">if</span>(*s1 != *s2)&#123;</span><br><span class="line">            <span class="keyword">return</span> *s1 - *s2;</span><br><span class="line">        &#125;</span><br><span class="line">        s1++;</span><br><span class="line">        s2++;</span><br><span class="line">    &#125; <span class="comment">// *s1 == &#x27;\0&#x27; || *s2 == &#x27;\0&#x27;</span></span><br><span class="line">    <span class="keyword">return</span> *s1 - *s2;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// my_strcmp(&quot;ABCDef&quot;, &quot;ABCDe&quot;)最后会跳出while循环并执行return *s1 - *s2;即&#x27;f&#x27;-&#x27;\0&#x27;,空字符在ascii 码表中值为0</span></span><br></pre></td></tr></table></figure></div><h2 id="指针常量与常量指针"><a href="#指针常量与常量指针" class="headerlink" title="指针常量与常量指针"></a>指针常量与常量指针</h2><p>在学习字符串库函数的过程中，我们经常会遇到“指针常量”和“常量指针”这两个术语。这两个术语可能会引起混淆，特别是在它们的英文表述中，这两个概念的差异更为明显。让我们从英文的角度来解释这两个概念：</p><h3 id="1-Pointer-to-const-指向常量的指针"><a href="#1-Pointer-to-const-指向常量的指针" class="headerlink" title="1. Pointer to const (指向常量的指针)"></a>1. Pointer to const (指向常量的指针)</h3><p>这种类型的指针可以指向不同的地址，但不能修改其指向地址处的数据。</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">int</span> *ptr;  <span class="comment">// 指向整型常量的指针</span></span><br><span class="line"><span class="type">int</span> value = <span class="number">5</span>;</span><br><span class="line">ptr = &amp;value;  <span class="comment">// 正确，ptr现在指向value</span></span><br><span class="line"><span class="comment">// *ptr = 10;  // 错误，不能通过ptr修改value，因为ptr是指向常量的指针</span></span><br></pre></td></tr></table></figure></div><h3 id="2-Constant-pointer-常量指针"><a href="#2-Constant-pointer-常量指针" class="headerlink" title="2. Constant pointer (常量指针)"></a>2. Constant pointer (常量指针)</h3><p>这种类型的指针一旦初始化后，其存储的地址就不能再被修改，但可以修改其指向地址处的数据。</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> value = <span class="number">5</span>;</span><br><span class="line"><span class="type">int</span> *<span class="type">const</span> ptr = &amp;value;  <span class="comment">// 常量指针，必须在声明时初始化</span></span><br><span class="line">*ptr = <span class="number">10</span>;  <span class="comment">// 正确，可以通过ptr修改value</span></span><br><span class="line"><span class="comment">// ptr = &amp;another_value;  // 错误，ptr不能再指向其他地址</span></span><br></pre></td></tr></table></figure></div><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><ul><li><strong>Pointer to const</strong> (“指向常量的指针”)：允许改变指针指向的地址，但不允许通过指针修改所指向的数据。</li><li><strong>Constant pointer</strong> (“常量指针”)：不允许改变指针的值（即地址），但允许修改指针指向地址处的数据。</li></ul><p>通过这种方式理解，可以更清楚地区分这两种类型的指针，并理解它们在实际编程中的用途和限制。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;C语言字符串库函数&quot;&gt;&lt;a href=&quot;#C语言字符串库函数&quot; class=&quot;headerlink&quot; title=&quot;C语言字符串库函数&quot;&gt;&lt;/a&gt;C语言字符串库函数&lt;/h1&gt;&lt;p&gt;在本文中，我们将探讨一些常用的 C 语言字符串库函数，包括 &lt;code&gt;strlen</summary>
      
    
    
    
    
    <category term="C语言" scheme="http://example.com/tags/C%E8%AF%AD%E8%A8%80/"/>
    
  </entry>
  
  <entry>
    <title>C语言中的字符串变量</title>
    <link href="http://example.com/2024/05/02/C%E8%AF%AD%E8%A8%80%E4%B8%AD%E7%9A%84%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%8F%98%E9%87%8F/"/>
    <id>http://example.com/2024/05/02/C%E8%AF%AD%E8%A8%80%E4%B8%AD%E7%9A%84%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%8F%98%E9%87%8F/</id>
    <published>2024-05-02T15:43:16.000Z</published>
    <updated>2024-09-01T16:06:29.146Z</updated>
    
    <content type="html"><![CDATA[<h1 id="C语言中的字符串变量"><a href="#C语言中的字符串变量" class="headerlink" title="C语言中的字符串变量"></a>C语言中的字符串变量</h1><pre><code>下文不包含字符串变量的所有知识点，仅为本人在课程学习中记录认为比较关键的部分，便于自己复习，也有助于C语言小白加深C语言中字符串变量的理解。</code></pre><h2 id="总纲"><a href="#总纲" class="headerlink" title="总纲"></a>总纲</h2><ol><li>C语言没有字符串类型</li><li>C语言中的字符串依赖字符数组存在</li><li>C语言中字符串是一种逻辑类型</li></ol><p><img                       lazyload                     src="/images/loading.svg"                     data-src="https://tc.aptzone.cc/images/2024/09/01/image-20240501160141931.md.png"                      alt="image-20240501160141931"                ></p><h2 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h2><ol><li>字符串必须以 <code>&#39;\0&#39;</code> 结尾</li><li>在C语言中求字符串的长度不是O(1)的时间复杂度</li></ol><h2 id="字符串的声明与初始化"><a href="#字符串的声明与初始化" class="headerlink" title="字符串的声明与初始化"></a>字符串的声明与初始化</h2><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 声明字符串变量并赋初始值</span></span><br><span class="line"><span class="type">char</span> str1[] = &#123;<span class="string">&#x27;H&#x27;</span>, <span class="string">&#x27;e&#x27;</span>, <span class="string">&#x27;l&#x27;</span>, <span class="string">&#x27;l&#x27;</span>, <span class="string">&#x27;o&#x27;</span>, <span class="string">&#x27;\0&#x27;</span>&#125;; <span class="comment">// 数组初始化式</span></span><br><span class="line"><span class="type">char</span> str2[] = <span class="string">&quot;hello&quot;</span>; <span class="comment">// 语法糖: &quot;hello&quot;是数组初始化式的简写形式</span></span><br><span class="line"></span><br><span class="line"><span class="type">char</span>* p = <span class="string">&quot;ABC&quot;</span> + <span class="number">1</span>; <span class="comment">// 这里的ABC是字符串字面值</span></span><br></pre></td></tr></table></figure></div><h2 id="字符串字面值与数组初始化"><a href="#字符串字面值与数组初始化" class="headerlink" title="字符串字面值与数组初始化"></a>字符串字面值与数组初始化</h2><p>当 <code>&quot;hello&quot;</code> 直接出现在代码中时,它被视为一个<strong>字符串字面值</strong>。字符串字面值是一个常量,并且在程序的整个生命周期内存在。这意味着它通常存储在程序的只读数据段中。例如:</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;%s&quot;</span>, <span class="string">&quot;hello&quot;</span>); <span class="comment">// 这里的 &quot;hello&quot; 是字符串字面值</span></span><br></pre></td></tr></table></figure></div><p>在这个例子中,<code>&quot;hello&quot;</code> 直接作为参数传递给 <code>printf</code> 函数,它在内存中的位置是固定的,并且通常不可修改。</p><p>当使用 <code>&quot;hello&quot;</code> 来初始化一个字符数组时,它作为<strong>数组初始化式</strong>的一部分。在这种情况下,字符串字面值 <code>&quot;hello&quot;</code> 用于初始化数组中的元素,数组的元素可以被修改。例如:</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">char</span> str2[] = <span class="string">&quot;hello&quot;</span>; <span class="comment">// 初始化字符数组</span></span><br></pre></td></tr></table></figure></div><p>这里,<code>&quot;hello&quot;</code> 用作数组 <code>str2</code> 的初始化式。这意味着字符串 <code>&quot;hello&quot;</code> 的内容(包括结尾的空字符 <code>\0</code>)被复制到 <code>str2</code> 数组中。因此,<code>str2</code> 是一个包含6个字符(<code>&#39;h&#39;</code>, <code>&#39;e&#39;</code>, <code>&#39;l&#39;</code>, <code>&#39;l&#39;</code>, <code>&#39;o&#39;</code>, <code>&#39;\0&#39;</code>)的数组,这些字符存储在栈上(如果在函数内部声明)或全局&#x2F;静态存储区(如果在函数外部声明),并且可以被修改。</p><p>在处理字符串时,这种区分对于理解如何安全地操作字符串非常关键。</p><p><strong>建议</strong>:</p><ol><li>如果初始化字符数组,{‘H’,’e’,’l’,’l’,’o’,’\0’}</li><li>如果初始化字符串,”hello”</li></ol><p>遵循上述建议代码的可读性将会比较好,但字符串字面值初始化字符数组也有其简洁性。</p><div class="code-container" data-rel="C"><figure class="iseeu highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">char</span> str[<span class="number">10</span>] = <span class="string">&quot;hello&quot;</span>; <span class="comment">// 长度为10的数组</span></span><br><span class="line"><span class="type">char</span> str[<span class="number">5</span>] = <span class="string">&quot;hello&quot;</span>; <span class="comment">// 长度为5,不表示字符串,会发生数组越界。</span></span><br></pre></td></tr></table></figure></div><h2 id="读写字符串的方式"><a href="#读写字符串的方式" class="headerlink" title="读写字符串的方式"></a>读写字符串的方式</h2><h3 id="第一种方式-printf-scanf-s"><a href="#第一种方式-printf-scanf-s" class="headerlink" title="第一种方式: printf&#x2F;scanf + %s"></a>第一种方式: printf&#x2F;scanf + %s</h3><p>匹配规则,建议自己敲一下。我得到的结果是 <code>%s</code> 会忽略前置的空白字符,遇到空白字符结束。</p><p><strong>scanf + %s 的缺点</strong>:</p><ol><li>不能够存储空白字符</li><li>不会检查数组越界</li></ol><h3 id="第二种方式-puts-gets"><a href="#第二种方式-puts-gets" class="headerlink" title="第二种方式: puts&#x2F;gets"></a>第二种方式: puts&#x2F;gets</h3><p><code>puts(str)</code>等价于 <code>printf(&quot;%s\n&quot;, str)</code>;</p><p><code>gets(str)</code> 从 stdin 中读取一行数据,存入字符数组,并将 <code>&#39;\n&#39;</code> 替换为 <code>&#39;\0&#39;</code>。</p><p><strong>gets 的缺点</strong>:</p><p>不会检查数组越界。<code>fgets(str, MAXLINE, stdin)</code> 可以弥补这个缺点,并且会存储 <code>\n</code>, 在后面额外添加 <code>\0</code></p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;C语言中的字符串变量&quot;&gt;&lt;a href=&quot;#C语言中的字符串变量&quot; class=&quot;headerlink&quot; title=&quot;C语言中的字符串变量&quot;&gt;&lt;/a&gt;C语言中的字符串变量&lt;/h1&gt;&lt;pre&gt;&lt;code&gt;下文不包含字符串变量的所有知识点，仅为本人在课程学习中记录认</summary>
      
    
    
    
    
    <category term="C语言" scheme="http://example.com/tags/C%E8%AF%AD%E8%A8%80/"/>
    
  </entry>
  
  <entry>
    <title>企业 ERP 助手</title>
    <link href="http://example.com/2024/03/01/%E4%BC%81%E4%B8%9A-ERP-%E5%8A%A9%E6%89%8B/"/>
    <id>http://example.com/2024/03/01/%E4%BC%81%E4%B8%9A-ERP-%E5%8A%A9%E6%89%8B/</id>
    <published>2024-03-01T15:55:18.000Z</published>
    <updated>2025-04-29T10:59:20.732Z</updated>
    
    <content type="html"><![CDATA[<p>ERP（企业资源管理）软件是企业的关键软件系统，目前一般使用 GUI 界面，复杂的操<br>作需要多次鼠标点击，操作非常麻烦。AI Agent 可以把用户的自然语言查询转换成 SQL<br>语句，从而实现自动化的查询。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="https://tc.aptzone.cc/images/2024/09/01/202404222005999.md.png"                      alt="ERP Assistant"                ></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;ERP（企业资源管理）软件是企业的关键软件系统，目前一般使用 GUI 界面，复杂的操&lt;br&gt;作需要多次鼠标点击，操作非常麻烦。AI Agent 可以把用户的自然语言查询转换成 SQL&lt;br&gt;语句，从而实现自动化的查询。&lt;/p&gt;
&lt;p&gt;&lt;img  
             </summary>
      
    
    
    
    
    <category term="Python" scheme="http://example.com/tags/Python/"/>
    
    <category term="Chatgpt" scheme="http://example.com/tags/Chatgpt/"/>
    
  </entry>
  
  <entry>
    <title>Typora + PicList 自定义命令上传图片并实现加速</title>
    <link href="http://example.com/2024/03/01/Typora-PicList-%E8%87%AA%E5%AE%9A%E4%B9%89%E5%91%BD%E4%BB%A4%E4%B8%8A%E4%BC%A0%E5%9B%BE%E7%89%87%E5%B9%B6%E5%AE%9E%E7%8E%B0%E5%8A%A0%E9%80%9F/"/>
    <id>http://example.com/2024/03/01/Typora-PicList-%E8%87%AA%E5%AE%9A%E4%B9%89%E5%91%BD%E4%BB%A4%E4%B8%8A%E4%BC%A0%E5%9B%BE%E7%89%87%E5%B9%B6%E5%AE%9E%E7%8E%B0%E5%8A%A0%E9%80%9F/</id>
    <published>2024-03-01T09:16:13.000Z</published>
    <updated>2024-09-01T16:12:46.410Z</updated>
    
    <content type="html"><![CDATA[<p>在使用Typora编辑器时，您可能遇到因网络原因导致图片加载缓慢或失败的问题。本文将介绍如何通过配置PicList和使用Cloudflare的CDN服务来加速图片访问，从而解决这一问题。下面是网络不好加载失败的图片：</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="https://tc.aptzone.cc/images/2024/09/01/202403111436545.png"                      alt="202403111436545"                ></p><h2 id="解决方案概述"><a href="#解决方案概述" class="headerlink" title="解决方案概述"></a>解决方案概述</h2><p>为了提高图片的访问速度，我们将利用Cloudflare的免费CDN服务。操作步骤主要包括：</p><ol><li>搭建Cloudflare Workers&#x2F;Pages。</li><li>自定义域名设置。</li><li>在Typora中将上传服务设定为自定义脚本。</li><li>（可选）在PicList中设置自定义链接格式。</li></ol><h2 id="搭建Cloudflare-Workers-Pages"><a href="#搭建Cloudflare-Workers-Pages" class="headerlink" title="搭建Cloudflare Workers&#x2F;Pages"></a>搭建Cloudflare Workers&#x2F;Pages</h2><p>首先，您需要创建一个Cloudflare Workers&#x2F;Pages服务，这可以通过以下链接中的指南完成：</p><p><a class="link"   href="https://github.com/Rongronggg9/rsstt-img-relay/blob/main/README_zh-CN.md" >这里有一个符合要求的易于自行搭建的项目<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></p><h2 id="自定义域名"><a href="#自定义域名" class="headerlink" title="自定义域名"></a>自定义域名</h2><p>由于<code>workers.dev</code>域名在某些地区可能被屏蔽，建议使用<code>pages.dev</code>或自定义域名以提高稳定性。GitHub学生认证可免费获取一年的<code>.me</code>域名。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="https://tc.aptzone.cc/images/2024/09/01/202403111416171.png"                      alt="202403111416171"                ></p><h2 id="Typora上传服务设定为自定义脚本"><a href="#Typora上传服务设定为自定义脚本" class="headerlink" title="Typora上传服务设定为自定义脚本"></a>Typora上传服务设定为自定义脚本</h2><p>您需要在Typora中设置上传服务为自定义脚本。这里提供了PowerShell和bash两种脚本的示例：(二选一，确保系统中安装了对于工具，对于bash脚本需要curl和jq，curl一般windows自带，jq可能需要手动安装，参考<a class="link"   href="https://jqlang.github.io/jq/download/" >https://jqlang.github.io/jq/download/<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a>)</p><p>编写PowerShell脚本，比如<code>upload_typora_piclist.ps1</code>：</p><div class="code-container" data-rel="Powershell"><figure class="iseeu highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 定义请求的URI</span></span><br><span class="line"><span class="variable">$uri</span> = <span class="string">&quot;http://127.0.0.1:36677/upload&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 如果POST请求需要上传文件或其他数据，这里需要调整</span></span><br><span class="line"><span class="comment"># 例如，上传文件，需要构造表单数据或直接使用文件内容</span></span><br><span class="line"><span class="comment"># $body = @&#123;file = Get-Content &#x27;path/to/your/file&#x27; -Raw&#125;</span></span><br><span class="line"><span class="variable">$body</span> = <span class="selector-tag">@</span>&#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 发送POST请求</span></span><br><span class="line"><span class="variable">$response</span> = <span class="built_in">Invoke-RestMethod</span> <span class="literal">-Uri</span> <span class="variable">$uri</span> <span class="literal">-Method</span> Post <span class="literal">-Body</span> <span class="variable">$body</span> <span class="literal">-ContentType</span> <span class="string">&quot;application/json&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 检查是否上传成功</span></span><br><span class="line"><span class="keyword">if</span> (<span class="variable">$response</span>.success <span class="operator">-eq</span> <span class="variable">$true</span>) &#123;</span><br><span class="line">    <span class="comment"># 构造并显示最终结果</span></span><br><span class="line">    <span class="variable">$resultUrl</span> = <span class="string">&quot;你的Cloudflare自定义域名&quot;</span> + <span class="variable">$response</span>.result[<span class="number">0</span>]</span><br><span class="line">    <span class="built_in">Write-Output</span> <span class="string">&quot;Upload Success:&quot;</span></span><br><span class="line">    <span class="built_in">Write-Output</span> <span class="variable">$resultUrl</span></span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="built_in">Write-Output</span> <span class="string">&quot;Upload Failed&quot;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><p>bash脚本，比如<code>upload_typora_piclist_sh.sh</code>：</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/bash</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 目标URL</span></span><br><span class="line">URL=<span class="string">&quot;http://127.0.0.1:36677/upload&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 发送POST请求并获取响应，这里假设不需要传递具体数据</span></span><br><span class="line"><span class="comment"># 如果需要上传文件，你可能需要使用-F 参数</span></span><br><span class="line">response=$(curl -s -X POST <span class="string">&quot;<span class="variable">$URL</span>&quot;</span> -H <span class="string">&quot;Content-Type: application/json&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用jq处理JSON响应，提取需要的信息</span></span><br><span class="line">success=$(<span class="built_in">echo</span> <span class="variable">$response</span> | jq -r <span class="string">&#x27;.success&#x27;</span>)</span><br><span class="line"><span class="keyword">if</span> [[ <span class="variable">$success</span> == <span class="string">&quot;true&quot;</span> ]]; <span class="keyword">then</span></span><br><span class="line">    resultUrl=$(<span class="built_in">echo</span> <span class="variable">$response</span> | jq -r <span class="string">&#x27;.result[0]&#x27;</span>)</span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;Upload Success:&quot;</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;你的Cloudflare自定义域名<span class="variable">$resultUrl</span>&quot;</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="string">&quot;Upload Failed&quot;</span></span><br><span class="line"><span class="keyword">fi</span></span><br></pre></td></tr></table></figure></div><h2 id="PicList设置自定义链接格式"><a href="#PicList设置自定义链接格式" class="headerlink" title="PicList设置自定义链接格式"></a>PicList设置自定义链接格式</h2><p>在PicList中，您可以设置自定义链接格式以使用Cloudflare CDN进行加速。例如，将链接格式修改为<code>https://xxxx.xxxxxx.workers.dev/$url</code>。之后，当您从相册中复制链接时，选择<code>Custom</code>选项，即可获取经过Cloudflare CDN加速的链接。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="https://alist.huzefan.me/d/GoogleDrive/202403111419569.png"                      alt="202403111419569"                ></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;在使用Typora编辑器时，您可能遇到因网络原因导致图片加载缓慢或失败的问题。本文将介绍如何通过配置PicList和使用Cloudflare的CDN服务来加速图片访问，从而解决这一问题。下面是网络不好加载失败的图片：&lt;/p&gt;
&lt;p&gt;&lt;img  
              </summary>
      
    
    
    
    
    <category term="Typora" scheme="http://example.com/tags/Typora/"/>
    
    <category term="PicList" scheme="http://example.com/tags/PicList/"/>
    
  </entry>
  
  <entry>
    <title>使用rclone挂载onedrive扩展服务器空间</title>
    <link href="http://example.com/2024/02/23/%E4%BD%BF%E7%94%A8rclone%E6%8C%82%E8%BD%BDonedrive%E6%89%A9%E5%B1%95%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%A9%BA%E9%97%B4/"/>
    <id>http://example.com/2024/02/23/%E4%BD%BF%E7%94%A8rclone%E6%8C%82%E8%BD%BDonedrive%E6%89%A9%E5%B1%95%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%A9%BA%E9%97%B4/</id>
    <published>2024-02-23T09:56:38.000Z</published>
    <updated>2025-07-31T03:29:54.053Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Rclone简介"><a href="#Rclone简介" class="headerlink" title="Rclone简介"></a>Rclone简介</h2><p>Rclone <em>(“rsync for cloud storage”)</em> is a command-line program to sync files and directories to and from different cloud storage providers.</p><p>参考文档<br><a class="link"   href="https://rclone.org/docs/" >https://rclone.org/docs/<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></p><p>本文尝试使用rclone将ondrive以磁盘的方式挂载到vps上。其他云存储如google drive、Amazon Drive等也可以自行尝试。</p><p>主要有以下几个步骤</p><h2 id="在本地下载rclone并获取Token"><a href="#在本地下载rclone并获取Token" class="headerlink" title="在本地下载rclone并获取Token"></a>在本地下载rclone并获取Token</h2><p>在Windows电脑上下载Rclone，下载地址:<a class="link"   href="https://rclone.org/downloads/" >https://rclone.org/downloads/<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a> ，然后解压，使用cmd进入解压后的文件夹。运行命令</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">`rclone.exe authorize <span class="string">&quot;onedrive&quot;</span></span><br></pre></td></tr></table></figure></div><p>然后会跳转到浏览器的登录页面，登录并授权成功后cmd命令框就会返回你的token，注意不要登错账号了。保存好{xxxxxxxxxxx}备用。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="https://tc.aptzone.cc/images/2025/07/31/20250731112715312.png"                                     ></p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="https://tc.aptzone.cc/images/2025/07/31/20250731112729663.png"                                     ></p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="https://tc.aptzone.cc/images/2025/07/31/20250731112746243.png"                                     ></p><h2 id="vps上安装并配置rclone"><a href="#vps上安装并配置rclone" class="headerlink" title="vps上安装并配置rclone"></a>vps上安装并配置rclone</h2><p>运行以下命令，下载并安装Rclone</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">curl https://rclone.org/install.sh | <span class="built_in">sudo</span> bash</span><br></pre></td></tr></table></figure></div><p>安装完成后输入：</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">rclone config</span><br></pre></td></tr></table></figure></div><p>输入 <code>n</code> 新建配置。</p><ul><li><code>name</code> 可以随便输入</li><li>输入序号，选择onedrive</li></ul><p><img                       lazyload                     src="/images/loading.svg"                     data-src="https://tc.aptzone.cc/images/2025/07/31/20250731112759550.png"                                     ></p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="https://tc.aptzone.cc/images/2025/07/31/20250731112829318.png"                                     ></p><ul><li><code>client_id</code> 和<code>client_secret</code> 直接回车默认即可。也可以进入 Microsoft Azure 应用注册页面，新注册一个，并添加需要的权限，如果使用自己的id, 请确保配置正确，不然最后会挂载失败。</li><li>注意这一步输入<code>n</code>，然后将第一步获取的token粘贴进去。其他的默认即可</li></ul><p>Use web browser to automatically authenticate rclone with remote?<br>* Say Y if the machine running rclone has a web browser you can use<br>* Say N if running rclone on a (remote) machine without web browser access<br>If not sure try Y. If Y failed, try N.<br>y) Yes (default)<br>n) No<br>y&#x2F;n&gt; n</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="https://tc.aptzone.cc/images/2025/07/31/20250731112848890.png"                                     ></p><p>验证一下是否能正常使用，<code>bufan</code>换成你的Name注意后面还有一个冒号</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="https://tc.aptzone.cc/images/2025/07/31/20250731112902715.png"                                     ></p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">touch</span> <span class="built_in">test</span> &amp;&amp; rclone move <span class="built_in">test</span> bufan:</span><br></pre></td></tr></table></figure></div><p><img                       lazyload                     src="/images/loading.svg"                     data-src="https://tc.aptzone.cc/images/2025/07/31/20250731112919010.png"                                     ></p><h2 id="最后将onedrive挂载到指定路径"><a href="#最后将onedrive挂载到指定路径" class="headerlink" title="最后将onedrive挂载到指定路径"></a>最后将onedrive挂载到指定路径</h2><p>创建挂载点<code>mkdir xxx/xxxx/你的文件夹</code><br>使用下面的命令挂载：</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">rclone mount &lt;配置的云盘名称&gt;:&lt;要挂载的云盘目录&gt; &lt;作为挂载点的本地目录&gt; --copy-links --no-gzip-encoding --no-check-certificate --allow-other --allow-non-empty --<span class="built_in">umask</span> 000 --daemon</span><br></pre></td></tr></table></figure></div><p>比如我用的代码：</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">rclone mount bufan:/racknerd  /root/bufandrive  --copy-links --no-gzip-encoding --no-check-certificate --allow-other --allow-non-empty --<span class="built_in">umask</span> 000 --daemon</span><br></pre></td></tr></table></figure></div><p>输入df -h查看是否挂载成功。</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="https://tc.aptzone.cc/images/2025/07/31/20250731112933536.png"                                     ></p><p>如果你安装了管理面板可以直接在面板状态看到对应云盘，下面是1panel</p><p><img                       lazyload                     src="/images/loading.svg"                     data-src="https://tc.aptzone.cc/images/2025/07/31/20250731112949131.png"                                     ></p><p>到此为止，我们就成功挂载了，但是如果重新启动vps, 需要重新执行第三步的挂载命令重新挂载。</p><p>如果报错failed to mount FUSE fs: fusermount: exec: “fusermount3”: executable file not found in $PATH<br>系统中找不到 fusermount3 这个必需的执行文件。fusermount3 是 FUSE (Filesystem in Userspace) 的一个组成部分，它允许非特权用户创建和管理自己的文件系统，而 rclone mount 命令需要 FUSE 来将云存储服务挂载为文件系统。对于 Debian&#x2F;Ubuntu</p><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt update</span><br><span class="line"><span class="built_in">sudo</span> apt install fuse3</span><br></pre></td></tr></table></figure></div><h2 id="补充"><a href="#补充" class="headerlink" title="补充"></a>补充</h2><p>我们可以使用 <code>systemd</code> 的系统，创建一个 <code>systemd</code> 服务单元文件来管理 <code>rclone</code> 挂载。这不仅可以在启动时自动挂载，而且还可以在挂载失败时自动重试。</p><p>要创建一个 <code>systemd</code> 服务单元文件来管理 <code>rclone</code> 挂载，你需要创建一个新的服务文件，并将其放置在 <code>/etc/systemd/system</code> 目录下。以下是一个服务单元文件的示例，它将根据你提供的命令自动挂载 <code>bufan:/</code> 到 <code>/data/bufan</code>：</p><ol><li>打开一个终端或连接到你的 VPS。</li><li>使用文本编辑器（如 <code>nano</code> 或 <code>vim</code>）创建服务文件。例如，使用以下命令创建一个名为 <code>rclone-mount.service</code> 的文件：</li></ol><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> nano /etc/systemd/system/rclone-mount.service</span><br></pre></td></tr></table></figure></div><ol start="3"><li>在打开的编辑器中，粘贴以下内容：</li></ol><div class="code-container" data-rel="Ini"><figure class="iseeu highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">[Unit]</span></span><br><span class="line"><span class="attr">Description</span>=RClone Service</span><br><span class="line"><span class="attr">After</span>=network.target</span><br><span class="line"></span><br><span class="line"><span class="section">[Service]</span></span><br><span class="line"><span class="attr">Type</span>=simple</span><br><span class="line"><span class="attr">User</span>=root</span><br><span class="line"><span class="attr">ExecStart</span>=/usr/bin/rclone mount bufan:/ /data/bufan \</span><br><span class="line">  --copy-links \</span><br><span class="line">  --no-gzip-encoding \</span><br><span class="line">  --no-check-certificate \</span><br><span class="line">  --allow-other \</span><br><span class="line">  --allow-non-empty \</span><br><span class="line">  --umask 000 \</span><br><span class="line">  --daemon</span><br><span class="line"><span class="attr">ExecStop</span>=/bin/fusermount -u /data/bufan</span><br><span class="line"><span class="attr">Restart</span>=<span class="literal">on</span>-abort</span><br><span class="line"></span><br><span class="line"><span class="section">[Install]</span></span><br><span class="line"><span class="attr">WantedBy</span>=default.target</span><br></pre></td></tr></table></figure></div><p>注意：</p><ul><li><code>ExecStart</code> 包含了你的 <code>rclone</code> 挂载命令。</li><li><code>ExecStop</code> 使用 <code>fusermount -u</code> 命令来卸载挂载点，这是在服务停止时需要执行的。</li><li>确保 <code>/usr/bin/rclone</code> 是 <code>rclone</code> 的正确路径。如果不是，请根据你的系统路径进行调整。</li><li><code>Restart=on-abort</code> 指示如果服务异常终止，系统应尝试重启服务。</li></ul><ol start="4"><li>保存并关闭文件。</li><li>重新加载 <code>systemd</code> 以使新的服务单元文件生效：</li></ol><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> systemctl daemon-reload</span><br></pre></td></tr></table></figure></div><ol start="6"><li>启用服务，使其在启动时自动运行：</li></ol><div class="code-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> systemctl <span class="built_in">enable</span> rclone-mount.service</span><br></pre></td></tr></table></figure></div><ol start="7"><li>现在，你可以使用以下命令来启动、停止或查看服务的状态：</li></ol><ul><li>启动服务：<code>sudo systemctl start rclone-mount.service</code></li><li>停止服务：<code>sudo systemctl stop rclone-mount.service</code></li><li>查看服务状态：<code>sudo systemctl status rclone-mount.service</code></li></ul><p>这样，每次你的 VPS 启动时，<code>rclone</code> 挂载就会自动进行，而不需要手动重新执行挂载命令。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;Rclone简介&quot;&gt;&lt;a href=&quot;#Rclone简介&quot; class=&quot;headerlink&quot; title=&quot;Rclone简介&quot;&gt;&lt;/a&gt;Rclone简介&lt;/h2&gt;&lt;p&gt;Rclone &lt;em&gt;(“rsync for cloud storage”)&lt;/em&gt; is</summary>
      
    
    
    
    
    <category term="onedrive" scheme="http://example.com/tags/onedrive/"/>
    
    <category term="linux" scheme="http://example.com/tags/linux/"/>
    
  </entry>
  
  <entry>
    <title>Java Methods</title>
    <link href="http://example.com/2024/02/15/Java-Methods/"/>
    <id>http://example.com/2024/02/15/Java-Methods/</id>
    <published>2024-02-14T17:01:22.000Z</published>
    <updated>2025-07-31T03:40:54.134Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Use-of-Java-Methods"><a href="#Use-of-Java-Methods" class="headerlink" title="Use of Java Methods"></a>Use of Java Methods</h1><p>when we are coding Java program, we usually need deal with thousands of line code. To maintain code clarity and manageability. Java provide a concept — Method.</p><h2 id="Nature-of-Methods"><a href="#Nature-of-Methods" class="headerlink" title="Nature of Methods"></a>Nature of Methods</h2><p>Java methods are a collection of statements that encapsulate code to perform a specific task. They have the following properties:</p><p>- <strong>Problem-solving tools</strong>: Methods are ordered combinations to solve a particular set of problems.</p><p>- <strong>Encapsulation</strong>: Methods are usually contained within classes or objects.</p><p>- <strong>Callable</strong>: Methods are defined in the program and can be called from other methods.</p><p>- <strong>Atomicity</strong>: Each method focuses on completing a specific task, maintaining a single functionality.</p><h2 id="Main-Method-and-Other-Methods"><a href="#Main-Method-and-Other-Methods" class="headerlink" title="Main Method and Other Methods"></a>Main Method and Other Methods</h2><p>- <strong>Main Method</strong>: It’s the entry point of the program, called by the Java runtime environment.</p><p>- <strong>Other Methods</strong>: Defined by developers to implement specific functionalities.</p><h2 id="Declaration-of-Methods"><a href="#Declaration-of-Methods" class="headerlink" title="Declaration of Methods"></a>Declaration of Methods</h2><p>Java method declaration follows a specific format, including modifiers, return type, method name, and parameter list:</p><div class="code-container" data-rel="Java"><figure class="iseeu highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">modifier returnType <span class="title function_">methodName</span><span class="params">(parameterType parameterName)</span> &#123;</span><br><span class="line">    <span class="comment">// Method body</span></span><br><span class="line">    <span class="keyword">return</span> returnValue;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><h3 id="Example-Compare-Two-Numbers-to-Find-the-Maximum"><a href="#Example-Compare-Two-Numbers-to-Find-the-Maximum" class="headerlink" title="Example: Compare Two Numbers to Find the Maximum"></a>Example: Compare Two Numbers to Find the Maximum</h3><div class="code-container" data-rel="Java"><figure class="iseeu highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">max</span><span class="params">(<span class="type">int</span> num1, <span class="type">int</span> num2)</span> &#123;</span><br><span class="line">    <span class="type">int</span> result;</span><br><span class="line">    <span class="keyword">if</span> (num1 &gt; num2) &#123;</span><br><span class="line">        result = num1;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        result = num2;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><h2 id="Method-Invocation"><a href="#Method-Invocation" class="headerlink" title="Method Invocation"></a>Method Invocation</h2><p><strong>Static Methods</strong>: Called directly using the class name.</p><p><strong>Instance Methods</strong>: Require the creation of a class instance before calling.</p><h3 id="Static-Method-Invocation-Example"><a href="#Static-Method-Invocation-Example" class="headerlink" title="Static Method Invocation Example"></a>Static Method Invocation Example</h3><div class="code-container" data-rel="Java"><figure class="iseeu highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">maxNumber</span> <span class="operator">=</span> max(<span class="number">20</span>, <span class="number">30</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;最大值为: &quot;</span> + maxNumber);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><h3 id="Instance-Method-Invocation-Example"><a href="#Instance-Method-Invocation-Example" class="headerlink" title="Instance Method Invocation Example"></a>Instance Method Invocation Example</h3><div class="code-container" data-rel="Java"><figure class="iseeu highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">Demo</span> <span class="variable">demo</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Demo</span>();</span><br><span class="line">        <span class="type">int</span> <span class="variable">maxNumber</span> <span class="operator">=</span> demo.max(<span class="number">20</span>, <span class="number">30</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;最大值为: &quot;</span> + maxNumber);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><h2 id="Method-Overloading"><a href="#Method-Overloading" class="headerlink" title="Method Overloading"></a>Method Overloading</h2><p>Method overloading allows defining multiple methods with the same name but different parameter lists within the same class.</p><h3 id="Conditions-for-Method-Overloading"><a href="#Conditions-for-Method-Overloading" class="headerlink" title="Conditions for Method Overloading"></a>Conditions for Method Overloading</h3><p>- The method name must be the same.</p><p>- The parameter list must be different (in number, type, or order of parameters).</p><p>- The return type can be the same or different; it does not affect overloading.</p><h2 id="Variable-Parameters"><a href="#Variable-Parameters" class="headerlink" title="Variable Parameters"></a>Variable Parameters</h2><p>Java supports variable parameters, allowing a variable number of parameters to be passed in when a method is called. In this example, numbers is actually an int[] array. You can call the sum method just as you would any normal method, passing in any number of integers.</p><div class="code-container" data-rel="Java"><figure class="iseeu highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">sum</span><span class="params">(<span class="type">int</span>... numbers)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">total</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> num : numbers) &#123;</span><br><span class="line">        total += num;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> total;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><h2 id="Value-Passing-and-Reference-Passing"><a href="#Value-Passing-and-Reference-Passing" class="headerlink" title="Value Passing and Reference Passing"></a>Value Passing and Reference Passing</h2><p>- Value passing: the method receives a copy of the parameter values and the original data is not changed.<br>- Reference passing: the method receives a reference to the object and may modify the original object.</p><h3 id="Value-Passing-Example"><a href="#Value-Passing-Example" class="headerlink" title="Value Passing Example"></a>Value Passing Example</h3><div class="code-container" data-rel="Java"><figure class="iseeu highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo2</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">a</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line">        System.out.println(<span class="string">&quot;调用前: &quot;</span> + a);</span><br><span class="line">        change(a);</span><br><span class="line">        System.out.println(<span class="string">&quot;调用后: &quot;</span> + a);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">change</span><span class="params">(<span class="type">int</span> a)</span> &#123;</span><br><span class="line">        a = <span class="number">10</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><h3 id="Reference-Passing-Example"><a href="#Reference-Passing-Example" class="headerlink" title="Reference Passing Example"></a>Reference Passing Example</h3><p>Objects in Java are stored in heap memory. When you create an object, such as Person person &#x3D; new Person();, the person variable actually stores a reference to the Person object in heap memory, not the object itself.</p><p>When you pass the person object as a parameter to a method, a copy of this reference is passed, not a copy of the object itself. Therefore, the method receives another reference to the same object.</p><div class="code-container" data-rel="Java"><figure class="iseeu highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Person</span> &#123;</span><br><span class="line">    String name;</span><br><span class="line"></span><br><span class="line">    Person(String name) &#123;</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Test</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">Person</span> <span class="variable">person</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Person</span>(<span class="string">&quot;John&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;Before: &quot;</span> + person.name); <span class="comment">// 输出 John</span></span><br><span class="line">        changeName(person);</span><br><span class="line">        System.out.println(<span class="string">&quot;After: &quot;</span> + person.name); <span class="comment">// 输出 Mike</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">changeName</span><span class="params">(Person p)</span> &#123;</span><br><span class="line">        p.name = <span class="string">&quot;Mike&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><p>Notice<br>Invariance of references: while you can modify the state of an object (i.e., its properties) by reference, you cannot change the object to which the reference itself points. For example, if you try to point a reference to a new object in a method, this will not affect the original reference.</p><p>Special case of primitive data types: Primitive data types in Java (e.g. int, double, etc.) use value passing, not reference passing. This means that methods receive copies of the original values, and any modifications to those values will not affect the original data.</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h1 id=&quot;Use-of-Java-Methods&quot;&gt;&lt;a href=&quot;#Use-of-Java-Methods&quot; class=&quot;headerlink&quot; title=&quot;Use of Java Methods&quot;&gt;&lt;/a&gt;Use of Java Methods&lt;/h1&gt;&lt;p&gt;wh</summary>
      
    
    
    
    <category term="Java Web" scheme="http://example.com/categories/Java-Web/"/>
    
    
    <category term="Java" scheme="http://example.com/tags/Java/"/>
    
  </entry>
  
</feed>
