MinsonLee's Blog Welcome To My WebSite ! https://minsonlee.github.io/ Thu, 05 Mar 2026 03:30:38 +0000 Thu, 05 Mar 2026 03:30:38 +0000 Jekyll v3.10.0 Windows 和 Linux 下如何查看内存条硬件信息 <h2 id="windows-查看内存条信息">Windows 查看内存条信息</h2> <ul> <li>通过命令 <code class="language-plaintext highlighter-rouge">wmic</code> 查看:<code class="language-plaintext highlighter-rouge">xxx get /format:list</code> 可以将列数据转为行显示 <ul> <li>获取当前内存条的详细信息:<code class="language-plaintext highlighter-rouge">wmic memorychip</code></li> <li>如果只关心「当前有多少条内存条?给自多大字节?」<code class="language-plaintext highlighter-rouge"> wmic memorychip get Tag,Capacity /format:list</code></li> <li>当前电脑主板支持最大扩展到多大内存?<code class="language-plaintext highlighter-rouge">wmic MEMPHYSICAL get /format:list</code></li> </ul> </li> </ul> <p><img src="/images/pig/windows-wmic-memorychip.png" alt="当前计算机内存卡槽信息" /></p> <p><img src="/images/pig/windows-wmic-memorychip.png" alt="当前计算机主板支持扩展最大内存" /></p> <ul> <li>通过「任务管理器-<code class="language-plaintext highlighter-rouge">taskmgr</code>」查看:</li> </ul> <p><img src="/images/pig/windows-taskmgr-show-memorychip.png" alt="通过“任务管理器-性能”查看内存条插槽信息" /></p> <h2 id="linux-查看内存条信息">Linux 查看内存条信息</h2> <ul> <li>通过 <code class="language-plaintext highlighter-rouge">dmidecode -t memory</code> 获取内存条硬件信息</li> <li>通过 <code class="language-plaintext highlighter-rouge">lsmem</code> 或 <code class="language-plaintext highlighter-rouge">free -h</code> 命令可以获取当前计算机的总内存信息</li> </ul> Fri, 19 Sep 2025 00:00:00 +0000 https://minsonlee.github.io/2025/09/show-system-hardware-information https://minsonlee.github.io/2025/09/show-system-hardware-information Windows Linux 简单的告警闪烁效果 <p>后台一些功能,经常一些选项发生变更之后其联动数据需要产品二次确认。然后…没有确认,导致错误添加一些翻译任务数据,又来找开发进行手动数据处理。为防止这种“老花眼”现象…做了一个简单的告警闪烁。</p> <p>效果如下:</p> <p><img src="https://cdn.jsdelivr.net/gh/MinsonLee/minsonlee.github.io/images/pig/warning-demo.gif" alt="warning flashing" /></p> <p>后台非常老旧…所以用原生的 <code class="language-plaintext highlighter-rouge">JS</code> 进行了简单实现,记录一下:</p> <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 闪烁函数</span> <span class="kd">function</span> <span class="nx">flash</span><span class="p">(</span><span class="nx">id</span><span class="p">,</span> <span class="nx">count</span><span class="p">,</span> <span class="nx">originalColor</span><span class="p">)</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="nx">count</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span> <span class="c1">// 闪烁3次后停止</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">count</span><span class="p">);</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="nx">id</span><span class="p">).</span><span class="nx">style</span><span class="p">.</span><span class="nx">backgroundColor</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">#ff0000</span><span class="dl">'</span><span class="p">;</span> <span class="nx">setTimeout</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="c1">// 恢复原始颜色</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="nx">id</span><span class="p">).</span><span class="nx">style</span><span class="p">.</span><span class="nx">backgroundColor</span> <span class="o">=</span> <span class="nx">originalColor</span><span class="p">;</span> <span class="nx">setTimeout</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="nx">flash</span><span class="p">(</span><span class="nx">id</span><span class="p">,</span> <span class="nx">count</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="nx">originalColor</span><span class="p">);</span> <span class="c1">// 递归调用</span> <span class="p">},</span> <span class="mi">500</span><span class="p">);</span> <span class="c1">// 原始颜色持续500ms</span> <span class="p">},</span> <span class="mi">500</span><span class="p">);</span> <span class="c1">// 红色持续500ms</span> <span class="p">}</span> <span class="c1">// 调用案例:闪烁 3 次</span> <span class="nx">flash</span><span class="p">(</span><span class="dl">"</span><span class="s2">lang_str</span><span class="dl">"</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">'</span><span class="s1">lang_str</span><span class="dl">'</span><span class="p">).</span><span class="nx">style</span><span class="p">.</span><span class="nx">backgroundColor</span><span class="p">);</span> </code></pre></div></div> Tue, 26 Aug 2025 00:00:00 +0000 https://minsonlee.github.io/2025/08/a-simple-warning-flashing-demo https://minsonlee.github.io/2025/08/a-simple-warning-flashing-demo HTML Windows 自带的电池健康检测 <ol> <li><code class="language-plaintext highlighter-rouge">Win + R</code> 输入 <code class="language-plaintext highlighter-rouge">cmd</code></li> <li>在 <code class="language-plaintext highlighter-rouge">cmd</code> 界面输入 <code class="language-plaintext highlighter-rouge">powercfg /BATTERYREPORT</code>,然后等待一会儿就会生成一份 <code class="language-plaintext highlighter-rouge">HTML</code> 版的电池使用时间报告</li> <li>通过浏览器打开对应的文件即可查看</li> </ol> <p>电池容量情况:</p> <p><img src="/images/pig/20250319224059.png" alt="" /></p> <p>电池使用时长:</p> <p><img src="/images/pig/20250319224125.png" alt="" /></p> Tue, 17 Jun 2025 00:00:00 +0000 https://minsonlee.github.io/2025/06/windows-battery-health-report https://minsonlee.github.io/2025/06/windows-battery-health-report Windows SEO:通过 IndexNow 向 Bing 推送站点链接 <p>因为微软是 <code class="language-plaintext highlighter-rouge">ChatGPT</code> 的大投资人,<code class="language-plaintext highlighter-rouge">AI</code> 搜索虽然还没有非常好的一个模式,但是是一个必然趋势。但站点在 <code class="language-plaintext highlighter-rouge">Bing</code> 上的收录数据在 <a href="https://www.bing.com/webmasters/home">Bing Webmaster Tool</a> 看到不是很乐观。因此想通过 <code class="language-plaintext highlighter-rouge">IndexNow</code> 接口提交一次链接,触发 <code class="language-plaintext highlighter-rouge">Bing</code> 重新抓取一次,看看能否提高站点内容再 Bing 搜索或 ChatGPT AI 搜索结果中的可见性,从而提高流量。</p> <h2 id="indexnow">IndexNow</h2> <table> <tbody> <tr> <td>页面 [How to add IndexNow to your website</td> <td>Bing Webmaster Tools](https://www.bing.com/indexnow/getstarted) 中提到推荐使用 <code class="language-plaintext highlighter-rouge">IndexNow</code> 来向其推送站点的链接。</td> </tr> </tbody> </table> <p><a href="https://www.indexnow.org/index"><code class="language-plaintext highlighter-rouge">IndexNow.org</code></a> 中提到 <code class="language-plaintext highlighter-rouge">IndexNow</code> 是一个面向搜索引擎的简单协议(<code class="language-plaintext highlighter-rouge">Simple ping</code>),用于告知搜索引擎对应 <code class="language-plaintext highlighter-rouge">URL</code> 及其内容已被添加、更新或删除,从而帮助搜索引擎优先处理这些 <code class="language-plaintext highlighter-rouge">URL</code>,使对应搜索引擎能在其搜索结果中快速反映对应 URL 的结果。</p> <p>因为如果不用 <code class="language-plaintext highlighter-rouge">IndexNow</code>,那么站点内容变更之后,就只能变更站点地图,然后去搜索引擎管理控制台删除重新提交一次站点地图。传统方式的弊端明显:「针对性不足、浪费抓取值」 - 搜索引擎只能被动全部爬取站点地图链接,及时 URL 对应的页面内容没有变化也会被重新的爬取检查…</p> <p>理念确实是蛮好的,但是…发现只有 <code class="language-plaintext highlighter-rouge">Bing/Naver/Seznam.cz/Yandex/Yep</code> 寥寥几家搜索引擎组织接入了。<code class="language-plaintext highlighter-rouge">Naver</code> 是一个韩语搜索引擎(类似“百度”于中国而言),大头的搜索引擎就 <code class="language-plaintext highlighter-rouge">Bing</code> 一家,有些可惜。</p> <table> <tbody> <tr> <td>对应搜索引擎的 <code class="language-plaintext highlighter-rouge">IndexNow</code> 接口地址,可以通过 <a href="https://www.indexnow.org/searchengines.json">https://www.indexnow.org/searchengines.json</a> 地址获取。或查看文档:[Documentation for search engines</td> <td>IndexNow.org](https://www.indexnow.org/searchengines)。更多使用上的 FAQ 可以查看官方文档:[FAQ</td> <td>IndexNow.org](https://www.indexnow.org/faq)</td> </tr> </tbody> </table> <h2 id="bing-indexnow-接入">Bing IndexNow 接入</h2> <ol> <li>去 <code class="language-plaintext highlighter-rouge">https://www.bing.com/indexnow/getstarted</code> 页面下载一个 <code class="language-plaintext highlighter-rouge">IndexNow</code> 密钥文件(随机的 32 位字符串),上传到站点项目根目录。验证 <code class="language-plaintext highlighter-rouge">https://www.example.com/xxx.txt</code> 能否访问</li> <li>向对应 <code class="language-plaintext highlighter-rouge">https://&lt;searchengine&gt;/indexnow</code> 接口提交 <code class="language-plaintext highlighter-rouge">URL</code> <ul> <li><code class="language-plaintext highlighter-rouge">Get</code> 请求提交单条链接:<code class="language-plaintext highlighter-rouge">https://&lt;searchengine&gt;/indexnow?url=&lt;url&gt;&amp;key=xxxxx&amp;keyLocation=https://www.example.com/xxx.txt</code></li> <li><code class="language-plaintext highlighter-rouge">POST</code> 请求批量提交如下:<code class="language-plaintext highlighter-rouge">https://&lt;searchengine&gt;/indexnow?key=xxxxx&amp;keyLocation=https://www.example.com/xxx.txt</code></li> </ul> <p>POST /indexnow HTTP/1.1 Content-Type: application/json; charset=utf-8 Host: <searchengine> { "host": "www.example.com", "key": "7b1f89baa1204612a933816dce46bcbf", "keyLocation": "https://www.example.com/myIndexNowKey63638.txt", "urlList": [ "https://www.example.com/url1", "https://www.example.com/folder/url2", "https://www.example.com/url3" ] }</searchengine></p> </li> </ol> <h3 id="注意事项">注意事项</h3> <ol> <li>首次请求的时候,可能会报 403 错误,跟你配置没有任何关系,等一段时间,重新请求即可</li> </ol> <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> </span><span class="nl">"code"</span><span class="p">:</span><span class="w"> </span><span class="s2">"SiteVerificationNotCompleted"</span><span class="p">,</span><span class="w"> </span><span class="nl">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Site Verification is not completed. Please wait for some time for the verification to complete and try again."" } </span></code></pre></div></div> <ol> <li> <p><strong>如果提交成功,<code class="language-plaintext highlighter-rouge">indexnow</code> 接口是通过 <code class="language-plaintext highlighter-rouge">HTTP Code</code> 是否为 200 来判断的。没有任何响应 <code class="language-plaintext highlighter-rouge">body</code></strong></p> <p>HTTP/2 200 Cache-Control: no-cache Pragma: no-cache Expires: -1 X-AspNet-Version: 4.0.30319 X-Powered-By: ASP.NET X-Cache: CONFIG_NOCACHE Accept-CH: Sec-CH-UA-Arch, Sec-CH-UA-Bitness, Sec-CH-UA-Full-Version, Sec-CH-UA-Full-Version-List, Sec-CH-UA-Mobile, Sec-CH-UA-Model, Sec-CH-UA-Platform, Sec-CH-UA-Platform-Version X-MSEdge-Ref: Ref A: D4365A3C600C4724A380EDCD7DF5DD4B Ref B: HKBEDGE0912 Ref C: 2025-06-17T06:07:42Z Date: Tue, 17 Jun 2025 06:07:46 GMT Content-Length: 0</p> </li> <li> <p>如何验证真的提交成功了呢?</p> </li> </ol> <p>提交后,等待一小会儿,登录 <a href="https://www.bing.com/webmasters/indexnow?siteUrl=https://&lt;yourHost&gt;/"><code class="language-plaintext highlighter-rouge">Bing Webmaster Tool</code></a>(如果已经登录,记得刷新一下页面),切换对应站点。点击菜单栏中 <code class="language-plaintext highlighter-rouge">IndexNow</code> 即可看见今天提交的链接:</p> <p><img src="https://cdn.jsdelivr.net/gh/MinsonLee/minsonlee.github.io/images/pig/20250617153744.png" alt="Bing IndexNow" /></p> <h2 id="抓取测试结果">抓取测试结果</h2> <p>提交结果之后大约 1 分钟左右,Bing 确实是来站点进行了抓取,但 <code class="language-plaintext highlighter-rouge">ChatGPT</code> 抓取频率没有什么显著变化</p> <p><img src="https://cdn.jsdelivr.net/gh/MinsonLee/minsonlee.github.io/images/pig/20250617155827.png" alt="IndexNow Submit Result" /></p> Tue, 17 Jun 2025 00:00:00 +0000 https://minsonlee.github.io/2025/06/bing-seo-submit-your-urls-by-indexnow https://minsonlee.github.io/2025/06/bing-seo-submit-your-urls-by-indexnow seo Java 中如何告别面条式的 null 判断” <h1 id="java-中如何告别面条式的-null-判断">Java 中如何告别面条式的 null 判断</h1> <p>[TOC]</p> <h2 id="背景">背景</h2> <p>最近在写 <code class="language-plaintext highlighter-rouge">Java</code>,其中 <code class="language-plaintext highlighter-rouge">MongoDB</code> 中存了某个文档数据,「<strong>如果存在</strong>」结构是如下这样:</p> <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> </span><span class="nl">"localtion"</span><span class="p">:{</span><span class="w"> </span><span class="nl">"pickup"</span><span class="p">:{</span><span class="w"> </span><span class="nl">"region"</span><span class="p">:{</span><span class="w"> </span><span class="nl">"region_en"</span><span class="p">:</span><span class="s2">"China"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre></div></div> <p>现在需要判断如果 <code class="language-plaintext highlighter-rouge">region_en</code> 有值就拿出来,在 <code class="language-plaintext highlighter-rouge">PHP</code> 中用 <code class="language-plaintext highlighter-rouge">empty()</code> 函数处理即可:</p> <div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$regionEn</span> <span class="o">=</span> <span class="s1">''</span><span class="p">;</span> <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="k">empty</span><span class="p">(</span><span class="nv">$carDetail</span><span class="p">[</span><span class="s1">'location'</span><span class="p">][</span><span class="s1">'pickup'</span><span class="p">][</span><span class="s1">'region'</span><span class="p">][</span><span class="s1">'rengion_en'</span><span class="p">]))</span> <span class="p">{</span> <span class="nv">$regionEn</span> <span class="o">=</span> <span class="nv">$carDetail</span><span class="p">[</span><span class="s1">'location'</span><span class="p">][</span><span class="s1">'pickup'</span><span class="p">][</span><span class="s1">'region'</span><span class="p">][</span><span class="s1">'rengion_en'</span><span class="p">];</span> <span class="p">}</span> </code></pre></div></div> <p>由于刚开始写 <code class="language-plaintext highlighter-rouge">Java</code> 时没有留意,写 <code class="language-plaintext highlighter-rouge">carDetail.getLocation().getPickup().getRegion().getRegionEn()</code> 经常会报 <code class="language-plaintext highlighter-rouge">NullPointExceptions</code>(很多<code class="language-plaintext highlighter-rouge">Java</code>文章会称其为<code class="language-plaintext highlighter-rouge">NPE</code>).</p> <p>改为:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">String</span> <span class="n">regionEn</span> <span class="o">=</span> <span class="s">""</span><span class="o">;</span> <span class="k">if</span><span class="o">(</span> <span class="n">carDetail</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span> <span class="n">carDetail</span><span class="o">.</span><span class="na">getLocation</span><span class="o">()</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span> <span class="n">carDetail</span><span class="o">.</span><span class="na">getLocation</span><span class="o">().</span><span class="na">getPickup</span><span class="o">()</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span> <span class="n">carDetail</span><span class="o">.</span><span class="na">getLocation</span><span class="o">().</span><span class="na">getPickup</span><span class="o">().</span><span class="na">getRegion</span><span class="o">().</span><span class="na">getRegionEn</span><span class="o">()</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">)</span> <span class="o">{</span> <span class="n">regionEn</span> <span class="o">=</span> <span class="n">carDetail</span><span class="o">.</span><span class="na">getLocation</span><span class="o">().</span><span class="na">getPickup</span><span class="o">().</span><span class="na">getRegion</span><span class="o">().</span><span class="na">getRegionEn</span><span class="o">();</span> <span class="o">}</span> </code></pre></div></div> <p>或者有其他逻辑需要处理那么可能是此类嵌套判断 <code class="language-plaintext highlighter-rouge">NPE</code> 的情况:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">String</span> <span class="n">regionEn</span> <span class="o">=</span> <span class="s">""</span><span class="o">;</span> <span class="k">if</span><span class="o">(</span><span class="n">carDetail</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> <span class="c1">// 其他逻辑</span> <span class="k">if</span><span class="o">(</span><span class="n">carDetail</span><span class="o">.</span><span class="na">getLocation</span><span class="o">()</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> <span class="c1">// 其他逻辑</span> <span class="k">if</span><span class="o">(</span><span class="n">carDetail</span><span class="o">.</span><span class="na">getLocation</span><span class="o">().</span><span class="na">getPickup</span><span class="o">()</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> <span class="c1">// 其他逻辑</span> <span class="n">regionEn</span> <span class="o">=</span> <span class="n">carDetail</span><span class="o">.</span><span class="na">getLocation</span><span class="o">().</span><span class="na">getPickup</span><span class="o">().</span><span class="na">getRegion</span><span class="o">().</span><span class="na">getRegionEn</span><span class="o">()</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">?</span> <span class="n">carDetail</span><span class="o">.</span><span class="na">getLocation</span><span class="o">().</span><span class="na">getPickup</span><span class="o">().</span><span class="na">getRegion</span><span class="o">().</span><span class="na">getRegionEn</span><span class="o">()</span> <span class="o">:</span> <span class="s">""</span><span class="o">;</span> <span class="o">}</span> <span class="o">}</span> <span class="o">}</span> </code></pre></div></div> <p>在 <code class="language-plaintext highlighter-rouge">Java8</code> 中引入 <a href="https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html"><code class="language-plaintext highlighter-rouge">java.util.Optional</code> 工具类</a> 可以相对简洁的处理这种「面条式“if-else if”」的 <code class="language-plaintext highlighter-rouge">null</code> 判断代码。</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">String</span> <span class="n">regionEn</span> <span class="o">=</span> <span class="nc">Optional</span><span class="o">.</span><span class="na">ofNullable</span><span class="o">(</span><span class="n">carDetail</span><span class="o">)</span> <span class="o">.</span><span class="na">map</span><span class="o">(</span><span class="n">c</span> <span class="o">-&gt;</span> <span class="n">c</span><span class="o">.</span><span class="na">getLocation</span><span class="o">())</span> <span class="o">.</span><span class="na">map</span><span class="o">(</span><span class="n">l</span> <span class="o">-&gt;</span> <span class="n">l</span><span class="o">.</span><span class="na">getPickup</span><span class="o">())</span> <span class="o">.</span><span class="na">map</span><span class="o">(</span><span class="n">p</span> <span class="o">-&gt;</span> <span class="n">p</span><span class="o">.</span><span class="na">getRegionEn</span><span class="o">())</span> <span class="o">.</span><span class="na">orElse</span><span class="o">(</span><span class="s">"else"</span><span class="o">);</span> </code></pre></div></div> <h2 id="optional-类源码简读"><code class="language-plaintext highlighter-rouge">Optional</code> 类源码简读</h2> <ul> <li><a href="https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html">Java 8 <code class="language-plaintext highlighter-rouge">java.util.Optional</code> 工具类文档</a></li> <li><a href="https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/Optional.html">Java 20 <code class="language-plaintext highlighter-rouge">java.util.Optional</code> 工具类文档</a></li> <li><a href="https://github.com/openjdk/jdk/blob/d60352e26fd8b7e51eeaf299e3f88783b739b02a/src/java.base/share/classes/java/util/Optional.java">源码:src/java.base/share/classes/java/util/Optional.java</a></li> <li><a href="https://lw900925.github.io/java/java8-optional.html">Java 8新特性(三):Optional类</a></li> <li><a href="https://www.cnblogs.com/datamining-bio/p/13831000.html">Optional类与使用==判断null有什么区别?使用Optional类有什么优势?</a></li> </ul> <p><code class="language-plaintext highlighter-rouge">Optional</code> 主要是用于避免空指针异常(<code class="language-plaintext highlighter-rouge">NullPointerException</code>)的一个工具类(可看做是类似一种“语法糖”),可以通过链式调用的方式进行管道处理数据。</p> <p>通过 IDEA 简单的浏览一下源码的 <code class="language-plaintext highlighter-rouge">Structure</code>,该类比较简单:</p> <ol> <li><code class="language-plaintext highlighter-rouge">@jdk.internal.ValueBased</code> 标注了这是一个<a href="https://docs.oracle.com/javase/8/docs/api/java/lang/doc-files/ValueBased.html"> <code class="language-plaintext highlighter-rouge">value-based</code> 类,即:基于值的类</a> <ul> <li>什么是基于值的?只要“值”相同,这两个对象就应该被视为完全相同的对象</li> <li>注意事项是什么?不应该使用引用相等(如:<code class="language-plaintext highlighter-rouge">==</code> 操作符)去判断基于值的对象是否相等,而是使用 <code class="language-plaintext highlighter-rouge">equals()</code> 方法</li> <li>https://matthung0807.blogspot.com/2019/05/java-value-based-classes.html</li> </ul> </li> <li>该类有两个私有属性 <ul> <li>空 Optinal 类对象:<code class="language-plaintext highlighter-rouge">private static final Optional&lt;?&gt; EMPTY = new Optional&lt;&gt;(null)</code></li> <li>泛型 T 的 value 对象:<code class="language-plaintext highlighter-rouge">private final T value</code></li> </ul> </li> <li>该类构造方法是私有的,不能直接实例化</li> <li><strong>注意:<code class="language-plaintext highlighter-rouge">Optional</code> 并非解决繁杂的 <code class="language-plaintext highlighter-rouge">if-else if</code>,而是为了避免用 <code class="language-plaintext highlighter-rouge">if-else if</code> 的进行 <code class="language-plaintext highlighter-rouge">null</code> 空指针异常判断而诞生的!!!</strong></li> </ol> <h3 id="1获取-optional-对象">1、获取 <code class="language-plaintext highlighter-rouge">Optional</code> 对象</h3> <ol> <li>获取一定不为空的 <code class="language-plaintext highlighter-rouge">Optional&lt;T&gt;</code> 对象:<code class="language-plaintext highlighter-rouge">Optional.of(T value)</code>,表示 <code class="language-plaintext highlighter-rouge">value</code> 一定为非空的,如果为 <code class="language-plaintext highlighter-rouge">null</code> 会抛出 <code class="language-plaintext highlighter-rouge">NullPointerException</code> 异常</li> <li>获取空的 <code class="language-plaintext highlighter-rouge">Optional&lt;T&gt;</code> 对象:<code class="language-plaintext highlighter-rouge">Optional.empty()</code>,返回 <code class="language-plaintext highlighter-rouge">(Optional&lt;T&gt;) new Optional&lt;&gt;()</code> 对象<strong>「这个空 Optional 对象的意义是什么呢??」</strong></li> <li>获取可能不为空的 <code class="language-plaintext highlighter-rouge">Optional&lt;T&gt;</code> 对象:<code class="language-plaintext highlighter-rouge">Optional.ofNullable(T value)</code>,其实就是 <code class="language-plaintext highlighter-rouge">return value == null ? Optional.empty() : Optional.of(value)</code></li> </ol> <h3 id="2过滤转换数据的方法">2、过滤、转换数据的方法</h3> <pre><code class="language-伪代码">if (value != null) { // true 处理逻辑 } else { // false 处理逻辑 } </code></pre> <ul> <li><code class="language-plaintext highlighter-rouge">filter(Predicate&lt;? super T&gt; predicate)</code> : 如果 <code class="language-plaintext highlighter-rouge">value!=null</code> 或者 <code class="language-plaintext highlighter-rouge">predicate.test(value)</code> 结果为 true 返回自身,否则返回 <code class="language-plaintext highlighter-rouge">Optional.empty</code></li> <li><code class="language-plaintext highlighter-rouge">ifPresent(Consumer&lt;? super T&gt; consumer)</code>:基于值 <code class="language-plaintext highlighter-rouge">null != null</code> 的情况执行<code class="language-plaintext highlighter-rouge">if</code> 逻辑</li> <li><code class="language-plaintext highlighter-rouge">ifPresentOrElse(Consumer&lt;? super T&gt; consumer)</code> : Java 9 新增方法,基于值的 <code class="language-plaintext highlighter-rouge">null</code> 情况,处理 <code class="language-plaintext highlighter-rouge">if-else</code> 的情况</li> <li><code class="language-plaintext highlighter-rouge">map(Function&lt;? super T, ? extends U&gt; mapper)</code> : 如果<strong>当前对象的 value 值</strong>存在则将 <code class="language-plaintext highlighter-rouge">mapper.apply()</code> 方法的结果包装为一个 <code class="language-plaintext highlighter-rouge">Optional</code> 对象返回,即:「Optional&lt;Optional&lt;U&gt;&gt;」 对象</li> <li><code class="language-plaintext highlighter-rouge">flatMap(Function&lt;? super T, Optional&lt;U&gt;&gt; mapper)</code> 当 <code class="language-plaintext highlighter-rouge">value != null</code> 时返回 <code class="language-plaintext highlighter-rouge">mapper.apply()</code> 结果「Optional&lt;U&gt;」对象。否则返回 <code class="language-plaintext highlighter-rouge">Optional.empty()</code></li> <li><code class="language-plaintext highlighter-rouge">or(Supplier&lt;? extends Optional&lt;? extends T&gt;&gt; supplier)</code> : Java 9 新增方法,如果 <code class="language-plaintext highlighter-rouge">value</code> 为 <code class="language-plaintext highlighter-rouge">null</code> 则返回 <code class="language-plaintext highlighter-rouge">(Optional&lt;T&gt;) supplier.get()</code></li> <li><code class="language-plaintext highlighter-rouge">stream()</code>:Java 9 新增方法,返回一个 <code class="language-plaintext highlighter-rouge">Stream&lt;T&gt;</code> 流对象</li> </ul> <p><strong>!!!注意:</strong></p> <ol> <li><code class="language-plaintext highlighter-rouge">Predicate</code>、<code class="language-plaintext highlighter-rouge">Consumer</code>、<code class="language-plaintext highlighter-rouge">Consumer</code>、<code class="language-plaintext highlighter-rouge">Function</code> 都是函数式接口,其中只有 1 个方法,因此可以用 <code class="language-plaintext highlighter-rouge">Lambda</code> 表达式来匿名实现</li> <li><strong>这些过滤、转换方法,传入参数是 <code class="language-plaintext highlighter-rouge">@FunctionalInterface</code> 的匿名函数式接口实现,如果传 <code class="language-plaintext highlighter-rouge">null</code> 都会报异常</strong></li> <li><strong>map、flatMap 两个方法是有差异的</strong></li> </ol> <h3 id="3获取-optional-的值">3、获取 <code class="language-plaintext highlighter-rouge">Optional</code> 的值</h3> <ul> <li><code class="language-plaintext highlighter-rouge">get()</code> : 获取 <code class="language-plaintext highlighter-rouge">value</code> 值,如果 <code class="language-plaintext highlighter-rouge">value</code> 为 <code class="language-plaintext highlighter-rouge">null</code> 则抛出 <code class="language-plaintext highlighter-rouge">NoSuchElementException</code> 异常,因此使用的时候需要确定对象的 <code class="language-plaintext highlighter-rouge">value</code> 是否确实有值</li> <li><code class="language-plaintext highlighter-rouge">orElse(T other)</code> : 如果 <code class="language-plaintext highlighter-rouge">value</code> 不为 <code class="language-plaintext highlighter-rouge">null</code> 则返回 <code class="language-plaintext highlighter-rouge">value</code>,否则返回 <code class="language-plaintext highlighter-rouge">other</code>「PHP 中的三元运算符 <code class="language-plaintext highlighter-rouge">a ?: b</code>,若 a true 则返回 a,否则返回 b」</li> <li><code class="language-plaintext highlighter-rouge">orElseGet(Supplier&lt;? extends T&gt; other)</code> : 如果 <code class="language-plaintext highlighter-rouge">value</code> 为 <code class="language-plaintext highlighter-rouge">null</code> 则调用实现了 <code class="language-plaintext highlighter-rouge">Supplier Interface</code> 的 <code class="language-plaintext highlighter-rouge">other.get()</code> 方法返回的默认内容</li> <li><code class="language-plaintext highlighter-rouge">orElseThrow(Supplier&lt;? extends X&gt; exceptionSupplier)</code> : 如果值为 <code class="language-plaintext highlighter-rouge">null</code> 则 <code class="language-plaintext highlighter-rouge">throw exceptionSupplier.get()</code> (自定义异常的 Message 信息)</li> <li><code class="language-plaintext highlighter-rouge">toString()</code> : 适合记录 log 的时候,返回字符串<code class="language-plaintext highlighter-rouge">String.format("Optional[%s]", value)</code> 或 字符串 <code class="language-plaintext highlighter-rouge">Optional.empty</code></li> <li><code class="language-plaintext highlighter-rouge">hashCode()</code> : 返回 <code class="language-plaintext highlighter-rouge">value</code> 值的 <code class="language-plaintext highlighter-rouge">HashCode</code>,如果值为 <code class="language-plaintext highlighter-rouge">null</code> 则返回 <code class="language-plaintext highlighter-rouge">0</code></li> <li><code class="language-plaintext highlighter-rouge">orElseThrow()</code> : <code class="language-plaintext highlighter-rouge">Java 10</code> 新增的方法,如果值为 <code class="language-plaintext highlighter-rouge">null</code> 抛出 ` NoSuchElementException(“No value present”)` 异常(难道官方发现大家都抛出类似的信息,为了方便所以直接加了一个这样的方法??)</li> </ul> <h3 id="4value-判断方法">4、<code class="language-plaintext highlighter-rouge">value</code> 判断方法</h3> <ul> <li><code class="language-plaintext highlighter-rouge">isPresent()</code> : 判断 <code class="language-plaintext highlighter-rouge">value != null</code> 则返回 <code class="language-plaintext highlighter-rouge">true</code></li> <li><code class="language-plaintext highlighter-rouge">equals()</code> : 判断两个<code class="language-plaintext highlighter-rouge">Optional</code>对象的值是否相等</li> <li><code class="language-plaintext highlighter-rouge">isEmpty()</code> : <code class="language-plaintext highlighter-rouge">Java 11</code> 添加的方法,用于判断 <code class="language-plaintext highlighter-rouge">Value</code> 是否为 <code class="language-plaintext highlighter-rouge">null</code></li> </ul> <h2 id="疑问">疑问</h2> <h3 id="1为什么要有-optionalempty-返回一个空-optional-对象">1、为什么要有 <code class="language-plaintext highlighter-rouge">Optional.empty()</code> 返回一个空 Optional 对象??</h3> <p>看到很多示例对 <code class="language-plaintext highlighter-rouge">Optional.empty()</code> 的解释都是一笔带过,因为理论上我们应该不需要主动的获取一个空对象呀…很纳闷为啥要有这么一个空对象呢?什么场景会用到呢?</p> <p><code class="language-plaintext highlighter-rouge">Optional.empty()</code> 生成一个没有包含任何值的 <code class="language-plaintext highlighter-rouge">Optional</code> 对象,当期望某个方法可能不会返回结果时,你可以返回 <code class="language-plaintext highlighter-rouge">Optional.empty()</code>,而不是返回 <code class="language-plaintext highlighter-rouge">null</code>。这样的话,调用者可以使用 <code class="language-plaintext highlighter-rouge">Optional</code> 的方法(如 <code class="language-plaintext highlighter-rouge">isPresent()</code> 或 <code class="language-plaintext highlighter-rouge">ifPresent()</code>)来处理可能的空值,而不必进行显式的 <code class="language-plaintext highlighter-rouge">null</code> 检查。</p> <p>例如,考虑一个方法,可能找不到期望的数据并返回 <code class="language-plaintext highlighter-rouge">null</code>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="nc">User</span> <span class="nf">findUserByName</span><span class="o">(</span><span class="nc">String</span> <span class="n">name</span><span class="o">)</span> <span class="o">{</span> <span class="c1">// 如果找不到用户,返回 null</span> <span class="o">}</span> </code></pre></div></div> <p>现在改用 <code class="language-plaintext highlighter-rouge">Optional</code>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="nc">Optional</span><span class="o">&lt;</span><span class="nc">User</span><span class="o">&gt;</span> <span class="nf">findUserByName</span><span class="o">(</span><span class="nc">String</span> <span class="n">name</span><span class="o">)</span> <span class="o">{</span> <span class="c1">// 如果找不到用户,返回 Optional.empty()</span> <span class="o">}</span> </code></pre></div></div> <p>对于调用者来说,处理方法如下:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Optional</span><span class="o">&lt;</span><span class="nc">User</span><span class="o">&gt;</span> <span class="n">optionalUser</span> <span class="o">=</span> <span class="n">findUserByName</span><span class="o">(</span><span class="s">"test"</span><span class="o">);</span> <span class="k">if</span> <span class="o">(</span><span class="n">optionalUser</span><span class="o">.</span><span class="na">isPresent</span><span class="o">())</span> <span class="o">{</span> <span class="nc">User</span> <span class="n">user</span> <span class="o">=</span> <span class="n">optionalUser</span><span class="o">.</span><span class="na">get</span><span class="o">();</span> <span class="c1">// 执行其他操作</span> <span class="o">}</span> <span class="k">else</span> <span class="o">{</span> <span class="c1">// 用户不存在的情况下的处理</span> <span class="o">}</span> </code></pre></div></div> <p>但是这种方法和直接使用 <code class="language-plaintext highlighter-rouge">if( user != null) {} else {}</code> 没有什么区别呀…</p> <p>但如果配上 <code class="language-plaintext highlighter-rouge">ifPresent()</code> 更简洁的方式:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">findUserByName</span><span class="o">(</span><span class="s">"test"</span><span class="o">).</span><span class="na">ifPresent</span><span class="o">(</span><span class="n">user</span> <span class="o">-&gt;</span> <span class="o">{</span> <span class="c1">// 执行其他操作</span> <span class="o">});</span> </code></pre></div></div> <p>似乎稍微有一些看头,让代码段可以更加集中于“逻辑的提现”,而避免了很多面条式的 <code class="language-plaintext highlighter-rouge">if-else</code> 堆积。</p> <p>但上面的代码,<code class="language-plaintext highlighter-rouge">else</code> 的逻辑会丢失。因此在 <code class="language-plaintext highlighter-rouge">Java 9</code> 中新增了 <code class="language-plaintext highlighter-rouge">ifPresentOrElse()</code> 方法:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">findUserByName</span><span class="o">(</span><span class="s">"test"</span><span class="o">).</span><span class="na">ifPresentOrElse</span><span class="o">(</span> <span class="n">user</span> <span class="o">-&gt;</span> <span class="o">{</span> <span class="c1">// 执行存在用户时的操作</span> <span class="o">},</span> <span class="o">()</span> <span class="o">-&gt;</span> <span class="o">{</span> <span class="c1">// 执行用户不存在时的操作,else 的逻辑</span> <span class="o">}</span> <span class="o">);</span> </code></pre></div></div> <h3 id="2map-和-flatmap-的区别">2、<code class="language-plaintext highlighter-rouge">map()</code> 和 <code class="language-plaintext highlighter-rouge">flatMap()</code> 的区别?</h3> <p>由于刚开始接触 Java,对泛型了解不多,因此被我忽略的点在于:<code class="language-plaintext highlighter-rouge">map(Function&lt;? super T, ? extends U&gt; mapper)</code> 和 <code class="language-plaintext highlighter-rouge">flatMap(Function&lt;? super T, Optional&lt;U&gt;&gt; mapper)</code> 的参数 <code class="language-plaintext highlighter-rouge">mapper</code> 的类型。</p> <p>两个函数的源码如下:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span><span class="o">&lt;</span><span class="no">U</span><span class="o">&gt;</span> <span class="nc">Optional</span><span class="o">&lt;</span><span class="no">U</span><span class="o">&gt;</span> <span class="nf">map</span><span class="o">(</span><span class="nc">Function</span><span class="o">&lt;?</span> <span class="kd">super</span> <span class="no">T</span><span class="o">,</span> <span class="o">?</span> <span class="kd">extends</span> <span class="no">U</span><span class="o">&gt;</span> <span class="n">mapper</span><span class="o">)</span> <span class="o">{</span> <span class="nc">Objects</span><span class="o">.</span><span class="na">requireNonNull</span><span class="o">(</span><span class="n">mapper</span><span class="o">);</span> <span class="k">if</span> <span class="o">(!</span><span class="n">isPresent</span><span class="o">())</span> <span class="k">return</span> <span class="nf">empty</span><span class="o">();</span> <span class="k">else</span> <span class="o">{</span> <span class="c1">// 此处对 mapper.apply(value) 的结果再次进行了 Optional.ofNullable 的包装</span> <span class="k">return</span> <span class="nc">Optional</span><span class="o">.</span><span class="na">ofNullable</span><span class="o">(</span><span class="n">mapper</span><span class="o">.</span><span class="na">apply</span><span class="o">(</span><span class="n">value</span><span class="o">));</span> <span class="o">}</span> <span class="o">}</span> <span class="kd">public</span><span class="o">&lt;</span><span class="no">U</span><span class="o">&gt;</span> <span class="nc">Optional</span><span class="o">&lt;</span><span class="no">U</span><span class="o">&gt;</span> <span class="nf">flatMap</span><span class="o">(</span><span class="nc">Function</span><span class="o">&lt;?</span> <span class="kd">super</span> <span class="no">T</span><span class="o">,</span> <span class="nc">Optional</span><span class="o">&lt;</span><span class="no">U</span><span class="o">&gt;&gt;</span> <span class="n">mapper</span><span class="o">)</span> <span class="o">{</span> <span class="nc">Objects</span><span class="o">.</span><span class="na">requireNonNull</span><span class="o">(</span><span class="n">mapper</span><span class="o">);</span> <span class="k">if</span> <span class="o">(!</span><span class="n">isPresent</span><span class="o">())</span> <span class="k">return</span> <span class="nf">empty</span><span class="o">();</span> <span class="k">else</span> <span class="o">{</span> <span class="c1">// 直接返回了 mapper.apply(value) 的结果,只要结果不是 null 即可,否则会抛 NPE</span> <span class="k">return</span> <span class="nc">Objects</span><span class="o">.</span><span class="na">requireNonNull</span><span class="o">(</span><span class="n">mapper</span><span class="o">.</span><span class="na">apply</span><span class="o">(</span><span class="n">value</span><span class="o">));</span> <span class="o">}</span> <span class="o">}</span> </code></pre></div></div> <ul> <li><code class="language-plaintext highlighter-rouge">Function&lt;? super T, ? extends U&gt; mapper</code> 表示 <code class="language-plaintext highlighter-rouge">mapper</code> 对象必须处理 T 类型,但接口是返回 U 类型</li> <li><code class="language-plaintext highlighter-rouge">Function&lt;? super T, Optional&lt;U&gt; mapper</code> 表示 <code class="language-plaintext highlighter-rouge">mapper</code> 对象必须处理 T 类型,但接口是返回 <code class="language-plaintext highlighter-rouge">Optional&lt;U&gt;</code> 类型</li> <li><code class="language-plaintext highlighter-rouge">map()</code> 和 <code class="language-plaintext highlighter-rouge">flatMap()</code> 都是要接收一个「实现了 <code class="language-plaintext highlighter-rouge">Fuction</code> 函数式接口的对象」的形参</li> </ul> <p>依然通过 <code class="language-plaintext highlighter-rouge">CarDetail</code> 写一个 <code class="language-plaintext highlighter-rouge">Demo</code>:</p> <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">package</span> <span class="nn">optional</span><span class="o">;</span> <span class="kn">import</span> <span class="nn">java.util.Optional</span><span class="o">;</span> <span class="kd">public</span> <span class="kd">class</span> <span class="nc">DemoOptional</span> <span class="o">{</span> <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span> <span class="nc">CarDetail</span> <span class="n">carDetail</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span> <span class="nc">String</span> <span class="n">regionEn</span> <span class="o">=</span> <span class="nc">Optional</span><span class="o">.</span><span class="na">ofNullable</span><span class="o">(</span><span class="n">carDetail</span><span class="o">)</span> <span class="o">.</span><span class="na">flatMap</span><span class="o">(</span><span class="nl">CarDetail:</span><span class="o">:</span><span class="n">getLocation</span><span class="o">)</span> <span class="c1">// 此处用的是:flatMap(),`CarDetail.location` 是 `Optional&lt;T&gt;`</span> <span class="c1">// .map(x -&gt; x.orElse(new Location()).getPickup()) // 如果 flatMap() 想用 map() 代替</span> <span class="o">.</span><span class="na">map</span><span class="o">(</span><span class="nl">Location:</span><span class="o">:</span><span class="n">getPickup</span><span class="o">)</span> <span class="c1">// 此处用的是:`map()`,`Location.pickup` 是一个 `Region` 实体</span> <span class="o">.</span><span class="na">map</span><span class="o">(</span><span class="nl">Region:</span><span class="o">:</span><span class="n">getRegionEn</span><span class="o">)</span> <span class="o">.</span><span class="na">orElse</span><span class="o">(</span><span class="s">"else"</span><span class="o">);</span> <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">regionEn</span><span class="o">);</span> <span class="o">}</span> <span class="kd">public</span> <span class="kd">static</span> <span class="kd">class</span> <span class="nc">CarDetail</span> <span class="o">{</span> <span class="kd">private</span> <span class="nc">Optional</span><span class="o">&lt;</span><span class="nc">Location</span><span class="o">&gt;</span> <span class="n">location</span><span class="o">;</span> <span class="kd">public</span> <span class="nc">Optional</span><span class="o">&lt;</span><span class="nc">Location</span><span class="o">&gt;</span> <span class="nf">getLocation</span><span class="o">()</span> <span class="o">{</span><span class="k">return</span> <span class="n">location</span><span class="o">;}</span> <span class="kd">public</span> <span class="nc">CarDetail</span> <span class="nf">setLocation</span><span class="o">(</span><span class="nc">Location</span> <span class="n">location</span><span class="o">)</span> <span class="o">{</span><span class="k">this</span><span class="o">.</span><span class="na">location</span> <span class="o">=</span> <span class="nc">Optional</span><span class="o">.</span><span class="na">ofNullable</span><span class="o">(</span><span class="n">location</span><span class="o">);</span> <span class="k">return</span> <span class="k">this</span><span class="o">;}</span> <span class="o">}</span> <span class="kd">public</span> <span class="kd">static</span> <span class="kd">class</span> <span class="nc">Location</span> <span class="o">{</span> <span class="kd">private</span> <span class="nc">Region</span> <span class="n">pickup</span><span class="o">;</span> <span class="kd">public</span> <span class="nc">Region</span> <span class="nf">getPickup</span><span class="o">()</span> <span class="o">{</span><span class="k">return</span> <span class="n">pickup</span><span class="o">;}</span> <span class="kd">public</span> <span class="nc">Location</span> <span class="nf">setPickup</span><span class="o">(</span><span class="nc">Region</span> <span class="n">pickup</span><span class="o">)</span> <span class="o">{</span><span class="k">this</span><span class="o">.</span><span class="na">pickup</span> <span class="o">=</span> <span class="n">pickup</span><span class="o">;</span><span class="k">return</span> <span class="k">this</span><span class="o">;}</span> <span class="o">}</span> <span class="kd">public</span> <span class="kd">static</span> <span class="kd">class</span> <span class="nc">Region</span> <span class="o">{</span> <span class="kd">private</span> <span class="nc">String</span> <span class="n">regionEn</span><span class="o">;</span> <span class="nc">Region</span><span class="o">(</span><span class="nc">String</span> <span class="n">regionEn</span><span class="o">)</span> <span class="o">{</span><span class="k">this</span><span class="o">.</span><span class="na">regionEn</span> <span class="o">=</span> <span class="n">regionEn</span><span class="o">;}</span> <span class="kd">public</span> <span class="nc">String</span> <span class="nf">getRegionEn</span><span class="o">()</span> <span class="o">{</span><span class="k">return</span> <span class="n">regionEn</span><span class="o">;}</span> <span class="o">}</span> <span class="o">}</span> </code></pre></div></div> <ol> <li><code class="language-plaintext highlighter-rouge">Optional.ofNullable(carDetail)</code> 得到了一个 <code class="language-plaintext highlighter-rouge">Optional&lt;CarDetail&gt;</code> 的对象</li> <li>由于 <code class="language-plaintext highlighter-rouge">CarDetail.location</code> 本身已经是一个 <code class="language-plaintext highlighter-rouge">Optional&lt;Location&gt;</code> 对象</li> <li>因此 <code class="language-plaintext highlighter-rouge">Optional.ofNullable(carDetail).map(CarDetail::getLocation)</code> 得到的将是一个 <code class="language-plaintext highlighter-rouge">Optional.ofNullable(CarDetail::getLocation)</code> 的对象</li> <li>而 <code class="language-plaintext highlighter-rouge">Optional.ofNullable(carDetail).flapMap(CarDetail::getLocation)</code> 得到的将是一个 <code class="language-plaintext highlighter-rouge">CarDetail::getLocation</code> 对象</li> </ol> <p><code class="language-plaintext highlighter-rouge">Java</code> 是希望对开发者屏蔽 「<strong>指针</strong>」这个复杂概念的,但是偏偏返回了 <code class="language-plaintext highlighter-rouge">null</code>…这是一个「空指针」,因此出现 <code class="language-plaintext highlighter-rouge">Optional</code> 类希望能用 <code class="language-plaintext highlighter-rouge">Optional.empty()</code> 来屏蔽返回真正 <code class="language-plaintext highlighter-rouge">null</code> 的情况。</p> <p>所以当我们选择 <code class="language-plaintext highlighter-rouge">map() or flipMap()</code> 的时候,即:面临 <strong>「“对象”嵌套“对象”」</strong> 情况下的要看 <strong>内部嵌套的这个对象是一个 <code class="language-plaintext highlighter-rouge">Optional&lt;T&gt;</code> 还是一个其他的实体对象?</strong></p> <ul> <li><a href="https://blog.csdn.net/qq_35634181/article/details/101109300">java中Optional的应用,以及map和flatMap的区别</a></li> <li><a href="https://segmentfault.com/a/1190000040491970">Optionalの应用 &amp;&amp; flatMap和map的区别</a></li> <li><a href="https://juejin.cn/post/7149345919077449764#heading-4">告别丑陋判空,一个Optional类搞定</a></li> </ul> Thu, 03 Aug 2023 00:00:00 +0000 https://minsonlee.github.io/2023/08/java-optional https://minsonlee.github.io/2023/08/java-optional java 是时候清理你的 Git 分支了” <h2 id="背景">背景</h2> <ol> <li>历史原因,拆分了 20 几个仓库出来,仓库太多而人员太少,没有人维护项目分支的管理</li> <li><code class="language-plaintext highlighter-rouge">Git</code> 分支的成本太廉价,创建且不删除的代价很小</li> <li><code class="language-plaintext highlighter-rouge">Windows</code> 上的 GUI 工具太给力,轻易就能搜索对应的分支,但在 <code class="language-plaintext highlighter-rouge">Linux</code> 下 <code class="language-plaintext highlighter-rouge">Git GUI Tools</code> 没什么很好的工具</li> <li>开发人员没有良好的习惯,自己创建的分支经常不删除、经常在过期的迭代分支调试代码…</li> </ol> <p>长期下来,导致分支泛滥,最严重的一个历史项目居然积压了 256 个无效分支。目前后端团队就 10 来人,因此 95% 的分支估计都是没什么用的!</p> <p>很多已经合并到了 <code class="language-plaintext highlighter-rouge">origin/master</code> 没有删除,很多虽然没有合并到 <code class="language-plaintext highlighter-rouge">origin/master</code> 但最后一次提交都要追溯到 2019-2022 年期间去了。</p> <h2 id="期望">期望</h2> <p>写一个脚本完成以下目标:</p> <ol> <li>自动拉取最新稳定项目的主干分支(master)</li> <li>清除本地已经丢失了的远程追踪的分支及其远程追踪记录</li> <li>提醒对应的开发人员及时清理已经合并到了 origin/master 的远程分支</li> <li>提醒对应的开发人员及时清理已经远远落后 origin/master 的远程分支</li> </ol> <h2 id="解决方案">解决方案</h2> <p>清明假期间写了一个脚本,自动清理 1100+ 个分支。</p> <p>很久没发文,保号发一篇,脚本如下:</p> <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span> <span class="c">#title : git-branch-alert</span> <span class="c">#description : Git 已合并/过期分支清理提醒脚本</span> <span class="c">#author : limingshuang</span> <span class="c">#date : 2023/04/05</span> <span class="c">#version : 1.0.1</span> <span class="c">#change-list : 2023-04-05 v1.0.0 发布脚本</span> <span class="c">#change-list : 2023-05-25 v1.0.1 修复不同目录 log 文件会被覆盖问题</span> <span class="c">#===============================================</span> <span class="c"># 定义项目目录</span> <span class="nv">project_path</span><span class="o">=</span><span class="s1">'/mnt/d/htdocs'</span> <span class="k">if</span> <span class="o">[</span> <span class="nv">$1</span> <span class="o">]</span><span class="p">;</span><span class="k">then </span><span class="nv">project_path</span><span class="o">=</span><span class="nv">$1</span> <span class="k">fi</span> <span class="c"># 定义默认检查最深目录层级</span> <span class="nv">level</span><span class="o">=</span>4 <span class="k">if</span> <span class="o">[</span> <span class="nv">$2</span> <span class="o">]</span><span class="p">;</span><span class="k">then </span><span class="nv">level</span><span class="o">=</span><span class="nv">$2</span> <span class="k">fi</span> <span class="c"># 定义过期分支时间参数,默认:非当前年的分支都视为过期分支</span> <span class="nv">timeReg</span><span class="o">=</span><span class="si">$(</span><span class="nb">date</span> +%Y<span class="si">)</span><span class="p">;</span> <span class="k">if</span> <span class="o">[</span> <span class="nv">$3</span> <span class="o">]</span><span class="p">;</span><span class="k">then </span><span class="nv">timeReg</span><span class="o">=</span><span class="nv">$3</span> <span class="k">fi</span> <span class="c"># 定义 log 输出文件</span> <span class="nv">log</span><span class="o">=</span><span class="s1">'/tmp/project-branch-alert-log_'</span><span class="si">$(</span><span class="nb">date</span> +%Y%m%d<span class="si">)</span><span class="s1">'.log'</span> <span class="k">if</span> <span class="o">[</span> <span class="o">!</span> <span class="nt">-a</span> <span class="nv">$log</span> <span class="o">]</span><span class="p">;</span> <span class="k">then </span><span class="nb">touch</span> <span class="nv">$log</span> <span class="nb">echo</span> <span class="nt">-e</span> <span class="s2">"创建 </span><span class="nv">$log</span><span class="se">\n</span><span class="s2">"</span> <span class="k">fi </span><span class="nb">cat</span> /dev/null <span class="o">&gt;</span> <span class="nv">$log</span> <span class="c"># 定义主干远程分支, 一般都为 master,Github 现在为了避嫌改为了 main</span> <span class="nv">master</span><span class="o">=</span><span class="s1">'master'</span> <span class="c"># 定义通用 Git命令</span> <span class="nv">specialBranch</span><span class="o">=</span><span class="s2">"|feature-k8s-supplier-api|feature-</span><span class="si">$(</span><span class="nb">date</span> +<span class="s1">'%Y'</span><span class="si">)</span><span class="s2">"</span> <span class="nv">keepBranch</span><span class="o">=</span><span class="s1">'dev|master|main|HEAD|-&gt;'</span> keepBranch+<span class="o">=</span><span class="nv">$specialBranch</span> <span class="c"># 自动拉取当前分支</span> <span class="nv">fb</span><span class="o">=</span><span class="s1">'git pull $(git remote) $(git symbolic-ref --short -q HEAD)'</span> <span class="c"># 检查远程分支列表中,有哪些远程分支是没有合并到当前所指向的远程分支的</span> <span class="nv">cm</span><span class="o">=</span><span class="s1">'git branch -r --format="%(refname:short) %(authorname) (%(committerdate:short))" --merged $(git remote)/$(git symbolic-ref --short -q HEAD) | grep -vE "$keepBranch"'</span> <span class="c"># 检查过期分支命令定义</span> <span class="nv">cv</span><span class="o">=</span><span class="s1">'git for-each-ref refs/remotes/ --format="%(refname:short) %(authorname) (%(committerdate:short))" | grep -Ev "$keepBranch|$timeReg"'</span> <span class="k">function </span>action<span class="o">()</span> <span class="o">{</span> <span class="nb">echo</span> <span class="nt">-e</span> <span class="s2">"</span><span class="se">\n</span><span class="s2">更新:</span><span class="nv">$1</span><span class="s2">"</span> <span class="c"># reset 当前工作域自动 fetch 远程信息</span> <span class="c"># 清理掉 .git/refs/remotes/origin/HEAD 分支文件</span> <span class="c"># `origin/HEAD` 并不是一个真正的分支,而是指向远程仓库中默认分支的引用,它通常指向 `origin/master` 或 `origin/main`,具体取决于你的远程仓库的默认分支名称</span> <span class="c"># `git remote set-head -d origin` 可以删除本地仓库中的 origin/HEAD 分支</span> <span class="nb">cd</span> <span class="nv">$1</span> <span class="o">&amp;&amp;</span> git reset <span class="nt">--hard</span> HEAD <span class="o">&amp;&amp;</span> git fetch <span class="nt">-pa</span> <span class="si">$(</span>git remote<span class="si">)</span> <span class="o">&amp;&amp;</span> git worktree prune <span class="o">&amp;&amp;</span> git remote set-head <span class="nt">-d</span> <span class="si">$(</span>git remote<span class="si">)</span> <span class="c"># fix:处理 github 仓库将 master 改为 main,导致上游分支丢失问题</span> <span class="c"># 自动将本地 master 切换为 main 分支</span> <span class="k">if</span> <span class="o">[[</span> <span class="sb">`</span>git symbolic-ref <span class="nt">--short</span> <span class="nt">-q</span> HEAD<span class="sb">`</span> <span class="o">=</span> <span class="s1">'master'</span> <span class="o">&amp;&amp;</span> <span class="sb">`</span>git branch <span class="nt">-r</span> | <span class="nb">grep </span>origin/main | <span class="nb">wc</span> <span class="nt">-l</span><span class="sb">`</span> <span class="o">!=</span> 0 <span class="o">]]</span><span class="p">;</span><span class="k">then </span><span class="nv">master</span><span class="o">=</span><span class="s1">'main'</span> <span class="k">elif</span> <span class="o">[[</span> <span class="sb">`</span>git symbolic-ref <span class="nt">--short</span> <span class="nt">-q</span> HEAD<span class="sb">`</span> <span class="o">!=</span> <span class="s1">'master'</span> <span class="o">&amp;&amp;</span> <span class="sb">`</span>git branch <span class="nt">-r</span> | <span class="nb">grep </span>origin/master | <span class="nb">wc</span> <span class="nt">-l</span><span class="sb">`</span> <span class="o">!=</span> 0 <span class="o">]]</span><span class="p">;</span><span class="k">then </span><span class="nv">master</span><span class="o">=</span><span class="s1">'master'</span> <span class="k">fi</span> <span class="c"># 先更新当前分支,然后切到主分支, 更新主干分支</span> <span class="nb">eval</span> <span class="nv">$fb</span> <span class="k">if</span> <span class="o">[[</span> <span class="sb">`</span>git symbolic-ref <span class="nt">--short</span> <span class="nt">-q</span> HEAD<span class="sb">`</span> <span class="o">!=</span> <span class="nv">$master</span> <span class="o">]]</span><span class="p">;</span><span class="k">then </span>git switch <span class="nv">$master</span> <span class="o">&amp;&amp;</span> <span class="nb">eval</span> <span class="nv">$fb</span> <span class="k">fi</span> <span class="c"># 清理本地已经丢失了远程分支的分支</span> git branch <span class="nt">-v</span> | <span class="nb">grep</span> <span class="nt">-F</span> <span class="o">[</span>gone] | <span class="nb">awk</span> <span class="s1">'{print $1}'</span> | xargs <span class="nt">-I</span> <span class="o">{}</span> bash <span class="nt">-c</span> <span class="s2">"if [ ! -z {} ];then git branch -D {};fi"</span> <span class="c"># 检查没有合并到当前分支所在远程分支</span> <span class="nb">local </span><span class="nv">cmLog</span><span class="o">=</span><span class="s1">''</span> <span class="nv">cmLog</span><span class="o">=</span><span class="si">$(</span><span class="nb">eval</span> <span class="nv">$cm</span><span class="si">)</span> <span class="k">if</span> <span class="o">[</span> <span class="o">!</span> <span class="nt">-z</span> <span class="s2">"</span><span class="nv">$cmLog</span><span class="s2">"</span> <span class="o">]</span><span class="p">;</span><span class="k">then </span><span class="nb">echo</span> <span class="nt">-e</span> <span class="s2">"</span><span class="se">\n</span><span class="s2">已经合并到 - "</span><span class="si">$(</span>git remote<span class="si">)</span>/<span class="si">$(</span>git symbolic-ref <span class="nt">--short</span> <span class="nt">-q</span> HEAD<span class="si">)</span><span class="s2">"分支(</span><span class="nv">$1</span><span class="s2"># "</span> <span class="si">$(</span>git remote get-url <span class="nt">--push</span> <span class="si">$(</span>git remote<span class="si">))</span><span class="s2">"),请及时清理:</span><span class="se">\n</span><span class="nv">$cmLog</span><span class="s2">"</span> <span class="o">&gt;&gt;</span> <span class="nv">$log</span> <span class="k">fi</span> <span class="c"># 检查过期分支</span> <span class="nb">local </span><span class="nv">cvLog</span><span class="o">=</span><span class="s1">''</span> <span class="nv">cvLog</span><span class="o">=</span><span class="si">$(</span><span class="nb">eval</span> <span class="nv">$cv</span><span class="si">)</span> <span class="k">if</span> <span class="o">[</span> <span class="o">!</span> <span class="nt">-z</span> <span class="s2">"</span><span class="nv">$cvLog</span><span class="s2">"</span> <span class="o">]</span><span class="p">;</span><span class="k">then </span><span class="nb">echo</span> <span class="nt">-e</span> <span class="s2">"</span><span class="se">\n</span><span class="s2">过期分支 - "</span><span class="si">$(</span>git remote<span class="si">)</span>/<span class="si">$(</span>git symbolic-ref <span class="nt">--short</span> <span class="nt">-q</span> HEAD<span class="si">)</span><span class="s2">"(</span><span class="nv">$1</span><span class="s2"># "</span> <span class="si">$(</span>git remote get-url <span class="nt">--push</span> <span class="si">$(</span>git remote<span class="si">))</span><span class="s2">"),请相关人员检查并清理:</span><span class="se">\n</span><span class="nv">$cvLog</span><span class="s2">"</span> <span class="o">&gt;&gt;</span> <span class="nv">$log</span> <span class="k">fi</span> <span class="c"># 如果存在子模块,则同步更新子模块</span> <span class="k">if</span> <span class="o">[</span> <span class="nt">-f</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">/.gitmodules"</span> <span class="o">]</span><span class="p">;</span><span class="k">then </span>git submodule update <span class="nt">--init</span> <span class="nt">--remote</span> <span class="nt">--recursive</span> <span class="o">&amp;&amp;</span> git submodule foreach <span class="s1">'echo "清理 $sm_path" &amp;&amp; git fetch -pa $(git remote)'</span> <span class="k">fi</span> <span class="o">}</span> <span class="k">function </span>listDir<span class="o">()</span> <span class="o">{</span> <span class="nb">local </span><span class="nv">project_path</span><span class="o">=</span><span class="nv">$1</span><span class="p">;</span> <span class="c"># 如果传入目录已经是一个 Git 项目直接执行更新退出</span> <span class="k">if</span> <span class="o">[</span> <span class="nt">-d</span> <span class="s2">"</span><span class="nv">$project_path</span><span class="s2">/.git"</span> <span class="o">]</span><span class="p">;</span><span class="k">then </span>action <span class="nv">$project_path</span> <span class="k">if</span> <span class="o">[</span> <span class="si">$(</span><span class="nb">echo</span> <span class="nv">$project_path</span> | <span class="nb">grep</span> <span class="s2">"/library"</span> | <span class="nb">wc</span> <span class="nt">-l</span><span class="si">)</span> <span class="nt">-eq</span> 1 <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="o">[</span> <span class="nt">-d</span> <span class="s2">"</span><span class="nv">$project_path</span><span class="s2">/erc-model"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then </span>action <span class="nv">$project_path</span>/erc-model <span class="k">fi return fi</span> <span class="c"># 如果不是 Git 目录</span> <span class="k">for </span><span class="nb">dir </span><span class="k">in</span> <span class="sb">`</span><span class="nb">ls</span> <span class="nt">-l</span> <span class="nv">$project_path</span>/ | <span class="nb">grep</span> <span class="nt">-E</span> <span class="s2">"^d"</span> | <span class="nb">awk</span> <span class="s1">'{print $NF}'</span> | <span class="nb">grep</span> <span class="nt">-v</span> <span class="s2">"vendor"</span><span class="sb">`</span><span class="p">;</span> <span class="k">do </span><span class="nb">local </span><span class="nv">l</span><span class="o">=</span><span class="nv">$2</span> <span class="c"># 判断目录层级</span> <span class="k">if</span> <span class="o">[</span> <span class="nv">$l</span> <span class="nt">-le</span> <span class="nv">$level</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="o">((</span>l+<span class="o">=</span>1<span class="o">))</span><span class="p">;</span><span class="k">then </span>listDir <span class="nv">$project_path</span>/<span class="nv">$dir</span> <span class="nv">$l</span> <span class="k">fi done</span> <span class="o">}</span> listDir <span class="nv">$project_path</span> 1 <span class="c"># 企业微信告警</span> <span class="nv">text</span><span class="o">=</span><span class="si">$(</span><span class="nb">cat</span> <span class="nv">$log</span><span class="si">)</span> <span class="k">if</span> <span class="o">[</span> <span class="o">!</span> <span class="nt">-z</span> <span class="s2">"</span><span class="nv">$text</span><span class="s2">"</span> <span class="o">]</span><span class="p">;</span><span class="k">then </span>curl <span class="s1">'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxxxx'</span> <span class="nt">-H</span> <span class="s1">'Content-Type: application/json'</span> <span class="nt">-d</span> <span class="s1">'{"msgtype": "text","text":{"content":"'</span><span class="s2">"</span><span class="nv">$text</span><span class="s2">"</span><span class="s1">'"}}'</span> <span class="k">fi</span> </code></pre></div></div> <p>将脚本拷贝到 <code class="language-plaintext highlighter-rouge">/usr/local/bin</code> 下,创建脚本名为:<code class="language-plaintext highlighter-rouge">git-branch-alert</code>,使用 <code class="language-plaintext highlighter-rouge">git-branch-alert &lt;dir-path&gt; &lt;dir-level&gt; &lt;fileter&gt;</code></p> <ul> <li>&lt;第1个参数:dir-path&gt; :指定检查目录,不想每次都输入可以修改一下脚本的 <code class="language-plaintext highlighter-rouge">project_path</code> 默认值</li> <li>&lt;第2个参数:dir-level&gt; :指定检查目录层级,这个根据自己项目部署情况修改,默认值是 4 层,本来想放开限制,但是…有个项目真尼玛太人才,子目录太多了</li> <li>&lt;第3个参数:filter&gt; :默认过滤 <code class="language-plaintext highlighter-rouge">HEAD|dev|master|main|-&gt;|(当前年份-2023)</code>,如果想保留其它年份和规则可以传递参数</li> </ul> <p><strong>!!!注意:使用这个脚本之前记得先提交你项目目录的记录,因为脚本中的 <code class="language-plaintext highlighter-rouge">git reset --hard HEAD</code> 会清掉你当前工作区的信息</strong>。</p> <h2 id="change-list">Change List</h2> <h3 id="2023-05-25---originhead-分支引发的告警异常">2023-05-25 - <code class="language-plaintext highlighter-rouge">origin/HEAD</code> 分支引发的告警异常</h3> <p>上述脚本正常的告警格式应该如下:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>已经合并到 - origin/master分支([email protected]:xx/xx.git),请及时清理: origin/branch-name author (YYYY-mm-dd) </code></pre></div></div> <p>但在公司电脑执行检查一个新目录时,突然发现了很多项目都存在一个异常的告警信息:<code class="language-plaintext highlighter-rouge">branche-name</code> 丢失</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>已经合并到 - origin/master分支([email protected]:xx/xx.git),请及时清理: origin author (YYYY-mm-dd) </code></pre></div></div> <p>服务器上检查没有问题,家里的电脑运行检查脚本也没有问题…<code class="language-plaintext highlighter-rouge">git branch -r</code> 对比了一下,发现了异常项目都多了一个 <code class="language-plaintext highlighter-rouge">origin/HEAD</code> 分支在结果列表中</p> <p><code class="language-plaintext highlighter-rouge">git branch -r</code> 其实就是将 <code class="language-plaintext highlighter-rouge">.git/refs/remote/origin/*</code> 文件全部列出来,通过 <code class="language-plaintext highlighter-rouge">ls</code> 查看目录发现确实是多了这么一个 <code class="language-plaintext highlighter-rouge">HEAD</code> 文件。</p> <p><code class="language-plaintext highlighter-rouge">ChatGPT</code> 问了一下:</p> <ol> <li><code class="language-plaintext highlighter-rouge">origin/HEAD</code> 是在你执行 <code class="language-plaintext highlighter-rouge">git clone</code> 一个仓库时自动创建的一个引用,它指向你从中克隆的远程仓库的默认分支(通常是<code class="language-plaintext highlighter-rouge">master</code>或者<code class="language-plaintext highlighter-rouge">main</code>分支)</li> <li><code class="language-plaintext highlighter-rouge">origin/HEAD</code> 并不是一个真正的分支,而是指向远程仓库中默认分支的引用,它通常指向 <code class="language-plaintext highlighter-rouge">origin/master</code> 或 <code class="language-plaintext highlighter-rouge">origin/main</code>,具体取决于你的远程仓库的默认分支名称</li> <li><code class="language-plaintext highlighter-rouge">git remote set-head -d origin</code> 命令可以删除本地仓库中的 <code class="language-plaintext highlighter-rouge">origin/HEAD</code> 分支,且该命令只影响本地视图</li> </ol> <p>多次尝试想在执行 <code class="language-plaintext highlighter-rouge">git branch -r --fromat="xxxx"</code> 的时候屏蔽掉 <code class="language-plaintext highlighter-rouge">origin/HEAD</code> 分支,尝试无果且实现方案感觉成本较大。删除 <code class="language-plaintext highlighter-rouge">origin/HEAD</code> 并未发现执行有什么异常。因此直接在检查前执行 <code class="language-plaintext highlighter-rouge">git remote set-head -d $(git remote)</code> 删除 <code class="language-plaintext highlighter-rouge">HEAD</code> 分支。</p> Wed, 05 Apr 2023 00:00:00 +0000 https://minsonlee.github.io/2023/04/clean-your-git-branch https://minsonlee.github.io/2023/04/clean-your-git-branch Git Tools 重学编程-PHP” <p>[TOC]</p> <h1 id="wsl2-debian-编译安装-php-8113lnmp">WSL2 Debian 编译安装 PHP 8.1.13(LNMP)</h1> <ul> <li><a href="https://cloud.tencent.com/developer/article/1119222">php/apache 和 php/nginx的区别</a></li> <li><a href="https://m.html.cn/softprog/php/1158307757900.html">PHP 线程安全与非线程安全版本的区别深入解析</a></li> </ul> <h2 id="安装-php">安装 PHP</h2> <ol> <li>前置准备</li> </ol> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt update <span class="nb">sudo </span>apt upgrade </code></pre></div></div> <p>安装依赖</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt-get <span class="nb">install</span> <span class="nt">-y</span> <span class="se">\</span> autoconf build-essential curl libtool <span class="se">\</span> libssl-dev libcurl4-openssl-dev libsqlite3-dev <span class="se">\</span> libxml2-dev libreadline8 libreadline-dev <span class="se">\</span> libzip-dev libonig-dev libzip4 openssl <span class="se">\</span> pkg-config zlib1g-dev libpng-dev libjpeg-dev <span class="se">\</span> libfreetype-dev libsodium-dev libgmp-dev </code></pre></div></div> <p>新增用户</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>groupadd <span class="nt">-g</span> 500 www <span class="c"># 创建 www 用户组,指定用户组的 groupId 为 500</span> <span class="nb">sudo </span>useradd <span class="nt">-u</span> 500 <span class="nt">-r</span> <span class="nt">-g</span> www <span class="nt">-d</span> /home/www www <span class="c"># 创建 www 用户,Id 为 500</span> passwd www <span class="c"># 设置 www 密码</span> </code></pre></div></div> <ol> <li>下载安装 <code class="language-plaintext highlighter-rouge">PHP8.1.13</code> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo mkdir</span> <span class="nt">-p</span> /apps/php81 /data/logs/php <span class="nb">cd</span> /apps wget https://www.php.net/distributions/php-8.1.13.tar.gz <span class="nb">tar</span> <span class="nt">-xf</span> php-8.1.13.tar.gz <span class="o">&amp;&amp;</span> <span class="nb">rm</span> <span class="nt">-f</span> php-8.1.13.tar.gz </code></pre></div> </div> </li> </ol> <p>编译安装 <code class="language-plaintext highlighter-rouge">PHP8.1.13</code>,可以通过 <code class="language-plaintext highlighter-rouge">./configure -h</code> 查看支持的编译参数</p> <blockquote> <p><a href="https://www.php.net/manual/zh/configure.about.php">configure 这些选项</a>只用在编译的时候。如果想要修改 PHP 的运行时配置,请阅读 <a href="https://www.php.net/manual/zh/configuration.php">运行时配置</a></p> </blockquote> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cd</span> /apps/php-8.1.13 <span class="nv">$ </span>./configure <span class="nt">--prefix</span><span class="o">=</span>/apps/php81 <span class="se">\</span> <span class="nt">--with-config-file-path</span><span class="o">=</span>/apps/php81/etc <span class="se">\</span> <span class="nt">--with-fpm-user</span><span class="o">=</span>www <span class="se">\</span> <span class="nt">--with-fpm-group</span><span class="o">=</span>www <span class="se">\</span> <span class="nt">--with-freetype</span><span class="o">=</span>/usr <span class="se">\</span> <span class="nt">--with-jpeg</span><span class="o">=</span>/usr <span class="se">\</span> <span class="nt">--with-libzip</span><span class="o">=</span>/usr/lib/x86_64-linux-gnu <span class="se">\</span> <span class="nt">--with-curl</span> <span class="se">\</span> <span class="nt">--with-openssl</span> <span class="se">\</span> <span class="nt">--with-pdo-mysql</span> <span class="se">\</span> <span class="nt">--with-mysqli</span> <span class="se">\</span> <span class="nt">--with-pdo-mysql</span><span class="o">=</span>mysqlnd <span class="se">\</span> <span class="nt">--with-gmp</span> <span class="se">\</span> <span class="nt">--with-zlib</span> <span class="se">\</span> <span class="nt">--with-pear</span> <span class="se">\</span> <span class="nt">--with-readline</span> <span class="se">\</span> <span class="nt">--with-sodium</span> <span class="se">\</span> <span class="nt">--with-zip</span> <span class="se">\</span> <span class="nt">--enable-mysqlnd</span> <span class="se">\</span> <span class="nt">--enable-gd</span> <span class="se">\</span> <span class="nt">--enable-iconv</span> <span class="se">\</span> <span class="nt">--enable-bcmath</span> <span class="se">\</span> <span class="nt">--enable-fpm</span> <span class="se">\</span> <span class="nt">--enable-mbstring</span> <span class="se">\</span> <span class="nt">--enable-opcache</span> <span class="se">\</span> <span class="nt">--enable-mbstring</span> <span class="se">\</span> <span class="nt">--enable-phpdbg</span> <span class="se">\</span> <span class="nt">--enable-shmop</span> <span class="se">\</span> <span class="nt">--enable-sockets</span> <span class="se">\</span> <span class="nt">--enable-soap</span> <span class="se">\</span> <span class="nt">--enable-sysvmsg</span> <span class="se">\</span> <span class="nt">--enable-sysvsem</span> <span class="se">\</span> <span class="nt">--enable-sysvshm</span> <span class="se">\</span> <span class="nt">--enable-zip</span> <span class="se">\</span> <span class="nt">--enable-pcntl</span> <span class="nv">$ </span>make <span class="nt">-j</span> <span class="c"># 查看编译结果</span> <span class="nv">$ </span>make <span class="o">&amp;&amp;</span> make <span class="nb">install</span> </code></pre></div></div> <ol> <li>配置信息 <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cp</span> /apps/php-8.1.13/php.ini-<span class="k">*</span> /apps/php81/etc/ <span class="nb">cd</span> /apps/php81/etc <span class="nb">cp </span>php.ini-development php.ini <span class="nb">cp </span>php-fpm.conf.default php-fpm.conf <span class="nb">cp </span>php-fpm.d/www.conf.default www.conf <span class="nb">touch </span>php-fpm.d/env.conf </code></pre></div> </div> </li> </ol> <p><code class="language-plaintext highlighter-rouge">php.ini</code> 配置信息,见 <a href="https://www.php.net/manual/zh/ini.core.php"><code class="language-plaintext highlighter-rouge">php.ini</code>核心配置选项说明</a></p> <p><code class="language-plaintext highlighter-rouge">php-fpm.conf</code> 全局配置</p> <div class="language-conf highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[<span class="n">global</span>] <span class="n">pid</span> = <span class="n">run</span>/<span class="n">php</span>-<span class="n">fpm</span>.<span class="n">pid</span> <span class="n">error_log</span> = /<span class="n">data</span>/<span class="n">logs</span>/<span class="n">php</span>/<span class="n">php</span>-<span class="n">fpm</span>.<span class="n">log</span> <span class="n">syslog</span>.<span class="n">facility</span> = <span class="n">daemon</span> <span class="n">syslog</span>.<span class="n">ident</span> = <span class="n">php</span>-<span class="n">fpm</span> <span class="n">log_level</span> = <span class="n">notice</span> <span class="n">process</span>.<span class="n">priority</span> = -<span class="m">19</span> <span class="n">daemonize</span> = <span class="n">yes</span> <span class="n">events</span>.<span class="n">mechanism</span> = <span class="n">epoll</span> <span class="n">include</span>=/<span class="n">apps</span>/<span class="n">php81</span>/<span class="n">etc</span>/<span class="n">php</span>-<span class="n">fpm</span>.<span class="n">d</span>/*.<span class="n">conf</span> </code></pre></div></div> <p><code class="language-plaintext highlighter-rouge">php-fpm.d/www.conf</code> 配置 web 配置</p> <div class="language-conf highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[<span class="n">www</span>] <span class="n">user</span> = <span class="n">www</span> <span class="n">group</span> = <span class="n">www</span> <span class="n">listen</span> = /<span class="n">dev</span>/<span class="n">shm</span>/<span class="n">php8</span>-<span class="m">1</span>-<span class="m">13</span>-<span class="n">fcgi</span>.<span class="n">sock</span> <span class="n">listen</span>.<span class="n">owner</span> = <span class="n">www</span> <span class="n">listen</span>.<span class="n">group</span> = <span class="n">www</span> <span class="n">pm</span> = <span class="n">static</span> <span class="n">pm</span>.<span class="n">max_children</span> = <span class="m">5</span> <span class="n">pm</span>.<span class="n">start_servers</span> = <span class="m">2</span> <span class="n">pm</span>.<span class="n">min_spare_servers</span> = <span class="m">1</span> <span class="n">pm</span>.<span class="n">max_spare_servers</span> = <span class="m">2</span> <span class="n">pm</span>.<span class="n">max_requests</span> = <span class="m">500</span> <span class="n">pm</span>.<span class="n">status_path</span> = /<span class="n">status_php_81</span> <span class="n">slowlog</span> = /<span class="n">data</span>/<span class="n">logs</span>/<span class="n">php</span>/$<span class="n">pool</span>.<span class="n">log</span>.<span class="n">slow</span> <span class="n">request_slowlog_timeout</span> = <span class="m">5</span> <span class="n">rlimit_files</span> = <span class="m">1024</span> <span class="n">catch_workers_output</span> = <span class="n">yes</span> </code></pre></div></div> <p><code class="language-plaintext highlighter-rouge">php-fpm.d/env.conf</code> 配置环境变量</p> <div class="language-conf highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[<span class="n">www</span>] <span class="n">env</span>[<span class="n">CURRENT_ENV</span>] = <span class="n">test</span> <span class="n">env</span>[<span class="n">IS_NEW_TEST_ENV</span>] = <span class="m">1</span> ;<span class="n">env</span>[<span class="n">MYSQL_HOST</span>] = <span class="n">xxx</span> ;<span class="n">env</span>[<span class="n">MYSQL_USERNAME</span>]= <span class="n">xxx</span> ;<span class="n">env</span>[<span class="n">MYSQL_PASSWORD</span>]= <span class="n">xxx</span> </code></pre></div></div> <ol> <li>设置软连 <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo ln</span> <span class="nt">-s</span> /apps/php81/bin/php /usr/local/bin/php </code></pre></div> </div> </li> <li>扩展安装 <ul> <li>获取或者 Browse Packages : https://pecl.php.net/packages.php</li> <li>在 <code class="language-plaintext highlighter-rouge">php.ini</code> 中开启扩展 <code class="language-plaintext highlighter-rouge">extension=memcache</code></li> <li>配置: 可以查看 <a href="https://www.php.net/manual/zh/refs.remote.other.php">PHP: 其它服务 - Manual</a></li> </ul> </li> </ol> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 安装 memcache</span> wget http://pecl.php.net/get/memcache <span class="o">&amp;&amp;</span> <span class="nb">tar</span> <span class="nt">-zxvf</span> memcache <span class="o">&amp;&amp;</span> <span class="nb">cd </span>memcache-<span class="k">*</span> <span class="o">&amp;&amp;</span> /apps/php81/bin/phpize ./configure <span class="nt">--with-php-config</span><span class="o">=</span>/apps/php81/bin/php-config make <span class="o">&amp;&amp;</span> make <span class="nb">install</span> </code></pre></div></div> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 安装 memcached</span> <span class="nb">sudo </span>apt <span class="nb">install</span> <span class="nt">-y</span> libmemcached-dev wget https://pecl.php.net/get/memcached <span class="o">&amp;&amp;</span> <span class="nb">tar</span> <span class="nt">-zxvf</span> memcached <span class="o">&amp;&amp;</span> <span class="nb">cd </span>memcached-<span class="k">*</span> <span class="o">&amp;&amp;</span> /apps/php81/bin/phpize ./configure <span class="nt">--with-php-config</span><span class="o">=</span>/apps/php81/bin/php-config make <span class="o">&amp;&amp;</span> make <span class="nb">install</span> </code></pre></div></div> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 安装 Gearman</span> <span class="nb">sudo </span>apt <span class="nb">install</span> <span class="nt">-y</span> libgearman-dev wget https://pecl.php.net/get/gearman <span class="o">&amp;&amp;</span> <span class="nb">tar</span> <span class="nt">-zxvf</span> gearman <span class="o">&amp;&amp;</span> <span class="nb">cd </span>gearman-<span class="k">*</span> <span class="o">&amp;&amp;</span> /apps/php81/bin/phpize ./configure <span class="nt">--with-php-config</span><span class="o">=</span>/apps/php81/bin/php-config make <span class="o">&amp;&amp;</span> make <span class="nb">install</span> </code></pre></div></div> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 安装 amqp 扩展</span> <span class="nb">sudo </span>apt <span class="nb">install</span> <span class="nt">-y</span> librabbitmq-dev wget https://pecl.php.net/get/amqp <span class="o">&amp;&amp;</span> <span class="nb">tar</span> <span class="nt">-zxvf</span> amqp <span class="o">&amp;&amp;</span> <span class="nb">cd </span>amqp-<span class="k">*</span> <span class="o">&amp;&amp;</span> /apps/php81/bin/phpize ./configure <span class="nt">--with-php-config</span><span class="o">=</span>/apps/php81/bin/php-config make <span class="o">&amp;&amp;</span> make <span class="nb">install</span> </code></pre></div></div> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 安装 redis 扩展</span> wget https://pecl.php.net/get/redis <span class="o">&amp;&amp;</span> <span class="nb">tar</span> <span class="nt">-zxvf</span> redis <span class="o">&amp;&amp;</span> <span class="nb">cd </span>redis-<span class="k">*</span> <span class="o">&amp;&amp;</span> /apps/php81/bin/phpize ./configure <span class="nt">--with-php-config</span><span class="o">=</span>/apps/php81/bin/php-config make <span class="o">&amp;&amp;</span> make <span class="nb">install</span> </code></pre></div></div> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 安装 mongodb 扩展</span> wget https://pecl.php.net/get/mongodb <span class="o">&amp;&amp;</span> <span class="nb">tar</span> <span class="nt">-zxvf</span> mongodb <span class="o">&amp;&amp;</span> <span class="nb">cd </span>mongodb-<span class="k">*</span> <span class="o">&amp;&amp;</span> /apps/php81/bin/phpize ./configure <span class="nt">--with-php-config</span><span class="o">=</span>/apps/php81/bin/php-config make <span class="o">&amp;&amp;</span> make <span class="nb">install</span> </code></pre></div></div> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 安装 mcrypt 扩展</span> <span class="nb">sudo </span>apt <span class="nb">install</span> <span class="nt">-y</span> libmcrypt-dev wget https://pecl.php.net/get/mcrypt <span class="o">&amp;&amp;</span> <span class="nb">tar</span> <span class="nt">-zxvf</span> mcrypt <span class="o">&amp;&amp;</span> <span class="nb">cd </span>mcrypt-<span class="k">*</span> <span class="o">&amp;&amp;</span> /apps/php81/bin/phpize ./configure <span class="nt">--with-php-config</span><span class="o">=</span>/apps/php81/bin/php-config make <span class="o">&amp;&amp;</span> make <span class="nb">install</span> </code></pre></div></div> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 安装 timezonedb 扩展</span> wget https://pecl.php.net/get/timezonedb <span class="o">&amp;&amp;</span> <span class="nb">tar</span> <span class="nt">-zxvf</span> timezonedb <span class="o">&amp;&amp;</span> <span class="nb">cd </span>timezonedb-<span class="k">*</span> <span class="o">&amp;&amp;</span> /apps/php81/bin/phpize ./configure <span class="nt">--with-php-config</span><span class="o">=</span>/apps/php81/bin/php-config make <span class="o">&amp;&amp;</span> make <span class="nb">install</span> </code></pre></div></div> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 安装 swoole 扩展</span> <span class="nb">sudo </span>apt <span class="nb">install</span> <span class="nt">-y</span> librabbitmq-dev wget https://pecl.php.net/get/swoole <span class="o">&amp;&amp;</span> <span class="nb">tar</span> <span class="nt">-zxvf</span> swoole <span class="o">&amp;&amp;</span> <span class="nb">cd </span>swoole-<span class="k">*</span> <span class="o">&amp;&amp;</span> /apps/php81/bin/phpize ./configure <span class="nt">--with-php-config</span><span class="o">=</span>/apps/php81/bin/php-config make <span class="o">&amp;&amp;</span> make <span class="nb">install</span> </code></pre></div></div> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 安装 gRPC 扩展</span> <span class="nb">sudo </span>apt <span class="nb">install</span> <span class="nt">-y</span> librabbitmq-dev wget https://pecl.php.net/get/gRPC <span class="o">&amp;&amp;</span> <span class="nb">tar</span> <span class="nt">-zxvf</span> gRPC <span class="o">&amp;&amp;</span> <span class="nb">cd </span>grpc-<span class="k">*</span> <span class="o">&amp;&amp;</span> /apps/php81/bin/phpize ./configure <span class="nt">--with-php-config</span><span class="o">=</span>/apps/php81/bin/php-config make <span class="o">&amp;&amp;</span> make <span class="nb">install</span> </code></pre></div></div> <ol> <li>安装包管理器 <code class="language-plaintext highlighter-rouge">composer</code></li> </ol> <div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">php</span> <span class="o">-</span><span class="n">r</span> <span class="s2">"copy('https://install.phpcomposer.com/installer', 'composer-setup.php');"</span> <span class="n">php</span> <span class="n">composer</span><span class="o">-</span><span class="n">setup</span><span class="mf">.</span><span class="n">php</span> <span class="n">php</span> <span class="o">-</span><span class="n">r</span> <span class="s2">"unlink('composer-setup.php');"</span> <span class="n">sudo</span> <span class="n">ln</span> <span class="o">-</span><span class="n">s</span> <span class="o">/</span><span class="n">apps</span><span class="o">/</span><span class="n">php</span><span class="o">-</span><span class="n">composer</span><span class="o">/</span><span class="n">composer</span><span class="mf">.</span><span class="n">phar</span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">local</span><span class="o">/</span><span class="n">bin</span><span class="o">/</span><span class="n">composer</span> </code></pre></div></div> <h2 id="nginx">NGINX</h2> <p>安装依赖</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">mkdir</span> <span class="nt">-p</span> /apps/nginx /data/logs/nginx <span class="o">&amp;&amp;</span> <span class="nb">cd</span> /apps <span class="nb">sudo </span>apt <span class="nb">install</span> <span class="nt">-y</span> build-essential libpcre3 libpcre3-dev zlib1g-dev openssl libssl-dev libxml2-dev libxslt1-dev </code></pre></div></div> <p>下载源码:<a href="http://nginx.org/en/docs/">nginx documentation</a></p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>wget https://nginx.org/download/nginx-1.22.1.tar.gz <span class="o">&amp;&amp;</span> <span class="nb">tar</span> <span class="nt">-zxvf</span> nginx-1.22.1.tar.gz <span class="o">&amp;&amp;</span> <span class="nb">rm </span>nginx-1.22.1.tar.gz wget https://github.com/wandenberg/nginx-push-stream-module/archive/refs/tags/0.5.5.tar.gz <span class="o">&amp;&amp;</span> <span class="nb">tar</span> <span class="nt">-zxvf</span> nginx-push-stream-module-0.5.5.tar.gz <span class="o">&amp;&amp;</span> <span class="nb">rm </span>nginx-push-stream-module-0.5.5.tar.gz </code></pre></div></div> <table> <tbody> <tr> <td>配置&amp;&amp;编译,配置参数可以参考 <a href="http://nginx.org/en/docs/configure.html">Nginx 官方 configure 说明</a>,也可以通过 <code class="language-plaintext highlighter-rouge">./configure --help</code> 查看说明([Nginx模块-HTTP Push Stream</td> <td>NGINX](https://www.nginx.com/resources/wiki/modules/push_stream/)))</td> </tr> </tbody> </table> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd </span>nginx-1.22<span class="k">*</span>/ ./configure <span class="se">\</span> <span class="nt">--prefix</span><span class="o">=</span>/apps/nginx1.22 <span class="se">\</span> <span class="nt">--sbin-path</span><span class="o">=</span>/apps/nginx1.22/sbin/nginx <span class="se">\</span> <span class="nt">--pid-path</span><span class="o">=</span>/apps/nginx1.22/nginx.pid <span class="se">\</span> <span class="nt">--lock-path</span><span class="o">=</span>/apps/nginx1.22/nginx.lock <span class="se">\</span> <span class="nt">--conf-path</span><span class="o">=</span>/apps/nginx1.22/conf/nginx.conf <span class="se">\</span> <span class="nt">--error-log-path</span><span class="o">=</span>/data/logs/nginx/error.log <span class="se">\</span> <span class="nt">--user</span><span class="o">=</span>www <span class="se">\</span> <span class="nt">--group</span><span class="o">=</span>www <span class="se">\</span> <span class="nt">--with-file-aio</span> <span class="se">\</span> <span class="nt">--with-threads</span> <span class="se">\</span> <span class="nt">--with-http_addition_module</span> <span class="se">\</span> <span class="nt">--with-http_auth_request_module</span> <span class="se">\</span> <span class="nt">--with-http_dav_module</span> <span class="se">\</span> <span class="nt">--with-http_flv_module</span> <span class="se">\</span> <span class="nt">--with-http_gunzip_module</span> <span class="se">\</span> <span class="nt">--with-http_gzip_static_module</span> <span class="se">\</span> <span class="nt">--with-http_mp4_module</span> <span class="se">\</span> <span class="nt">--with-http_random_index_module</span> <span class="se">\</span> <span class="nt">--with-http_realip_module</span> <span class="se">\</span> <span class="nt">--with-http_secure_link_module</span> <span class="se">\</span> <span class="nt">--with-http_slice_module</span> <span class="se">\</span> <span class="nt">--with-http_ssl_module</span> <span class="se">\</span> <span class="nt">--with-http_stub_status_module</span> <span class="se">\</span> <span class="nt">--with-http_sub_module</span> <span class="se">\</span> <span class="nt">--with-http_v2_module</span> <span class="se">\</span> <span class="nt">--with-mail</span> <span class="se">\</span> <span class="nt">--with-mail_ssl_module</span> <span class="se">\</span> <span class="nt">--with-stream</span> <span class="se">\</span> <span class="nt">--with-stream_realip_module</span> <span class="se">\</span> <span class="nt">--with-stream_ssl_module</span> <span class="se">\</span> <span class="nt">--with-stream_ssl_preread_module</span> <span class="se">\</span> <span class="nt">--add-module</span><span class="o">=</span>/apps/nginx-push-stream-module-0.5.5 make <span class="o">&amp;&amp;</span> make <span class="nb">install</span> </code></pre></div></div> <ul> <li>启动 nginx : /apps/nginx1.22/sbin/nginx</li> <li>停止 nginx : /apps/nginx1.22/sbin/nginx -s quit</li> <li>重启 nginx : /apps/nginx1.22/sbin/nginx -s reload</li> <li>重启日志 : /apps/nginx1.22/sbin/nginx -s reopen</li> </ul> <blockquote> <p><code class="language-plaintext highlighter-rouge">reload</code> 重新检查加载配置文件(若配置文件错误则继续保持当前配置正常运行),采用滚动发布方式,Nginx 会使用新配置启动新的工作主进程,并向旧的工作进程发送关闭信号。当旧进程收到关闭信号后,它将会停止接受新的请求,并在处理完旧请求的自行退出;</p> <p><code class="language-plaintext highlighter-rouge">reopen</code> 滚动日志,当Nginx的日志文件过大,我们将日志文件 <code class="language-plaintext highlighter-rouge">mv</code> 到其他位置后会发现日志文件仍在写入,这是由于<code class="language-plaintext highlighter-rouge">mv</code>后的文件 inode 相关信息不变,因此 Nginx 还会将日志写入到该文件中。这时我们就可以执行 <code class="language-plaintext highlighter-rouge">reopen</code> 操作,Nginx 就会关闭原来的句柄,在配置的日志目录下重新创建新的日志文件来进行日志记录。<code class="language-plaintext highlighter-rouge">inode</code> 相当于一个文件的身份证号,文件路径只是相当于一个住址而已。</p> </blockquote> <ul> <li> <table> <tbody> <tr> <td>查看 Nginx 进程 : ps -ef</td> <td>grep nginx 或 ps -ax</td> <td>grep nginx</td> </tr> </tbody> </table> </li> <li><a href="https://www.aiopsclub.com/nginx/nginx_signal/">Nginx系列之nginx信号控制 (aiopsclub.com)</a></li> </ul> <p>将 Nginx 注册为 System 服务 (通过 systemd 服务用 systemctl 管理,通过 放在:/lib/systemd/system/nginx.service或/etc/systemd/system/nginx.service)</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">ExecStart</span><span class="o">=</span> <span class="nv">ExecReload</span><span class="o">=</span>/apps/nginx1.22/sbin/nginx <span class="nt">-s</span> reload <span class="nv">ExecStop</span><span class="o">=</span>/apps/nginx1.22/sbin/nginx <span class="nt">-s</span> quit ps /apps/nginx1.22/sbin/nginx <span class="nt">-V</span> <span class="c"># 查看编译参数</span> <span class="nb">sudo </span>vi /lib/systemd/system/nginx.service <span class="c">#写入如下内容</span> <span class="o">[</span>Unit] <span class="nv">Description</span><span class="o">=</span>nginx - high performance web server <span class="nv">Documentation</span><span class="o">=</span>http://nginx.org/en/docs/ <span class="nv">After</span><span class="o">=</span>network-online.target remote-fs.target nss-lookup.target <span class="nv">Wants</span><span class="o">=</span>network-online.target <span class="o">[</span>Service] <span class="nv">Type</span><span class="o">=</span>forking <span class="nv">PIDFile</span><span class="o">=</span>/usr/local/nginx/logs/nginx.pid <span class="nv">ExecStart</span><span class="o">=</span>/usr/local/nginx/sbin/nginx <span class="nt">-c</span> /usr/local/nginx/conf/nginx.conf <span class="nv">ExecReload</span><span class="o">=</span>/bin/sh <span class="nt">-c</span> <span class="s2">"/bin/kill -s HUP </span><span class="si">$(</span>/bin/cat /usr/local/nginx/logs/nginx.pid<span class="si">)</span><span class="s2">"</span> <span class="nv">ExecStop</span><span class="o">=</span>/bin/sh <span class="nt">-c</span> <span class="s2">"/bin/kill -s TERM </span><span class="si">$(</span>/bin/cat /usr/local/nginx/logs/nginx.pid<span class="si">)</span><span class="s2">"</span> <span class="o">[</span>Install] <span class="nv">WantedBy</span><span class="o">=</span>multi-user.target </code></pre></div></div> <p>注册后需要 <code class="language-plaintext highlighter-rouge">systemctl daemon-reload</code> 重新加载服务,然后就可以通过 <code class="language-plaintext highlighter-rouge">systemctl start|stop|reload nginx</code></p> <p>如果想通过 service 进行管理 nginx 服务(通过 init.d 用 service 管理,在 /etc/init.d/nginx)</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Download nginx startup script</span> wget <span class="nt">-O</span> init-deb.sh https://www.linode.com/docs/assets/660-init-deb.sh <span class="c"># Move the script to the init.d directory &amp; make executable</span> <span class="nb">sudo mv </span>init-deb.sh /etc/init.d/nginx <span class="nb">sudo chmod</span> +x /etc/init.d/nginx <span class="c"># Add nginx to the system startup</span> <span class="nb">sudo</span> /usr/sbin/update-rc.d <span class="nt">-f</span> nginx defaults <span class="c"># service nginx start</span> <span class="c"># service nginx stop</span> <span class="c"># service nginx reload</span> <span class="c"># 不支持 status </span> </code></pre></div></div> <p>可参考 docker 的脚本自行实现</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>❯ cat /etc/init.d/docker #!/bin/sh set -e ### BEGIN INIT INFO # Provides: docker # Required-Start: $syslog $remote_fs # Required-Stop: $syslog $remote_fs # Should-Start: cgroupfs-mount cgroup-lite # Should-Stop: cgroupfs-mount cgroup-lite # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Create lightweight, portable, self-sufficient containers. # Description: # Docker is an open-source project to easily create lightweight, portable, # self-sufficient containers from any application. The same container that a # developer builds and tests on a laptop can run at scale, in production, on # VMs, bare metal, OpenStack clusters, public clouds and more. ### END INIT INFO export PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin BASE=docker # modify these in /etc/default/$BASE (/etc/default/docker) DOCKERD=/usr/bin/dockerd # This is the pid file managed by docker itself DOCKER_PIDFILE=/var/run/$BASE.pid # This is the pid file created/managed by start-stop-daemon DOCKER_SSD_PIDFILE=/var/run/$BASE-ssd.pid DOCKER_LOGFILE=/var/log/$BASE.log DOCKER_OPTS= DOCKER_DESC="Docker" # Get lsb functions . /lib/lsb/init-functions if [ -f /etc/default/$BASE ]; then . /etc/default/$BASE fi # Check docker is present if [ ! -x $DOCKERD ]; then log_failure_msg "$DOCKERD not present or not executable" exit 1 fi check_init() { # see also init_is_upstart in /lib/lsb/init-functions (which isn't available in Ubuntu 12.04, or we'd use it directly) if [ -x /sbin/initctl ] &amp;&amp; /sbin/initctl version 2&gt;/dev/null | grep -q upstart; then log_failure_msg "$DOCKER_DESC is managed via upstart, try using service $BASE $1" exit 1 fi } fail_unless_root() { if [ "$(id -u)" != '0' ]; then log_failure_msg "$DOCKER_DESC must be run as root" exit 1 fi } cgroupfs_mount() { # see also https://github.com/tianon/cgroupfs-mount/blob/master/cgroupfs-mount if grep -v '^#' /etc/fstab | grep -q cgroup \ || [ ! -e /proc/cgroups ] \ || [ ! -d /sys/fs/cgroup ]; then return fi if ! mountpoint -q /sys/fs/cgroup; then mount -t tmpfs -o uid=0,gid=0,mode=0755 cgroup /sys/fs/cgroup fi ( cd /sys/fs/cgroup for sys in $(awk '!/^#/ { if ($4 == 1) print $1 }' /proc/cgroups); do mkdir -p $sys if ! mountpoint -q $sys; then if ! mount -n -t cgroup -o $sys cgroup $sys; then rmdir $sys || true fi fi done ) } case "$1" in start) check_init fail_unless_root cgroupfs_mount touch "$DOCKER_LOGFILE" chgrp docker "$DOCKER_LOGFILE" ulimit -n 1048576 # Having non-zero limits causes performance problems due to accounting overhead # in the kernel. We recommend using cgroups to do container-local accounting. if [ "$BASH" ]; then ulimit -u unlimited else ulimit -p unlimited fi log_begin_msg "Starting $DOCKER_DESC: $BASE" start-stop-daemon --start --background \ --no-close \ --exec "$DOCKERD" \ --pidfile "$DOCKER_SSD_PIDFILE" \ --make-pidfile \ -- \ -p "$DOCKER_PIDFILE" \ $DOCKER_OPTS \ &gt;&gt; "$DOCKER_LOGFILE" 2&gt;&amp;1 log_end_msg $? ;; stop) check_init fail_unless_root if [ -f "$DOCKER_SSD_PIDFILE" ]; then log_begin_msg "Stopping $DOCKER_DESC: $BASE" start-stop-daemon --stop --pidfile "$DOCKER_SSD_PIDFILE" --retry 10 log_end_msg $? else log_warning_msg "Docker already stopped - file $DOCKER_SSD_PIDFILE not found." fi ;; restart) check_init fail_unless_root docker_pid=`cat "$DOCKER_SSD_PIDFILE" 2&gt;/dev/null` [ -n "$docker_pid" ] \ &amp;&amp; ps -p $docker_pid &gt; /dev/null 2&gt;&amp;1 \ &amp;&amp; $0 stop $0 start ;; force-reload) check_init fail_unless_root $0 restart ;; status) check_init status_of_proc -p "$DOCKER_SSD_PIDFILE" "$DOCKERD" "$DOCKER_DESC" ;; *) echo "Usage: service docker {start|stop|restart|status}" exit 1 ;; esac </code></pre></div></div> <blockquote> <p>推荐阅读:<a href="https://www.myfreax.com/how-to-install-nginx-on-debian-10/">Nginx配置文件的结构和最佳做法</a></p> </blockquote> <p>根据上述文章,我的配置文件结构如下:</p> <ul> <li>Nginx配置文件存储在 <code class="language-plaintext highlighter-rouge">/apps/nginx1.22/conf/nginx.conf</code></li> <li> <table> <tbody> <tr> <td>虚拟主机配置:[如何在Debian 10配置Nginx虚拟主机</td> <td>myfreax](https://www.myfreax.com/how-to-set-up-nginx-server-blocks-on-debian-10/)</td> </tr> </tbody> </table> <ul> <li>虚拟主机配置文件统一存储在 <code class="language-plaintext highlighter-rouge">conf/sites-available/*.conf</code></li> <li>将 <code class="language-plaintext highlighter-rouge">conf/sites-available</code> 链接到 <code class="language-plaintext highlighter-rouge">conf/sites-enabled</code>目录 时,启用配置</li> <li>命名规范:<code class="language-plaintext highlighter-rouge">域名.conf</code></li> </ul> </li> <li>日志统一存放路径:<code class="language-plaintext highlighter-rouge">/data/logs/nginx/*.log</code>(access.log 分域名存储不同的 log 文件中)</li> <li> <table> <tbody> <tr> <td>[如何使用mkcert创建SSL证书</td> <td>myfreax](https://www.myfreax.com/how-to-create-locally-trusted-ssl-certificates-on-linux-and-macos-with-mkcert/)</td> </tr> </tbody> </table> </li> <li><a href="https://blog.csdn.net/cold___play/article/details/106713416">Nginx——访问日志、错误日志、日志文件切割</a></li> <li><a href="https://mp.weixin.qq.com/s/-EPKekSo5qeWE4ASnr3NPQ">Nginx Shell 部署文档</a></li> </ul> <div class="language-nginx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">user</span> <span class="s">www</span> <span class="s">www</span><span class="p">;</span> <span class="k">worker_processes</span> <span class="s">auto</span><span class="p">;</span> <span class="k">worker_rlimit_nofile</span> <span class="mi">51200</span><span class="p">;</span> <span class="k">error_log</span> <span class="n">/data/logs/nginx/nginx_error.log</span> <span class="s">error</span><span class="p">;</span> <span class="c1"># error_log 错误等级:debug info notice error</span> <span class="k">pid</span> <span class="n">/apps/nginx1.22/nginx.pid</span><span class="p">;</span> <span class="k">events</span> <span class="p">{</span> <span class="kn">use</span> <span class="s">epoll</span><span class="p">;</span> <span class="kn">multi_accept</span> <span class="no">on</span><span class="p">;</span> <span class="kn">worker_connections</span> <span class="mi">1024</span><span class="p">;</span> <span class="p">}</span> <span class="k">http</span> <span class="p">{</span> <span class="kn">include</span> <span class="s">mime.types</span><span class="p">;</span> <span class="kn">default_type</span> <span class="nc">application/octet-stream</span><span class="p">;</span> <span class="kn">log_format</span> <span class="s">main</span> <span class="s">'</span><span class="nv">$remote_addr</span> <span class="s">-</span> <span class="nv">$remote_user</span> <span class="s">[</span><span class="nv">$time_local</span><span class="s">]</span> <span class="s">"</span><span class="nv">$host</span><span class="s">"</span> <span class="s">"</span><span class="nv">$request</span><span class="s">"</span> <span class="s">'</span> <span class="s">'</span><span class="nv">$status</span> <span class="nv">$body_bytes_sent</span> <span class="s">"</span><span class="nv">$http_referer</span><span class="s">"</span> <span class="s">'</span> <span class="s">'"</span><span class="nv">$http_user_agent</span><span class="s">"</span> <span class="s">"</span><span class="nv">$http_x_forwarded_for</span><span class="s">"</span> <span class="s">'</span> <span class="s">'responsetime=</span><span class="nv">$request_time</span><span class="s">'</span><span class="p">;</span> <span class="kn">log_format</span> <span class="s">access</span> <span class="s">'</span><span class="nv">$request_time</span><span class="s">|</span><span class="nv">$remote_addr</span><span class="s">|</span><span class="nv">$remote_user</span><span class="s">|</span><span class="nv">$host</span><span class="s">|[</span><span class="nv">$time_local</span><span class="s">]|</span><span class="nv">$request</span><span class="s">|'</span> <span class="s">'</span><span class="nv">$status</span><span class="s">|</span><span class="nv">$body_bytes_sent</span><span class="s">|</span><span class="nv">$http_referer</span><span class="s">|</span><span class="nv">$http_x_forwarded_for</span><span class="s">|'</span> <span class="s">'</span><span class="nv">$upstream_addr</span><span class="s">|</span><span class="nv">$upstream_status</span><span class="s">|</span><span class="nv">$upstream_response_time</span><span class="s">|'</span> <span class="s">'"</span><span class="nv">$http_user_agent</span><span class="s">"|</span><span class="nv">$request_body</span><span class="s">|</span><span class="nv">$scheme</span><span class="s">'</span><span class="p">;</span> <span class="kn">log_format</span> <span class="s">access-debug</span> <span class="s">'</span><span class="nv">$request_time</span><span class="s">|</span><span class="nv">$remote_addr</span><span class="s">|</span><span class="nv">$remote_user</span><span class="s">|</span><span class="nv">$host</span><span class="s">|[</span><span class="nv">$time_local</span><span class="s">]|</span><span class="nv">$request</span><span class="s">|'</span> <span class="s">'</span><span class="nv">$status</span><span class="s">|</span><span class="nv">$body_bytes_sent</span><span class="s">|</span><span class="nv">$http_referer</span><span class="s">|</span><span class="nv">$http_x_forwarded_for</span><span class="s">|'</span> <span class="s">'</span><span class="nv">$upstream_addr</span><span class="s">|</span><span class="nv">$upstream_status</span><span class="s">|</span><span class="nv">$upstream_response_time</span><span class="s">|'</span> <span class="s">'"</span><span class="nv">$http_user_agent</span><span class="s">"|</span><span class="nv">$request_body</span><span class="s">|</span><span class="nv">$scheme</span><span class="s">|</span><span class="nv">$http_if_modified_since</span><span class="s">'</span><span class="p">;</span> <span class="c1"># access_log /data/logs/nginx/nginx_access_log.log;</span> <span class="kn">sendfile</span> <span class="no">on</span><span class="p">;</span> <span class="kn">tcp_nopush</span> <span class="no">on</span><span class="p">;</span> <span class="kn">tcp_nodelay</span> <span class="no">on</span><span class="p">;</span> <span class="kn">keepalive_timeout</span> <span class="mi">10</span><span class="p">;</span> <span class="kn">gzip</span> <span class="no">on</span><span class="p">;</span> <span class="kn">gzip_static</span> <span class="no">on</span><span class="p">;</span> <span class="kn">gzip_proxied</span> <span class="s">any</span><span class="p">;</span> <span class="kn">gzip_min_length</span> <span class="mi">1k</span><span class="p">;</span> <span class="kn">gzip_buffers</span> <span class="mi">4</span> <span class="mi">16k</span><span class="p">;</span> <span class="kn">gzip_http_version</span> <span class="mi">1</span><span class="s">.0</span><span class="p">;</span> <span class="kn">gzip_comp_level</span> <span class="mi">2</span><span class="p">;</span> <span class="kn">gzip_types</span> <span class="nc">text/plain</span> <span class="nc">application/javascript</span> <span class="nc">application/x-javascript</span> <span class="nc">text/javascript</span> <span class="nc">text/css</span> <span class="nc">application/xml</span> <span class="nc">application/json</span><span class="p">;</span> <span class="kn">gzip_vary</span> <span class="no">on</span><span class="p">;</span> <span class="kn">push_stream_shared_memory_size</span> <span class="mi">128M</span><span class="p">;</span> <span class="kn">push_stream_max_channel_id_length</span> <span class="mi">200</span><span class="p">;</span> <span class="kn">push_stream_max_messages_stored_per_channel</span> <span class="mi">1</span><span class="p">;</span> <span class="kn">push_stream_message_ttl</span> <span class="mi">1m</span><span class="p">;</span> <span class="kn">include</span> <span class="s">sites-enabled/*.conf</span><span class="p">;</span> <span class="kn">add_header</span> <span class="s">hostname</span> <span class="nv">$hostname</span> <span class="s">always</span><span class="p">;</span> <span class="kn">port_in_redirect</span> <span class="no">off</span><span class="p">;</span> <span class="c1"># 302 重定向时,不自动加上当前URL的端口</span> <span class="kn">add_header</span> <span class="s">X-Frame-Options</span> <span class="s">DENY</span><span class="p">;</span> <span class="c1"># 站点是否允许用 iframe 嵌套使用(DENY-不允许;SAMEORIGIN-同源允许;ALLOW-FROM &lt;uri&gt; - 指定链接允许)</span> <span class="p">}</span> </code></pre></div></div> Sat, 26 Nov 2022 00:00:00 +0000 https://minsonlee.github.io/2022/11/install-php-in-debain https://minsonlee.github.io/2022/11/install-php-in-debain PHP 005 Why You Need To Learn Design Patterns <h1 id="设计模式学习导读">设计模式学习导读</h1> <p>[TOC]</p> <h2 id="为什么说每个程序员都要尽早地学习并掌握设计模式相关知识"><a href="https://time.geekbang.org/column/article/160981">为什么说每个程序员都要尽早地学习并掌握设计模式相关知识?</a></h2> <ol> <li>很多程序员都知道基础知识的重要性,觉得要夯实基础,才能走得更远,但:基础知识有哪些?基础要怎么样才算是“夯实”?如何将基础知识转化成开发“生产力”? <ul> <li>基础一般有哪些?框架、语言都是变化的,“不变”的是基础,如:计算机组成原理、操作系统原理、编译原理、网络原理、算法与数据结构、设计模式…</li> <li>“夯实”的基础是怎么定义的?用最简单的方式,能快速实现解决问题,并明白其“本质”、“原理”</li> <li>为什么需要学习这些基础?CURD 其实可能用不上,但这些基础其实在潜移默化的影响你的学习速度、提高你对技术的理解、编写程序的质量;其次,功利性的说就是“大厂面试,都注重基础和经验”</li> </ul> </li> <li>为什么学习“设计模式”?如果说前面的基础是让你学会如何写出高效的代码(内功心法),那“设计模式”更像是“屠龙记”(具体的武功招式)教你学会“如何写出高质量的代码”。</li> <li>烂代码的特征:命名不规范,类设计不合理,分层不清晰,没有模块化,代码混乱,高度耦合,没有抽象,代码行数太长,逻辑结构太复杂,代码参数太多,参数没有做任何封装</li> <li>如何设计一个通用的模块/架构,该从哪些方面去思考: <ul> <li>如何分层、分模块?</li> <li>应该怎么划分类?</li> <li>每个类应该具有哪些属性、方法?</li> <li>怎么设计类之间的交互?该用继承还是组合?该使用接口还是抽象类?</li> <li>怎样做到解耦、高内聚低耦合?</li> <li>该用单例模式还是静态方法?</li> <li>用工厂模式创建对象还是直接 new 出来?</li> <li>如何避免引入设计模式提高扩展性的同时带来的降低可读性问题?……</li> </ul> </li> <li>关于看源码的一些思考和观点 <ol> <li><strong>看源码是为了看什么?</strong>优秀的开源项目、框架、中间件,代码量、类的个数都会比较多,类结构、类之间的关系极其复杂,常常调用来调用去。所以,为了保证代码的扩展性、灵活性、可维护性等,代码中会使用到很多设计模式、设计原则或者设计思想。我们可以从这些优秀的源码中去学习别人的编写规范、设计原则、设计思想、实现方案技巧等信息</li> <li><strong>看源码遇到的两类问题</strong>: <ul> <li><strong>看不懂、看不下去</strong>:有一些人看源码的时候,经常会遇到看不懂、看不下去的问题。实际上,这个问题的原因很简单,那就是你积累的基本功还不够,你的能力还不足以看懂这些代码。不懂这些设计模式、原则、思想,在看代码的时候,你可能就会琢磨不透作者的设计思路,对于一些很明显的设计思路,你可能要花费很多时间才能参悟。事倍功半,持续得不到正反馈,就会让人倦怠。</li> <li><strong>以为看懂,但不会用</strong>:你自己觉得看懂了,实际上,里面的精髓你并没有 get 到多少?这是一个隐藏的问题,自己可能都比较难发现。因为优秀的开源项目、框架、中间件,就像一个集各种高精尖技术在一起的战斗机。如果你想剖析它的原理、学习它的技术,而你没有积累深厚的基本功,就算把这台战斗机摆在你面前,你也不能完全参透它的精髓,只是了解个皮毛,看个热闹而已。</li> </ul> </li> </ol> </li> <li>学好设计模式相关的知识,不仅能让你更轻松地读懂开源项目,还能更深入地参透里面的技术精髓,做到事半功倍。</li> <li>内容中涉及的问题:如何设计一个餐厅系统、停车场系统、售票系统?(作者觉得这题目老套…,然而我却不懂) <ul> <li><a href="https://zhuanlan.zhihu.com/p/143406821">一套完整的停车场管理系统设计方案 - 知乎 (zhihu.com)</a></li> <li><a href="https://www.cnblogs.com/fzhovo/p/14212113.html">12306售票系统设计方案 - fzhovo - 博客园 (cnblogs.com)</a></li> </ul> </li> </ol> <h2 id="02--从哪些维度评判代码质量的好坏如何具备写出高质量代码的能力"><a href="https://time.geekbang.org/column/article/160985">02 | 从哪些维度评判代码质量的好坏?如何具备写出高质量代码的能力?</a></h2> <p>如何评价代码质量的好坏,比如:<strong>是否符合编程规范?可读性是否强?是否易扩展?是否易维护?是否足够简单?</strong>但…<strong>什么才算是可读性好、易扩展、易维护、足够简单的“标准”</strong></p> <ol> <li>可维护性:一个项目的维护时间远远大于编写代码的时间,因此代码的可维护性格外重要。可维护性就是指:在不破坏原有代码设计、不引入新的bug的情况下,能够快速地修改或者添加代码。(事实上,<strong>当代码的可读性好、简洁、可扩展性好,就会使得代码的维护性更简单</strong>。即:代码分层清晰、模块化好、高内聚低耦合、基于接口而非实现编程的设计原则; 易维护性与项目代码量、业务复杂度、技术复杂度、文档全面、开发水平等因素有关)</li> <li><strong>可读性</strong>:软件设计大师 Martin Fowler 曾经说过:“Any fool can write code that a computer can understand. Good programmers write code that humans can understand.”翻译成中文就是:“任何傻瓜都会编写计算机能理解的代码。好的程序员能够编写人能够理解的代码”。个人认为可读性应该是最重要的,如果连让人读明白都不行,谈什么可维护?<strong>代码是否符合编码规范、命名是否达意、注释是否详尽、函数是否长短合适、模块划分是否清晰、是否符合高内聚低耦合</strong>。</li> <li><strong>可扩展性</strong>:代码预留了一些功能扩展点,支持新加功能、用户自定义时能直接最小改动。开闭原则(OCP):“对修改关闭,对扩展开放”。“对修改关闭”是为了保证现有代码的稳定性,“对扩展开放”开放是为了应对变化</li> <li>简洁性:KISS原则-“Keep It Simple,Keep It Stupid”,让代码保持简单、逻辑清晰,自然就意味着“可读性高、易维护”</li> <li><strong>可复用性</strong>:DRY原则-“Don’t Repeat Yourself”,尽量减少重复代码的编写,复用已有的代码。要么一起错,更容易暴露问题,要么一起对没有问题。 <ul> <li>面向对象中“继承、多态”存在的目的之一就是为了提高可复用性</li> <li>“单一职责”的设计原则</li> <li>重构技巧:解耦(低耦合)、高内聚、模块化</li> </ul> </li> <li>可测试性:代码的可测试性差,比较难写单元测试,那基本上就能说明代码设计得有问题。可测试性对于代码重构时非常的重要</li> </ol> <blockquote> <p>==<strong>思从深而行从简,真正的高手能云淡风轻地用最简单的方法解决最复杂的问题。</strong>==</p> </blockquote> <h2 id="03--面向对象设计原则设计模式编程规范重构这五者有何关系"><a href="https://time.geekbang.org/column/article/160991">03 | 面向对象、设计原则、设计模式、编程规范、重构,这五者有何关系?</a></h2> <ul> <li> <p>面向对象:是一种设计思想和开发方法,其核心就是“关注各个构件,提高构件的<strong>独立性</strong>,通过将其<strong>组合</strong>起来实现系统整体功能”。即:将系统抽象设计为众多的“独立性较高”的对象,对象包含不同的方法、属性。面向对象分析(OOA)、面向对象设计(OOD)、面向对象编程(OOP)</p> <ul> <li>面向对象编程(OOP):将程序定义为一个个独立性不同的类,通过类创建实例对象,实例对象之间的相互作用,构成实现了软件的功能(即:类是模板,实例对象是实际工作的人)。面向对象的四大特效:抽象、封装、继承、多态</li> <li>面向对象的四大特性:封装、抽象、继承、多态</li> <li>面向对象编程与面向过程编程的区别和联系</li> <li>面向对象分析、面向对象设计、面向对象编程</li> <li>接口和抽象类的区别以及各自的应用场景</li> <li>基于接口而非实现编程的设计思想</li> <li>多用组合少用继承的设计思想</li> <li>面向过程的贫血模型和面向对象的充血模型</li> </ul> </li> <li> <p>设计原则:是指导我们代码设计的一些<strong>经验总结</strong></p> <ul> <li>SOLID 原则 -SRP 单一职责原则</li> <li>SOLID 原则 -OCP 开闭原则</li> <li>SOLID 原则 -LSP 里式替换原则</li> <li>SOLID 原则 -ISP 接口隔离原则</li> <li>SOLID 原则 -DIP 依赖倒置原则</li> <li>DRY 原则、KISS 原则、YAGNI 原则、LOD 法则</li> </ul> </li> <li> <p>设计模式:设计模式是针对软件开发中经常遇到的一些设计问题,总结出来的一套解决方案或者设计思路。</p> <ul> <li>==<strong>设计原则是经验总结、是心法口诀</strong>==</li> <li>==<strong>设计模式是对设计原则的“最佳实践和扩展”</strong>==。因此:<strong>设计模式并非是一成不变的</strong>,随着语言的演进,一些设计模式(如:Singleton)也随之过时甚至是反例模式,本身设计模式在最初也是为了解决某些语言在语法特性上的局限性而诞生的最佳实践方案。</li> </ul> <blockquote> <p>设计模式分类:创建型、结构型、行为型 <a href="https://blog.csdn.net/qq_42785226/article/details/106862603">23种设计模式的UML图</a></p> <ul> <li>创建型: <ul> <li>常用的有:单例模式、工厂模式(工厂方法或抽象工厂)、建造者模式</li> <li>不常用的:原型模式</li> </ul> </li> <li>结构型: <ul> <li>常用的有:代理模式、桥接模式、装饰者模式、适配器模式、门面模式(作者归位不常用,但很多类库都有用,如:ORM框架、写日志框架)</li> <li>不常用的:组合模式、享元模式</li> </ul> </li> <li>行为型: <ul> <li>常用的有:观察者模式、模板模式、策略模式、职责链模式、迭代器模式、状态模式</li> <li>不常用的:访问者模式、备忘录模式、命令模式、解释器模式、中介模式</li> </ul> </li> </ul> </blockquote> </li> <li> <p>编程规范:主要解决的是代码的可读性问题,提供一些细节上的约束从而形成“共识”,减少大家的无效沟通交流。可以看:《重构》、《代码大全》、《代码整洁之道》</p> </li> <li> <p>代码重构:没有一劳永逸的设计,只有持续迭代变化的需求。随着需求的变化,原有的设计必定存在一些问题,此时针对这些问题就需要对代码进行重构。<strong>持续重构是保持代码质量不下降的有效手段,能有效避免代码腐化到无可救药的地步</strong>。重构代码之前需要知道:</p> <ul> <li>重构的目的(why)、对象(what)、时机(when)、方法(how)</li> <li>保证重构不出错的技术手段:单元测试(TDD)和代码可测试性</li> <li>不同规模的重构:大重构(大规模高层次)和小重构(小规模低层次)。大重构往往是一个漫长而新旧不断交替的痛苦过程,因此可测试性显得格外重要。</li> </ul> </li> </ul> <p><img src="https://static001.geekbang.org/resource/image/f3/d3/f3262ef8152517d3b11bfc3f2d2b12d3.png?wh=5013*3903" alt="img" /></p> <ul> <li> <p>“设计原则”提供了心法口诀</p> </li> <li>设计模式对这些心法口诀进行了具象化,成为了一门门具体的“内功秘籍”</li> <li>面向对象是修炼这些武功秘籍的“基础、丹药、武器、工具”</li> <li>编程规范是你练好这些武功的注意事项,避免走火入魔</li> <li>“重构”是利用上述提到的东西,反复不断的“锤炼”</li> </ul> Sat, 24 Sep 2022 00:00:00 +0000 https://minsonlee.github.io/2022/09/005-why-you-need-to-learn-design-patterns https://minsonlee.github.io/2022/09/005-why-you-need-to-learn-design-patterns 004 Personal Growth Experience <h1 id="个人成长经验">个人成长经验</h1> <p>[TOC]</p> <h2 id="什么才是所谓的编程能力如何考核如何提升">什么才是所谓的编程能力?如何考核?如何提升?</h2> <p>所谓编程能力,指的是把“逻辑”(或者说“功能”“业务”)翻译成代码的能力。所谓编程能力强,指的是,不仅能编写正确的代码,而且编写代码的速度很快,写出来的代码 bug 很少、性能很好、质量很高。</p> <p>更加具体点讲,一个编程能力强的人,能熟练使用编程语言、开发类库等工具,思路清晰,面对复杂的逻辑,能够编写出 bug free 的代码,能够合理地利用数据结构和算法编写高效的代码,能够灵活地使用设计思想、原则和模式,编写易读、易扩展、易维护、可复用的高质量代码。</p> <h3 id="如何考核">如何考核</h3> <p>考核一个人的编程能力,主要包含三方面:编程语言、数据结构与算法、设计思想、设计原则和设计模式。</p> <p>不管从事什么行业,要积累的东西都可以分为“变”与“不变”两类。==<strong>“不变”的是内功,“变”的是招式</strong>==。我们要善于发现、持续积累那种“不变”的能力,而不是要去盲目追逐一直都在“变”的招式。</p> <p>编程领域,不变的东西:</p> <ul> <li>技术上:数据结构,操作系统,网络,设计模式,数据库(SQL、NoSQL)、MQ</li> <li>非技术:产品意识(关注问题本身,而非产品的需求文档)、沟通能力、快速定位发现问题的能力</li> </ul> <h3 id="如何提升">如何提升</h3> <p>算法与数据结构:刷 LeetCode,不仅仅能加强个人对数据结构和算法的掌握,还能锻炼你的逻辑思维能力,写出 bug free 代码的能力、快速实现复杂逻辑的能力,也能锻炼你的性能意识</p> <p>设计思想原则和模式:平时工作中“刻意练习思考”</p> <ol> <li>拿到一个需求时:先思考如何设计,再开始写代码。</li> <li>写代码时:多思考代码是否遵循了经典的设计思想、设计原则。如:是否足够可扩展、是否满足 SOLID 原则、可读性如何、可维护性如何、代码是否有进一步优化的空间?</li> <li>做 Code Review 时:看到别人的优秀代码我们就去思考一下:有哪些值得借鉴的地方</li> </ol> <p>问题:将 IPv4 地址字符串(仅包含数字、点、空格)转化成 32 位整数(32位的二进制数字)?</p> <h2 id="基础学科的知识如何转化程实际的技术生产力">基础学科的知识,如何转化程实际的技术生产力?</h2> <ol> <li> <p>没有直接用得上并不代表没有用:事实上,“二八法则”适用于任何一门技术。我们能够经常用在工作中的那部分,都只占 20% 左右,近80%都会用不到,但往往这 20% 需要的知识是需要其余的 80% 作为辅助依托的。好比:有一个人想要一个葫芦,于是他就种了一棵葫芦树,葫芦树叶子生了虫子,有人建议他赶紧杀杀虫。这个人却说:”我要的是葫芦,管葫芦叶子干嘛”。</p> </li> <li> <p>现在用不到并不代表以后用不到:你高数、微积分学的再溜,口算可能依然赶不上市场买菜大妈们,但是换一个工作环境,那你的这些知识能让你在工作上甩几条街。因此:随便培训3-5个月的人,就能掌握框架、工具熟练的进行编程,但是牢固的基础会让你在后面的工作时间中,优势逐渐凸显出来</p> </li> <li> <p>学了记不住并不代表白学了:学习本身是一种能力锻炼,是让你在遇到问题的时候可以快速检索曾经看过的知识,而不是为了考验你“记忆力”。从:自学能力、理解能力、逻辑思维能力,到:时间控件复杂度分析能力、分析发现解决问题的能力,这些都是学习能给我们带来的锻炼。</p> <p><strong>比起编程语言、框架、工具,基础学科知识确实很难直接转化成生产力,但它却是你构建整个“技能树”的根本,构建整个“知识大楼”的地基。基础掌握不牢,你对很多应用层技术的理解就会不够有深度,略知皮毛,只能做个技术熟练工。相反,基础扎实能让你学东西更快、更有深度、理解更透彻,也就间接地增强了你的开发能力。可以这么说,在一定程度上,基础知识本身,就是技术生产力。</strong></p> </li> <li></li> </ol> <h2 id="程序员怎么才能让自己走的更高更远">程序员怎么才能让自己走的更高、更远?</h2> <ol> <li><strong>技术方面的竞争壁垒主要来自:==在一个细分技术领域长期、深入的积累==。</strong></li> <li>业务壁垒:对某个业务有深入的积累。实际上,很多领导之所以能做领导,并非技术能力强,而是对业务熟悉。</li> <li>技术、业务都没有什么复杂度,能做就是<strong>多积累自己</strong></li> <li>学历、项目、履历毕竟是入场门票,所以要适当学会:“面向简历打工”“面向跳槽打工”,提前做一些职业规划,把自己的履历弄好看点。</li> <li>不要让职场软技能成为短板:职场不是学校,影响你向上发展的因素很多,肯定不是单靠技术,所以,学生思维要不得。</li> </ol> <h2 id="作为面试官或候选人如何面试或回答设计模式问题">作为面试官或候选人,如何面试或回答设计模式问题?</h2> <ol> <li>能盲写常用的设计模式,要求并不过分,毕竟在开发中,徒手写个单例模式、工厂模式,也是常有的事情</li> <li><strong>但,学习设计模式的初衷是:提高代码质量。学习设计模式的重点:掌握应用场景、能解决哪些问题,而非纯考验记忆</strong></li> <li>用真实功能需求来面试: <ul> <li>明确需求:考察你的沟通能力、分析能力,是否能通过挖掘、假设、取舍,搞清楚具体的需求,梳理出可以执行落地的需求列表</li> <li>设计与实现:由最简单的设计和实现方案做起,基于最简单方式后可以通过什么设计模式进行优化,对代码进一步解耦,提供代码的可读、扩展性</li> </ul> </li> <li>通过对代码片段进行 Code Review 来考察:考察编程能力、习惯、设计、改进优化。</li> </ol> <h2 id="如何接手一坨烂业务代码如何在烂业务代码中成长">如何接手一坨烂业务代码?如何在烂业务代码中成长?</h2> <ol> <li> <p>要想接手一个业务系统,前提是要读懂代码,而<strong>读懂代码的关键,是要熟悉业务</strong>。代码只是业务的翻译实现,对照业务看代码是事半功倍。</p> </li> <li> <p>没文档、没熟悉业务的同事,那么只能硬着头皮读代码,反推业务。将读懂的代码业务进行文档化。</p> </li> <li> <p><strong>偏底层的开发更加考验程序员在某一细分领域的技术深度,偏业务的开发更加考验程序员的能力,比如沟通能力、分析问题解决问题能力、权衡取舍能力、架构能力等,毕竟业务多种多样,问题千奇百怪,单一细分领域的经验很难应对所有问题</strong>。</p> </li> <li> <p>业务系统的开发难度一般来自两个方面:<strong>高性能要求和业务复杂</strong>。</p> <ol> <li> <p>解决性能问题,主要依赖:<strong>技术广度、技术深度、工作经验</strong>。</p> <blockquote> <p>你需要具备一定的架构能力,有一定的技术广度,需要对各种基础架构、框架、中间件都有所了解。光了解还不够,还要有一定的技术深度,最好能对原理甚至是源码有所研究。</p> </blockquote> </li> <li> <p>业务复杂性,需要有很强的<strong>业务建模能力、复杂逻辑的抽象能力、代码翻译能力</strong>等。</p> </li> </ol> </li> </ol> <p><img src="/images/pig/how-to-refactor-review.png" alt="精彩评论" /></p> Fri, 23 Sep 2022 00:00:00 +0000 https://minsonlee.github.io/2022/09/004-personal-growth-experience https://minsonlee.github.io/2022/09/004-personal-growth-experience 003 Code Review <h1 id="代码审计code-review">代码审计(Code Review)</h1> <p>[TOC]</p> <h2 id="聊一聊-google-是如何做-code-review-的">聊一聊 Google 是如何做 Code Review 的</h2> <ul> <li><a href="https://mp.weixin.qq.com/s/mBMugpsIokfUxD3qG_dRcg">腾讯工程师,万字长文说 Code Review</a></li> <li><a href="https://mp.weixin.qq.com/s/ED4oOc0nfuVQMIR0CaRjtQ">Code Review 的巅峰</a></li> <li><a href="https://mp.weixin.qq.com/s/AM-8-bQt5sOkDO1uqk3OwA">Code Review 快把我逼疯了</a></li> <li><a href="https://mp.weixin.qq.com/s/V5UvbSwWS2dww_QLMRD44Q">技术团队如何高效的落地代码CR</a></li> <li> <table> <tbody> <tr> <td>[Google官方:How to do a code review</td> <td>eng-practices (google.github.io)](https://google.github.io/eng-practices/review/reviewer/)</td> </tr> </tbody> </table> </li> </ul> <h3 id="为什么国内企业不重视-code-review">为什么国内企业不重视 Code Review?</h3> <p>很多人会认为是:“项目工期紧,没时间做 Code Review”,但是…不忙的时候你做了吗?你一直呆的公司都是这么忙碌吗?之前做过很好的 CR 实践吗?如果让你来做 Code Review 你准备怎么做?</p> <p>回答不上,那么其实就是不重视,因为缺乏 <strong>“技术文化传承”</strong>,一个 996 的工程师到了其他的公司,也大概率会带领新团队继续 996 而已。其次就是…要面子,讲究“为人处世”,有问题又不好意思当面指出</p> <p>Code Review 到底是在考察什么?代码质量、代码规范、代码设计问题。</p> <p>问题:如果新项目或新需求之初,一次提交几十个文件、几百行代码…怎么落地 code review?</p> <h3 id="google-是如何进行-code-review-的">Google 是如何进行 Code Review 的?</h3> <ol> <li> <p>每次提交的代码片段叫做一个 CL(Change List),每次提交一段至少含 100 行代码且稍微复杂的 CL 给 CR 评审委员会,但 CL 不能太大,太大容易导致 Review 缓慢,迟迟不能合并代码,最终引发代码合并时冲突…</p> <p>CL 必须有前因后果描述、上下文背景,让 Review 人员能一眼看懂设计意图,及时当面高效沟通</p> </li> <li> <p>委员会委派一名资深工程师给你 Review 代码,并提出修改建议,反复进行,直到他觉得没有问题(最终提交的代码是一个 可读代码、符合编码规范代码)才会给你颁发一个 Readablity 证书:代码结构是否何来?代码是否容易理解?业务是否正确?异常考虑是否全面?是否有隐藏 bug?代码是否安全?性能是否满足业务需求?是否满足编程规范?</p> </li> </ol> <h2 id="google-那些让人值得学习快速成长的地方">Google 那些让人值得学习快速成长的地方?</h2> <ol> <li> <p>完善的培训课程:如新技术分享、对新人友好的入门级101教程、比较有深度的系列教程、G2G计划-公司内部人员录制视频分享给其他的同事(有优质课程提供学习,且不反对员工占用上班时间来学习)==&gt; 问题:如何合理的评估出工作需要的时间?</p> </li> <li> <p>公开的文档和代码:项目的设计文档、代码及架构设计,除了核心代码基本都内部公开学习</p> <blockquote> <p>看别人代码是看什么?带着问题去看,参考别人的设计思路、实现技巧方案,从而提高自己的设计和实现能力</p> </blockquote> </li> <li> <p>清晰的成长路径:公司关注员工的个人成长,希望跟员工共同进步,而不只是将员工代做达成目标的工具。在不同阶段,公司会帮组员制定不同的成长计划</p> <ul> <li>新人入职:学习编码规范、单元测试、Code Review、开放工具、行为准则等</li> <li>导师制度:新人入职更多是迷茫不知道自己的工作目标是什么?导师制度可以更好帮新人明确目标,也可以通过导师更好快速的融入公司</li> <li>每隔半年,Leader 会跟你一块制定 OKR,包含你的:工作目标、个人成长部分目标(学习、个人成长方向都可以写)</li> <li>Leader 会协助你制定升职计划,如:做什么项目、做哪些有影响力的事情、管理多大的团队等等,也会有意安排一些帮你晋升的事情给你做</li> <li>鼓励内部转岗,鼓励跳出舒适区,换岗到其他的团队,用不熟悉的语言、不熟悉的技术做一些不熟悉的项目</li> </ul> </li> </ol> <h2 id="听小争哥对-google-工程师文化的解读">听小争哥对 Google 工程师文化的解读</h2> <ul> <li>什么是工程师文化?技术团队的价值观(这个团队最看重什么,比如效率、质量等等),更直白点讲就是做事风格。</li> <li>为什么想学习 Google 工程师i文化?打造优秀的技术团队-执行力强、工作效率高、创新能力强的优秀团队</li> <li>作者认为 Google 的这种文化是如何形成的? <ul> <li>“尖子生”文化,Google 在用人条件苛刻、招聘要求高,过滤出了一批优秀的工程师,而这批优秀的人聚集在一起只需要稍加引导就可以形成优秀的工程师文化,如:高产出、高效率、高创新,至于前方说的“重视代码质量”、“工匠精神”、“重视效率”…自然不在话下</li> <li>招最优秀的人才,给足钱、自由和尊重,这些人必然发挥最大价值</li> <li>控制住招聘人才入口:如乔布斯所说:“<strong>A 类工程师招聘 A 类工程师,B 类工程师只能招聘 C 类工程师</strong>”</li> </ul> </li> <li>普通公司如何参考呢? <ul> <li>招聘一小撮足够优秀的工程师,让这部分工程师影响公司里更多的人,带动起我们想要的工程师文化</li> <li>人才培养上,尽可能留住最符合公司工程师文化的员工,让他们在公司内部有好的职场发展,避免劣币驱逐良币</li> </ul> </li> </ul> Thu, 22 Sep 2022 00:00:00 +0000 https://minsonlee.github.io/2022/09/003-Code-Review https://minsonlee.github.io/2022/09/003-Code-Review