<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>aHangssBlog</title>
        <link>https://www.ahang.asia/</link>
        <description>🏠HOME</description>
        <lastBuildDate>Tue, 22 Jul 2025 12:08:49 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en-US</language>
        <copyright>All rights reserved 2025, aHang</copyright>
        <item>
            <title><![CDATA[git checkout妙用]]></title>
            <link>https://www.ahang.asia/article/git-checkout</link>
            <guid>https://www.ahang.asia/article/git-checkout</guid>
            <pubDate>Thu, 08 May 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[git checkout检出文件]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-1ed8033396ca80ba8df3ecd76222f07e"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1ed8033396ca80fbb462f92a4ed22ff4" data-id="1ed8033396ca80fbb462f92a4ed22ff4"><span><div id="1ed8033396ca80fbb462f92a4ed22ff4" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca80fbb462f92a4ed22ff4" title="概述"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">概述</span></span></h2><div class="notion-text notion-block-1ed8033396ca80e48691d1077adf8cac">git checkout的作用通常有两个：</div><ul class="notion-list notion-list-disc notion-block-1ed8033396ca80fe8a27d62ce38afaf5"><li>切换到指定分支或者commit</li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca8005a878d9f6ddad1ff9"><li>从指定分支或commit中恢复文件到当前工作目录</li></ul><div class="notion-text notion-block-1ed8033396ca8048b381d0a61e845201">第一个作用大家都很清楚，通过 <code class="notion-inline-code">git checkout &amp;lt;branch&amp;gt;|&amp;lt;commit id&amp;gt;</code> 很轻松能做到。本文重点记录第二个作用，也就是检出文件的作用。</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1ed8033396ca8020a94ee1c9a7ab2904" data-id="1ed8033396ca8020a94ee1c9a7ab2904"><span><div id="1ed8033396ca8020a94ee1c9a7ab2904" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca8020a94ee1c9a7ab2904" title="背景"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">背景</span></span></h2><div class="notion-text notion-block-1ed8033396ca808cb409f5e6ee60ab45">我学习项目的时候通常喜欢在README.md中记录笔记。然后现在我希望将当前项目的版本回退到前一个版本，但是同时我又希望只保留当前版本的 README.md中的新增笔记内容。</div><div class="notion-text notion-block-1ed8033396ca80429fabc6802e04895a">如果直接使用 <code class="notion-inline-code">git revert &amp;lt;commit id&amp;gt;</code> 那么会直接恢复到前一个版本，但是随之而来的，我的新增的笔记内容会消失。使用 <code class="notion-inline-code">git reset</code> 也是一样。下方git树图示将展示我希望git树的变化:</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1ed8033396ca80acaafce0a7a4282a11" data-id="1ed8033396ca80acaafce0a7a4282a11"><span><div id="1ed8033396ca80acaafce0a7a4282a11" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca80acaafce0a7a4282a11" title="解决方案"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">解决方案</span></span></h2><div class="notion-text notion-block-1ed8033396ca80c5bbd0df7e2988b57b">通过 <code class="notion-inline-code">git checkout</code> 检出文件的功能，可以实现此目的。</div><div class="notion-text notion-block-1ed8033396ca80cb8815c06b42932c3d">新建一个临时分支，这个分支保留了主分支最新的提交。然后在主分支执行回滚操作，回滚到指定的版本。然后通过<code class="notion-inline-code">git checkout</code> 的文件检出功能，将之前的临时分支中最新的文件内容检出到当前分支。就实现了 &quot;版本回退但是保留特定文件最新状态&quot; 的目的。</div><div class="notion-text notion-block-1ed8033396ca80f989d5fa1dca4b8efd">具体流程如下：</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-1ed8033396ca80a39fced75e53530ac7" data-id="1ed8033396ca80a39fced75e53530ac7"><span><div id="1ed8033396ca80a39fced75e53530ac7" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca80a39fced75e53530ac7" title="流程"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">流程</span></span></h3><ol start="1" class="notion-list notion-list-numbered notion-block-1ed8033396ca805198e0e0273d5718f2"><li>新建一个临时分支，该分支会保存当前分支的所有最新提交</li></ol><div class="notion-text notion-block-1ed8033396ca80a58910c3423a39b1f5">此时，我们的git树会像这样：</div><ol start="1" class="notion-list notion-list-numbered notion-block-1ed8033396ca80c9bc9df79c86857955"><li>此时temp-branch相当于main分支的备份。我们现在可以放心的回滚main分支的提交了。回滚提交到指定的版本：</li></ol><div class="notion-text notion-block-1ed8033396ca806e924eeccc40178127">此时，我们的git树会像这样：</div><ol start="1" class="notion-list notion-list-numbered notion-block-1ed8033396ca809ba721c119f49882cd"><li>此时README文件中的内容也一同回滚到b的版本了，但是别担心，我们可以使用<code class="notion-inline-code">git checkout</code> 指令来恢复文件。</li></ol><div class="notion-text notion-block-1ed8033396ca802fb68dc9c6bf91c15f">还记得<code class="notion-inline-code">temp-branch</code>分支吗？他是main分支最新提交的备份，该分支中保存了a提交中的最新内容。因此，我们使用<code class="notion-inline-code">git checkout</code> 来从<code class="notion-inline-code">temp-branch</code>分支中恢复内容：</div><div class="notion-text notion-block-1ed8033396ca806aa208cdee968871a4">假如现在我改变了想法，<a target="_blank" rel="noopener noreferrer" class="notion-link" href="http://xn--README-2e8io5gzw2dxlco54fe44e.md">我除了想保留README.md</a>，还想保留之前版本的所有yml文件，那么可以这样写：</div><div class="notion-text notion-block-1ed8033396ca8002a7d1d76693104dcb">完成这一切后，删除这个临时分支（它已经发挥了它的作用）。我们的目的达到了。</div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[开发随记]]></title>
            <link>https://www.ahang.asia/article/开发随记</link>
            <guid>https://www.ahang.asia/article/开发随记</guid>
            <pubDate>Sat, 20 Jan 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[开发过程中对问题的记录]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-1ed8033396ca81368b9cf9f05c56c3ac"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1ed8033396ca8055b449dc63b70608aa" data-id="1ed8033396ca8055b449dc63b70608aa"><span><div id="1ed8033396ca8055b449dc63b70608aa" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca8055b449dc63b70608aa" title="集合去重"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">集合去重</span></span></h2><div class="notion-text notion-block-1ed8033396ca800b904bf9d6df94f487">如果是单纯对一个对象整体，或者是其他包装类型的集合进行去重，可以使用HashSet或TreeSet。使用set去重需要注意，HashSet去重是通过对象的equals方法来进行区分的，所以一般需要重写对象的equals方法。但是也可以使用如下无侵入的方式:</div><ul class="notion-list notion-list-disc notion-block-1ed8033396ca807c9ecbf0384f5543e2"><li>HashSet根据某一字段去重</li><ul class="notion-list notion-list-disc notion-block-1ed8033396ca807c9ecbf0384f5543e2"></ul></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca80599c0bfa2e3d8ee992"><li>TreeSet</li><ul class="notion-list notion-list-disc notion-block-1ed8033396ca80599c0bfa2e3d8ee992"></ul></ul><div class="notion-text notion-block-1ed8033396ca80ccaebef48b13516caf">如果是想要根据对象中某个字段来进行去重，可以使用Stream中的toMap方法, 利用map的键不可重复的特性，曲线救国:</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1ed8033396ca80878c96f4910fbec3a8" data-id="1ed8033396ca80878c96f4910fbec3a8"><span><div id="1ed8033396ca80878c96f4910fbec3a8" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca80878c96f4910fbec3a8" title="Arrays.asList注意事项"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Arrays.asList注意事项</span></span></h2><div class="notion-text notion-block-1ed8033396ca80f28825c05b01d1be95">使用asList将元素转为list时，要注意，转换后的list没有add和remove方法。因此无法执行add和remove操作。否则会报错。可以通过如下操作解决：</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1ed8033396ca80dfa6fbeeb9ff152ed6" data-id="1ed8033396ca80dfa6fbeeb9ff152ed6"><span><div id="1ed8033396ca80dfa6fbeeb9ff152ed6" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca80dfa6fbeeb9ff152ed6" title="Transactional注意事项"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Transactional注意事项</span></span></h2><div class="notion-text notion-block-1ed8033396ca8016bab1fd578bfde2f8">在项目中遇到一个bug。</div><div class="notion-text notion-block-1ef8033396ca80c8ac01f4276e7c47cd">就是以下代码中，邮件发送的逻辑是异步执行的。但是整个 <code class="notion-inline-code">apply</code> 方法被 <code class="notion-inline-code">@Transactional</code> 注解标记，<b>这个方法中所有的数据库操作都会等到事务执行完毕后才真正提交到数据库</b>。因此就导致当代码执行到邮件发送的逻辑后，邮件发送逻辑中如果需要从数据库获取数据，会无法获取到，因为此时可能异步的任务执行到获取数据那一步的时候，业务代码中的事务并未提交，导致数据没有入库。</div><div class="notion-text notion-block-1ed8033396ca80d891bac00d0fb972c9">业务代码：</div><div class="notion-text notion-block-1ed8033396ca8049affef8eaaf303f24">producer：</div><div class="notion-text notion-block-1ed8033396ca804fafb8d45d9f0d550b">consumer：</div><div class="notion-text notion-block-1ed8033396ca809e8987f4242fbfd93a">解决方案：</div><div class="notion-text notion-block-1ed8033396ca801e91f5d080f0b81fee">在事务提交之后执行邮件发送逻辑</div><ul class="notion-list notion-list-disc notion-block-1ed8033396ca80fe902acebb0feed0b1"><li>可以考虑使用 <code class="notion-inline-code">TransactionSynchronizationManager.registerSynchronization</code> 实现 <code class="notion-inline-code">afterCommit</code> 方法，让邮件发送的逻辑在事务提交之后执行</li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca8006b0c7c83ad77532d1"><li>也可以手动提交事务。但是如果手动提交事务，那么 <code class="notion-inline-code">@Transactional</code> 注解会失效，就需要手动回滚事务。</li></ul><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1ed8033396ca804db967c481dca45f12" data-id="1ed8033396ca804db967c481dca45f12"><span><div id="1ed8033396ca804db967c481dca45f12" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca804db967c481dca45f12" title="BUG排查思路"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">BUG排查思路</span></span></h2><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-1ed8033396ca80d5bd65d4af7691aa6d" data-id="1ed8033396ca80d5bd65d4af7691aa6d"><span><div id="1ed8033396ca80d5bd65d4af7691aa6d" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca80d5bd65d4af7691aa6d" title="500异常"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">500异常</span></span></h3><ol start="1" class="notion-list notion-list-numbered notion-block-1ed8033396ca80fe9f4dc3d62b9289e0"><li>打开控制台，查看报错的接口是哪个</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1ed8033396ca8036bd04f901672b26e0"><li>观察发送的请求，是否有重复请求？考虑部分接口在重复调用的情况下是否会出现无法预料的异常？</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1ed8033396ca80b790a4dcdf6d536cc4"><li>观察payload和response，观察数据结构是否正确</li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-1ed8033396ca8053a865ee258e4be78e"><li>打开后端代码，进行调试，排错</li></ol><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1ed8033396ca8078beffc87240a60a97" data-id="1ed8033396ca8078beffc87240a60a97"><span><div id="1ed8033396ca8078beffc87240a60a97" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca8078beffc87240a60a97" title="日期转换"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">日期转换</span></span></h2><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-1ed8033396ca808aa16fce217ce19c3a" data-id="1ed8033396ca808aa16fce217ce19c3a"><span><div id="1ed8033396ca808aa16fce217ce19c3a" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca808aa16fce217ce19c3a" title="Java中的日期转换"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Java中的日期转换</span></span></h3><ol start="1" class="notion-list notion-list-numbered notion-block-1ed8033396ca80d6ac9fd1f01aacf5cb"><li><code class="notion-inline-code"><b>Date</b></code> 转 <code class="notion-inline-code"><b>LocalDateTime</b></code></li><ol class="notion-list notion-list-numbered notion-block-1ed8033396ca80d6ac9fd1f01aacf5cb"><div class="notion-text notion-block-1ed8033396ca801abe0feb65358e15e8">首先将date转为<code class="notion-inline-code"><b>Instant</b></code>，再指定时区</div></ol></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1ed8033396ca8005b64dcab243c42526"><li><code class="notion-inline-code"><b>LocalDateTime</b></code> 转 <code class="notion-inline-code"><b>Date</b></code></li><ol class="notion-list notion-list-numbered notion-block-1ed8033396ca8005b64dcab243c42526"><div class="notion-text notion-block-1ed8033396ca8026828ed16ec7ad5c32"><code class="notion-inline-code"><b>LocalDateTime</b></code> 先指定时区，转为 <code class="notion-inline-code"><b>Instant</b></code>, 再通过from转为date</div></ol></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1ed8033396ca80da9f00e6becdd81703"><li><code class="notion-inline-code"><b>LocalDate</b></code><b> to </b><code class="notion-inline-code"><b>Date</b></code></li><ol class="notion-list notion-list-numbered notion-block-1ed8033396ca80da9f00e6becdd81703"><div class="notion-text notion-block-1ed8033396ca8001a208cb20ac96fa41"><code class="notion-inline-code">LocalDate</code> 只包含日期信息，没有时间信息。 这里我们使用 <code class="notion-inline-code">atStartOfDay()</code> 方法将它设置为当天开始的时间 (00:00:00)，然后按照与 <code class="notion-inline-code">LocalDateTime</code> 相同的方式转换为 <code class="notion-inline-code">Date</code>。 同样，这里也使用了系统默认时区。</div></ol></ol><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1ed8033396ca8060ae6dcace96019120" data-id="1ed8033396ca8060ae6dcace96019120"><span><div id="1ed8033396ca8060ae6dcace96019120" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca8060ae6dcace96019120" title="LocalDateTime序列化问题"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">LocalDateTime序列化问题</span></span></h2><div class="notion-text notion-block-1ed8033396ca805baa28eba1c7716acf">LocalDateTime类型数据在序列化为JSON数据时，会被默认转化为时间戳的格式。想要让其以正常格式输出，可以使用 <code class="notion-inline-code">@JsonFormat</code> 注解。</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1ed8033396ca8005931ce98f9980026c" data-id="1ed8033396ca8005931ce98f9980026c"><span><div id="1ed8033396ca8005931ce98f9980026c" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca8005931ce98f9980026c" title="序列化问题"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">序列化问题</span></span></h2><div class="notion-text notion-block-1ed8033396ca80e5bc4fe01a58e97146">定时任务的持久化异常。因为实体类发生过变动，在序列化时，由于没有手动指定序列化id，而实体类发生变动（字段修改）后，类hash值发生改变，序列化id也发生了变化。所以quartz无法根据之前旧的序列化id找到对应的实体类。</div><div class="notion-text notion-block-1ed8033396ca80ce8428e5f131c1759b">解决方案：为实体类手动定义序列化id</div><blockquote class="notion-quote notion-gray notion-block-1ed8033396ca8007b989c3bf87f85eca"><div>[!NOTE]</div><div class="notion-text notion-gray notion-block-1ed8033396ca80f1836cdf5bffdb0a4f">建议所有可序列化的类都显式声明serialVersionUID的值，因为默认的serialVersionUID计算对类的详细信息非常敏感，这些详细信息可能因编译器实现而异。因此在反序列化期间可能导致意外的错误(InvalidClassException)。因此为了保证不同java编译器反序列结果一致，可序列化的类强烈建议显示声明serialVersionUID的值，并建议使用private修饰符，因为此声明仅适用于立即声明的类，而不适用于其子类。</div></blockquote><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1ed8033396ca80a8ac6ff747d23e6990" data-id="1ed8033396ca80a8ac6ff747d23e6990"><span><div id="1ed8033396ca80a8ac6ff747d23e6990" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca80a8ac6ff747d23e6990" title="数据库字段插入列表数据"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">数据库字段插入列表数据</span></span></h2><div class="notion-text notion-block-1ed8033396ca802ebfefcc753e451932">数据库中字段是 <code class="notion-inline-code">varchar</code> ，而希望存储一个形如 <code class="notion-inline-code">[1,2323,545]</code> 的列表数据。 实体类应该直接为 <code class="notion-inline-code">List&lt;Integer&gt;</code> ，并用注解 <code class="notion-inline-code">@TableField(typeHandler = JacksonTypeHandler.class)</code> 标记。</div><div class="notion-text notion-block-1ed8033396ca80f7a8dfc1ace3af044c">然后在@TableName注解上开启autoResultMap属性即可。</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1ed8033396ca8056bffdc9a0770c3605" data-id="1ed8033396ca8056bffdc9a0770c3605"><span><div id="1ed8033396ca8056bffdc9a0770c3605" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca8056bffdc9a0770c3605" title="计算时间差"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">计算时间差</span></span></h2><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1ed8033396ca80679ab3d67a700c176f" data-id="1ed8033396ca80679ab3d67a700c176f"><span><div id="1ed8033396ca80679ab3d67a700c176f" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca80679ab3d67a700c176f" title="QUARTZ"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">QUARTZ</span></span></h2><div class="notion-text notion-block-1ed8033396ca80bea7d7fbbc73924d73">三个重要角色：</div><ul class="notion-list notion-list-disc notion-block-1ef8033396ca80d2a50bc2d35a178b8a"><li>job 具体的任务</li></ul><ul class="notion-list notion-list-disc notion-block-1ef8033396ca802abf34d001b2077dad"><li> trigger 触发器，决定了job将在何时触发</li></ul><ul class="notion-list notion-list-disc notion-block-1ef8033396ca807b98e1d29b2430d28a"><li>scheduler 调度器，是job和trigger的管理者和调度者，可以获取触发器状态，调用触发器，取消触发器。定时任务的一切操作都是使用 <code class="notion-inline-code">scheduler</code> 完成的</li></ul><div class="notion-text notion-block-1ed8033396ca8034b91fd8f1359e86e3">其他的看源码即可，源码简洁易懂。</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1ed8033396ca80ba9fa9d8cfef313abe" data-id="1ed8033396ca80ba9fa9d8cfef313abe"><span><div id="1ed8033396ca80ba9fa9d8cfef313abe" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca80ba9fa9d8cfef313abe" title="SSE"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">SSE</span></span></h2><div class="notion-text notion-block-1ed8033396ca80bd861bc85b6b4208b3">SSE的核心是 <code class="notion-inline-code">SseEmitter</code> ，用以向客户端发送消息。</div><div class="notion-text notion-block-1ed8033396ca8082a92ec278c1830702">当客户端和服务器建立连接之后，服务器会以 <code class="notion-inline-code">text/event-stream</code> 的MIME格式返回一个 <code class="notion-inline-code">SseEmitter</code> 给客户端，之后，客户端和服务器就会一直维持这个连接，直到时间达到 <code class="notion-inline-code">SseEmitter</code> 设定的过期时间。</div><div class="notion-text notion-block-1ed8033396ca80d3a12df815cd101edb">SSE实践：</div><ol start="1" class="notion-list notion-list-numbered notion-block-1ed8033396ca80318521fc9dd47ffa39"><li>客户端通过调用一次服务器的接口来建立SSE的连接。</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1ed8033396ca80e996b8d6348819c4b4"><li>服务器向客户端推送数据。</li></ol><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-1ed8033396ca8032ae8ee592ed64d4c4" data-id="1ed8033396ca8032ae8ee592ed64d4c4"><span><div id="1ed8033396ca8032ae8ee592ed64d4c4" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca8032ae8ee592ed64d4c4" title="中文乱码"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">中文乱码</span></span></h3><div class="notion-text notion-block-1ed8033396ca806089b5d57d9566f993"><code class="notion-inline-code">SseEmitter</code> 在SpringMVC中默认是ISO编码格式，此时若客户端采用的编码格式是UTF-8，则会导致中文乱码。因此需要重写 <code class="notion-inline-code">SseEmitter</code> 的 <code class="notion-inline-code">extendResponse</code> 方法，手动设置其编码格式。</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1ed8033396ca802e9896fa4fc574b715" data-id="1ed8033396ca802e9896fa4fc574b715"><span><div id="1ed8033396ca802e9896fa4fc574b715" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca802e9896fa4fc574b715" title="并发编程"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">并发编程</span></span></h2><div class="notion-text notion-block-1ed8033396ca80fd8958e5f10e23ef45">使用 <code class="notion-inline-code">ThreadPoolExecutor</code> 来显式创建线程池。因为这样可以手动指定核心线程数，最大连接数，拒绝策略等。不要使用 <code class="notion-inline-code">Executors</code> 工具类的静态方法例如 <code class="notion-inline-code">newFixedThreadPool</code> 来创建线程池。虽然这样更加方便，但是这样创建出来的线程池不利于维护和拓展。<code class="notion-inline-code">newFixedThreadPool</code> 线程池是一个固定大小的线程池，并且使用的是<b>无界阻塞队列</b>，容易造成内存溢出。以下是 <code class="notion-inline-code">newFixedThreadPool</code> 源码：</div><div class="notion-text notion-block-1ed8033396ca8035ad8dc73d06c7d3ed">应当使用 <code class="notion-inline-code">ThreadPoolExecutor</code> 来显式创建线程池：</div><div class="notion-text notion-block-1ed8033396ca8038a64fc072b388b222">线程池使用：</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-1ed8033396ca80788b7ac690c26999c2" data-id="1ed8033396ca80788b7ac690c26999c2"><span><div id="1ed8033396ca80788b7ac690c26999c2" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca80788b7ac690c26999c2" title="线程池拒绝策略"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">线程池拒绝策略</span></span></h3><ul class="notion-list notion-list-disc notion-block-1ed8033396ca80359841d345332b7f9b"><li>DiscardOldestPolicy：丢弃最老的任务；</li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca8090b948d7374823381e"><li>CallerRunsPolicy：同步调用, 遇到新任务会将任务返回给调用者线程，让其执行；</li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca805baf16cd5fc269d4b5"><li>AbortPolicy：丢弃新任务并抛出异常；</li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca8096aad3e15b99b3652c"><li>DiscardPolicy：丢弃新任务；</li></ul><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-1ed8033396ca80bfa917c57517f854bf" data-id="1ed8033396ca80bfa917c57517f854bf"><span><div id="1ed8033396ca80bfa917c57517f854bf" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca80bfa917c57517f854bf" title="异步编排"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">异步编排</span></span></h3><div class="notion-text notion-block-1ed8033396ca804a8a63dc9cdb0b74bb"><a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://www.cnblogs.com/kevin-ying/p/14383380.html">参考文章👈</a></div><div class="notion-text notion-block-1ed8033396ca80debfa0d9cfd2b28e49">使用：</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1ed8033396ca8056baacd2174e31c43c" data-id="1ed8033396ca8056baacd2174e31c43c"><span><div id="1ed8033396ca8056baacd2174e31c43c" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca8056baacd2174e31c43c" title="多线程环境中的上下文传递问题"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">多线程环境中的上下文传递问题</span></span></h2><div class="notion-text notion-block-1ed8033396ca800ab189d41682b2213c">今天线上出了一个问题，同事反映说会议预约时发送的邮件中显示的预约人信息不对，我一看，明明会议是A预约的，邮件中却显示是B。</div><div class="notion-text notion-block-1ee8033396ca8085910be85a82c8d76f">这个情况让我非常好奇，后来经过思考和排查，大致将问题锁定在并发安全的问题上。</div><div class="notion-text notion-block-1ee8033396ca8013933ffdbed8cfa444">因为预约会议的业务执行过程中，发送邮件是交给线程池来执行的，在处理邮件发送的逻辑中有通过用户上下文获取当前登录用户信息的操作，我初步判断，可能是由于主线程和线程池中执行邮件发送线程之间的上下文没有按预期传递导致的问题。</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-1ed8033396ca8057b362f7bf86ab4912" data-id="1ed8033396ca8057b362f7bf86ab4912"><span><div id="1ed8033396ca8057b362f7bf86ab4912" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca8057b362f7bf86ab4912" title="解决方案"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">解决方案</span></span></h3><ol start="1" class="notion-list notion-list-numbered notion-block-1ed8033396ca80f98ea8f72c33bfb555"><li>直接传递用户id</li><ol class="notion-list notion-list-numbered notion-block-1ed8033396ca80f98ea8f72c33bfb555"><div class="notion-text notion-block-1ed8033396ca8002ac91cc2980ca25fc">在调用发送邮件方法时将主线程中的登录用户信息作为参数进行传递。</div></ol></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1ed8033396ca8071a1c4ec3bc8d9c163"><li>使用 <code class="notion-inline-code">transmittable-thread-local</code> 作为存储用户上下文的容器，配合<code class="notion-inline-code">TtlExcutors</code> 或 <code class="notion-inline-code">TtlRunnable</code> 实现上下文在多线程环境中的传递。</li><ol class="notion-list notion-list-numbered notion-block-1ed8033396ca8071a1c4ec3bc8d9c163"></ol></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1ed8033396ca80989667c1ae0416bd65"><li>使用Spring Security提供的 <code class="notion-inline-code">DelegatingSecurityContextRunnable</code></li><ol class="notion-list notion-list-numbered notion-block-1ed8033396ca80989667c1ae0416bd65"><div class="notion-text notion-block-1ed8033396ca808ea99df6f92766f194">或者</div></ol></ol><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1ed8033396ca80eabd59e6b698608f69" data-id="1ed8033396ca80eabd59e6b698608f69"><span><div id="1ed8033396ca80eabd59e6b698608f69" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca80eabd59e6b698608f69" title="SRPING循环依赖"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">SRPING循环依赖</span></span></h2><div class="notion-text notion-block-1ed8033396ca80618131cc2cb859d9de">spring通过三级缓存来解决循环依赖问题。</div><div class="notion-text notion-block-1ed8033396ca8065a682e4d1b12082a6">一级缓存： singletonObjects 单例池，存放完全初始化好的bean</div><div class="notion-text notion-block-1ed8033396ca809aa6d8e829e7e76be0">二级缓存：earlySingletonObjects 早期bean，存放实例化，但是并未进行依赖注入和初始化的早期bean</div><div class="notion-text notion-block-1ed8033396ca8003b1c0d244343741ee">三级缓存：singletonFactories 存放bean工厂，用于创建早期引用</div><div class="notion-text notion-block-1ed8033396ca80cc805adab3d1406300">假设A和B相互依赖，以下是A和B的创建流程：</div><ol start="1" class="notion-list notion-list-numbered notion-block-1ed8033396ca80208b1fe13052034e8b"><li>创建A实例</li><ol class="notion-list notion-list-numbered notion-block-1ed8033396ca80208b1fe13052034e8b"><ul class="notion-list notion-list-disc notion-block-1ed8033396ca80fbad16f05a870ef0f4"><li>检查一级、二级、三级缓存，都没有A</li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca8030bd90f8fc06908304"><li>实例化A</li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca80a1a6f8c07bf1c84072"><li>将A的 ObjectFactory 放入三级缓存</li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca80e6b224dcb773dd5875"><li>依赖注入，发现依赖B</li></ul></ol></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1ed8033396ca80a1a502c287ded2c435"><li>创建B实例</li><ol class="notion-list notion-list-numbered notion-block-1ed8033396ca80a1a502c287ded2c435"><ul class="notion-list notion-list-disc notion-block-1ed8033396ca8097b55fcc824d1fef65"><li>检查一级、二级、三级缓存，都没有B</li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca804e9525d1203c8e0bfc"><li>实例化B</li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca80608277f6618c86cd4f"><li>将B的 ObjectFactory 放入三级缓存</li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca80f68c5ed3f5f2e81781"><li>依赖注入，发现依赖A</li></ul></ol></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1ed8033396ca80d89823c7f849d53f2d"><li>解决循环依赖</li><ol class="notion-list notion-list-numbered notion-block-1ed8033396ca80d89823c7f849d53f2d"><ul class="notion-list notion-list-disc notion-block-1ed8033396ca80a0a8b0fe5209543d3c"><li>检查一级缓存，没有A</li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca8070b9c1ead485990320"><li>检查二级缓存，没有A</li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca80cda446d36050ee6d8e"><li>检查三级缓存，发现有A，获取A的早期引用</li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca806e9304f37f2889d95e"><li>将A的早期引用放入二级缓存，并将三级缓存中A的早期引用移除</li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca80909d8ce0d7fe2fc9b3"><li>B完成依赖注入</li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca803a996bf922d4b2f3fe"><li>B初始化完成，放入一级缓存，清除二级、三级缓存中的B</li></ul></ol></ol><ol start="4" class="notion-list notion-list-numbered notion-block-1ed8033396ca806da144d26b9d1b438d"><li>完成 A 的创建：</li><ol class="notion-list notion-list-numbered notion-block-1ed8033396ca806da144d26b9d1b438d"><ul class="notion-list notion-list-disc notion-block-1ed8033396ca80d199f3e93ecace8185"><li>A 得到了完全初始化的 B</li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca802dbfb7d712efd5e781"><li>A 完成属性注入和初始化</li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca801c9d30f3fc4032d2be"><li>A 放入一级缓存 ，清除二、三级缓存中的 A</li></ul></ol></ol><ol start="5" class="notion-list notion-list-numbered notion-block-1ed8033396ca805c953cd2d32a3220af"><li>最终状态：</li><ol class="notion-list notion-list-numbered notion-block-1ed8033396ca805c953cd2d32a3220af"><ul class="notion-list notion-list-disc notion-block-1ed8033396ca80a2bcc8cf9371af489b"><li>一级缓存中有完全初始化好的 A 和 B</li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca80abb390d8dd5639238b"><li>二级缓存和三级缓存为空</li></ul></ol></ol><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-1ed8033396ca80f0b97bd550cd3f3c29" data-id="1ed8033396ca80f0b97bd550cd3f3c29"><span><div id="1ed8033396ca80f0b97bd550cd3f3c29" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca80f0b97bd550cd3f3c29" title="为什么需要三级缓存？"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">为什么需要三级缓存？</span></span></h3><div class="notion-text notion-block-1ed8033396ca80378e84ed934efff1a8">先说结论，三级缓存的作用是为了优化性能。如果没有三级缓存，spring在实例化A的时候，并不知道这个A需不需要使用aop（aop的执行时机是在bean初始化完成之后），那这种情况下，sping是创建A的原始对象放入二级缓存呢？还是创建A的代理对象放入缓存呢？</div><ul class="notion-list notion-list-disc notion-block-1ed8033396ca804db0b6c1bce66dc747"><li>如果创建原始对象放入缓存，后面发生循环依赖时，B注入的就是A原始对象，但是当A初始化完成后，开始执行aop逻辑时，又需要创建代理对象并放入一级缓存。这就导致了B中注入的是原始对象，其他地方获取A的时候，获取的是代理对象。这有可能会导致aop失效。</li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca80c4a34de3cf76c26453"><li>如果一开始就为所有的bean创建代理对象并放入二级缓存，会造成资源的浪费。</li></ul><div class="notion-text notion-block-1ed8033396ca80b3a186f77aa92f4db6">三级缓存的设计，就是为了延迟创建bean的早期引用，<b>在需要时</b>才决定返回代理对象还是原始对象：</div><ol start="1" class="notion-list notion-list-numbered notion-block-1ed8033396ca800cb8c8f889430b37d8"><li>创建Bean A：</li><ol class="notion-list notion-list-numbered notion-block-1ed8033396ca800cb8c8f889430b37d8"><ul class="notion-list notion-list-disc notion-block-1ed8033396ca80c2a5cae3f12a7b899e"><li>实例化A，得到A的原始对象</li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca80f3b911cbe83ab995d1"><li>将A的ObjectFactory放入三级缓存：<code class="notion-inline-code">singletonFactories.put(&quot;A&quot;, () -&gt; getEarlyBeanReference(A))</code></li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca8091aedbfe6df068c754"><li>开始填充A的属性，发现依赖B</li></ul></ol></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1ed8033396ca8066a36de42769625c09"><li>创建Bean B：</li><ol class="notion-list notion-list-numbered notion-block-1ed8033396ca8066a36de42769625c09"><ul class="notion-list notion-list-disc notion-block-1ed8033396ca806882a5dd479211c3ec"><li>实例化B，得到B的原始对象</li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca80d3916dfc8aef33e26a"><li>将B的ObjectFactory放入三级缓存</li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca80d28258fad6c0fa0b02"><li>开始填充B的属性，发现依赖A</li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca809bb5dde8c9509448fc"><li>检查一、二级缓存没有A</li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca80ab9b99cc89aabf7bd3"><li>从三级缓存中获取A的工厂，调用<code class="notion-inline-code">getEarlyBeanReference</code>，该方法可以判断A是否需要代理，如果A需要代理，此时就会创建代理对象 <b>（循环依赖的特殊场景，</b><em><b>提前</b></em><b>创建代理对象）</b></li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca8065b27ee7d246523c0b"><li>将得到的A（可能是代理对象）放入二级缓存，并从三级缓存中移除</li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca8088bad2d8c7ed00b7ea"><li>B完成初始化，放入一级缓存</li></ul></ol></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1ed8033396ca80f1bc08f5b1c0694f64"><li>完成A的创建：</li><ol class="notion-list notion-list-numbered notion-block-1ed8033396ca80f1bc08f5b1c0694f64"><ul class="notion-list notion-list-disc notion-block-1ed8033396ca80f989a6f140e4207d95"><li>A注入了完整的B</li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca80d699eec9afb3a8c93a"><li>A完成初始化</li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca80698d7fda2f1935f82f"><li>检查A是否已经被提前代理过（通过标记），如果是，则不再创建新的代理</li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca80ae8d47c9ed3a406ee9"><li>A放入一级缓存</li></ul></ol></ol><ol start="4" class="notion-list notion-list-numbered notion-block-1ed8033396ca807fb697c8d014629c6d"><li>最终结果：</li><ol class="notion-list notion-list-numbered notion-block-1ed8033396ca807fb697c8d014629c6d"><ul class="notion-list notion-list-disc notion-block-1ed8033396ca8025a6f2e5af870d45b4"><li>B中注入的是A的早期引用（可能是代理对象）</li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca80b4970ee30747882448"><li>其他地方获取A时，也是获取相同的对象</li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca801caac0fb78e2473027"><li>保证了所有引用A的地方获取的都是同一个对象（统一都是代理对象或都是原始对象）</li></ul></ol></ol><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-2148033396ca801f8544dd57c82c0f2d" data-id="2148033396ca801f8544dd57c82c0f2d"><span><div id="2148033396ca801f8544dd57c82c0f2d" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2148033396ca801f8544dd57c82c0f2d" title="SQL中’ “ `是不一样的"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">SQL中’ “ `是不一样的</span></span></h2><div class="notion-text notion-block-2148033396ca807ca705f50c8f814300">SQL 中单引号、双引号和反引号作用具体如下：</div><div class="notion-text notion-block-2148033396ca80ae97ddd26682c44393">单引号（&#x27;）：用于包裹字符串常量。例如：WHERE name = &#x27;张三&#x27;
双引号（&quot;）：用于标识符（如表名、列名），区分大小写，主要在 Oracle、PostgreSQL 等数据库中使用。例如：<code class="notion-inline-code">SELECT &quot;UserName&quot; FROM &quot;UserTable&quot;</code>
反引号（<code class="notion-inline-code">`</code>）：MySQL 专用，用于标识符（表名、列名），防止与关键字冲突。例如:</div><div class="notion-text notion-block-2148033396ca804a9914e63a9eb9114d"><code class="notion-inline-code">SELECT  user FROM `order`</code>
总结：
字符串用单引号
标识符用双引号（标准 SQL/Oracle/PostgreSQL）或反引号（MySQL）</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-2158033396ca8015bc19c8d9c2f003ef" data-id="2158033396ca8015bc19c8d9c2f003ef"><span><div id="2158033396ca8015bc19c8d9c2f003ef" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2158033396ca8015bc19c8d9c2f003ef" title="NGINX PROXY_PASS斜杠问题"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">NGINX PROXY_PASS斜杠问题</span></span></h2><div class="notion-text notion-block-2158033396ca8006803dc101c8beee97">nginx配置中，proxy_pass和root后面的url末尾是否带有斜杠（/）会影响URL的处理方式：</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-2158033396ca806ba4e0ffafa257390c" data-id="2158033396ca806ba4e0ffafa257390c"><span><div id="2158033396ca806ba4e0ffafa257390c" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2158033396ca806ba4e0ffafa257390c" title="proxy_pass 斜杠区别"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">proxy_pass 斜杠区别</span></span></h3><div class="notion-text notion-block-2158033396ca80b99eb8e73cbfee16e5">proxy_pass后的url加与不加斜杠的区别在于是否保留location中匹配的路径部分：</div><ul class="notion-list notion-list-disc notion-block-2158033396ca80389dbcd29d5945f5f9"><li>不带斜杠：会将location的匹配路径部分也传递给后端</li><ul class="notion-list notion-list-disc notion-block-2158033396ca80389dbcd29d5945f5f9"></ul></ul><ul class="notion-list notion-list-disc notion-block-2158033396ca80e4a9cbfe0290c41578"><li>带斜杠：会去除location匹配的路径</li><ul class="notion-list notion-list-disc notion-block-2158033396ca80e4a9cbfe0290c41578"></ul></ul><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-2158033396ca8049a33ec9b8bfcc73e7" data-id="2158033396ca8049a33ec9b8bfcc73e7"><span><div id="2158033396ca8049a33ec9b8bfcc73e7" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2158033396ca8049a33ec9b8bfcc73e7" title="location 斜杠区别"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">location 斜杠区别</span></span></h3><div class="notion-text notion-block-2158033396ca80499c3bd06354e84a3c">location匹配路径末尾是否带斜杠的区别在于对目录的处理方式：</div><ul class="notion-list notion-list-disc notion-block-2158033396ca80e5b586e30a50e3bf8d"><li>不带斜杠：严格匹配，必须完全符合路径</li><ul class="notion-list notion-list-disc notion-block-2158033396ca80e5b586e30a50e3bf8d"></ul></ul><ul class="notion-list notion-list-disc notion-block-2158033396ca802a98bcf19233d2be7c"><li>带斜杠：表示目录匹配，会匹配该<span class="notion-red">目录</span>下的所有内容</li><ul class="notion-list notion-list-disc notion-block-2158033396ca802a98bcf19233d2be7c"></ul></ul><div class="notion-text notion-block-2158033396ca80289d9ffb541a179d3b">最佳实践：如果要匹配目录，建议在location中使用斜杠结尾，这样可以避免一些意外的404错误。如果要严格匹配某个路径，则不要使用斜杠结尾。</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-2168033396ca80a799a3fa7912e566e5" data-id="2168033396ca80a799a3fa7912e566e5"><span><div id="2168033396ca80a799a3fa7912e566e5" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2168033396ca80a799a3fa7912e566e5" title="动态渲染表格"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">动态渲染表格</span></span></h2><div class="notion-text notion-block-2168033396ca805f9116e1fadba508e6">有些场景下我们需要动态渲染表格，也就是列是不固定的。这中情况有些复杂，一下记录两种解决方案：</div><ol start="1" class="notion-list notion-list-numbered notion-block-2168033396ca80998e28e8721a5dbf4b"><li>后端返回数据结构形如</li><ol class="notion-list notion-list-numbered notion-block-2168033396ca80998e28e8721a5dbf4b"><div class="notion-text notion-block-2168033396ca80439a2dd14739eb7744">前端渲染</div></ol></ol><ol start="2" class="notion-list notion-list-numbered notion-block-2168033396ca80dba1d0fa0ebb8df751"><li>后端返回数据结构形如</li><ol class="notion-list notion-list-numbered notion-block-2168033396ca80dba1d0fa0ebb8df751"><div class="notion-text notion-block-2168033396ca8094a43feedfb58b797f">这种是以一行数据为一个单位。</div></ol></ol><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-2178033396ca80fcbe6ae13ebffaf31d" data-id="2178033396ca80fcbe6ae13ebffaf31d"><span><div id="2178033396ca80fcbe6ae13ebffaf31d" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2178033396ca80fcbe6ae13ebffaf31d" title="Sortable库"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Sortable库</span></span></h2><div class="notion-text notion-block-2178033396ca802a9940d9a0e15e3fc8"><a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://sortablejs.github.io/Sortable/">SortableJS</a></div><div class="notion-text notion-block-2178033396ca80039e59d4a9281f123c"><b>JavaScript library for reorderable drag-and-drop lists</b></div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-21c8033396ca80d8987edfe6dc8fa4ef" data-id="21c8033396ca80d8987edfe6dc8fa4ef"><span><div id="21c8033396ca80d8987edfe6dc8fa4ef" class="notion-header-anchor"></div><a class="notion-hash-link" href="#21c8033396ca80d8987edfe6dc8fa4ef" title="Holder静态内部类技巧"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Holder静态内部类技巧</span></span></h2><div class="notion-text notion-block-21c8033396ca80589279c3f508a5f4ea">读取文件内容，只读一次，然后缓存下来供全局使用</div><div class="notion-text notion-block-21d8033396ca80cb8174d76d8dc432e9">这种做法有很多好处：</div><ol start="1" class="notion-list notion-list-numbered notion-block-21d8033396ca800fa1cbcc422e995307"><li>简洁优雅</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-21d8033396ca80bf84b1d0a3c4bfb0a7"><li>天然线程安全，因为Jvm会保证类加载的过程是线程安全的</li></ol><div class="notion-text notion-block-21d8033396ca808f8946f5c2da221fd8">在Spring、Mybatis等著名开发框架中经常能看到这种开发技巧。</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-21e8033396ca80a6a6c9ce6a2e2880ed" data-id="21e8033396ca80a6a6c9ce6a2e2880ed"><span><div id="21e8033396ca80a6a6c9ce6a2e2880ed" class="notion-header-anchor"></div><a class="notion-hash-link" href="#21e8033396ca80a6a6c9ce6a2e2880ed" title="Vue组件拖拽实现笔记"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Vue组件拖拽实现笔记</span></span></h2><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-21e8033396ca804295ded3cc12dd530e" data-id="21e8033396ca804295ded3cc12dd530e"><span><div id="21e8033396ca804295ded3cc12dd530e" class="notion-header-anchor"></div><a class="notion-hash-link" href="#21e8033396ca804295ded3cc12dd530e" title="问题背景"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">问题背景</span></span></h3><div class="notion-text notion-block-21e8033396ca80689a17d397e44587b5">实现表格行拖拽排序时，遇到了&quot;拖动后只改变序号但不改变实际内容&quot;以及&quot;第一次拖动后无法继续拖动&quot;的问题。</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-21e8033396ca80d482ddf0414d2f7293" data-id="21e8033396ca80d482ddf0414d2f7293"><span><div id="21e8033396ca80d482ddf0414d2f7293" class="notion-header-anchor"></div><a class="notion-hash-link" href="#21e8033396ca80d482ddf0414d2f7293" title="核心知识点"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">核心知识点</span></span></h3><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-21e8033396ca80e8a15cffaddbe6d288" data-id="21e8033396ca80e8a15cffaddbe6d288"><span><div id="21e8033396ca80e8a15cffaddbe6d288" class="notion-header-anchor"></div><a class="notion-hash-link" href="#21e8033396ca80e8a15cffaddbe6d288" title="Vue 的渲染机制"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Vue 的渲染机制</span></span></h4><ul class="notion-list notion-list-disc notion-block-21e8033396ca8061b8e2cfe85fb742bb"><li>虚拟 DOM: Vue 使用虚拟 DOM 来高效更新实际 DOM，比较新旧虚拟 DOM 树的差异。</li></ul><ul class="notion-list notion-list-disc notion-block-21e8033396ca8037aa10fdb52ce867e3"><li>key 属性作用:  在组件或元素上使用 key 属性可以帮助 Vue 跟踪每个节点的身份，正确复用和重新排序元素。<code class="notion-inline-code"> &lt;el-table :key=&quot;tableKey&quot; ...&gt;</code></li></ul><ul class="notion-list notion-list-disc notion-block-21e8033396ca80d7a60ae799e66f4580"><li>组件重新渲染：<span class="notion-pink">当 key 值改变时，Vue 会销毁并重新创建整个组件</span>，而不是尝试更新它。</li></ul><ol start="1" class="notion-list notion-list-numbered notion-block-21e8033396ca80568545ce6de92001e2"><li>v-if/v-else/v-else-if 控制的组件会销毁和重建</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-21e8033396ca80519300f9d1dc15beb2"><li>v-for 列表渲染时，key 的选择影响性能和状态复用</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-21e8033396ca8000b95efb76f4e9aea2"><li>props 变化不会销毁组件，只会触发更新</li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-21e8033396ca8049b352d918150f0c60"><li>setup 只在组件创建时执行一次</li></ol><ol start="5" class="notion-list notion-list-numbered notion-block-21e8033396ca80c0838ed0a0c917a74a"><li>响应式数据变更只会触发局部 DOM 更新</li></ol><ol start="6" class="notion-list notion-list-numbered notion-block-21e8033396ca8088935ccae805beec6b"><li>异步组件加载时，挂载和卸载流程与普通组件一致，Suspense 可控制加载占位和错误处理。</li><ol class="notion-list notion-list-numbered notion-block-21e8033396ca8088935ccae805beec6b"></ol></ol><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-21e8033396ca803588f8ff4f009c6ef9" data-id="21e8033396ca803588f8ff4f009c6ef9"><span><div id="21e8033396ca803588f8ff4f009c6ef9" class="notion-header-anchor"></div><a class="notion-hash-link" href="#21e8033396ca803588f8ff4f009c6ef9" title="nextTick 方法"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">nextTick 方法</span></span></h4><ul class="notion-list notion-list-disc notion-block-21e8033396ca80268b9ace30a6a1a51e"><li>定义: Vue 提供的全局 API，用于在 DOM 更新循环完成后执行延迟回调。</li></ul><ul class="notion-list notion-list-disc notion-block-21e8033396ca8083a88efe534bdef250"><li>原理: Vue 更新 DOM 是异步的，修改数据后 DOM 不会立即更新，nextTick 允许等待 DOM 更新完成后再执行操作。</li></ul><ul class="notion-list notion-list-disc notion-block-21e8033396ca80c8a38ac6e5a902ffe1"><li>用法:</li><ul class="notion-list notion-list-disc notion-block-21e8033396ca80c8a38ac6e5a902ffe1"></ul></ul><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-21e8033396ca8002932cf55256a9402a" data-id="21e8033396ca8002932cf55256a9402a"><span><div id="21e8033396ca8002932cf55256a9402a" class="notion-header-anchor"></div><a class="notion-hash-link" href="#21e8033396ca8002932cf55256a9402a" title="Sortable.js 的使用"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Sortable.js 的使用</span></span></h4><ul class="notion-list notion-list-disc notion-block-21e8033396ca804c87cae2cf8e17f0b7"><li>创建与销毁: 拖拽功能实现时需要管理 Sortable 实例的生命周期</li><ul class="notion-list notion-list-disc notion-block-21e8033396ca804c87cae2cf8e17f0b7"><div class="notion-text notion-block-21e8033396ca80fcb384d8ae91bcdca6"><span class="notion-pink">sortable实例是与dom元素相绑定的。当用户拖动元素后，js修改了数组中数据的顺序，此时为了让table重新渲染，需要为table设置一个key。当key发生改变，table就会被强制渲染。此时sortable原来绑定的dom元素丢失，会导致下一次无法进行拖拽。因此需要重新创建sortable实例。</span></div><blockquote class="notion-quote notion-purple notion-block-2228033396ca8015a857cb2e1ddd19e3"><div>组件挂载完成执行 <code class="notion-inline-code">initSortable</code>  方法，第一次执行的时候直接创建新的 <code class="notion-inline-code">Sortable</code> 实例。
开始监听 <code class="notion-inline-code">drag-handler</code> 的拖动。
当发生拖动的时候 <code class="notion-inline-code">tableKey++</code> , Table销毁后重新渲染，触发 <code class="notion-inline-code">nextTick</code> 回调，再次执行 <code class="notion-inline-code">initSortable</code> 方法.
第二次执行的时候，会销毁之前的 <code class="notion-inline-code">Sortable</code> 实例（因为这个实例所绑定的dom元素已经销毁，需要重新绑定），重新创建 <code class="notion-inline-code">Sortable</code> 实例并且绑定新的dom元素</div></blockquote></ul></ul><ul class="notion-list notion-list-disc notion-block-21e8033396ca805bb4afe225dfd4c77f"><li>事件处理: 使用 onEnd 回调处理拖拽结束事件</li><ul class="notion-list notion-list-disc notion-block-21e8033396ca805bb4afe225dfd4c77f"></ul></ul><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-21e8033396ca80098bd0c4c40fbe6261" data-id="21e8033396ca80098bd0c4c40fbe6261"><span><div id="21e8033396ca80098bd0c4c40fbe6261" class="notion-header-anchor"></div><a class="notion-hash-link" href="#21e8033396ca80098bd0c4c40fbe6261" title="BigDecimal处理小数点"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">BigDecimal处理小数点</span></span></h2><div class="notion-text notion-block-2298033396ca800daddcfb1d4c87831a">在java中处理小数点，通常来说有3中方法：</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-2298033396ca805da54cebb5269a40a5" data-id="2298033396ca805da54cebb5269a40a5"><span><div id="2298033396ca805da54cebb5269a40a5" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2298033396ca805da54cebb5269a40a5" title="Math"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Math</span></span></h3><div class="notion-text notion-block-2298033396ca80a88d5ee2c0ce39dc55">通过 <code class="notion-inline-code">Math.round()</code> 四舍五入，截取小数</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-2298033396ca80668587c1d9137bc22b" data-id="2298033396ca80668587c1d9137bc22b"><span><div id="2298033396ca80668587c1d9137bc22b" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2298033396ca80668587c1d9137bc22b" title="String.format"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">String.format</span></span></h3><div class="notion-text notion-block-2298033396ca8075b2a5c6de992a070d">用于展示而非计算</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-2298033396ca80fda0afe4ad3a28a3fe" data-id="2298033396ca80fda0afe4ad3a28a3fe"><span><div id="2298033396ca80fda0afe4ad3a28a3fe" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2298033396ca80fda0afe4ad3a28a3fe" title="BigDecimal"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">BigDecimal</span></span></h3><div class="notion-blank notion-block-2298033396ca802498e0c112e4413801"> </div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[使用GraalVM打包原生应用]]></title>
            <link>https://www.ahang.asia/article/graalvm</link>
            <guid>https://www.ahang.asia/article/graalvm</guid>
            <pubDate>Sat, 28 Jun 2025 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-2208033396ca80549d4bc6b6cd1553ef"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><div class="notion-text notion-block-2208033396ca8035818ed6cdb85bc6c4">GraalVM 是一个由 Oracle 开发的 <b>多语言运行时（runtime）</b>，它最大的亮点是：</div><blockquote class="notion-quote notion-block-2208033396ca8006b3f4f896822e5ac1"><div>💥 让 Java 和其他语言（像 JS、Python、Ruby、LLVM 等）都能高效运行、互操作，甚至打包成原生可执行文件（.exe/.out）！</div></blockquote><div class="notion-text notion-block-2208033396ca8082973ffc58e0aa0ceb">简化来说，它是一种更「万能」的 Java 运行环境，但远不止于 Java。</div><div class="notion-text notion-block-2208033396ca80209081e9d0c52d1d37">graalvm可以将java项目打包为二进制文件，类似于 <code class="notion-inline-code">go build</code> 那种。这是通过 <code class="notion-inline-code">native-iamge</code> 工具实现的。</div><div class="notion-text notion-block-2208033396ca805ab3ffc3f2a9164b49">下面介绍具体操作方法：</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-2208033396ca80be8057d8aa30f90549" data-id="2208033396ca80be8057d8aa30f90549"><span><div id="2208033396ca80be8057d8aa30f90549" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2208033396ca80be8057d8aa30f90549" title="准备环境"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">准备环境</span></span></h2><div class="notion-text notion-block-2208033396ca8081809fdde0fa7aaeaf">需要准备两点：</div><ul class="notion-list notion-list-disc notion-block-2208033396ca8099b107fd1a2a4ef742"><li>windows下需要准备c++构建工具</li></ul><ul class="notion-list notion-list-disc notion-block-2208033396ca80e1a17ede76a17ddb40"><li>下载graalvm</li></ul><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-2208033396ca808fa88be7bec51b6aea" data-id="2208033396ca808fa88be7bec51b6aea"><span><div id="2208033396ca808fa88be7bec51b6aea" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2208033396ca808fa88be7bec51b6aea" title="C++构建工具"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">C++构建工具</span></span></h3><ol start="1" class="notion-list notion-list-numbered notion-block-2208033396ca80a9a51dc8f761ca9670"><li>下载 <a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://visualstudio.microsoft.com/downloads/">vs installer</a></li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-2208033396ca801aa409c5b28b06e710"><li>选择单个组件：</li><ol class="notion-list notion-list-numbered notion-block-2208033396ca801aa409c5b28b06e710"><ul class="notion-list notion-list-disc notion-block-2208033396ca80f891bcf2507bce61ed"><li>C++ 生成工具</li></ul><ul class="notion-list notion-list-disc notion-block-2208033396ca80a2b5ace285bea703bb"><li>MSVC 生成工具</li></ul><ul class="notion-list notion-list-disc notion-block-2208033396ca809897ecc64c4e26d76b"><li>Windows 11 SDK</li></ul></ol></ol><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-2208033396ca80ed8cb8f0546d07da1f" data-id="2208033396ca80ed8cb8f0546d07da1f"><span><div id="2208033396ca80ed8cb8f0546d07da1f" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2208033396ca80ed8cb8f0546d07da1f" title="下载graalvm"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">下载graalvm</span></span></h3><div class="notion-text notion-block-2208033396ca8050802bf2df88e16e2c"><a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://www.graalvm.org/downloads/">Download GraalVM</a></div><div class="notion-text notion-block-2208033396ca80218374c479db387ac6">下载完graalvm后，实际上它自带一个jdk。此时我们将环境变量中的JAVA_HOME修改为graalvm的安装目录。</div><div class="notion-text notion-block-2208033396ca80888913d9a3757ecc00">然后再配置一个GRAALVM_HOME, 值也是graalvm的安装目录。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-2208033396ca80119183dddbd5a7a3a7"><div style="position:relative;display:flex;justify-content:center;align-self:start;width:595.9801025390625px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A4f577f45-343c-4402-83b1-168ddabdcd73%3Aimage.png?table=block&amp;id=22080333-96ca-8011-9183-dddbd5a7a3a7&amp;t=22080333-96ca-8011-9183-dddbd5a7a3a7" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-2208033396ca80a69adde146362e4ac5">然后在path里将graalvm安装目录下的bin目录配置进去，注意要放在其他jdk配置之前，防止jdk版本被覆盖。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-2208033396ca80749e7dc40b11962f4f"><div style="position:relative;display:flex;justify-content:center;align-self:start;width:407.9829406738281px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A846edf29-e389-4402-974f-324d13c3b160%3Aimage.png?table=block&amp;id=22080333-96ca-8074-9e7d-c40b11962f4f&amp;t=22080333-96ca-8074-9e7d-c40b11962f4f" alt="notion image" loading="lazy" decoding="async"/></div></figure><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-2208033396ca8051b3c5ee0ee7144dbf" data-id="2208033396ca8051b3c5ee0ee7144dbf"><span><div id="2208033396ca8051b3c5ee0ee7144dbf" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2208033396ca8051b3c5ee0ee7144dbf" title="SpringBoot项目配置"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">SpringBoot项目配置</span></span></h2><ol start="1" class="notion-list notion-list-numbered notion-block-2208033396ca8013b43ef45a76d8163d"><li>添加 <code class="notion-inline-code">native-maven-plugin</code> 插件</li><ol class="notion-list notion-list-numbered notion-block-2208033396ca8013b43ef45a76d8163d"></ol></ol><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-2208033396ca80f0bd8ef4a9fe7c77a7" data-id="2208033396ca80f0bd8ef4a9fe7c77a7"><span><div id="2208033396ca80f0bd8ef4a9fe7c77a7" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2208033396ca80f0bd8ef4a9fe7c77a7" title="项目打包"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">项目打包</span></span></h2><ol start="1" class="notion-list notion-list-numbered notion-block-2208033396ca801a925ef6cea191f267"><li>直接通过idea的maven工具，选择plugins中的 <code class="notion-inline-code">native:compile</code> 命令执行</li></ol><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-2208033396ca80c5b10adbd08451050e"><div style="position:relative;display:flex;justify-content:center;align-self:start;width:566.9744262695312px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Aafae787d-e398-4634-a892-920e0bd1e3ee%3Aimage.png?table=block&amp;id=22080333-96ca-80c5-b10a-dbd08451050e&amp;t=22080333-96ca-80c5-b10a-dbd08451050e" alt="notion image" loading="lazy" decoding="async"/></div></figure><ol start="1" class="notion-list notion-list-numbered notion-block-2208033396ca80be8dc1ffa2f0a57d4f"><li>如果执行失败，可以先复制idea生成的maven命令，然后在 <code class="notion-inline-code">x64 Native Tools Command Prompt for VS 2022 Preview</code> 命令行工具中执行此命令。这个命令行工具在安装指定组件的vs后，就会有。</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-2208033396ca807486a3ea1967d1f8aa"><li>成功打包exe文件。</li></ol><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-2208033396ca8063a640f54aadfcd33b"><div style="position:relative;display:flex;justify-content:center;align-self:start;width:348.9772644042969px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Ae825f843-8a9e-4156-b960-c00e463c05dc%3Aimage.png?table=block&amp;id=22080333-96ca-8063-a640-f54aadfcd33b&amp;t=22080333-96ca-8063-a640-f54aadfcd33b" alt="notion image" loading="lazy" decoding="async"/></div></figure></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[logstash应用]]></title>
            <link>https://www.ahang.asia/article/logstash</link>
            <guid>https://www.ahang.asia/article/logstash</guid>
            <pubDate>Tue, 27 May 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[logstash的实际应用]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-2008033396ca8091a4bdd498cce56396"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-2008033396ca81c0b001ca9712127b16" data-id="2008033396ca81c0b001ca9712127b16"><span><div id="2008033396ca81c0b001ca9712127b16" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2008033396ca81c0b001ca9712127b16" title="ELK配置"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">ELK配置</span></span></h3><div class="notion-text notion-block-2008033396ca8161b785e39218d53d05">先把elk跑起来，具体的docker compose文件和logstash的配置文件内容如下：</div><blockquote class="notion-quote notion-block-2008033396ca81c3bee9d563bc82e537"><div>logstash.conf</div></blockquote><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-2008033396ca8185aef3dcfc84a06f83" data-id="2008033396ca8185aef3dcfc84a06f83"><span><div id="2008033396ca8185aef3dcfc84a06f83" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2008033396ca8185aef3dcfc84a06f83" title="Java应用配置"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Java应用配置</span></span></h3><div class="notion-text notion-block-2008033396ca815caf6ed73fa21d0d9d">配置完elk后，我们开始配置springboot应用，在resources目录下创建logback-spring.xml文件：</div><div class="notion-text notion-block-2008033396ca819993abe78901572a8c">同时需要在pom.xml中添加logstash依赖：</div><div class="notion-text notion-block-2008033396ca81f5b47fe8ce2f09dbdd">这样配置后，应用程序的日志会同时输出到控制台和发送到LogStash。LogStash会将日志转发到Elasticsearch，然后可以在Kibana中查看和分析这些日志。</div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-2008033396ca81709406ee563aefdae9" data-id="2008033396ca81709406ee563aefdae9"><span><div id="2008033396ca81709406ee563aefdae9" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2008033396ca81709406ee563aefdae9" title="Kibana数据分析"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Kibana数据分析</span></span></h3><div class="notion-text notion-block-2008033396ca819ca800efe36466ea0b">我们在 <code class="notion-inline-code">Management → Index Management</code>  中可以看到自动创建的索引</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-2008033396ca811483bac8c797d4673f"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A438036b4-70f8-409e-b39f-6495457959df%3Aimage.png?table=block&amp;id=20080333-96ca-8114-83ba-c8c797d4673f&amp;t=20080333-96ca-8114-83ba-c8c797d4673f" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-2008033396ca818eb490c39039b2439e">在 <code class="notion-inline-code">Analytics → Discover</code> 中可以查看并分析日志内容</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-2008033396ca81c387d2c137859549c1"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3A6904f652-5265-4740-866c-8d72c4510b39%3Aimage.png?table=block&amp;id=20080333-96ca-81c3-87d2-c137859549c1&amp;t=20080333-96ca-81c3-87d2-c137859549c1" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-2008033396ca8153879fcc70ab46b221">在<code class="notion-inline-code"> Analytics → Dashboards</code> 中可以构建图表数据视图，直观的展示数据</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-2008033396ca81219ba4d206e67969fd"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/attachment%3Adc8b6cc9-d11f-466f-abbc-2255e4c29e02%3Aimage.png?table=block&amp;id=20080333-96ca-8121-9ba4-d206e67969fd&amp;t=20080333-96ca-8121-9ba4-d206e67969fd" alt="notion image" loading="lazy" decoding="async"/></div></figure></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[nginx正向代理]]></title>
            <link>https://www.ahang.asia/article/正向代理</link>
            <guid>https://www.ahang.asia/article/正向代理</guid>
            <pubDate>Thu, 08 May 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[nginx正向代理]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-1ed8033396ca8086bb09e78ddf971135"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1ed8033396ca80dbbdf9c1a4441b8d17" data-id="1ed8033396ca80dbbdf9c1a4441b8d17"><span><div id="1ed8033396ca80dbbdf9c1a4441b8d17" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca80dbbdf9c1a4441b8d17" title="Nginx正向代理"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Nginx正向代理</span></span></h2><div class="notion-text notion-block-1ed8033396ca802e9affe4616122113a">nginx正向代理需要借助一个模块：<code class="notion-inline-code">ngx_http_proxy_connect_module</code></div><div class="notion-text notion-block-1ed8033396ca80babe3cebc56c0fc187">该模块的<a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://github.com/chobits/ngx_http_proxy_connect_module">源码地址</a></div><div class="notion-text notion-block-1ed8033396ca80ad9272d45187229fc0">我们需要先下载这个源码，然后通过编译的方式将其安装到nginx里。nginx安装方式有很多，比如通过apt直接安装，或者使用docker等。本文使用的nginx安装方式是源码编译安装。</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-1ed8033396ca80ff82abf73cb1dab039" data-id="1ed8033396ca80ff82abf73cb1dab039"><span><div id="1ed8033396ca80ff82abf73cb1dab039" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca80ff82abf73cb1dab039" title="步骤"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">步骤</span></span></h3><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-1ed8033396ca8034b038da4df115f17a" data-id="1ed8033396ca8034b038da4df115f17a"><span><div id="1ed8033396ca8034b038da4df115f17a" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca8034b038da4df115f17a" title="1. 下载ngx_http_proxy_connect_module模块"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">1. 下载ngx_http_proxy_connect_module模块</span></span></h4><div class="notion-text notion-block-1ed8033396ca801c9d1bef446064a93b">直接clone其github仓库即可。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-1ed8033396ca80a5b96de4d9efbf1965" data-id="1ed8033396ca80a5b96de4d9efbf1965"><span><div id="1ed8033396ca80a5b96de4d9efbf1965" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca80a5b96de4d9efbf1965" title="2. 下载nginx （v1.24.0）"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">2. 下载nginx （v1.24.0）</span></span></h4><div class="notion-callout notion-block-1ed8033396ca804784a1e37ccedeef73"><div class="notion-page-icon-inline notion-page-icon-image"><svg class="notion-page-icon" alt="安装nginx前，你可能需要一些前置的准备：" viewBox="0 0 30 30" width="16"><path d="M16,1H4v28h22V11L16,1z M16,3.828L23.172,11H16V3.828z M24,27H6V3h8v10h10V27z M8,17h14v-2H8V17z M8,21h14v-2H8V21z M8,25h14v-2H8V25z"></path></svg></div><div class="notion-callout-text">安装nginx前，你可能需要一些前置的准备：<ul class="notion-list notion-list-disc notion-block-1ed8033396ca8016be45d8836130dd00"><li>安装gcc</li><ul class="notion-list notion-list-disc notion-block-1ed8033396ca8016be45d8836130dd00"></ul></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca805fa150e8fa9c34ecbd"><li>安装pcre （正则库）</li><ul class="notion-list notion-list-disc notion-block-1ed8033396ca805fa150e8fa9c34ecbd"></ul></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca806bba2bdd2d0362ea07"><li>openssl</li><ul class="notion-list notion-list-disc notion-block-1ed8033396ca806bba2bdd2d0362ea07"></ul></ul></div></div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-1ed8033396ca805a83c7fa3dfc2ce34c" data-id="1ed8033396ca805a83c7fa3dfc2ce34c"><span><div id="1ed8033396ca805a83c7fa3dfc2ce34c" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca805a83c7fa3dfc2ce34c" title="3. 打补丁"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">3. 打补丁</span></span></h4><div class="notion-callout notion-block-1ed8033396ca802983c0f85fc9a30bb2"><div class="notion-page-icon-inline notion-page-icon-image"><svg class="notion-page-icon" alt="这里我使用nginx1.24.0，需要的补丁是 proxy_connect_rewrite_102101.patch。
如果你使用其他nginx版本，可以在ngx_http_proxy_connect_module模块的github仓库查看对应的补丁。只需要将上述 &#x27;proxy_connect_rewrite_102101.patch&#x27; 替换为对应版本的补丁即可。" viewBox="0 0 30 30" width="16"><path d="M16,1H4v28h22V11L16,1z M16,3.828L23.172,11H16V3.828z M24,27H6V3h8v10h10V27z M8,17h14v-2H8V17z M8,21h14v-2H8V21z M8,25h14v-2H8V25z"></path></svg></div><div class="notion-callout-text">这里我使用nginx1.24.0，需要的补丁是 proxy_connect_rewrite_102101.patch。
如果你使用其他nginx版本，可以在ngx_http_proxy_connect_module模块的github仓库<a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://github.com/chobits/ngx_http_proxy_connect_module?tab=readme-ov-file#select-patch">查看对应的补丁</a>。只需要将上述 &#x27;proxy_connect_rewrite_102101.patch&#x27; 替换为对应版本的补丁即可。</div></div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-1ed8033396ca807bbe2de4d99b740b28" data-id="1ed8033396ca807bbe2de4d99b740b28"><span><div id="1ed8033396ca807bbe2de4d99b740b28" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca807bbe2de4d99b740b28" title="4. 编译nginx源码，安装nginx"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">4. 编译nginx源码，安装nginx</span></span></h4><div class="notion-callout notion-block-1ed8033396ca80beab6ec59346b2dc49"><div class="notion-page-icon-inline notion-page-icon-image"><svg class="notion-page-icon" alt="通过编译安装的nginx主目录位置在： /usr/local/nginx 下" viewBox="0 0 30 30" width="16"><path d="M16,1H4v28h22V11L16,1z M16,3.828L23.172,11H16V3.828z M24,27H6V3h8v10h10V27z M8,17h14v-2H8V17z M8,21h14v-2H8V21z M8,25h14v-2H8V25z"></path></svg></div><div class="notion-callout-text">通过编译安装的nginx主目录位置在： /usr/local/nginx 下</div></div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-1ed8033396ca80f0a8a2fad8cfe32dcb" data-id="1ed8033396ca80f0a8a2fad8cfe32dcb"><span><div id="1ed8033396ca80f0a8a2fad8cfe32dcb" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca80f0a8a2fad8cfe32dcb" title="5. 编辑配置文件，启用正向代理"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">5. 编辑配置文件，启用正向代理</span></span></h4><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-1ed8033396ca8025bdbbe045456ff2bb" data-id="1ed8033396ca8025bdbbe045456ff2bb"><span><div id="1ed8033396ca8025bdbbe045456ff2bb" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca8025bdbbe045456ff2bb" title="6. 配置代理"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">6. 配置代理</span></span></h4><div class="notion-text notion-block-1ed8033396ca80158a8ad5a13617fda0">在win上配置代理，填入云主机的ip地址和端口即可。</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-1ed8033396ca808b9f2bc601638b970f" data-id="1ed8033396ca808b9f2bc601638b970f"><span><div id="1ed8033396ca808b9f2bc601638b970f" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca808b9f2bc601638b970f" title="已经安装了nginx？"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">已经安装了nginx？</span></span></h3><div class="notion-text notion-block-1ed8033396ca80f4b7bdf6be543fbdbb">上面是从头开始安装nginx的例子。 如果你的云主机上已经存在了nginx，但是不想重装，可以通过平滑升级方式实现。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-1ed8033396ca80449e34d3b1dd152549" data-id="1ed8033396ca80449e34d3b1dd152549"><span><div id="1ed8033396ca80449e34d3b1dd152549" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca80449e34d3b1dd152549" title="步骤"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">步骤</span></span></h4><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-1ed8033396ca809db6b1c294da7af010" data-id="1ed8033396ca809db6b1c294da7af010"><span><div id="1ed8033396ca809db6b1c294da7af010" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca809db6b1c294da7af010" title="1. 查看当前nginx配置项"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">1. 查看当前nginx配置项</span></span></h4><div class="notion-text notion-block-1ed8033396ca808e866aef7711d99503">记住<code class="notion-inline-code">configure arguments:</code> 后面的内容， 我们需要保留这些 配置项，这样才不会影响你原来的nginx</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-1ed8033396ca80e59813f1c35a105ec2" data-id="1ed8033396ca80e59813f1c35a105ec2"><span><div id="1ed8033396ca80e59813f1c35a105ec2" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca80e59813f1c35a105ec2" title="2. 配置新的配置项"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">2. 配置新的配置项</span></span></h4><div class="notion-text notion-block-1ed8033396ca80068e20d5f81f5970fa">进入新下载的nginx源码目录， 通过configure配置新的配置项，这个配置项会覆盖掉原来的配置。因此把原来的 配置加上，再添加你想要的 其他模块。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-1ed8033396ca8064b28af82888c2609e" data-id="1ed8033396ca8064b28af82888c2609e"><span><div id="1ed8033396ca8064b28af82888c2609e" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca8064b28af82888c2609e" title="3. 复制二进制文件"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">3. 复制二进制文件</span></span></h4><div class="notion-text notion-block-1ed8033396ca80be8662dfc994957d45">将 objs 目录下，编译好的nginx二进制文件复制到nginx的安装目录下的/sbin里。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-1ed8033396ca8091b57fd2429d5b5b83" data-id="1ed8033396ca8091b57fd2429d5b5b83"><span><div id="1ed8033396ca8091b57fd2429d5b5b83" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca8091b57fd2429d5b5b83" title="4. 升级nginx"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">4. 升级nginx</span></span></h4><div class="notion-text notion-block-1ed8033396ca805b86fff83228a4631a">回到源码的目录，执行make upgrade指令</div><div class="notion-callout notion-block-1ed8033396ca8012b68cd61e5a633fb3"><div class="notion-page-icon-inline notion-page-icon-image"><svg class="notion-page-icon" alt="如果在执行make upgrade指令时报错： make: *** [Makefile:22：upgrade] 错误 1
尝试先kill掉原来的nginx进程，然后使用绝对路径来启动新的nginx：" viewBox="0 0 30 30" width="16"><path d="M16,1H4v28h22V11L16,1z M16,3.828L23.172,11H16V3.828z M24,27H6V3h8v10h10V27z M8,17h14v-2H8V17z M8,21h14v-2H8V21z M8,25h14v-2H8V25z"></path></svg></div><div class="notion-callout-text">如果在执行make upgrade指令时报错： make: *** [Makefile:22：upgrade] 错误 1
尝试先kill掉原来的nginx进程，然后使用绝对路径来启动新的nginx：<div class="notion-text notion-block-1ed8033396ca8018ba00f41242b4f49c">之后再执行 make upgrade 应该不会有问题了。</div></div></div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-1ed8033396ca807b8bcfd5682c66402c" data-id="1ed8033396ca807b8bcfd5682c66402c"><span><div id="1ed8033396ca807b8bcfd5682c66402c" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca807b8bcfd5682c66402c" title="5. 配置nginx.conf"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">5. 配置nginx.conf</span></span></h4><div class="notion-text notion-block-1ed8033396ca801097a9edb70dca9102">安装完ngx_http_proxy_connect_module模块后，剩下的就是配置nginx.conf了，和上面一样，不再赘述。</div><div class="notion-blank notion-block-1ed8033396ca805e915cfc603f5b2eec"> </div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[frp内网穿透]]></title>
            <link>https://www.ahang.asia/article/内网穿透</link>
            <guid>https://www.ahang.asia/article/内网穿透</guid>
            <pubDate>Thu, 08 May 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[frp内网穿透]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-1ed8033396ca80259e32dbc3dfbc878e"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><div class="notion-text notion-block-1ed8033396ca80b78936dc840419cc86">在本文中，你将学习如何使用frp实现内网穿透，主要包含以下内容：</div><ul class="notion-list notion-list-disc notion-block-1ed8033396ca802eb60cfc128131807c"><li>下载和安装frp</li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca80d8a202cff8ac25da1a"><li>配置frp客户端和服务端</li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca80e9a0ebd61b3f38e472"><li>通过systemd管理frp服务</li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca80958265d031b53903d8"><li>实现web服务的内网穿透</li></ul><div class="notion-text notion-block-1ed8033396ca806c96e5cb39a3508432">前提条件：</div><div class="notion-callout notion-block-1ed8033396ca80c1b104ee15e090413f"><div class="notion-page-icon-inline notion-page-icon-image"><svg class="notion-page-icon" alt="✨ 在开始之前，请确保你已经：" viewBox="0 0 30 30" width="16"><path d="M16,1H4v28h22V11L16,1z M16,3.828L23.172,11H16V3.828z M24,27H6V3h8v10h10V27z M8,17h14v-2H8V17z M8,21h14v-2H8V21z M8,25h14v-2H8V25z"></path></svg></div><div class="notion-callout-text">✨ 在开始之前，请确保你已经：<ul class="notion-list notion-list-disc notion-block-1ed8033396ca801c93a0fafb3a7fa41e"><li>拥有一个域名</li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca80faa58df201bc4a7994"><li>拥有一台具有公网IP的云服务器</li></ul></div></div><div class="notion-text notion-block-1ed8033396ca809eba43df0a406f34e3">本文介绍如何通过frp在自己的云服务器上搭建内网穿透服务，提供对外暴露的web服务。</div><div class="notion-text notion-block-1ed8033396ca80dfb174efe27a37e19d">内网穿透可以将本机的服务通过云服务器拥有的公有ip暴露到公网上供他人访问，应用场景有很多，比如做微信小程序开发时需要调试接口，或者向别人演示自己还未正式上线的小demo等。</div><div class="notion-text notion-block-1ed8033396ca80e6bfb6eda1b1388076">在阅读本文内容之前，需要有一些前置准备：</div><ul class="notion-list notion-list-disc notion-block-1ed8033396ca8054b7abeb30b6f1a487"><li>你拥有一个域名</li></ul><ul class="notion-list notion-list-disc notion-block-1ed8033396ca80138adddfdd1cb5ca1e"><li>你拥有一台具有公网ip地址的云主机</li></ul><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-1ed8033396ca80af9667d38b40fdc702" data-id="1ed8033396ca80af9667d38b40fdc702"><span><div id="1ed8033396ca80af9667d38b40fdc702" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca80af9667d38b40fdc702" title="frp"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">frp</span></span></h3><div class="notion-text notion-block-1ed8033396ca8022943bc9d488bcaa68">frp 是一款高性能的反向代理应用，专注于内网穿透。它支持多种协议，包括 TCP、UDP、HTTP、HTTPS 等，并且具备 P2P 通信功能。使用 frp，您可以安全、便捷地将内网服务暴露到公网，通过拥有公网 IP 的节点进行中转。</div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-1ed8033396ca80078dced79cb2250df3" data-id="1ed8033396ca80078dced79cb2250df3"><span><div id="1ed8033396ca80078dced79cb2250df3" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca80078dced79cb2250df3" title="下载"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">下载</span></span></h3><div class="notion-text notion-block-1ed8033396ca80918644f8c0802e49be">如果你是win系统，可以通过frp的<a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://github.com/fatedier/frp/releases">github releases</a>点击下载文件。</div><div class="notion-text notion-block-1ed8033396ca80b88434d306965f5142">如果你是linux系统，你可以通过wget下载:</div><div class="notion-text notion-block-1ed8033396ca8048b68dd025d8f6ffcb">这里以win为例，下载完压缩包解压之后，你会看到如下的目录结构：</div><div class="notion-text notion-block-1ed8033396ca800ca100f714e0eed34a">这里介绍一下，这个frpc.toml是客户端的配置文件，客户端指的是你需要进行内网穿透的机器。c就是client的意思。frpc.exe就是客户端的frp程序的启动文件。</div><div class="notion-text notion-block-1ed8033396ca806bb872e87b7382964f">frps.toml就是源服务器的配置文件，源服务器指的就是你的云服务器（拥有公网ip的）。同理，这个s就是source的意思。</div><div class="notion-text notion-block-1ed8033396ca80fd9086da1bcb5a3aa9">那其实我们现在用的win机器，只需要保留frpc.toml和frpc.exe就行了，剩下的frps.toml和frps.exe可以删除掉。因为云服务器使用linux系统这个exe他也用不了，所以我们在云服务器上还需要从giithub上下载这个frp的文件。</div><div class="notion-text notion-block-1ed8033396ca80b79669c96c183bbd7c">同样的，在云服务器上下载了文件后，也可以删除掉frpc.toml和frpc，只需要保留frps.toml和frps即可。</div><div class="notion-text notion-block-1ed8033396ca80658896ee35d2237dde">接下来介绍具体操作步骤：</div><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1ed8033396ca80ea8b40fc817411a921" data-id="1ed8033396ca80ea8b40fc817411a921"><span><div id="1ed8033396ca80ea8b40fc817411a921" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca80ea8b40fc817411a921" title="对于客户端（win机器）"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">对于客户端（win机器）</span></span></h4><ol start="1" class="notion-list notion-list-numbered notion-block-1ed8033396ca803fa81ceef5d22394dc"><li>通过<a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://github.com/fatedier/frp/releases">github releases</a>下载文件</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1ed8033396ca803c94b6db967fb0d383"><li>保留文件中的<code class="notion-inline-code">frpc.toml</code>和<code class="notion-inline-code">frpc.exe</code>即可</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1ed8033396ca807d88f5fc52bd941a38"><li>配置<code class="notion-inline-code">frpc.toml</code></li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-1ed8033396ca80b9a9a9cc9094a01881"><li>启动frpc服务</li></ol><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1ed8033396ca8081a361f76840a3306f" data-id="1ed8033396ca8081a361f76840a3306f"><span><div id="1ed8033396ca8081a361f76840a3306f" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca8081a361f76840a3306f" title="对于云主机（linux机器）"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">对于云主机（linux机器）</span></span></h4><ol start="1" class="notion-list notion-list-numbered notion-block-1ed8033396ca80288d8ed18a2bbf5695"><li>通过sh命令获取frp文件</li></ol><ol start="1" class="notion-list notion-list-numbered notion-block-1ed8033396ca809dbc33d6d49d9d3aab"><li>保留文件中的<code class="notion-inline-code">frps.toml</code>和<code class="notion-inline-code">frps</code>即可</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1ed8033396ca80ca88f1c7e4574e7735"><li>配置<code class="notion-inline-code">frps.toml</code></li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1ed8033396ca8068b533f07e706b1af7"><li>启动frps服务</li></ol><div class="notion-text notion-block-1ed8033396ca800bb13ec1e666115ac0">如果你不想每次都进入frp安装目录下通过 <code class="notion-inline-code">xxx/frps -c xxx/frps.toml</code> 来启动的话，可以通过<code class="notion-inline-code">systemd</code>来添加frp服务，通过<code class="notion-inline-code">systemctl</code>统一管理。具体步骤如下：</div><ol start="1" class="notion-list notion-list-numbered notion-block-1ed8033396ca80ff97ffc02c57cf0b8b"><li>在<code class="notion-inline-code">/lib/systemd/system/</code>下创建 <code class="notion-inline-code">frps.service</code>文件, 内容如下：</li></ol><ol start="1" class="notion-list notion-list-numbered notion-block-1ed8033396ca80b5b88bd7701e31513a"><li>通过systemd管理frps</li></ol><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-1ed8033396ca80eab8a4e46376f0988b" data-id="1ed8033396ca80eab8a4e46376f0988b"><span><div id="1ed8033396ca80eab8a4e46376f0988b" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1ed8033396ca80eab8a4e46376f0988b" title="对外提供web服务"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">对外提供web服务</span></span></h3><ol start="1" class="notion-list notion-list-numbered notion-block-1ed8033396ca807184e1eb8a772dc0f9"><li>在云主机上配置frps.toml</li></ol><ol start="1" class="notion-list notion-list-numbered notion-block-1ed8033396ca80b59ffdd426a80a231c"><li>在win机器（客户端）上配置frpc.toml</li></ol><div class="notion-callout notion-block-1ed8033396ca80839d3dd5686ce9e49f"><div class="notion-page-icon-inline notion-page-icon-image"><svg class="notion-page-icon" alt="记得把自己的域名解析到云主机上！" viewBox="0 0 30 30" width="16"><path d="M16,1H4v28h22V11L16,1z M16,3.828L23.172,11H16V3.828z M24,27H6V3h8v10h10V27z M8,17h14v-2H8V17z M8,21h14v-2H8V21z M8,25h14v-2H8V25z"></path></svg></div><div class="notion-callout-text"><b>记得把自己的域名解析到云主机上！</b></div></div><ol start="1" class="notion-list notion-list-numbered notion-block-1ed8033396ca80759ecbc03358b0c7e8"><li>启动服务</li></ol><div class="notion-text notion-block-1ed8033396ca80feb931fd2f04ca9847">或者 通过systemd</div><div class="notion-text notion-block-1ed8033396ca8023ac2be1b740876318">win机器同理，通过powershell或者cmd执行exe程序并通过 <code class="notion-inline-code">-c</code> 指定应用的配置文件。</div><div class="notion-text notion-block-1ed8033396ca802bb1d4e511a6048ca0">至此，内网穿透已经实现，你可以愉快的向别人分享你的demo啦！😊</div><hr class="notion-hr notion-block-1ed8033396ca8021a5c5fdd498b84a5f"/><div class="notion-text notion-block-1ed8033396ca808fbf6ae1e109888029">结尾附上<a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://gofrp.org/zh-cn/docs/">frp的官方文档</a>，本文还有些不完善的地方或者没有提到的功能，可以参考frp官方文档。</div></main></div>]]></content:encoded>
        </item>
    </channel>
</rss>