<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>连波的闲谈杂鱼</title><description>Godruoyi&apos;s personal blog, I enjoy the process of building something using any technology stack</description><link>https://godruoyi.com/</link><item><title>2025 年终总结</title><link>https://godruoyi.com/posts/2025/</link><guid isPermaLink="true">https://godruoyi.com/posts/2025/</guid><description>许老三今年过得很累，身心疲惫。这累不是来自于别人，也不是来自生活，而是来自他自己</description><pubDate>Mon, 16 Mar 2026 21:00:00 GMT</pubDate><content:encoded>&lt;p&gt;许老三今年过得很累，身心疲惫。&lt;/p&gt;
&lt;p&gt;这累不是来自于别人，也不是来自生活，而是来自他自己 —— 他变得不会说话了。&lt;/p&gt;
&lt;p&gt;但这并不怪他，他本来就是个嘴笨的人；远程工作几年下来，他变得更笨了；就像他老婆说的，他就像个孩子。&lt;/p&gt;
&lt;p&gt;比如有一次开会，同事问他某个问题时，他滔滔不绝地讲了五分钟，最后同事不得不打断他：“我问的不是这个”。许老三老抓不住重点，从这里就能看出来。并且他也不会讲重点，他最喜欢的就是打比方。比如什么什么的，举个例子之类的，许老三就经常用到；他以为这样能让观点更实际，而他不知道的是他往往把一个简单的事情搞得复杂。&lt;/p&gt;
&lt;p&gt;年初的时候，许老三就因为这方面的问题被同事认为是初级工程师。虽然他嘴上说对这些称谓无所谓，但面子上还是挂不住。看不起谁呢？他堂堂工作了十年的资深工程师，竟然被人当作初级工程师看待。他急需要证明自己，证明自己是优秀的。于是那段时间他努力的写 PR，拼命的接任务，想让同事们看到他的工作和质量。而时间久了，当没人说他的不是了，许老三就觉得自己变得优秀了，又开始放纵了；直到下一次因为同样的问题再次犯错，他才知道自己的优秀是假的，是自已为的。而这种感觉许老三不会让它待太久，过了不出十天，他就忘了，又觉得别人是不如他的了。&lt;/p&gt;
&lt;p&gt;他就是这样，戳一下，跳一下。&lt;/p&gt;
&lt;p&gt;还有一次，许老三跟了一个任务一个月都没做完，然后又被说了，说他做得慢。这许老三又不服了，他觉得自己做了这么多工作，提了 8、9 个 PR，写了一堆文档，这一揽子下来，还被人嫌做得慢，这肯定不是他的问题，他自己依然是优秀的。而他不知道的是，他那个高级工程师的同事，就没他这些破事。&lt;/p&gt;
&lt;p&gt;后来有一天，许老三的领导找他谈话，委婉地说：“你可能需要在沟通上多注意一下。” 许老三听了，也记下了。后面再跟同事沟通的时候，许老三就会特别注意，甚至每次都要用 AI 润色一下。 时间久了，没人说他的不是了，他就觉得自己在沟通上已经做得很好了，就又开始放纵了。他就是这样， 戳一下，跳一下。&lt;/p&gt;
&lt;p&gt;要说许老三今年有什么值得写的事情，大概就是他已经把所有的工作流都迁移到 AI 了。用 AI 写代码，用 AI 回复 Slack，用 AI 创建 PR，用 AI 写文档；他变成了给 AI 打工的人。半年下来，他已经没有手写过代码了，现在连改个错别字，他都要指挥 AI。噢，他还写了一个 macOS mebubar App - &lt;a href=&quot;https://github.com/godruoyi/myfriendtime&quot;&gt;myfriendtime&lt;/a&gt;，虽然至今只有他一个人在用。他还因为喜欢 Zed Editor，做了个 &lt;a href=&quot;https://zed.tips/&quot;&gt;zed.tips&lt;/a&gt; 网站，虽然也就那样；但许老三挺享受 vibe 的过程的，尽管做得很粗糙，一步一步把它们搭起来的感觉却是挺好的。&lt;/p&gt;
&lt;p&gt;这年来 AI 的发展让许老三有一些不安。从去年下半年开始，许老三就感觉风向变了。之前读的那些书，学的那些不为人知的技术，好像都不重要了。他不止一次地问过 AI：这次兴起的 AI 革命，是不是和之前的互联网、iPhone4 一样，都是跨时代的产品？他，许老三，是不是得趁机站在风口上，不然这波窗口期过后，他又能靠什么还房贷呢？&lt;/p&gt;
&lt;p&gt;而许老三最擅长的就是想。他想了很多，却从来不实践。&lt;/p&gt;
&lt;p&gt;比如许老三想做一个有记忆的翻译 App，想了很久，到现在都没下文。比如他还想过做一个自己的 Agent，至今这个项目还没启动。而所有这些，还没等许老三去想好，市面上就已经有现成的产品了。&lt;/p&gt;
&lt;p&gt;他就像个旁观者，看着别人把他的想法都做出来，然后他继续想下一个。&lt;/p&gt;
&lt;h2 id=&quot;往年总结&quot;&gt;往年总结&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/2024&quot;&gt;2024 年终总结&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/review-2023&quot;&gt;2023 年终总结&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/review-of-2022&quot;&gt;2022 年终总结&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/review-2021&quot;&gt;2021 年终总结&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/2020-year-end-review&quot;&gt;2020 年终总结&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/continue-refueling-in-2019&quot;&gt;继续加油 2019&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>许老三的腿断了</title><link>https://godruoyi.com/posts/the-leg/</link><guid isPermaLink="true">https://godruoyi.com/posts/the-leg/</guid><description>许老三听说他的腿会被接到另一个人身上。那是许老三的腿，但又不是他的腿了。那个人住更好的房子，穿更舒适的裤子和鞋子</description><pubDate>Wed, 21 Jan 2026 17:36:00 GMT</pubDate><content:encoded>&lt;p&gt;许老三的腿断了，是在梦里。&lt;/p&gt;
&lt;p&gt;梦里一架直升机朝他飞来，螺旋桨的声音震得耳疼。几个人从机舱里下来，穿着白色制服，拿着斧头，一言不发地朝他走过来。&lt;/p&gt;
&lt;p&gt;他们什么也不说，什么也不问；两个人按住他，另一个举起斧头就开始砍腿，动作熟练得仿佛这腿本就是他自己的一样。许老三想哭，想喊，却发不出任何声音。虽然害怕但他却感觉不到疼。斧头落下来的时候，他突然觉得踏实了，好像这件事本来就会发生。&lt;/p&gt;
&lt;p&gt;路上不断有行人经过，许老三想叫住他们，喉咙却像被什么堵住了一样。 他们把许老三砍下来的那条腿包好，放进保温箱，坐着直升机飞走了。&lt;/p&gt;
&lt;p&gt;许老三的腿飞走了，但他心里还挺庆幸的，他还有一条腿。路上的行人回过头来看了看许老三，又看了看自己的腿，转头继续往前走了。&lt;/p&gt;
&lt;p&gt;许老三听说他的腿会被接到另一个人身上。那是许老三的腿，但又不是他的腿了。那个人住更好的房子，穿更舒适的裤子和鞋子。许老三想，那条腿在那里应该会过得更好。&lt;/p&gt;
&lt;p&gt;他又看了看自己的手、耳朵、眼睛，这些好像都不是他的了。&lt;/p&gt;
&lt;p&gt;许老三醒了，还好是一个梦，梦里的世界真可怕。他又摸了摸自己的腿，还在，但为什么觉得那么陌生，好像随时都会有人来把它取走。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/2024/&quot;&gt;https://godruoyi.com/posts/2024/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/banjiu/&quot;&gt;https://godruoyi.com/posts/banjiu/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>吐口水</title><link>https://godruoyi.com/posts/fuck-spitting/</link><guid isPermaLink="true">https://godruoyi.com/posts/fuck-spitting/</guid><description>中国人人人具有吐口水的本领，而要说把这本领发挥得淋漓尽致的，要数许老三。</description><pubDate>Tue, 18 Nov 2025 15:56:00 GMT</pubDate><content:encoded>&lt;p&gt;中国人人人具有吐口水的本领，而要说把这本领发挥得淋漓尽致的，要数许老三。&lt;/p&gt;
&lt;p&gt;许老三吐口水，向来随心所欲。不管是在商场中，或是餐馆的地板上，甚至是电梯里。只需要微微低头，嘴唇轻启，酝酿片刻。那神情专注得仿佛在做什么了不起的大事；接着先是呼噜一声，将鼻涕全数吸入鼻腔；再深呵几下，调动喉咙深处的痰液；最后噗地吐出，一道浑浊的白练破空而来。整套动作行云流水，毫无半点滞涩。&lt;/p&gt;
&lt;p&gt;我对此道向来不甚了解，说起来有些丢人。偶尔在街头巷尾，看见有人呸地一声，总是侧目而视，心中颇为不自在。但自从见识了许老三的本事，才知道这吐口水原来也是一门学问。&lt;/p&gt;
&lt;p&gt;许老三也不是在什么地方都随心所欲，什么时候应该克制，什么时候应该放纵，他有着自己的一套哲学。比如在与人聊天时，他就绝不轻易出手，显得颇有分寸。即使实在憋不住了，也一定是先瞄准脚边，再噗地吐在地上，然后慌忙用鞋踩上去，来回摩擦，企图将那摊污物与地面融为一体，生怕被别人看穿了他口水里的秘密。&lt;/p&gt;
&lt;p&gt;更令人称奇的是，许老三吐出的口水，竟有不同的品类。有时细长如丝，稍不注意就会落到自己衣服裤子上；有时圆润如珠，力道十足；更为奇妙的是当许老三感冒时吐的口水，竟然会变成如绿玉般的温润莹泽。据他自己说，这全凭当时的心境和目标而定，绝非随心所欲。&lt;/p&gt;
&lt;p&gt;我曾亲眼见他与人打赌，说能隔着三丈远吐中一个烟头。大家都以为他在吹牛。谁知许老三不慌不忙，深吸一口气，鼓起腮帮，呸地一声，那口水划出一道完美的弧线，哒的一声正中烟头。众人无不拍手叫绝。&lt;/p&gt;
&lt;p&gt;许老三得意的笑了气，又呸地吐了一口，这次却是随意得很，落在自己脚上。他说：刚才那口是技术，这口才是享受。&lt;/p&gt;
&lt;p&gt;我本来见吐口水感到不适，写到这里，更觉得恶心起来。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/tiktok/&quot;&gt;刷抖音&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>葡萄架</title><link>https://godruoyi.com/posts/a-grape-trellis/</link><guid isPermaLink="true">https://godruoyi.com/posts/a-grape-trellis/</guid><description>老丈人的院塘旁边有一颗葡萄树，几十年了，却没人吃到过葡萄。</description><pubDate>Mon, 03 Mar 2025 11:09:00 GMT</pubDate><content:encoded>&lt;p&gt;老丈人的院塘旁边有一颗葡萄树，几十年了，却没人吃到过葡萄。&lt;/p&gt;
&lt;p&gt;葡萄是长在院塘岸边的，长得不高，全靠一棵刺玫瑰和一笼杂草把它撑起来。正因为如此，虽然有这么多年了，却很少结果。但我是看到过葡萄的，就吊在那根刺玫瑰上，绿色的一串，也只有一串。&lt;/p&gt;
&lt;p&gt;几年前，老丈人的院坝前还有一个丝瓜架；丝瓜我不太喜欢，就挖了一根葡萄种在旁边，想着让它顺上丝瓜的架子。葡萄是种活了的，还发了芽；但是被老丈人砍了；老丈人看起来更喜欢丝瓜。&lt;/p&gt;
&lt;p&gt;前几年，老丈人的丝瓜架因为修房子拆了，我就再不能在旁边种葡萄了。但我看到院塘边上的葡萄又发了芽，想着在院塘边也搭一个葡萄架吧。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;这是那种本地葡萄，酸得很&lt;/code&gt;，老丈人说到。&lt;/p&gt;
&lt;p&gt;我知道老丈人说的这种葡萄，我小时候见过，就在三公家院坝，缠在一棵李子树上的，每年都结得很多。三公却很小气，总是在李子树分叉的树干绑上很多刺，不准我们爬上去；偶尔我们会吃到一串，我记得那个味道，一点也不酸。&lt;/p&gt;
&lt;p&gt;但这些场景只在我心里，我体验过，现在却只能通过机器帮我画出来；而机器画出来的，孩子体验不到，所以我想着再搭个葡萄架。&lt;/p&gt;
&lt;p&gt;又过了一年，院塘边的葡萄又长起来了，我看着很难受，对阿宝说，搭个架子吧。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;现在还不是时候，要等来年叶子全掉了，才好搭架子&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;对，叶子掉了才好搭&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;于是又过了一年，叶子掉了，但没人还记得要搭架子，我也忘记了。&lt;/p&gt;
&lt;p&gt;直到上周，我们送老丈人回去，又看到这棵葡萄树了，叶子全掉完了，趴在那里，有气无力的；这次我再也不能把它忘了，我得把架子搭起来。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;quot;这葡萄不好吃的&amp;quot;
&amp;quot;后面有时间慢慢搭嘛&amp;quot;
&amp;quot;你搭这个来干啥子嘛&amp;quot;
&amp;quot;你一个人搭不起的&amp;quot;
&amp;quot;得在水里立一根木柱&amp;quot;
&amp;quot;应该把架子搭在这里，搭在水塘上不好摘&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;搭葡萄架是一件很小的事情，行动起来却有如此多的阻碍。我不管了，中午下班后，找了把锯子，一把砍刀。就去砍竹子去了。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;那竹子太高了你砍倒了也拖不上来&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;你去砍点树，等他干了明年再来搭吧&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;不，我就要去砍竹子。我花了两个小时才砍了一根竹子上来，如老人所说，确实不太容易；再在众人七嘴八舌的指导下，完成了这个葡萄架的搭建。搭一个葡萄架最好的时间原来不是去年冬天，就是现在。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/gblog/images/banners/d5c487cf-c279-4170-b935-de57c17466f6.avif&quot; alt=&quot;葡萄架&quot;&gt;&lt;/p&gt;
&lt;p&gt;虽然很简陋，但我期望它今年能结果。&lt;/p&gt;
&lt;h2 id=&quot;最近更新&quot;&gt;最近更新&lt;/h2&gt;

&lt;div class=&quot;relative not-prose my-8&quot;&gt; &lt;div class=&quot;flex gap-x-2&quot;&gt; &lt;div class=&quot;relative -ml-2 last:after:hidden after:absolute after:top-7 after:bottom-0 after:start-3.5 after:w-px after:-translate-x-[0.5px] after:bg-gray-200 dark:after:bg-neutral-700 after:bg-neutral-400&quot;&gt; &lt;div class=&quot;relative z-10 size-7 flex justify-center items-center&quot;&gt; &lt;div class=&quot;size-2 rounded-full bg-neutral-400 dark:bg-neutral-600&quot;&gt;&lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;div class=&quot;grow mb-4 group&quot;&gt; &lt;time class=&quot;text-sm font-normal leading-none text-neutral-500 dark:text-neutral-400 group-hover:text-neutral-400&quot;&gt;20250-03-03&lt;/time&gt; &lt;h3 class=&quot;text-lg font-semibold text-neutral-700 group-hover:text-orange-600 dark:text-neutral-200 dark:group-hover:text-orange-300&quot;&gt;开始搭葡萄架&lt;/h3&gt;   &lt;/div&gt; &lt;/div&gt;&lt;div class=&quot;flex gap-x-2&quot;&gt; &lt;div class=&quot;relative -ml-2 last:after:hidden after:absolute after:top-7 after:bottom-0 after:start-3.5 after:w-px after:-translate-x-[0.5px] after:bg-gray-200 dark:after:bg-neutral-700 after:bg-neutral-400&quot;&gt; &lt;div class=&quot;relative z-10 size-7 flex justify-center items-center&quot;&gt; &lt;div class=&quot;size-2 rounded-full bg-neutral-400 dark:bg-neutral-600&quot;&gt;&lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;div class=&quot;grow mb-4 group&quot;&gt; &lt;time class=&quot;text-sm font-normal leading-none text-neutral-500 dark:text-neutral-400 group-hover:text-neutral-400&quot;&gt;20250-03-12&lt;/time&gt; &lt;h3 class=&quot;text-lg font-semibold text-neutral-700 group-hover:text-orange-600 dark:text-neutral-200 dark:group-hover:text-orange-300&quot;&gt;发芽了&lt;/h3&gt;  &lt;div class=&quot;mb-4 mt-4&quot;&gt; &lt;figure&gt; &lt;img src=&quot;https://images.godruoyi.com/gblog/images/banners/d6e4e791-c2d3-4668-8268-ed13f926ec9c.avif&quot; alt=&quot;葡萄发芽了&quot; class=&quot;w-full h-auto rounded-lg shadow-md max-w-[350px] max-h-[480px]&quot;&gt; &lt;/figure&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt;&lt;div class=&quot;flex gap-x-2&quot;&gt; &lt;div class=&quot;relative -ml-2 last:after:hidden after:absolute after:top-7 after:bottom-0 after:start-3.5 after:w-px after:-translate-x-[0.5px] after:bg-gray-200 dark:after:bg-neutral-700 after:bg-neutral-400&quot;&gt; &lt;div class=&quot;relative z-10 size-7 flex justify-center items-center&quot;&gt; &lt;div class=&quot;size-2 rounded-full bg-neutral-400 dark:bg-neutral-600&quot;&gt;&lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;div class=&quot;grow mb-4 group&quot;&gt; &lt;time class=&quot;text-sm font-normal leading-none text-neutral-500 dark:text-neutral-400 group-hover:text-neutral-400&quot;&gt;2025-05-31&lt;/time&gt; &lt;h3 class=&quot;text-lg font-semibold text-neutral-700 group-hover:text-orange-600 dark:text-neutral-200 dark:group-hover:text-orange-300&quot;&gt;开始结果了&lt;/h3&gt; &lt;p class=&quot;text-base text-neutral-600 group-hover:text-neutral-500 dark:text-neutral-350 dark:group-hover:text-neutral-400&quot;&gt;一个多月就快铺满整个葡萄架了，并且还结果了。&lt;/p&gt; &lt;div class=&quot;mb-4 mt-4&quot;&gt; &lt;div class=&quot;flex flex-wrap gap-4&quot;&gt; &lt;img src=&quot;https://images.godruoyi.com/gblog/images/banners/3fb20d22-d7ba-4532-bbe1-5a0e12c59950.avif&quot; alt=&quot;葡萄架&quot; class=&quot;w-full max-w-[320px] max-h-[480px] h-auto object-cover rounded-lg shadow-md&quot;&gt;&lt;img src=&quot;https://images.godruoyi.com/gblog/images/banners/8abf821e-8210-4c17-bd44-6f05e166ba53.avif&quot; alt=&quot;葡萄架&quot; class=&quot;w-full max-w-[320px] max-h-[480px] h-auto object-cover rounded-lg shadow-md&quot;&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt;&lt;div class=&quot;flex gap-x-2&quot;&gt; &lt;div class=&quot;relative -ml-2 last:after:hidden after:absolute after:top-7 after:bottom-0 after:start-3.5 after:w-px after:-translate-x-[0.5px] after:bg-gray-200 dark:after:bg-neutral-700 after:bg-neutral-400&quot;&gt; &lt;div class=&quot;relative z-10 size-7 flex justify-center items-center&quot;&gt; &lt;div class=&quot;size-2 rounded-full bg-neutral-400 dark:bg-neutral-600&quot;&gt;&lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;div class=&quot;grow mb-4 group&quot;&gt; &lt;time class=&quot;text-sm font-normal leading-none text-neutral-500 dark:text-neutral-400 group-hover:text-neutral-400&quot;&gt;2025-07-04&lt;/time&gt; &lt;h3 class=&quot;text-lg font-semibold text-neutral-700 group-hover:text-orange-600 dark:text-neutral-200 dark:group-hover:text-orange-300&quot;&gt;变成紫葡萄了&lt;/h3&gt; &lt;p class=&quot;text-base text-neutral-600 group-hover:text-neutral-500 dark:text-neutral-350 dark:group-hover:text-neutral-400&quot;&gt;今儿回来一看，铺满整个葡萄架了，就是绿葡萄为啥变紫了呢&lt;/p&gt; &lt;div class=&quot;mb-4 mt-4&quot;&gt; &lt;figure&gt; &lt;img src=&quot;https://images.godruoyi.com/gblog/images/banners/db2aade0-83aa-40f6-bb02-30f968988633.avif&quot; alt=&quot;Alt text 1&quot; class=&quot;w-full h-auto rounded-lg shadow-md max-w-[350px] max-h-[480px]&quot;&gt; &lt;/figure&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt;&lt;div class=&quot;flex gap-x-2&quot;&gt; &lt;div class=&quot;relative -ml-2 last:after:hidden after:absolute after:top-7 after:bottom-0 after:start-3.5 after:w-px after:-translate-x-[0.5px] after:bg-gray-200 dark:after:bg-neutral-700 after:bg-neutral-400&quot;&gt; &lt;div class=&quot;relative z-10 size-7 flex justify-center items-center&quot;&gt; &lt;div class=&quot;size-2 rounded-full bg-neutral-400 dark:bg-neutral-600&quot;&gt;&lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;div class=&quot;grow mb-4 group&quot;&gt; &lt;time class=&quot;text-sm font-normal leading-none text-neutral-500 dark:text-neutral-400 group-hover:text-neutral-400&quot;&gt;2025-09-28&lt;/time&gt; &lt;h3 class=&quot;text-lg font-semibold text-neutral-700 group-hover:text-orange-600 dark:text-neutral-200 dark:group-hover:text-orange-300&quot;&gt;葡萄架垮了&lt;/h3&gt; &lt;p class=&quot;text-base text-neutral-600 group-hover:text-neutral-500 dark:text-neutral-350 dark:group-hover:text-neutral-400&quot;&gt;今天回来给丈母娘过生，看到我搭的葡萄架居然垮了，哈，啥豆腐渣工程。&lt;/p&gt; &lt;div class=&quot;mb-4 mt-4&quot;&gt; &lt;figure&gt; &lt;img src=&quot;https://images.godruoyi.com/gblog/images/banners/8fac9b13-7d12-4ee1-90d6-20e53873d53a.avif&quot; alt=&quot;葡萄架居然垮了&quot; class=&quot;w-full h-auto rounded-lg shadow-md max-w-[350px] max-h-[480px]&quot;&gt; &lt;/figure&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt;&lt;div class=&quot;flex gap-x-2&quot;&gt; &lt;div class=&quot;relative -ml-2 last:after:hidden after:absolute after:top-7 after:bottom-0 after:start-3.5 after:w-px after:-translate-x-[0.5px] after:bg-gray-200 dark:after:bg-neutral-700 after:bg-neutral-400&quot;&gt; &lt;div class=&quot;relative z-10 size-7 flex justify-center items-center&quot;&gt; &lt;div class=&quot;size-2 rounded-full bg-neutral-400 dark:bg-neutral-600&quot;&gt;&lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;div class=&quot;grow  group&quot;&gt; &lt;time class=&quot;text-sm font-normal leading-none text-neutral-500 dark:text-neutral-400 group-hover:text-neutral-400&quot;&gt;todo&lt;/time&gt; &lt;h3 class=&quot;text-lg font-semibold text-neutral-700 group-hover:text-orange-600 dark:text-neutral-200 dark:group-hover:text-orange-300&quot;&gt;未完待续...&lt;/h3&gt;   &lt;/div&gt; &lt;/div&gt; &lt;/div&gt;
&lt;h2 id=&quot;老丈人系列&quot;&gt;老丈人系列&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/dads-tricycle/&quot;&gt;老丈人的三轮车&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/a-turtle/&quot;&gt;一条甲鱼&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>2024 年终总结</title><link>https://godruoyi.com/posts/2024/</link><guid isPermaLink="true">https://godruoyi.com/posts/2024/</guid><description>许老三今年没什么大事，但他有自己的想法，比如把会见诺贝尔奖得主改为出去和朋友喝了点啤酒、把日本泡温泉改为省外一日游，把跑了几千公里的步改为回了几趟农村。</description><pubDate>Fri, 03 Jan 2025 21:00:00 GMT</pubDate><content:encoded>&lt;p&gt;许老三今年是不打算写年终总结的，这总结本该是成功人士的事儿；他今年没有成功；那本不用写；但为什么又动笔了呢？想来是看别人都写了，他也得写点；大家都干的事儿，他又为什么不能干呢？就像跨年夜后的广场，一片狼藉，大家都在丢垃圾，就他把垃圾放到口袋里，那他就吃了一个哑巴亏；又比如所有人都在裁员名单里，就他不在，他就恨不得把自己的名字也加上去，不然又吃了个亏；许老三不愿意吃亏，特别不愿意吃年终总结这种小事的亏。&lt;/p&gt;
&lt;p&gt;然而别人写年终总结时手到拈来，许老三这儿可费劲了；他不得不一篇一篇的翻着别人写的总结，看别人怎么写，用了什么结构，记录了那些亮点， 又或是规划了什么样的未来。然后许老三拿出自己的看家本领，将它们都一一抄过来。把那些发生在别人身上的新鲜事，改为自己的；许老三今年没什么大事，但他有自己的想法，比如把会见诺贝尔奖得主改为出去和朋友喝了点啤酒、把日本泡温泉改为省外一日游，把跑了几千公里的步改为回了几趟农村。&lt;/p&gt;
&lt;p&gt;就这样许老三的总结很快就写完了，但他对这并不满意，这没有突出他对生活的热爱，对技术的追求。比如年初的时候，他看到大家都去日本看樱花，去金阁寺烧香；金阁寺让不让烧香他不知道，但名字有个寺，终归是能烧香的吧。他倒不是说要去烧香，他不喜欢烧香，也不是说要去这金阁旅游， 而是大家都在讨论金阁，他也得参与近来，不然他就吃了个亏；而他又对金阁一无所知，只好买了本书来看，名字就叫金阁寺。&lt;/p&gt;
&lt;p&gt;书是买回来了，才看到一半，大家又在讨论独立开发了。许老三就把书放回了书架，心想此一时彼一时，之前大家都在讨论金阁，自己不看点什么就是吃亏了；但现在大家讨论的是独立开发了，那之前这亏，他必是吃不到了。于是就动手写了几个浏览器扩展。为什么写浏览器扩展而不是 APP，一是他不会写 APP，二是因为大家都在写浏览器扩展，他去写 APP 他就吃了不会写浏览器扩展的亏，他不想吃亏。刚开始时许老三很自信，自己的产品将是全世界最完美的最独特最人性化的，所以每一行代码都经过精心打磨，设计模式最佳实践什么的，许老三最爱了。许老三还交了 5 刀的 Google Chrome Store 入门费，但他的扩展却至今没有提交到谷歌应用商店，因为 AI 又来了。&lt;/p&gt;
&lt;p&gt;时代变了，之前大家讨论的是独立开发，但现在大家讨论的更多的是 AI，此一时又彼一时，那这独立开发的亏，被 AI 替代后，他必是又吃不到了。不过许老三觉得有点累了，他甚至开始不满这些追逐潮流的人，或者说是搅屎棍。这些搅屎棍让许老三吃的亏越来越多了，什么 Cursor，Windsurf，Gemini，V0，许老三还来不及学第一个，大家就已经在讨论下一个了。就拿前几天他刚付了 1000 块钱找人开通了 Azure OpenAI，但大家突然又说 OpenAI 不行了，还是换 Gemini 吧。许老三心里十分不满，对这些搅屎棍的恶意又多了几分。许老三心想，明明一个 GPT4 就能搞定的东西，瞎折腾什么呢。但许老三又不愿意吃亏，就不得不跟在这些时代的搅屎棍后面，吃别人剩下的。&lt;/p&gt;
&lt;p&gt;要说许老三今年又什么值得骄傲的，那想必是孩子长到两岁了，写了个博客，写了几篇文章，吃了几顿媳媳妇做的好吃的。孩子长到两岁本不是许老三的功劳，是他媳妇的；吃饭做饭洗澡穿衣，都是他媳妇做的；但夹边人只知道他和他媳妇一起将孩子养到了两岁，那这功劳，便有他一份了。写了个博客倒是获得了很多人的喜欢，但这喜欢并不是对他的，而是他抄的那个博客的；大家喜欢的不是他许老三的博客，而是他抄的那个博客，但大部分人不知道许老三从那里抄的，那这份喜欢，许老三便接下了。许老三抄东西在行，但写没人写过的东西就犯难；但许老三自己不知道这一点，他以为自己是牛逼的；就算抄别人的东西，抄完变成自己的，就觉得别人是不如自己的；&lt;/p&gt;
&lt;p&gt;许老三的老婆知道他这一点，还告诉过他要务实，要脚踏实地，要专一， 但许老三说： 我不想吃亏。&lt;/p&gt;
&lt;p&gt;许老三不知道，他吃的亏，不是别人给的，而是自己找的。&lt;/p&gt;
&lt;h2 id=&quot;also-read&quot;&gt;Also Read&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/review-2023&quot;&gt;2023 年终总结&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/review-of-2022&quot;&gt;2022 年终总结&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/review-2021&quot;&gt;2021 年终总结&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/2020-year-end-review&quot;&gt;2020 年终总结&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/continue-refueling-in-2019&quot;&gt;2019 年终总结&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;some-pictures-of-2024&quot;&gt;Some Pictures of 2024&lt;/h2&gt;
&lt;p&gt;Next Year Predictions By AI
&lt;img src=&quot;https://images.godruoyi.com/gblog/images/posts/2024/2025.avif&quot; alt=&quot;Next Year Predictions For You&quot;&gt;&lt;/p&gt;
&lt;p&gt;A Cat In My Home
&lt;img src=&quot;https://images.godruoyi.com/gblog/images/posts/2024/banner2.avif&quot; alt=&quot;A Cat&quot;&gt;&lt;/p&gt;
&lt;p&gt;Work Hard, Play Hard
&lt;img src=&quot;https://images.godruoyi.com/gblog/images/posts/2024/github.avif&quot; alt=&quot;Next Year Predictions For You&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/gblog/images/posts/2024/seedling.avif&quot; alt=&quot;Next Year Predictions For You&quot;&gt;&lt;/p&gt;</content:encoded></item><item><title>如何快速搭建自己的博客网站</title><link>https://godruoyi.com/posts/how-to-build-your-blog/</link><guid isPermaLink="true">https://godruoyi.com/posts/how-to-build-your-blog/</guid><description>前段时间将我的博客开源后，陆陆续续的收到很多朋友的喜欢，也有朋友在部署或自定义时遇到了各种各样的问题，更收到了快 50 岁的非专业程序员的邮件咨询，很是开心</description><pubDate>Fri, 16 Aug 2024 16:40:00 GMT</pubDate><content:encoded>&lt;p&gt;前段时间将我的博客开源后，陆陆续续的收到很多朋友的喜欢，也有朋友在部署或自定义时遇到了各种各样的问题，更收到了快 50 岁的非专业程序员的邮件咨询，很是开心。所以我打算写一篇文章来帮助大家更快的基于 &lt;a href=&quot;https://github.com/godruoyi/gblog&quot;&gt;gblog&lt;/a&gt; 搭建自己的博客。&lt;/p&gt;
&lt;h2 id=&quot;准备&quot;&gt;准备&lt;/h2&gt;
&lt;p&gt;在开始之前需要有一些准备知识；原本我是想写一篇从 0 开始的，那我发现那涉及到的知识太多了；所以我假设大家都有如下的背景：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;会一些简单的前端技术(如知道什么是 HTML/JS/TS)&lt;/li&gt;
&lt;li&gt;有 GitHub 账号&lt;/li&gt;
&lt;li&gt;本机安装了 GIT/NodeJS (可选)&lt;/li&gt;
&lt;li&gt;会使用终端执行命令行程序(如 &lt;code&gt;npm run dev&lt;/code&gt; 等，可选)&lt;/li&gt;
&lt;li&gt;有 Cloudflare 账号(可选)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果你没有任何技术背景可能会问为什么需要掌握这些知识，因为这是一个静态博客，没有管理后台、没有数据库；这意味着所有的博客数据都是存储在一个个 HTML 中的，你想做的任何修改，都只能直接修改源代码。&lt;/p&gt;
&lt;p&gt;不过不用过于担心，我将在下面的教程中一步步的引导你如何设置。&lt;/p&gt;
&lt;h2 id=&quot;如何部署&quot;&gt;如何部署&lt;/h2&gt;
&lt;p&gt;本着 Success First 的原则，我们先来将它部署到 Cloudflare 平台上。如果你想要部署到其他平台如 Zeabur 和 Vercel，只需要点击&lt;a href=&quot;https://github.com/godruoyi/gblog?tab=readme-ov-file#deploy-to-zeabur&quot;&gt;文档&lt;/a&gt;里的 Deploy 按钮就能一键部署到对应的平台上。&lt;/p&gt;
&lt;h3 id=&quot;deploy-to-cloudflare-1---create-a-new-fork&quot;&gt;Deploy to Cloudflare 1 - Create a new Fork&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;登录你的 GitHub 账号并 Fork 一下 &lt;a href=&quot;https://github.com/godruoyi/gblog/fork&quot;&gt;gblog&lt;/a&gt; 项目&lt;/li&gt;
&lt;li&gt;注意在 fork 时需要&lt;strong&gt;取消&lt;/strong&gt;勾选「Copy the &lt;code&gt;astro&lt;/code&gt; branch only」&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;GitHub 的 fork 允许你快速的在自己的账号下创建一个和原项目一模一样的项目；而为什么要取消勾选是因为我们要使用另外的分支(&lt;code&gt;gblog-template&lt;/code&gt;)来部署；默认的 &lt;code&gt;astro&lt;/code&gt; 分支包含了我所有的博客文章和图片，它会导致在首次部署时因为需要优化图片而花费很长的时间；当然你也可以使用最新的 astro 分支然后删除 &lt;code&gt;src/content/posts&lt;/code&gt; 下的数据也加快编译。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/gblog/images/posts/gblog/1-fork.avif&quot; alt=&quot;fork&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;deploy-to-cloudflare-2---build&quot;&gt;Deploy to Cloudflare 2 - Build&lt;/h3&gt;
&lt;p&gt;项目 fork 完成后，登录你的 Cloudflare 账号。在左侧菜单栏找到 Workers &amp;amp; Pages，然后新建一个 Page。具体步骤可以参考这个&lt;a href=&quot;https://docs.astro.build/en/guides/deploy/cloudflare/#how-to-deploy-a-site-with-git&quot;&gt;文档&lt;/a&gt;，也可以参考下面的视频片段。注意在选择 &lt;strong&gt;Production branch&lt;/strong&gt; 时为了节省 build 时间可以选择 &lt;code&gt;gblog-template&lt;/code&gt; 分支。&lt;/p&gt;
&lt;video controls=&quot;&quot;&gt;&lt;source src=&quot;https://github.com/user-attachments/assets/c8556b49-d7a4-43fa-9a7f-5a27db5c74f8&quot; type=&quot;video/mp4&quot;&gt;&lt;/video&gt;
&lt;p&gt;部署成功后，你可通过 Cloudflare 提供的默认域名访问我们刚刚创建的博客，如果一切正常，你能看到一个很好的博客页面如 &lt;a href=&quot;https://gblog-test-1.pages.dev%E3%80%82&quot;&gt;https://gblog-test-1.pages.dev。&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;如何发布文章&quot;&gt;如何发布文章&lt;/h2&gt;
&lt;p&gt;静态类型的博客网站并没有一个管理后台来管理博客数据，我们也没有计划为 gblog 添加 CRM 支持。所以如果你想要发布自己的文章，你需要在对应的目录添加一个新文件，一个文件就是一篇博客。&lt;/p&gt;
&lt;p&gt;最简单的方式是通过 GitHub 在 &lt;code&gt;src/content/posts&lt;/code&gt; 文件夹下添加文件，如下所示：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/gblog/images/posts/gblog/2-publish.avif&quot; alt=&quot;publish&quot;&gt;&lt;/p&gt;
&lt;p&gt;文件后缀一般为 &lt;code&gt;.md&lt;/code&gt; 或许 &lt;code&gt;.mdx&lt;/code&gt;；博客文章的固定格式如下所示，更多的配置项你可以查看&lt;a href=&quot;https://github.com/godruoyi/gblog/blob/astro/src/content/config.ts#L3&quot;&gt;这里&lt;/a&gt;的定义：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;md&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;md&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display:grid&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-light-font-weight:bold;--shiki-dark:#6CB6FF;--shiki-dark-font-weight:bold&quot;&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;title: &amp;quot;标题&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;description: &amp;quot;描述&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;pubDate: &amp;quot;发布时间如 2021-11-12 03:10:04&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;category: &amp;quot;tool&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;banner: &amp;quot;封面图“&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;tags: [&amp;quot;标签1&amp;quot;, &amp;quot;标签2&amp;quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-light-font-weight:bold;--shiki-dark:#6CB6FF;--shiki-dark-font-weight:bold&quot;&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;正文&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;---
title: &amp;#34;标题&amp;#34;
description: &amp;#34;描述&amp;#34;
pubDate: &amp;#34;发布时间如 2021-11-12 03:10:04&amp;#34;
category: &amp;#34;tool&amp;#34;
banner: &amp;#34;封面图“
tags: [&amp;#34;标签1&amp;#34;, &amp;#34;标签2&amp;#34;]
---

正文&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&apos;rehype-pretty-copied&apos;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;blockquote&gt;
&lt;p&gt;MD/MDX 都是 Markdown 格式的文本内容，如果你对这种格式不太熟悉，我推荐你看看这里的 &lt;a href=&quot;https://www.markdowntutorial.com/zh-cn/&quot;&gt;Markdown 教程&lt;/a&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;博客文章的固定格式这里有几个需要注意的点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;英文冒号 (:) 前面的为属性名，后面的为属性值；属性值通常会使用英文双引号包裹起来&lt;/li&gt;
&lt;li&gt;一篇文章的分类 (category) 只能有一个，并且只能是 &lt;code&gt;src/content/categories&lt;/code&gt; 下面已有文件的&lt;strong&gt;文件名&lt;/strong&gt;(如 astro/tool 等)&lt;/li&gt;
&lt;li&gt;如果你想添加&amp;amp;删除更多的分类，请直接修改 &lt;code&gt;src/content/categories&lt;/code&gt; 下面的文件&lt;/li&gt;
&lt;li&gt;文章的封面图(banner)可以是在线图片地址如 &lt;a href=&quot;https://example.com/a.png&quot;&gt;https://example.com/a.png&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;文章的封面图(banner)如果是本地图片，需要放到 &lt;code&gt;src/images/&lt;/code&gt; 目录下
&lt;ol&gt;
&lt;li&gt;如你在 &lt;code&gt;src/images&lt;/code&gt; 目录下新创建了一个文件夹 &lt;code&gt;hello&lt;/code&gt; 并且把一张图片如 &lt;code&gt;banner1.png&lt;/code&gt; 放了进去，那你这篇文章的 banner 字段可配置为：&lt;code&gt;@images/hello/banner1.png&lt;/code&gt;，注意前面有个 &lt;code&gt;@&lt;/code&gt; 符号&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;下面请看一个例子。我们新建了一个 &lt;code&gt;hello.md&lt;/code&gt; 的文件，当所有内容都准备好了并点击第四步 Commit changes 后；由于我们在第一步时已经成功部署到了 Cloudflare 平台，所以如果这里不出意外的话当你提交代码后，你的修改会自动部署到 Cloudflare。待其部署成功后，就可以通过如 &lt;a href=&quot;https://example.com/posts/hello&quot;&gt;https://example.com/posts/hello&lt;/a&gt; 访问刚刚新增的博客内容。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/gblog/images/posts/gblog/3-newfile.avif&quot; alt=&quot;newfile&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;如何在线修改项目可选&quot;&gt;如何在线修改项目(可选)&lt;/h2&gt;
&lt;p&gt;如果你不方便在本地修改&amp;amp;运行项目，你可以通过 github.dev 在线修改博客内容；具体做法是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;将你的项目地址如 &lt;a href=&quot;https://github.com/godruoyi/gblog&quot;&gt;https://github.com/godruoyi/gblog&lt;/a&gt; 中的域名后缀从 &lt;code&gt;com&lt;/code&gt; 换为 &lt;code&gt;dev&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;或者直接在 GitHun 的项目页面按 &lt;code&gt;.&lt;/code&gt; 键(英文输入法的句号)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/gblog/images/posts/gblog/4-githubdev.avif&quot; alt=&quot;githubdev&quot;&gt;&lt;/p&gt;
&lt;p&gt;这都将打开一个基于 VSCode 的在线编辑器，你可以在里面修改任何你想修改的内容，更多 github.dev 的用法请参考 &lt;a href=&quot;https://docs.github.com/zh/codespaces/the-githubdev-web-based-editor&quot;&gt;github.dev 基于 web 的编辑器&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&quot;如何在本地启动项目可选&quot;&gt;如何在本地启动项目(可选)&lt;/h2&gt;
&lt;p&gt;在本地启动项目前先确认你安装了 Git；你可以打开你的电脑终端(&lt;a href=&quot;https://www.freecodecamp.org/chinese/news/how-to-open-the-command-prompt-in-windows-10-how-to-open-command-prompt-as-an-administrator/&quot;&gt;Windows&lt;/a&gt; &amp;amp; &lt;a href=&quot;https://support.apple.com/zh-cn/guide/terminal/apdb66b5242-0d18-49fc-9c47-a2498b7c91d5/mac&quot;&gt;MacOS&lt;/a&gt;)并在上面输入如 &lt;code&gt;git --version&lt;/code&gt; 确认，这通常会输出已安装软件的版本信息，如下面我本地的版本。如果你还没有安装他们，可以点击文末的链接安装它。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/gblog/images/posts/gblog/5-local.avif&quot; alt=&quot;local&quot;&gt;&lt;/p&gt;
&lt;p&gt;安装成功后在你的终端里执行下面的命令：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display:grid&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; clone&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; git@github.com:yourname/your-blog.git&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; myblog&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;git clone git@github.com:yourname/your-blog.git myblog&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&apos;rehype-pretty-copied&apos;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Git Clone 命令会将你的项目代码下载到当前运行命令所在目录下的 myblog 文件夹下，&lt;strong&gt;请注意&lt;/strong&gt;修改这里的项目地址为你项目的地址，如下：
&lt;img src=&quot;https://images.godruoyi.com/gblog/images/posts/gblog/7-ssh.avif&quot; alt=&quot;ssh&quot;&gt;&lt;/p&gt;
&lt;p&gt;这里可以选择 HTTPS 类型的项目地址或者 SSH 类型的，前者在你每次提交代码的时候都需要你输入 GitHub 的账号密码，后者可以免密提交。你可以参考&lt;a href=&quot;https://docs.github.com/en/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account?platform=windows&quot;&gt;这里的文章&lt;/a&gt;配置 SSH。&lt;/p&gt;
&lt;p&gt;接下来通过 CD 命令进入到博客所在目录，并执行 &lt;code&gt;npm install&lt;/code&gt; 安装依赖。如果这一步安装失败或者用时太久，你可以试试将 npm 的镜像替换为国内源，参见&lt;a href=&quot;https://npmmirror.com/&quot;&gt;淘宝镜像设置教程&lt;/a&gt;。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display:grid&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;# 进入 myblog 文件夹&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;cd&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; myblog&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;# 安装博客所需依赖&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;npm&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; install&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;# 在本地启动一个开发预览版本&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;npm&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; dev&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;# 进入 myblog 文件夹
cd myblog

# 安装博客所需依赖
npm install

# 在本地启动一个开发预览版本
npm run dev&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&apos;rehype-pretty-copied&apos;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;最后一条命令运行成功后，你应该能看到一个如下的提示，这意味着我们的博客已经成功在本地运行。你可以访问 &lt;a href=&quot;http://localhost:4321/&quot;&gt;http://localhost:4321/&lt;/a&gt; 查看本地预览版本，也可以用自己感兴趣的编辑器如 Sublime Text、VSCode、IDEA 打开并编辑它们。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/gblog/images/posts/gblog/8-rundev.avif&quot; alt=&quot;rundev&quot;&gt;&lt;/p&gt;
&lt;p&gt;本地修改完成后，你可以通过下面的方式将这些新的改变提交到 GitHub：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display:grid&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; add&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; .&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; commit&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; -m&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &amp;quot;简短的描述下本次修改的内容&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; push&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;git add .
git commit -m &amp;#34;简短的描述下本次修改的内容&amp;#34;
git push &quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&apos;rehype-pretty-copied&apos;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;不论你是将你的博客部署到  Zeabur、Vercel 还是 Cloudflare，它们在你提交代码后都会自动部署，这通常是由于平台服务商在我们首次集成时已经为我们自动设置好了相应的触发器，所以才能提交代码后就自动部署。&lt;/p&gt;
&lt;h2 id=&quot;如何自定义&quot;&gt;如何自定义&lt;/h2&gt;
&lt;p&gt;默认的博客模版包含了很多预设的配置如 Logo/评论/SEO，默认的文案等，请使用你任何喜欢的编辑器打开源代码并编辑它们。&lt;/p&gt;
&lt;h3 id=&quot;自定义配置&quot;&gt;自定义配置&lt;/h3&gt;
&lt;p&gt;博客大多数的配置都定义在 &lt;code&gt;src/config.ts&lt;/code&gt; 文件中，你可以修改它们为任何你期望的样子：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;typescript&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;typescript&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display:grid&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; const&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; SITE&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; Site&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    author: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&amp;#39;Godruoyi&amp;#39;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    url: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&amp;#39;https://godruoyi.com&amp;#39;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    title: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&amp;#39;连波的闲谈杂鱼&amp;#39;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    description: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&amp;#39;Godruoyi&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#F47067&quot;&gt;\&amp;#39;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;s personal blog, I enjoy the process of building something using any technology stack&amp;#39;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;export const SITE: Site = {
    author: &apos;Godruoyi&apos;,
    url: &apos;https://godruoyi.com&apos;,
    title: &apos;连波的闲谈杂鱼&apos;,
    description: &apos;Godruoyi\&apos;s personal blog, I enjoy the process of building something using any technology stack&apos;,
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&apos;rehype-pretty-copied&apos;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h3 id=&quot;自定义图片&quot;&gt;自定义图片&lt;/h3&gt;
&lt;p&gt;目前博客使用下面几张图片作为系统图片，请用你自己的图片替换它们；如果需要修改文件名，请全局搜索所有相关的文件名并一起替换。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;src/images/brand_logo.jpeg        Logo 
src/images/default_home_og.png    分享博客首页到社交平台如 Twitter 展示的分享图片 
src/images/favicon_icon.png       ICON
src/images/favicon_icon.svg       ICON SVG 格式 
src/images/footer.png             页面底部右侧显示的展位图
src/images/home.png               首页头部图片
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;自定义描述&quot;&gt;自定义描述&lt;/h3&gt;
&lt;p&gt;有一些文案描述如「生活不能让人处处满意，但我们还要热情的生活下去」并没有放到配置文件中，你需要全局搜索然后找到对应的文件并修改它们。&lt;/p&gt;
&lt;h3 id=&quot;自定义评论&quot;&gt;自定义评论&lt;/h3&gt;
&lt;p&gt;我们目前使用 &lt;a href=&quot;https://giscus.app/&quot;&gt;https://giscus.app/&lt;/a&gt; 作为评论系统，你可以参考 Giscus 官网查看如何配置它们，也可以参考 &lt;strong&gt;liruifengv&lt;/strong&gt; 的这篇博客 &lt;a href=&quot;https://liruifengv.com/posts/add-comments-to-astro/&quot;&gt;https://liruifengv.com/posts/add-comments-to-astro/&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;注意博客评论默认是关闭状态，你可将 COMMENT_ENABLE 环境变量的值设置为 true 或者直接在 src/config.ts 文件中修改它们。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;typescript&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;typescript&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display:grid&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; const&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Settings&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    Comment: {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;        // enable: !!(import.meta.env.COMMENT_ENABLE),&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        enable: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        giscus: {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;            repo: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&amp;#39;godruoyi/gblog&amp;#39;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;            repoId: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&amp;#39;MDEwOlJlcG9zaXRvcnkxMjcyODI0NzA&amp;#39;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;            category: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&amp;#39;Announcements&amp;#39;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;            categoryId: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&amp;#39;DIC_kwDOB5YtJs4CfZnX&amp;#39;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;            darkThem: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&amp;#39;noborder_gray&amp;#39;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;            lightThem: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&amp;#39;light&amp;#39;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        },&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;export const Settings = {
    Comment: {
        // enable: !!(import.meta.env.COMMENT_ENABLE),
        enable: true,
        giscus: {
            repo: &apos;godruoyi/gblog&apos;,
            repoId: &apos;MDEwOlJlcG9zaXRvcnkxMjcyODI0NzA&apos;,
            category: &apos;Announcements&apos;,
            categoryId: &apos;DIC_kwDOB5YtJs4CfZnX&apos;,
            darkThem: &apos;noborder_gray&apos;,
            lightThem: &apos;light&apos;,
        },
    },
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&apos;rehype-pretty-copied&apos;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h3 id=&quot;将资源上传到-s3-加速访问&quot;&gt;将资源上传到 S3 加速访问&lt;/h3&gt;
&lt;p&gt;我们可以将编译后的所有资源文件如图片/CSS/JS 全部上传到 S3/R2 平台以加速访问；这个特性基于 &lt;strong&gt;Yufan&lt;/strong&gt; 老师的 &lt;a href=&quot;https://github.com/syhily/astro-uploader&quot;&gt;astro-uploader&lt;/a&gt; 工具，请访问该项目查看如何配置它们。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;typescript&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;typescript&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display:grid&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// src/config.ts&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; const&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Settings&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    Assets: {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        uploadAssetsToS3: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;!!&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;meta&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.env.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;S3_ENABLE&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        config: {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;            paths: [&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&amp;#39;assets&amp;#39;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;            endpoint: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;meta&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.env.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;S3_ENDPOINT&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;            bucket: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;meta&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.env.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;S3_BUCKET&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;            accessKey: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;meta&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.env.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;S3_ACCESS_KEY&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;            secretAccessKey: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;meta&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.env.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;S3_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;            root: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&amp;#39;gblog&amp;#39;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        },&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;// src/config.ts
export const Settings = {
    Assets: {
        uploadAssetsToS3: !!(import.meta.env.S3_ENABLE),
        config: {
            paths: [&apos;assets&apos;],
            endpoint: import.meta.env.S3_ENDPOINT as string,
            bucket: import.meta.env.S3_BUCKET as string,
            accessKey: import.meta.env.S3_ACCESS_KEY as string,
            secretAccessKey: import.meta.env.S3_SECRET_ACCESS_KEY as string,
            root: &apos;gblog&apos;,
        },
    },
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&apos;rehype-pretty-copied&apos;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;上述配置的含义是将编译后的 assets 文件夹下的所有文件上传到 S3 服务器的 gblog 文件夹下，这通常需要配合修改 astro.config.mjs 文件如下：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;typescript&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;typescript&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display:grid&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// astro.config.mjs&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; default&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; defineConfig&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#F69D50&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    build: {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        assets: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&amp;#39;assets&amp;#39;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        assetsPrefix: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&amp;#39;https://images.godruoyi.com/gblog&amp;#39;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#F69D50&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#F69D50&quot;&gt;})&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;// astro.config.mjs
export default defineConfig({
    build: {
        assets: &apos;assets&apos;,
        assetsPrefix: &apos;https://images.godruoyi.com/gblog&apos;,
    },
})&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&apos;rehype-pretty-copied&apos;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;即通过 build 配置告诉 astro 在编译时将资源文件全部打包到 assets 文件夹下，编译完成后会程序自动调用 &lt;a href=&quot;https://github.com/syhily/astro-uploader&quot;&gt;astro-uploader&lt;/a&gt; 将这些文件上传到 S3；所以我们还需要告诉 Astro 使用 S3 的路径如 &lt;a href=&quot;https://images.godruoyi.com/gblog&quot;&gt;https://images.godruoyi.com/gblog&lt;/a&gt; 作为前缀。&lt;/p&gt;
&lt;p&gt;注意你可能还需要到对应的部署平台设置相应的环境变量。&lt;/p&gt;
&lt;h2 id=&quot;如何添加新功能&quot;&gt;如何添加新功能&lt;/h2&gt;
&lt;p&gt;博客的 UI 使用的是 &lt;a href=&quot;https://preline.co/&quot;&gt;Preline UI&lt;/a&gt;，你可以从它们的 &lt;a href=&quot;https://preline.co/examples.html&quot;&gt;Example&lt;/a&gt; 或者 &lt;a href=&quot;https://preline.co/templates.html&quot;&gt;Template&lt;/a&gt; 中 copy 你想要的样式文件并直接粘贴到 gblog 中，这通常不需要做过多的修改就能 clone 你看到的效果。&lt;/p&gt;
&lt;h2 id=&quot;参考&quot;&gt;参考&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://cali.so/blog/guide-for-cloning-my-site&quot;&gt;如何复刻本网站，零氪快速建博客 | Cali Castle&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nodejs.org/en/download/prebuilt-installer&quot;&gt;Download Node.js&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://git-scm.com/downloads&quot;&gt;Download Git&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntutorial.com/zh-cn/&quot;&gt;Markdown 教程&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.freecodecamp.org/chinese/news/how-to-open-the-command-prompt-in-windows-10-how-to-open-command-prompt-as-an-administrator/&quot;&gt;如何在 Windows 10 中打开命令提示符&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://support.apple.com/zh-cn/guide/terminal/apdb66b5242-0d18-49fc-9c47-a2498b7c91d5/mac&quot;&gt;在 Mac 上的“终端”中执行命令和运行工具&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/git-guides&quot;&gt;Git Guides&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://preline.co/&quot;&gt;Preline UI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/mearashadowfax/ScrewFast&quot;&gt;ScrewFast 项目&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/syhily/astro-uploader&quot;&gt;https://github.com/syhily/astro-uploader&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>没人抬</title><link>https://godruoyi.com/posts/the-village/</link><guid isPermaLink="true">https://godruoyi.com/posts/the-village/</guid><description>乡里人怕的，是自己死了后，找不到人抬，我不明白。</description><pubDate>Fri, 09 Aug 2024 13:54:00 GMT</pubDate><content:encoded>&lt;p&gt;乡里人怕的，是自己死了后，找不到人抬，我不明白。&lt;/p&gt;
&lt;p&gt;在电话里跟人吵了起来，说我不懂人情事故，自己家亲叔叔泡学酒都不来。而所谓学酒，无非就是自家的孩子考上了大学，请上两桌聊表庆祝，再收回这两年送出去的人亲钱，这在乡里十分常见。&lt;/p&gt;
&lt;p&gt;一大早，他们懂人情世故的人，就驱车两三个小时前往市里去吃酒了，我没去，我要上班。&lt;/p&gt;
&lt;p&gt;乡里人有个规矩，无论周围哪家办酒，亲戚还是邻居，还是上个寨子的人。在外面的人都要回来；即使家里人父母健在，远在他乡的人都要赶回来；有从上海回来的，有从贵阳回来的，开十几个小时的车，再请上三五天假，以表示对这家人的尊重，特别是如果他们家有个儿子的话。&lt;/p&gt;
&lt;p&gt;而那些选择不回来的人，乡里人对他们是不满的，这些不满往往会在他们办酒时体现出来，如找不到人来帮忙；乡里人是没有丧事喜事一条龙服务的，靠的只是邻里邻居相互帮忙。若恰巧他们办的是丧事，那出殡时怕是找不到人抬的，乡里人就站在边上，也不接散的烟，睥睨着一切。&lt;/p&gt;
&lt;p&gt;你现在傲气，到时候有你求人的那天。&lt;/p&gt;
&lt;p&gt;我不知道如何回复电话那头这样的质问，我也不明白所谓的人情世故到底是什么，我只是更愿意过好自己的生活。&lt;/p&gt;
&lt;p&gt;关于乡里人的风土人情，你可以看看我之前的&lt;a href=&quot;https://godruoyi.com/posts/the-changes-in-my-hometown/&quot;&gt;老家的变化&lt;/a&gt;，而乡里人对办酒的热爱，从&lt;a href=&quot;https://godruoyi.com/posts/banjiu/&quot;&gt;办酒&lt;/a&gt;可见一般。&lt;/p&gt;</content:encoded></item><item><title>什么是发件箱模式</title><link>https://godruoyi.com/posts/what-is-the-outbox-pattern/</link><guid isPermaLink="true">https://godruoyi.com/posts/what-is-the-outbox-pattern/</guid><description>早上在群里看到有同事在问关于 Outbox Pattern 的问题，而在此之前我对此一无所知，我甚至还错误的发了一些毫不相干的回复，那就姑且把 Outbox Pattern 当作今天 TIL 的对象吧，下面是本次学习的记录</description><pubDate>Sun, 04 Aug 2024 15:43:00 GMT</pubDate><content:encoded>&lt;p&gt;早上在群里看到有同事在问关于 Outbox Pattern 的问题，而在此之前我对此一无所知，我甚至还错误的发了一些毫不相干的回复，那就姑且把 Outbox Pattern 当作今天 TIL 的对象吧，下面是本次学习的记录。&lt;/p&gt;
&lt;h2 id=&quot;什么情况下引入的-outbox-pattern&quot;&gt;什么情况下引入的 Outbox Pattern?&lt;/h2&gt;
&lt;p&gt;当需要同时插入数据到数据库并且还要发送消息到消息队列时，传统操作无法保证这两个操作都完成；举个例子：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;db.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;Transact&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#E36209;--shiki-dark:#F69D50&quot;&gt;tx&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;sqlx&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;Tx&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    mq.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;PublishOrderCreatedEventToKafka&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(data)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    db.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;InsertOrderToDB&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(data)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;})&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;db.Transact(func(tx *sqlx.Tx) {
    mq.PublishOrderCreatedEventToKafka(data)
    db.InsertOrderToDB(data)
})&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;ol&gt;
&lt;li&gt;发布到 Kafka 成功但是数据保存失败，整个事务回滚；但已经发布到 Kafka 的消息无法简单的回滚，导致消费者收到了本不该存在的数据。&lt;/li&gt;
&lt;li&gt;保存数据成功但发布消息失败，整个事务提交，数据已保存到 DB 中；但由于没有发布到 Kafka，消费者丢失了本次更新。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;即对外部服务的调用没法随着本地事务一起提交一起回滚，导致数据的完整性出了问题；那有没有什么办法可以将这两个操作放到同一个本地事务中，并利用事务的 ACID 来保证数据的完整性呢？&lt;/p&gt;
&lt;p&gt;这就引入了 Outbox Pattern。&lt;/p&gt;
&lt;h2 id=&quot;什么是-outbox-pattern&quot;&gt;什么是 Outbox Pattern？&lt;/h2&gt;
&lt;p&gt;我们知道大多数数据库都能保证两条 SQL 操作要么全都成功，要么全都失败，参见&lt;a href=&quot;https://godruoyi.com/posts/why-do-we-need-database-transactions/&quot;&gt;为什么我们需要数据库事务&lt;/a&gt;；如果我们能将「发送消息到队列」这一操作转化为 —— 插入一条数据到数据库；我们就能利用数据库的特性来保证数据的完整性，然后再利用额外的程序来异步读取数据并发送到 Kafka。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;db.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;Transact&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#E36209;--shiki-dark:#F69D50&quot;&gt;tx&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;sqlx&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;Tx&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    db.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;InsertOutboxTable&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(data)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    db.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;InsertOrderToDB&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(data)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;})&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;db.Transact(func(tx *sqlx.Tx) {
    db.InsertOutboxTable(data)
    db.InsertOrderToDB(data)
})&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/gblog/images/posts/outbox-pattern/outbox-pattern.avif&quot; alt=&quot;outbox pattern&quot;&gt;
图片来自于 &lt;a href=&quot;https://github.com/debezium/debezium-examples/tree/main/outbox&quot;&gt;Outbox Pattern Example&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;这样做有什么好处&quot;&gt;这样做有什么好处？&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;通过同一个本地事务来保证数据的完整性，即这两个操作要么全都成功，要么全都失败&lt;/li&gt;
&lt;li&gt;不需要依赖 MQ 组件&lt;/li&gt;
&lt;li&gt;业务方只需要关注如何操作数据&lt;/li&gt;
&lt;li&gt;更可靠；一旦事务提交，异步程序会通过多种方式(如重试)保证数据正确的投递到 MQ，不会因为短时间的 Kafka 断开连接而投递失败&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;这样做有什么弊端&quot;&gt;这样做有什么弊端？&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;引入了额外的程序；需要单独的程序&amp;#x26;进程异步读取数据并发送到 MQ&lt;/li&gt;
&lt;li&gt;更高的延迟；之前是直接将数据投递到 MQ，现在需要先插入数据到 DB，再由异步程序读取 DB 的数据，最后再投递到 MQ&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;我们可以设计一个通用的表结构来保存消息实体，它应该是和业务无关的，如：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;sql&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;sql&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;CREATE&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; TABLE&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; reference_outbox&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.outbox (&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    id &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;integer&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; NOT NULL&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    event_name &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;character varying&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;256&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;NOT NULL&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    event_type &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;character varying&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;256&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    event_key &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;character varying&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1024&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;NOT NULL&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    event_payload &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;bytea&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; NOT NULL&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    event_timestamp &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;bigint&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; DEFAULT&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (date_part(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;epoch&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;text&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;now&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()) &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1000&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;double precision&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;NOT NULL&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    headers &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;text&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    target_kafka_topic &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;character varying&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;256&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;NOT NULL&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;CREATE TABLE reference_outbox.outbox (
    id integer NOT NULL,
    event_name character varying(256) NOT NULL,
    event_type character varying(256),
    event_key character varying(1024) NOT NULL,
    event_payload bytea NOT NULL,
    event_timestamp bigint DEFAULT (date_part(&amp;#x27;epoch&amp;#x27;::text, now()) * (1000)::double precision) NOT NULL,
    headers text,
    target_kafka_topic character varying(256) NOT NULL
);&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h3 id=&quot;为什么不将数据都发送到-kafka&quot;&gt;为什么不将数据都发送到 Kafka?&lt;/h3&gt;
&lt;p&gt;那为什么我们要将两条数据都插入到数据库而不是将他们都投递到 Kafka 呢？即应用程序只需要将消息投递到 Kafka 然后再由单独的消费者来处理它们，这样也能保证「发送消息到队列」和「插入到数据库」这两个操作的完整性。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;mq.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;PublishOrderCreatedEventToKafka&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(data) &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// 生产者&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;mq.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;pull&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(kafka&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;topic, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (e &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;Event) { &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// 消费者&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    db.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;Transact&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#E36209;--shiki-dark:#F69D50&quot;&gt;tx&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;sqlx&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;Tx&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        db.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;InsertOrderToDB&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(e)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    })&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;})&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;mq.PublishOrderCreatedEventToKafka(data) // 生产者

mq.pull(kafka-topic, func (e *Event) { // 消费者
    db.Transact(func(tx *sqlx.Tx) {
        db.InsertOrderToDB(e)
    })
})&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;因为这样违背了一个原则：Read-Your-Wrirte。假设消费者在处理这个消息时延迟了 5 秒钟，那意味着你通过 APP 下的订单需要 5 秒后才能查看它们，这对于用户来说是不可接受的。&lt;/p&gt;
&lt;h2 id=&quot;如何实现-outbox-pattern&quot;&gt;如何实现 Outbox Pattern&lt;/h2&gt;
&lt;p&gt;Outbox Pattern 的难点在于如何设计一个程序，可以异步的读取数据并发送到 MQ。它应该支持：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;不会意外停止服务，即使停止服务重启后也能继续投递数据到 MQ，不会造成丢失数据&lt;/li&gt;
&lt;li&gt;支持失败重试&lt;/li&gt;
&lt;li&gt;侵入性小&lt;/li&gt;
&lt;li&gt;低延时&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;目前关于 Outbox Pattern 的实现中采用最多的可能是开源的 &lt;a href=&quot;https://github.com/debezium/debezium&quot;&gt;Debezium&lt;/a&gt; 方案，它支持捕获数据库的任何变更并将它们发送到 MQ 平台；支持监控数据库中行级别(row-level)的更改，并且只关注已提交的事务；也支持本地持久化，即就算 Debezium 异常停止服务，重启后也能保证所有的事件都能被正确的处理。&lt;/p&gt;
&lt;h3 id=&quot;postgresql-connector&quot;&gt;PostgreSQL Connector&lt;/h3&gt;
&lt;p&gt;Debezium 为不同的数据库都提供了不同的 Connector，以 PostgreSQL Connector 为例，它基于 WAL 日志变更的方式来捕获 Outbox 表中的新记录并将它们投递到 Apache Kafka 中。与任何基于轮询的方法相比，基于日志的事件捕获几乎是实时的，并且开销也很低。&lt;/p&gt;
&lt;p&gt;不过由于 PostgreSQL 的 WAL 可能隔一段时间就被清理，这导致 Debezium 没法获取数据库的所有变更历史，所以 PostgreSQL Connector 在第一次启动的时候会尝试对数据库做一次快照，后续将基于这个快照来同步数据到 Kafka，参见 &lt;a href=&quot;https://debezium.io/documentation/reference/2.7/connectors/postgresql.html#postgresql-overview&quot;&gt;Debezium connector for PostgreSQL&lt;/a&gt;。&lt;/p&gt;
&lt;h3 id=&quot;如何配置-connector-以捕获数据变更&quot;&gt;如何配置 Connector 以捕获数据变更？&lt;/h3&gt;
&lt;p&gt;我们可以参考&lt;a href=&quot;https://debezium.io/documentation/reference/2.7/tutorial.html#starting-kafka-connect&quot;&gt;这里的例子&lt;/a&gt;通过 Docker 启动一个 Connector，启动成功后 Connector 会对外暴露一个 REST API，可以通过向这个 API POST 一些配置告诉 Connector 应该如何处理数据库变更。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ docker run -it --rm --name connect -p 8083:8083 
-e GROUP_ID=1 
-e CONFIG_STORAGE_TOPIC=my_connect_configs 
-e OFFSET_STORAGE_TOPIC=my_connect_offsets 
-e STATUS_STORAGE_TOPIC=my_connect_statuses 
--link kafka:kafka 
--link mysql:mysql 
quay.io/debezium/connect:2.7
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;下面的这个示例来自官网教程的一个 &lt;a href=&quot;https://debezium.io/documentation/reference/2.7/connectors/postgresql.html#postgresql-example-configuration&quot;&gt;PostgreSQL 样例配置&lt;/a&gt;，其中指定了数据库的账号密码以及捕获哪个表的数据变更等，你可以从&lt;a href=&quot;https://debezium.io/documentation/reference/2.7/connectors/postgresql.html#postgresql-connector-properties&quot;&gt;这个表格&lt;/a&gt;查看每个字段对应的具体含义。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;json&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;json&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;  &quot;name&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;fulfillment-connector&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,  &lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;  &quot;config&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;connector.class&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;io.debezium.connector.postgresql.PostgresConnector&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;database.hostname&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;192.168.99.100&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;database.port&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;5432&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;database.user&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;postgres&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;database.password&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;postgres&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;database.dbname&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; : &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;postgres&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;topic.prefix&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;fulfillment&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;table.include.list&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;public.inventory&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt; // 监控的数据表&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;{
  &amp;#x22;name&amp;#x22;: &amp;#x22;fulfillment-connector&amp;#x22;,  
  &amp;#x22;config&amp;#x22;: {
    &amp;#x22;connector.class&amp;#x22;: &amp;#x22;io.debezium.connector.postgresql.PostgresConnector&amp;#x22;, 
    &amp;#x22;database.hostname&amp;#x22;: &amp;#x22;192.168.99.100&amp;#x22;, 
    &amp;#x22;database.port&amp;#x22;: &amp;#x22;5432&amp;#x22;, 
    &amp;#x22;database.user&amp;#x22;: &amp;#x22;postgres&amp;#x22;, 
    &amp;#x22;database.password&amp;#x22;: &amp;#x22;postgres&amp;#x22;, 
    &amp;#x22;database.dbname&amp;#x22; : &amp;#x22;postgres&amp;#x22;, 
    &amp;#x22;topic.prefix&amp;#x22;: &amp;#x22;fulfillment&amp;#x22;, 
    &amp;#x22;table.include.list&amp;#x22;: &amp;#x22;public.inventory&amp;#x22; // 监控的数据表
  }
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;我们甚至可以为 Connector 配置删除策略以忽略 Outbox 的删除操作，如下：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;json&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;json&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;  &quot;name&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;fulfillment-connector&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,  &lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;  &quot;config&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;table.include.list&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;public.outbox_table&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// 监控的数据表&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;tombstones.on.delete&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;false&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;{
  &amp;#x22;name&amp;#x22;: &amp;#x22;fulfillment-connector&amp;#x22;,  
  &amp;#x22;config&amp;#x22;: {
    &amp;#x22;table.include.list&amp;#x22;: &amp;#x22;public.outbox_table&amp;#x22;, // 监控的数据表
    &amp;#x22;tombstones.on.delete&amp;#x22;: false
  }
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;将 &lt;code&gt;tombstones.on.delete&lt;/code&gt; 设置为 false 后 Connector 会忽略对 &lt;code&gt;public.outbox_table&lt;/code&gt; 表的删除操作，我们可以将应用程序的业务代码修改为如下形式：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;db.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;Transact&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#E36209;--shiki-dark:#F69D50&quot;&gt;tx&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;sqlx&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;Tx&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    id &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; db.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;InsertOutboxTable&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(data)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    db.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;DeleteOutboxTable&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(id)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    db.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;InsertOrderToDB&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(data)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;})&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;db.Transact(func(tx *sqlx.Tx) {
    id := db.InsertOutboxTable(data)
    db.DeleteOutboxTable(id)

    db.InsertOrderToDB(data)
})&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;这样做有一个好处就是可以保证 outbox 数据表的大小不会太大，并且即使我们在新增后马上就删除了 outbox 的记录，也不会影响 Connector 将新增的消息数据投递到 Kafka。&lt;/p&gt;
&lt;h2 id=&quot;总结&quot;&gt;总结&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Outbox Pattern 将对外部服务的调用转为对两个本地事务内的操作，然后通过本地事务特性来保证数据数据的完整性&lt;/li&gt;
&lt;li&gt;Outbox Pattern 的难点在于设计一个程序异步低延迟的读取数据的变化并将它们投递到 MQ，好在我们有现成的解决方案&lt;/li&gt;
&lt;li&gt;Debezium 的配置很复杂&lt;/li&gt;
&lt;li&gt;今天就学到这儿了&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;参考&quot;&gt;参考&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/contino-engineering/publishing-events-to-kafka-using-a-outbox-pattern-867a48e29d35&quot;&gt;Publishing Events to Kafka using an Outbox Pattern&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://debezium.io/blog/2019/02/19/reliable-microservices-data-exchange-with-the-outbox-pattern/&quot;&gt;Reliable Microservices Data Exchange With the Outbox Pattern - must read&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://microservices.io/patterns/data/transactional-outbox.html&quot;&gt;Pattern: Transactional outbox&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://debezium.io/documentation/reference/2.7/connectors/postgresql.html#debezium-connector-for-postgresql&quot;&gt;Debezium connector for PostgreSQL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://debezium.io/documentation/reference/2.7/tutorial.html#introduction-debezium&quot;&gt;Tutorial :: Debezium Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://arpitbhayani.me/blogs/read-your-write-consistency/&quot;&gt;Read-your-write consistency&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/why-do-we-need-database-transactions/&quot;&gt;为什么我们需要数据库事务&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>老爸的日记</title><link>https://godruoyi.com/posts/dads-diary/</link><guid isPermaLink="true">https://godruoyi.com/posts/dads-diary/</guid><description>2021 年老爸老妈第二次前往上海打工，一去就被关在赵屯两个多月，期间只能吃救济，菜只能依赖周边农户。每到晚上，人们拿着肥料口袋去田里偷蚕豆。</description><pubDate>Fri, 26 Jul 2024 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;2021 年老爸老妈第二次前往上海打工，一去就被关在赵屯两个多月，期间只能吃救济，菜只能依赖周边农户。每到晚上，人们拿着肥料口袋去田里偷蚕豆。&lt;/p&gt;
&lt;p&gt;上一年老爸老妈挣了 6 万块钱，这一年老爸几个月倒用 5 千。下面是老爸的记录，无奈中带着几分风趣。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;2 月 30 号从老家到上海，3 月 1 号到赵屯。&lt;/p&gt;
&lt;p&gt;5 月 21 号从赵屯到务川，22 号到达务川；务川隔离 7 天，回家隔离 7 天；每天生活费 60 元。有两个月 20 天没上班。&lt;/p&gt;
&lt;p&gt;今年就是这个情况，完了阿。&lt;/p&gt;</content:encoded></item><item><title>美国</title><link>https://godruoyi.com/posts/america/</link><guid isPermaLink="true">https://godruoyi.com/posts/america/</guid><description>昨天晚上开了一个小时的会，受益良多；以下只是我的感悟，请不要信以为真。</description><pubDate>Fri, 05 Jul 2024 16:16:00 GMT</pubDate><content:encoded>&lt;p&gt;昨天晚上开了一个小时的会，受益良多；以下只是我的感悟，&lt;strong&gt;请不要&lt;/strong&gt;信以为真。&lt;/p&gt;
&lt;p&gt;这周娃一直咳嗽，我几乎什么更新都没有，所以在前十分钟聊完项目后，我们仨开始了闲谈。&lt;/p&gt;
&lt;p&gt;J 是我们的老板，当我在闲聊中问到美区公司有没有招中国人然后全职 Remote 时，他默默的摇了头。他给我们说美国现在的程序员只要从大厂离职，也很难找到工作；各个大厂也都在裁员，加上 AI 的崛起，之前 5 个人能干的事，现在只要 3 个人就够了。&lt;/p&gt;
&lt;p&gt;我觉得 AI 的崛起和找工作难甚至公司的裁员计划应该没有太大的关系；工具会让强能力者更好的适应这个社会，而不会使用工具的人，注定会被淘汰。&lt;/p&gt;
&lt;p&gt;然后他给我们讲了半个小时左右的 AI，给我们讲了自然语言识别，讲了 Google 的 &lt;a href=&quot;https://www.tensorflow.org/&quot;&gt;TensorFlow&lt;/a&gt; 框架，讲了模型是如何运转的，还有很多算法。说实话这部分我基本没听懂，我对 AI 的理解还停留在如何调用 API 上。在看他来，未来是 AI 的。想要自己有更好的竞争力，现在就应该动手学点 AI 相关的技术，比如吴恩达的斯坦福课程就是一个不错的入门课程。&lt;/p&gt;
&lt;p&gt;然后他语重心长的建议我们，一旦有机会一定要出去，不要待在中国。国内的环境在他看来比较严峻。当房地产不能拉动经济，地方政府又有很大的债务危机时，很有可能未来几年政府会开始印钱，而印钱就可能会导致通货膨胀，加上就业压力大，以后老百姓的日子可能不会好过。&lt;/p&gt;
&lt;p&gt;在他看来，生活在国内的人都挺不容易的。买一个 200 万的房子，前前后后一共要花费近 300 万，还要还款 30 年；买个奥迪要 200 万。按照普通程序员一年年薪 20 万算，我们得不吃不喝 10 年才买得起一款奥迪；然而在国内卖 200 万的奥迪在美国甚至别的任何国家，都只需要 5 万刀左右，这折合人民币只有 40 万左右。&lt;/p&gt;
&lt;p&gt;关于这点我下来自己搜了下，参考&lt;a href=&quot;http://www.cada.cn/data/info_87_6798.html&quot;&gt;中国汽车流通协会的这篇文章&lt;/a&gt;；如果进口汽车排量大于 4.0L，那取除了收取常规 25% 的关税，17% 的增值税，还要承担 40% 的消费税。按照这个方式计算的话，一辆 5 万美元的进口汽车在国内的报价大约是 63 万人名币。&lt;/p&gt;
&lt;p&gt;还有一个点我觉得他说的非常有理，那就是发达国家对知识产权的保护。在那里，即使是一位非常普通的员工做出的比较新颖的东西，公司也会花费几百万美元来帮助他申请知识产权；并且公司自老板开始，都会将资源倾斜给这位员工。我猜这也是为什么这么多年来硅谷依然能成为全世界顶尖科技的源头的原因。因为在这里能培养出大量的人才。他们没有任何后顾之忧，公司帮他们解决了几乎一切的生活问题，他们要做的就是全身心的投入自己想做的事业，并开创一个新未来。&lt;/p&gt;
&lt;p&gt;这里我又想起了&lt;a href=&quot;https://godruoyi.com/posts/the-revolution-in-silicon-valley/&quot;&gt;几年前&lt;/a&gt;写下的一段话：1981 年的乔布斯，开着保时捷，Macintosh 团队年薪较低的工程师 22000 美元。&lt;/p&gt;
&lt;p&gt;而想想你在国内公司上班搞个开源还被辞退、拿着月薪 5000 的工资却还想做为人类的伟大胜利而奋斗终身、好不容易实现的 idea 却被别人无情的抄袭。在这样的土地上，又怎么能长出参天大树呢。&lt;/p&gt;</content:encoded></item><item><title>Lambda Connect RDS</title><link>https://godruoyi.com/posts/lambda-connect-rds/</link><guid isPermaLink="true">https://godruoyi.com/posts/lambda-connect-rds/</guid><description>我们的目的是把 RDS 和 Lambda 放在同一个VPC 下，并且要支持 Lambda 访问 RDS，Lambda 可以访问 SecretManager，Lambda 可以访问 DynamoDB</description><pubDate>Tue, 21 May 2024 17:47:00 GMT</pubDate><content:encoded>&lt;p&gt;这份文章解释了如何通过 Lambda 连接 AWS RDS 数据库以及 SecretManager 和 DynamoDB。&lt;/p&gt;
&lt;h2 id=&quot;background&quot;&gt;Background&lt;/h2&gt;
&lt;p&gt;我们计划为项目引入关系型数据库支持，进过调研后我们选择了 AWS 的 PostgreSQL。而 PostgreSQL 数据库实例是运行在一个私有网络(VPC)内的，只有在同一个私有网络中的服务才能相互连接。&lt;/p&gt;
&lt;p&gt;而在默认情况下，部署到 AWS 的 Lambda 运行在自己的另一套私有网络内，Lambda 的这个私有网络我们没法控制也不能自定义，就没法实现 Lambda 和 RDS 的互通。&lt;/p&gt;
&lt;p&gt;现在我们的目的是把它们都放在同一个VPC 下，并且要支持：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Lambda 访问 RDS&lt;/li&gt;
&lt;li&gt;Lambda 可以访问 SecretManager&lt;/li&gt;
&lt;li&gt;Lambda 可以访问 DynamoDB&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;vpc&quot;&gt;VPC&lt;/h2&gt;
&lt;p&gt;所有云厂商都提供了 VPC 的配置，这属于网络的基石部分。默认情况下，AWS 会自动为我们创建一条的 VPC，不同的 VPC 默认情况下不能互通网络；请不要使用默认的 VPC 来部署你的应用，这可能因为一些缺失的配置导致你后面不得不花费更多的时间来 debug。&lt;/p&gt;
&lt;h2 id=&quot;create-vpc&quot;&gt;Create VPC&lt;/h2&gt;
&lt;p&gt;可以按照 AWS 控制台的引导快速的创建一条 VPC，不过有几个点还是需要注意的：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;我们的 VPC 需要访问外网(SecretManager &amp;#x26; DynamoDB)&lt;/li&gt;
&lt;li&gt;我们的 VPC 网络下会包含各种云资源(RDS)及 Lambda&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;所以我们应该至少在这个 VPC 下创建两个子网：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;所有的云资源如数据库、EC2 等放到子网 1&lt;/li&gt;
&lt;li&gt;Lambda 运行在子网 2 下&lt;/li&gt;
&lt;li&gt;子网2 需要能访问外网，因为 SecretManager 和 DynamoDB 的服务是通过 Internet 公开访问的&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这里涉及到了几个新的概念：VPC、子网、路由表。&lt;/p&gt;
&lt;h3 id=&quot;子网&quot;&gt;子网&lt;/h3&gt;
&lt;p&gt;一个 VPC 可以包含多个子网，可以理解为 VPC 管理了某一个网络下的所有服务资源，而子网管理了某一个网段下的所有资源。&lt;/p&gt;
&lt;p&gt;举个例子，我们在创建 VPC 时会指定 IPV4 CIDR Block 如 &lt;code&gt;192.168.0.0/16&lt;/code&gt; 这代表这个 VPC 下的所有服务资源它们的 IP 最后都会是 &lt;code&gt;192.168.x.x&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;而子网则是更加具体的管理了&lt;strong&gt;某一个网段下&lt;/strong&gt;的资源，如子网 1 的 IPv4 CIDR 为 &lt;code&gt;192.168.0.0/20&lt;/code&gt; 它代表部署在这个子网下的所有服务，他们的 IP 范围应该为 &lt;code&gt;192.168.0.0 → 192.168.15.255&lt;/code&gt; 。&lt;/p&gt;
&lt;p&gt;因为这个特性所以子网能更加具体的控制某一个网段下的流量访问规则。&lt;/p&gt;
&lt;h3 id=&quot;路由表&quot;&gt;路由表&lt;/h3&gt;
&lt;p&gt;在创建子网时默认会创建这个子网相应的路由表，路由表定义了在这个子网中流量的流出流入规则。举个例子，下面的路由表里包含了两条默认的规则：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;0.0.0.0/0               igw-123456789
192.168.0.0/16          local
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;它代表部署在这个子网中的服务，访问目标地址时的流量流出规则。假设我们将一个 EC2 实例部署到了这个子网：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;当在 EC2 中 Ping &lt;code&gt;192.168.x.x&lt;/code&gt; 时，EC2 会将请求转发到所在子网的路由表，路由表会匹配上面的第二条规则，将流量转发到 Local，Local 会在整个 VPC 范围下广播，最终实现局域网内的通信。&lt;/li&gt;
&lt;li&gt;当在 EC2 中访问如 GitHub 等公网服务时，由于 GitHub 的 IP 地址只满足上述的第一条规则，所以在 EC2 中访问 GitHub 会将流量转发到 &lt;code&gt;igw&lt;/code&gt; 网关。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这里的 &lt;code&gt;igw-123456789&lt;/code&gt; 是一个网关，它的全名叫 &lt;code&gt;Internet Gateway&lt;/code&gt; 。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;如果你的服务只需要在局域网中通信，即不需要访问外网，也不需要外网访问它们；你可以删除上面的 0.0.0.0 这条规则。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;internet-gateway&quot;&gt;Internet Gateway&lt;/h3&gt;
&lt;p&gt;VPC 下的服务默认情况是不能直接访问公共网络的，如果要访问公共网络，我们需要创建一个 Internet Gateway。Internet Gateway 允许与 Internet 之间的双向通信。换句话说，它将来自 VPC 的数据包通过 IGW 转发到 Internet，并将来自 Internet 的请求路由回 VPC，如 WEB 服务器。&lt;/p&gt;
&lt;p&gt;假设我们的网络拓扑图如下，因为有了 IGW 的加持，理想情况下 Lambda 就可以访问外网 + 连接 RDS 数据库了：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;VPC
  - sunnet1(lambda) -&gt; route table1  -&gt; 0.0.0.0/0       -&gt; igw
                           ^         -&gt; 192.168.0.0/16  -&gt; local
                           |
  - subnet2(rds)    -------/
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;Lambda 被部署到了 subnet1，它访问 RDS 时，流量会经过 Route Table 1 然后通过 Local 最后转发到部署在 subnet2 中的 RDS 上，这确实是可行的 ✅。&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;Lambda Connect RDS -&gt; Route Table1 -&gt; 192.168.0.0/16 ---&gt; Local
                                                            |
                                                            |
                                    RDS &amp;#x3C;- subnet2(rds) &amp;#x3C;---/
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Lambda 访问外网时流量会经过 Route Table1 然后转发到 IGW 网关最后由 IGW 转发到外部服务中去。&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;Lambda Connect GitHub -&gt; Route Table1 -&gt; 0.0.0.0/0 ---&gt; IGW -&gt; Internet
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;然而这实际上是行不通的&lt;/strong&gt;，要想通过 IGW 访问外网，部署到该子网的服务&lt;strong&gt;必须有一个公网 IP&lt;/strong&gt;。如 EC2 实例要想通过 IGW 访问外网，那该 EC2 实例必须得有一个公网 IP。&lt;/p&gt;
&lt;p&gt;默认我们部署在 VPC 环境下的 Lambda 并没有一个公网 IP，所以按照上述方式，Lambda 并不能直接访问外部网络。我们还需要引入一个单独的配置—— NAT。&lt;/p&gt;
&lt;h3 id=&quot;nat&quot;&gt;NAT&lt;/h3&gt;
&lt;p&gt;NAT，全称为 Network Address Translation，即网络地址转换。它的主要作用是帮助局域网（LAN）上的私有地址与 Internet 或其他网络上的公有地址进行转换，使得私有地址可以通过 NAT 直接访问外部网络。&lt;/p&gt;
&lt;p&gt;NAT 与 IGW 的区别：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;NAT 是单向的，流量只能从 NAT → 外部网络，外部网络无法访问 NAT 里面的资源。所以如果一些服务只需要访问外网但不允许外网访问内部网络，就可以使用 NAT。&lt;/li&gt;
&lt;li&gt;IGW 是双工的，可以从 IGW → 外部网络，也可以从外部网络到 → NAT。比如 WEB 服务器，即需要服务器能返回 Response 给用户，也需要用户能直接请求服务器上的资源。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;有了 NAT 后，我们的网络拓扑图如下。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;VPC
  - sunnet1(lambda) -&gt; route table1  -&gt; 0.0.0.0/0       -&gt; nat
                                     -&gt; 192.168.0.0/16  -&gt; local
                           
  - subnet2(rds)    -&gt; route table2  -&gt; 0.0.0.0/0       -&gt; igw
                                     -&gt; 192.168.0.0/16  -&gt; local
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;Lambda 被部署在 subnet1 子网，当它在 Connect RDS 时，会通过上面的 &lt;code&gt;route table1 -&gt; 192.168.0.0/16 -&gt; local -&gt; subnet2 -&gt; RDS&lt;/code&gt; 访问到 RDS 服务。&lt;/li&gt;
&lt;li&gt;Lambda 访问外网时，会通过 &lt;code&gt;route table1 -&gt; 0.0.0.0/0 -&gt; nat&lt;/code&gt; 然后经过 NAT 转发出去。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;为了使每个子网的职责更加分明，我们重新设计了它们的网络结构：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;VPC
  - subnet1(lambda) -&gt; route table1  -&gt; 0.0.0.0/0       -&gt; nat
                           ^         -&gt; 192.168.0.0/16  -&gt; local
                           |
  - subnet2(rds)    -------/
    
                                     
  - subnet3(igw)    -&gt; route table2  -&gt; 0.0.0.0/0       -&gt; igw
                                     -&gt; 192.168.0.0/16  -&gt; local
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里单独创建了一个 IGW 子网(&lt;code&gt;subnet3(igw)&lt;/code&gt;)，它将所有的外部流量转发到 IGW；而其他所有的子网都进过 route table 1 将外部流量转发到 NAT。&lt;/p&gt;
&lt;p&gt;举个例子，在上面的网络结构中，如果 Lambda 想要连接 GitHub，那他的流量流向为：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Lambda Connect GitHub -&gt; Route Table1 -&gt; 0.0.0.0/0    ---&gt;    NAT
                                                               |
                                                               |
GitHub &amp;#x3C;- igw &amp;#x3C;- 0.0.0.0/0 &amp;#x3C;- Route Table2 &amp;#x3C;- subnet3(igw) &amp;#x3C;---/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;有了上述的背景知识，我们再来看如何在 AWS 中创建一个 VPC。&lt;/p&gt;
&lt;h3 id=&quot;create-vpc-on-aws&quot;&gt;Create VPC On AWS&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;前往 AWS VPC 控制台点击 &lt;a href=&quot;https://us-east-1.console.aws.amazon.com/vpcconsole/home?region=us-east-1#CreateVpc:createMode=vpcWithResources&quot;&gt;Create VPC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;选择 VPC and More&lt;/li&gt;
&lt;li&gt;指定 IPv4 CIDR，可以用默认的 10.0.0.0/16&lt;/li&gt;
&lt;li&gt;设置 Number of Availability Zones，为了高可用&amp;#x26;简单这里我们设置两个可用区&lt;/li&gt;
&lt;li&gt;设置 Number of public subnets，这里设置为 2 个
&lt;ol&gt;
&lt;li&gt;这里你可能有疑问什么是 Public Subnet，其实就是子网路由表中 &lt;code&gt;0.0.0.0/0&lt;/code&gt; 是不是指向的 IGW，如果是，就是 Public Subnet，如下面的 &lt;code&gt;subnet3(igw)&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;VPC
  - subnet3(igw)    -&gt; route table2  -&gt; 0.0.0.0/0       -&gt; igw
                                     -&gt; 192.168.0.0/16  -&gt; local
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Number of private subnets 这里设置为 2 个&lt;/li&gt;
&lt;li&gt;NAT gateways ($) 设置为 &lt;code&gt;In 1 AX&lt;/code&gt;
&lt;ol&gt;
&lt;li&gt;NAT 是需要单独收费的，如果选择 &lt;code&gt;1 per AZ&lt;/code&gt; 那每个可用区都会设置一个 NAT，对于我们的项目来说一个就够了。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;VPC endpoints 选择 None&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;最后生成的网络结构如下：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/gblog/images/posts/lambda-rds/default-vpc.avif&quot; alt=&quot;Lambda-RDS&quot;&gt;&lt;/p&gt;
&lt;p&gt;这里有两个 Public Subnet 经过路由表 &lt;code&gt;project-rbt-public&lt;/code&gt; 将流量流向了 &lt;code&gt;project-igw&lt;/code&gt;，剩下两个 Private Subnet 各自有一个路由表，最后都将流量流向 &lt;code&gt;project-nat&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;我们重命名并删除不必要的路由表后：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/gblog/images/posts/lambda-rds/kmind-vpc.avif&quot; alt=&quot;Lambda-RDS&quot;&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;每个可用区都包含一个 Public Subnet 和 两个 Private Subnet&lt;/li&gt;
&lt;li&gt;两个可用区的所有 Public Subnet 会将&lt;strong&gt;外部&lt;/strong&gt;流量通过 kmind-igw-routetable 转发到 IGW 网关&lt;/li&gt;
&lt;li&gt;两个可用区的所有 Private Subnet 会将&lt;strong&gt;外部&lt;/strong&gt;流量通过 kmind-NAT-routetable 转发到 NAT 网关。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;以上是我们的基础网络配置，在配置 AWS 服务时，我们只需要把有外网访问需求的服务部署在 private 子网即可。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注意 NAT 的配置，NAT 必须要 Attach 一个 Public 类型的子网才能使得可以从 NAT 访问外部网络，注意看下面的时序图。NAT 访问外部网络的流量最终是从 IWG 网关发出去的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;Lambda Connect GitHub -&gt; Route Table1 -&gt; 0.0.0.0/0    ---&gt;    NAT
                                                               |
                                                               |
GitHub &amp;#x3C;- igw &amp;#x3C;- 0.0.0.0/0 &amp;#x3C;- Route Table2 &amp;#x3C;- subnet3(igw) &amp;#x3C;---/
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;rds&quot;&gt;RDS&lt;/h2&gt;
&lt;p&gt;RDS 的创建没有太多的注意事项，只需要按照提示一步一步创建就好了。唯一需要注意的是在创建时要选择刚刚创建好的 VPC。&lt;/p&gt;
&lt;p&gt;创建好的 RDS 数据库默认情况下是不能直接通过账号密码访问的，因为 PostgreSQL 16 开启了严格模式，在连接数据库时必须要通过 SSL 形式。即除了指定正确的账号密码外，还需要设置正确的证书。&lt;/p&gt;
&lt;p&gt;在测试阶段如果不想这么麻烦，可以通过在 Parameter groups 里创建一个自定义的参数组来覆盖它，具体流程为：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;前往 &lt;a href=&quot;https://us-east-1.console.aws.amazon.com/rds/home?region=us-east-1#parameter-group-list:&quot;&gt;Parameter groups&lt;/a&gt; 创建一个新的参数组&lt;/li&gt;
&lt;li&gt;在创建好的参数组中将 rds_force_ssl 改为 0&lt;/li&gt;
&lt;li&gt;修改数据库，将 DB Parameter Group 改为刚刚创建的参数组。&lt;/li&gt;
&lt;li&gt;重启数据库&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/gblog/images/posts/lambda-rds/rds_force_ssl.avif&quot; alt=&quot;Lambda-RDS-Force-SSL&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;lambda&quot;&gt;Lambda&lt;/h2&gt;
&lt;p&gt;Lambda 默认情况下并不会部署到 VPC 中，我们需要修改 Lambda 的定义，使它能正常部署到上面创建的 VPC 中。&lt;/p&gt;
&lt;p&gt;以 CreateNoteCommandLambda 为例，其配置为：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;yaml&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;yaml&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;  CreateNoteCommandLambda&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;    Type&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;AWS::Serverless::Function&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;    Properties&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;      FunctionName&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;CreateNoteCommandLambda&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;      CodeUri&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;course_deploy.jar&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;      Handler&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;com.x.CourseServiceHandler::handleRequest&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;      Policies&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;        # 指定 VPC 策略&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        - &lt;/span&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;VPCAccessPolicy&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: {}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;      # VPC 配置&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;      VpcConfig&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;        SecurityGroupIds&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;          - &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;sg-12345&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;        SubnetIds&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;          - &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;subnet-12345&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt; # kmind-private1-nat-east-1a&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;          - &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;subnet-23456&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt; # kmind-private1-nat-east-1b&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;      Events&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;        ApiEvents&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;          Type&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;Api&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;          Properties&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;            RestApiId&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;!Ref&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; ApiGatewayApi&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;            Path&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;/notes&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;            Method&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;POST&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;            Auth&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;              Authorizer&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;NONE&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;  CreateNoteCommandLambda:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: CreateNoteCommandLambda
      CodeUri: course_deploy.jar
      Handler: com.x.CourseServiceHandler::handleRequest
      Policies:
        # 指定 VPC 策略
        - VPCAccessPolicy: {}
      # VPC 配置
      VpcConfig:
        SecurityGroupIds:
          - sg-12345
        SubnetIds:
          - subnet-12345 # kmind-private1-nat-east-1a
          - subnet-23456 # kmind-private1-nat-east-1b
      Events:
        ApiEvents:
          Type: Api
          Properties:
            RestApiId: !Ref ApiGatewayApi
            Path: /notes
            Method: POST
            Auth:
              Authorizer: NONE&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;新增的配置为：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;指定 VPCAccessPolicy 因为 Lambda 需要能访问 VPC&lt;/li&gt;
&lt;li&gt;配置 Lambda SubnetVPC
&lt;ol&gt;
&lt;li&gt;配置 Lambda 函数的 SecurityGroup，这我在后面说明&lt;/li&gt;
&lt;li&gt;配置 Lambda 函数部署的子网，为了满足跨分区可用，这里指定了不同区域的两个子网。&lt;/li&gt;
&lt;li&gt;注意这里配置的子网应该是上面 VPC 网络设置中的 Private 子网(&lt;code&gt;kmind-private*&lt;/code&gt;)。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/gblog/images/posts/lambda-rds/kmind-vpc.avif&quot; alt=&quot;Lambda-RDS&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;security-group&quot;&gt;Security Group&lt;/h3&gt;
&lt;p&gt;Security Group 是 AWS 的一种安全机制，用于控制进出的网络流量。你可以将 Security Group 想象成由规则组成的虚拟防火墙。我们的目的是想 Lambda 访问 RDS，所以我们需要配置 Lambda Security Group 的 &lt;strong&gt;Outbound rules&lt;/strong&gt;，如：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Security group ID     Protocol  Ports  Destination 
sg-0467b06b178edb777  All       All    0.0.0.0/0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这代表从 Lambda 访问任意目标地址都是允许的。我们还需要配置 RDS Security Group 的 &lt;strong&gt;Inbound rules&lt;/strong&gt; 使得他能允许来自 Lambda 的请求，如：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Security group ID      Protocol      Ports   Source
sg-0467b06b178edb777   Custom TCP    5432    0.0.0.0/0 
sg-0467b06b178edb777   AlL           All     sg-0467b06b178edb777
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;到此我们的所有配置就已经结束了，当 Lambda 部署成功后，它应该能同时访问 RDS + 外部网络。&lt;/p&gt;</content:encoded></item><item><title>Blog 的延续</title><link>https://godruoyi.com/posts/blog-3/</link><guid isPermaLink="true">https://godruoyi.com/posts/blog-3/</guid><description>最近花了一些时间将我的博客迁移到了 Astro 平台，我感觉整个过程挺美妙的，很喜欢 Astro。</description><pubDate>Thu, 16 May 2024 13:06:49 GMT</pubDate><content:encoded>&lt;p&gt;最近花了一些时间将我的博客迁移到了 Astro 平台，我感觉整个过程挺美妙的，很喜欢 Astro。&lt;/p&gt;
&lt;p&gt;博客迁移的初衷是因为我的主机被腾讯云封禁了。因为域名到期后未续费导致被解析到一个有辱国家斯文的网站，我的主机被永久封禁不得申诉。于是我将自己的博客迁移到了 &lt;a href=&quot;https://fly.io&quot;&gt;Fly&lt;/a&gt; 平台上，而 Fly 每个月 6/7 刀的费用又让我不堪重负。&lt;/p&gt;
&lt;p&gt;我在 &lt;code&gt;2015-12-25&lt;/code&gt; 注册了自己的第一个域名，并一直用到了现在，我已经忘记当时部署的是什么网站，隐约记得写了个 LOVE 阿宝 FOREVER。以前觉得互联网上的东西都能保存十年百年，而现在 9 年不到我之前写的任何内容已经了无踪迹。互联网是没有记忆的。大学期间写的几十篇日记还在我的贵人鸟鞋盒里静静地躺着。&lt;/p&gt;
&lt;p&gt;想象前两次博客的改版，我从 Symfony 博客迁移到 Laravel 再到 Golang Gin；每一次迁移都会造成数据丢失，每一次迁移都要折腾很久的云环境。从如何安装 PHP、安装 Nginx、安装 Redis、安装 MySQL、安装 Docker、安装 Clash、配置 &lt;a href=&quot;https://github.com/Neilpang/acme.sh&quot;&gt;Acme.sh&lt;/a&gt;。我的网站能正常运行完全是因为恰巧这些服务都正常工作。&lt;/p&gt;
&lt;p&gt;你能想象两年内它完全是运行在下面这样的脚本中吗？我甚至都没有为他配置 &lt;a href=&quot;https://docs.docker.com/config/containers/start-containers-automatically/&quot;&gt;Restart Policies&lt;/a&gt; 或者 Supervisor，相当于只要程序一 Panic，网站就挂了。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;shell&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;shell&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;docker&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; --name&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; gblog&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; -p&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; 9900:9900&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; -d&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ${imageName}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;docker run --name gblog -p 9900:9900 -d ${imageName}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;博客应该被静态化，我心里这样想着。静态化的博客可以部署到任何 Serverless 平台中，一个个 HTML 页面就包含了你博客的所有数据，它们不会因为你忘记续费就停止服务，也不会因为你的定时脚本出错了就不能 HTTPS 访问，更不会因为你忘记配置 Clash 就无法使用 GitHub 登录。你所有需要做的，就是提交你的博客内容；Git 记录了你所有的操作历史，只要 GitHub 不倒，你的网站久依旧能地老天荒。&lt;/p&gt;
&lt;p&gt;我写博客的初衷是为了多年后能自省当年的自己，看看当时的心态。我比较厌烦流水账的记录，所以更多的时候我愿意写下此刻的感悟；我不明白为什么很多人的年终总结要写一月份干了什么二月份干了什么，十年后的你在回首时真的在乎你某年某月干了什么吗？&lt;/p&gt;
&lt;h3 id=&quot;截图&quot;&gt;截图&lt;/h3&gt;
&lt;p&gt;按照约定，放几张新版本博客的截图。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/gblog/images/posts/blog-3/home.avif&quot; alt=&quot;Godruoyi Blog&quot;&gt;
&lt;img src=&quot;https://images.godruoyi.com/gblog/images/posts/blog-3/categories.avif&quot; alt=&quot;Godruoyi Categories&quot;&gt;
&lt;img src=&quot;https://images.godruoyi.com/gblog/images/posts/blog-3/timeline.avif&quot; alt=&quot;Godruoyi Timeline&quot;&gt;&lt;/p&gt;</content:encoded></item><item><title>2023 年终总结</title><link>https://godruoyi.com/posts/review-2023/</link><guid isPermaLink="true">https://godruoyi.com/posts/review-2023/</guid><description>我记得某位推友说过，如果你只是一味的吸取别人的知识，那就算你是在逛 Hacker News，读专业论文，看世界名著，甚至是在读 Linux 源码；那你本质上和那些只知道刷抖音的人来说并没有什么不同</description><pubDate>Fri, 09 Feb 2024 15:10:51 GMT</pubDate><content:encoded>&lt;h1 id=&quot;&quot;&gt;&lt;/h1&gt;
&lt;p&gt;我在楼上写今年的总结，窗外在下雪。&lt;/p&gt;
&lt;h2 id=&quot;life&quot;&gt;Life&lt;/h2&gt;
&lt;p&gt;今年很累，因为有娃了。还记得去年冬天刚抱着娃儿回来时我和阿宝的不知所措，到现在能熟练的掌握娃的一日三餐衣食住行，我俩也算是勉强及格的父母了。可能在老家人看来两个人合伙照顾一个孩子还喊累难免有点儿娇滴滴，他们也总会拿自己当初是如何一个人带大四五个孩子的总总让你自行惭愧，但我还是想说，养娃很累。&lt;/p&gt;
&lt;p&gt;在最开始的那一个月里，我们花了 13800 请了个月嫂；这是我觉得今年最有价值的投资。有月嫂在的那 26 天里，我们不用考虑每天吃什么，不在担心娃的吃喝拉撒，还能每晚睡个整觉；即使月嫂烧的饭菜和我有得一拼，即使她照顾娃儿的方式有时我们不太认同，但那段日子我俩真的过得很舒心；阿宝有轻微的抑郁症、焦虑症，我很难想象如果没有那段时间的缓冲，我们的日子会过成怎样。&lt;/p&gt;
&lt;p&gt;月嫂走后，生活就变得复杂起来了。为了防止阿宝变得更抑郁，白天我边上班边辅助带娃，阿宝则负责照顾大人孩子的一日三餐。晚上娃和我睡在次卧，我睡眠很好，就算娃再怎么折腾，我也能抽空睡两觉；而阿宝的睡眠就很差了，稍微一点点声响就整宿睡不着；所以算算到现在，我已经和娃单独睡了八九个月了。比起白天工作时总被无限次数的打断，我更愿意晚上带娃，也换来白天的一丝宁静。而当娃开始吃辅食后，这一丝宁静也没有了，我很难有连续超过两个小时的专注时间。我和阿宝的的很大一部分时间都在厨房度过，早饭/午饭/孩子的午饭/晚饭/孩子的晚饭，我们每天都在为吃什么而烦扰，每天都刚收拾完一切准备躺在沙发上喘口气时，孩子又醒了。阿宝现在最讨厌的一句话就是：孩子睡你就睡嘛。仿佛说这句话的人根本不用收拾那摊烂摊子似的。&lt;/p&gt;
&lt;p&gt;好在是养娃带来的幸福感和付出的辛苦是成比的。看到她指着你的鼻子呀呀呀的叫着，踉踉跄跄的朝你走来，分享她的食物给你吃时，这一切都是值得的。&lt;/p&gt;
&lt;h2 id=&quot;someting&quot;&gt;Someting&lt;/h2&gt;
&lt;p&gt;我记得某位推友说过，如果你只是一味的吸取别人的知识，那就算你是在逛 Hacker News，读专业论文，看世界名著，甚至是在读 Linux 源码；那你本质上和那些只知道刷抖音的人来说并没有什么不同。你依然是在向这个社会吸取而不是制造点什么。我觉得人不应该像寄生虫一样活着；我身边有一些很厉害的大佬，他们的技术能力和思维能力一直让我望尘莫及，但很遗憾他们却没有输出一点点能帮助后人达到他们一样高度的东西。我们都会老去，但如果我们留下的东西能帮助到后人成长，那应该很有趣。&lt;/p&gt;
&lt;p&gt;所以从今年开始，我开始做一些&lt;strong&gt;自己觉得很有趣&lt;/strong&gt;的小项目；如果这些小项目又恰巧能满足一部分人的需求的话，那我就更开心了。得益于强大的 GPT，我们能非常快速的从零经验上手任意一门语言。在学完 &lt;a href=&quot;https://github.com/rust-lang/rustlings&quot;&gt;Rustlings&lt;/a&gt; 后，我开始给 &lt;a href=&quot;https://github.com/Xuanwo&quot;&gt;Xuanwo&lt;/a&gt; 的 &lt;a href=&quot;https://github.com/Xuanwo/reqsign&quot;&gt;reqsign&lt;/a&gt; 贡献 &lt;a href=&quot;https://github.com/Xuanwo/reqsign/pulls?q=is:pr+is:closed+author:godruoyi&quot;&gt;PR&lt;/a&gt;；当初的想法是先混个脸熟然后再打入内部最后被顺利招安，因为我对 Remote/OpenSource 十分感兴趣；现在想起来也是十分滑稽，路肯定是行得通的，只是怪自己没能坚持下来。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202402/09/kc2OjTBKwSoCUzvAcavmlpGqmxX7BQyrKQtufDKi.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;p&gt;接下来我用 Rust 写了我的第一个小项目 &lt;a href=&quot;https://github.com/godruoyi/laravel-tips&quot;&gt;Laravel Tips&lt;/a&gt;，可以在终端展示各种有趣和生涩的提示；后来在推友的建议下我又写了一个 &lt;a href=&quot;https://github.com/godruoyi/laravel-tips-raycast&quot;&gt;Laravel Tips Raycast&lt;/a&gt; 扩展；Bing Dalle-2/3 出来后，我又基于 yihong 大哥的 &lt;a href=&quot;https://github.com/yihong0618/tg_bing_dalle&quot;&gt;tg_bing_dalle&lt;/a&gt;写了一个起床机器人 &lt;a href=&quot;https://github.com/godruoyi/wakeup&quot;&gt;Wakeup&lt;/a&gt;；而最近我正在写一个浏览器扩展 &lt;a href=&quot;https://github.com/godruoyi/readog&quot;&gt;Readog&lt;/a&gt;，它可以将你的链接保存到任何平台。我很享受整个过程，很多时候都是当孩子睡着后我就迫不及待的冲到电脑旁敲下在脑子里过了很多遍的最佳实践。我觉得对于工程师来说，将自己的想法一步步实现出来，不限语言，不限技术，这本身就是一件十分美妙的事情。&lt;/p&gt;
&lt;h2 id=&quot;action&quot;&gt;Action&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;如果你吃到一颗好吃的鸡蛋，你会去寻找下单的母鸡吗？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我把钱钟书说的这句话又搬出来了，放到现在依然适用，找不找得到母鸡我不知道，但只要行动起来，收获肯定还是有的。我经常会在推上看到一些有趣的信息，如远程工作的招聘、开发工具、开源项目等；大部分推友都只会点个赞，亦或加个收藏，而真正开始行动起来的人少之又少。比如你一直想找一个远程工作的机会，现在机会都摆在面前了你却不愿意试着去改变，只是加了个收藏想着以后离职了再去尝试；比如你看到一个很不错的开源项目，你只是点个 star，少有的会去深度了解下它的背景和具体解决的什么问题，而读它的源码了解它优秀的设计的人就更少之又少了。&lt;/p&gt;
&lt;p&gt;我一直觉得机会是留给行动起来的人的，站在岸上学不会游泳。我并不是一个行动派，就拿每次推着孩子出去因为路口被私家车挡着的事儿来说，我每次都是嘴上骂两句，即使心里想过很多遍市长信箱投诉，交通执法大队举报，却每次都没有实际行动起来。我希望来年我能行动起来，如果某一个市政的改善有你的功劳，那未尝不是一件有趣的事儿呢。&lt;/p&gt;
&lt;p&gt;就写到这吧，春晚太吵了。&lt;/p&gt;
&lt;h2 id=&quot;往年总结&quot;&gt;往年总结&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/review-of-2022&quot;&gt;2022 年终总结&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/review-2021&quot;&gt;2021 年终总结&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/2020-year-end-review&quot;&gt;2020 年终总结&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/continue-refueling-in-2019&quot;&gt;2019 年度总结&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>办酒</title><link>https://godruoyi.com/posts/banjiu/</link><guid isPermaLink="true">https://godruoyi.com/posts/banjiu/</guid><description>许老三今天吃了五家人户的酒，送出去了八百块钱。</description><pubDate>Wed, 31 Jan 2024 08:15:49 GMT</pubDate><content:encoded>&lt;p&gt;许老三今天吃了五家人户的酒，送出去了八百块钱。&lt;/p&gt;
&lt;p&gt;许老三早上九点就出门了，出门前他翻了翻两年前办酒的礼簿，在第二页找到了刘大姐家送的 100 元记录，他打算两还送她 100 块。毕竟只是满个 50 岁，又不是啥大酒，而这一百块可是要一天工钱呢。&lt;/p&gt;
&lt;p&gt;许老三远远的看见刘大姐家门前稀稀捞捞的站着几个人，他不知道往哪里送钱。现在政府查的严，只有红白喜事和年满八十才能办酒，所以收钱的地方往往比较偏僻，也避免被政府抓到后没收，那样就违背了办酒的初衷赚不到钱了。许老三送完了钱，看到自己的名字记到礼薄上后，接过一支烟就走了，他还不打算吃饭，后面还有几家没送呢。&lt;/p&gt;
&lt;p&gt;许老三走到了李家，这里要热闹些，马路上停满了车。李家老头儿满 60，他家的儿子特意回来给他办酒庆祝。许老三决定在这家吃饭，这里多半是要好吃一点的。许老三年纪较大，坐在「上席」的位置，菜已经上了两个；有煎的干洋芋片，还有凉拌海带丝；许老三看到凉拌海带里面还有豆豉和折耳根，很是欢喜。于是他转过头喊了一声：添饭；一个小伙端着一盆热气腾腾的饭走了过来，舀了一瓢到许老三的碗里。许老三吃了两碗饭后，菜终于上齐了；压轴的是一碗扣肉，然后是海椒酱炒肉，还有酸海椒炒肥肠丝，还有油炸豆腐，一共十二个菜，摆成一个正方形在桌子上。许老三敛了一件全肥的扣肉，一口就吃了，用手抹了抹嘴，下席了。他还要往周家坪走去；他走的时候回头看了一眼这家人两层楼高的小洋房，在周围的木房子中显得格外洋气，只是办的酒菜却和大家一摸一样。&lt;/p&gt;
&lt;p&gt;周家坪这家是房子酒，她们家的儿子在市里买了房子，老两口决定在老家也办个酒，收回点儿这两年来送出去的人亲钱。周老太是个精明人，他花了不到 2000 块钱酒把酒席办下来了；猪是自家现杀的、菜是自家种的，只需要买点烟酒饮料，弄个十来桌的菜，三四万块钱就送来了。许老三刚把钱送完，就看到十几个人成群结队的来了，其中还有穿警服的。许老三心想，糟了，政府的人来了。政府的人走到了门口，带头儿的一脚踢开了大门，剩下的人像抓逃犯一样快速涌入不同的房间；还在吃饭的人被统统追了出来，&lt;a href=&quot;https://x.com/godruoyi/status/1751086997727563983?s=20&quot;&gt;几口锑锅被端到了大门口&lt;/a&gt;，里面装着这次办酒的菜。带头儿的一边对自己人喊「端完了没，还有没得，取证了吗？」一边大声的对着周老太等人说：「政府明确规定了不准办酒，我们也一而再再而三的通知你们，你们就是不听，再有这样，我们就不是端两个菜这么简单了」。&lt;/p&gt;
&lt;p&gt;政府一行人就这样端着四五口锅离开了，突然人群中传来一个声音：菜你们可以端走，锅必须给我留下。喊话的钟大姐和几个中年妇女跑着追了出去，势必要要回自己的锅。许老三没有追出去看，他还要去送钱呢。&lt;/p&gt;
&lt;p&gt;许老三送完了所有的钱，突然想到孙女的小仓鼠，它们有时候会被放进跑轮里，只要在轮子前面放一点食物，老鼠就会开始跑起来；不想动的老鼠只能跟着一起跑，不然就会被摔得鼻青脸肿，而跑步的老鼠越多，轮子转得越快，越不容易停。最后所有的老鼠都被摔得稀耙烂才停下来，谁都没有吃到食物。许老三心想，办酒就是那挂在轮子前的食物，而我们都是老鼠。&lt;/p&gt;
&lt;p&gt;许老三的儿子在城里上班，每次到了下班时间他们都你望我我望你的不敢走，常常熬到晚上 10 点才回来。许老三觉得他的儿子也是只老鼠。&lt;/p&gt;</content:encoded></item><item><title>为什么 Laravel 这么优秀</title><link>https://godruoyi.com/posts/why-laravel/</link><guid isPermaLink="true">https://godruoyi.com/posts/why-laravel/</guid><description>Laravel 一直是我心中最优雅的后端框架，为了向更多的人解释为什么 Laravel 这么优雅？框架本身都做了什么操作？比起其他框架的优势在哪里等？我准备从一个后端最常用的 CURD 例子说起，一步一步阐述这过程中 Laravel 都是怎么完成的；以及~~大家~~(我)为什么喜欢用 Laravel。</description><pubDate>Wed, 24 Jan 2024 04:45:05 GMT</pubDate><content:encoded>&lt;p&gt;Laravel 一直是我心中最优雅的后端框架，为了向更多的人解释为什么 Laravel 这么优雅？框架本身都做了什么操作？比起其他框架的优势在哪里等？我准备从一个后端最常用的 CURD 例子说起，一步一步阐述这过程中 Laravel 都是怎么完成的；以及&lt;del&gt;大家&lt;/del&gt;(我)为什么喜欢用 Laravel。&lt;/p&gt;
&lt;h2 id=&quot;introduction-laravel&quot;&gt;Introduction Laravel&lt;/h2&gt;
&lt;p&gt;Laravel 的定位是一个全栈 WEB 框架，它提供了 WEB 开发的全套组件；如路由、中间件、MVC、ORM、Testing 等。这篇文章中我使用的 Demo 是最新版的 Laravel 10.x 以及 PHP 8.2。虽说从 Laravel 5.x 后 Laravel 的版本变化比较快，基本一年一个大版本，但它的核心几乎从 4.X 以来没有发生过特别大的变化。Laravel 的目录结构可能对第一次接触的人来说会很繁琐，它有十来个文件夹，但其实大部分文件夹的位置都是精心设计的，都待在应该待的位置上。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202401/24/0eZEaFkEf58NGYMvZV84bSHfyeEJhKfMALYRPU6T.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;laravel-artisan&quot;&gt;Laravel Artisan&lt;/h2&gt;
&lt;p&gt;Laravel 第一个优雅的设计就是给开发者暴露了一个 ALLINONE 的入口 ———&lt;code&gt;Artisan&lt;/code&gt;。Artisan 是一个 SHELL 脚本，是通过命令行操作 Laravel 的唯一入口。所有和 Laravel 的交互包括操作队列，数据库迁移，生成模版文件等；你都可以通过这个脚本来完成，这也是官方推荐的最佳实践之一。如你可以通过：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;php artisan serv&lt;/code&gt; 启动本地开发环境&lt;/li&gt;
&lt;li&gt;&lt;code&gt;php artisan tinker&lt;/code&gt; Local Playground&lt;/li&gt;
&lt;li&gt;&lt;code&gt;php artisan migrate&lt;/code&gt; 执行数据库迁移等&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202401/24/0dY3oaCDbjASJyuOd1cQK0KmtollNexQcmNaQ5gB.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;和其他框架类似，Ruby on Rails 为我们提供了 &lt;a href=&quot;https://guides.rubyonrails.org/command_line.html&quot;&gt;rails&lt;/a&gt;、Django 为我们提供了 &lt;a href=&quot;https://docs.djangoproject.com/en/5.0/ref/django-admin/#getting-runtime-help&quot;&gt;manage.py&lt;/a&gt;。我觉得优秀的框架都会提供一系列的 Dev Tools 帮助开发者更好的驾驭它，更优秀的框架如 Spring 除外。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;接下来我们将尝试构建一个简易的课程系统，在这个系统中有教师(Teacher)，学生(Student)和课程(Course)，它们之间覆盖了简单的一对一、一对多、多对多等的关系，这在日常开发中也很常见。&lt;/p&gt;
&lt;p&gt;我会按照我理解的最佳实践的做法，一步步实现一个完整的 CURD；但不会一来就把 Laravel 的各个优秀组件抛出来，而是遇到什么组件后再尝试理解它为什么要这样设计、比起其他框架的优势在哪里。这篇文章不会包含所有的代码，但你仍然可以通过这个仓库 &lt;a href=&quot;https://github.com/godruoyi/laravel-best-practice&quot;&gt;godruoyi/laravel-best-practice&lt;/a&gt; 的提交记录看到我是如何一一步构建起来的。&lt;/p&gt;
&lt;h2 id=&quot;make-model&quot;&gt;Make Model&lt;/h2&gt;
&lt;p&gt;我们的第一步是根据 Laravel 提供的 Artisan 命令生成对应的 Model；在实际的开发中我们通常会提供额外的参数以便生成模型的时候一起生成额外的模版文件，如数据库迁移文件、测试文件、Controller 等等；我们还将用 &lt;code&gt;make:model&lt;/code&gt; 为 Course 生成一个 CURD Controller，相关的几个 commit 我列在下面了，每个 Commit 我都尽量做到了最小：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/godruoyi/laravel-best-practice/commit/ae21f0050899a11cdbaadb01eca8cd8ec255e54d&quot;&gt;artisan make:model Teacher -msf&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/godruoyi/laravel-best-practice/commit/7e79632d09c28f12d08f0d46d5032c2969cdbb9b&quot;&gt;artisan make:model Course -a —api —pest&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/godruoyi/laravel-best-practice/commit/0d03f1fab91596e771ed225db3f3ce4e79928c4a&quot;&gt;definition database fields of courses table&lt;/a&gt; &amp;#x26; &lt;a href=&quot;https://github.com/godruoyi/laravel-best-practice/commit/6d10dd2df134ad5be659c23c4b11218e83c68341&quot;&gt;definition model relation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/godruoyi/laravel-best-practice/commit/54f676ce6470b3693d3c3b5cf8e9535e14eca109&quot;&gt;definition course seeder&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;当模型及模型之间的关系定义完成后，在我看来整个开发任务就已经完成 50% 了。因为我们已经完成了数据表中字段的定义、表与表的关系、以及最重要的一步：如何将数据及数据之间的关系写入数据库中，下面简单的来介绍下在 Laravel 是如何完成的。&lt;/p&gt;
&lt;h3 id=&quot;database-migration&quot;&gt;Database Migration&lt;/h3&gt;
&lt;p&gt;Laravel 的 Migration 提供了一套便捷的 API 方便我们完成绝大多数数据库及表字段的定义。Migration 的定义完整的保留了整个应用的所有迁移历史。通过这些文件我们可以在任何一个新的地方快速的重建我们的数据库设计。所有数据库的变更都通过 migration 的方式来完成也是 Laravel 推荐的最佳实践之一。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Laravel Migration 还提供了 Rollback 机制，既可以 rollback 最近的一次数据库变更。不过我不建议大家在生产环境这样做；生产环境的数据库迁移应该始终保持&lt;strong&gt;向前&lt;/strong&gt;滚动，而不应该含有向后 Rollback 的操作。比如你在上一次变更操作中错误的设置了某个表的索引，那&lt;em&gt;我理解的&lt;/em&gt;正确的做法不是回滚，而是创建一个新的迁移文件，并在新的迁移文件中 ALTER 之前的修改。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;一个现代化的框架，应该有 Migration，下面是 Course 及中间表的定义：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Schema&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;create&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;courses&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Blueprint&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $table) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    $table&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    $table&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;name&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    $table&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;description&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;nullable&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    $table&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;unsignedBigInteger&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;teacher_id&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;index&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    $table&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;text&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;content&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;nullable&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    $table&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;timestamps&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// create pivot table&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Schema&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;create&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;course_student&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Blueprint&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $table) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    $table&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;unsignedBigInteger&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;course_id&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;index&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    $table&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;unsignedBigInteger&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;student_id&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;index&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    $table&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;timestamps&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    $table&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;primary&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;([&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;course_id&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;student_id&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;id();
    $table-&gt;string(&amp;#x27;name&amp;#x27;);
    $table-&gt;string(&amp;#x27;description&amp;#x27;)-&gt;nullable();
    $table-&gt;unsignedBigInteger(&amp;#x27;teacher_id&amp;#x27;)-&gt;index();
    $table-&gt;text(&amp;#x27;content&amp;#x27;)-&gt;nullable();
    $table-&gt;timestamps();
});

// create pivot table
Schema::create(&amp;#x27;course_student&amp;#x27;, function (Blueprint $table) {
    $table-&gt;unsignedBigInteger(&amp;#x27;course_id&amp;#x27;)-&gt;index();
    $table-&gt;unsignedBigInteger(&amp;#x27;student_id&amp;#x27;)-&gt;index();
    $table-&gt;timestamps();
    $table-&gt;primary([&amp;#x27;course_id&amp;#x27;, &amp;#x27;student_id&amp;#x27;]);
});&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h3 id=&quot;model-relationship&quot;&gt;Model Relationship&lt;/h3&gt;
&lt;p&gt;Laravel 另一个强大之处在于可以通过 Eloquent 抽象「模型与模型」之间的关系；举个例子，在下面的定义中我们描述了一个 Course 可以有多个 Student、一个 Teacher；以及一个 Student 可能有多个 Course。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// Models/Course.php&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; students&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; BelongsToMany&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;belongsToMany&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Student&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; teacher&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; hasOne&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;hasOne&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Teacher&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;belongsToMany(Student::class);
}

public function teacher(): hasOne
{
    return $this-&gt;hasOne(Teacher::class);
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;一旦模型间的关系定义完成，我们就可以非常方便的通过 Laravel Eloquent 查询它们之间的数据关系。Laravel 会自动帮我们处理复杂的 Join 操作，还能在一定条件下帮我们处理如 N+1 问题。来看一个例子：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$course &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Course&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;with&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;teacher&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;students&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;find&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// assert&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;expect&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($course)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    -&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;toBe&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    -&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;students&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;each&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;toBeInstanceOf&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Student&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) &lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    -&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;teacher&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;toBeInstanceOf&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Teacher&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;find(1)

// assert
expect($course)
    -&gt;id-&gt;toBe(1)
    -&gt;students-&gt;each-&gt;toBeInstanceOf(Student::class) 
    -&gt;teacher-&gt;toBeInstanceOf(Teacher::class);&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;这个例子中我们查询了 ID 为 1 的课程及它所关联的教师及学生；这将产生 3 条 SQL操作，其中还包含了一条跨中间表(course_student)的查询，而这过程中我们不需要做任何操作，Laravel 会自动根据你 model 的定义生成对应的 Join 操作。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;sql&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;sql&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;select&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &quot;courses&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; where&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &quot;id&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;select&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &quot;teachers&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; where&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &quot;teachers&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;5&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;select&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;    &quot;students&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;    &quot;course_student&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;course_id&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &quot;pivot_course_id&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;    &quot;course_student&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;student_id&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; as&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &quot;pivot_student_id&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &quot;students&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;inner join&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &quot;course_student&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;on&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &quot;students&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &quot;course_student&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;student_id&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;where&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &quot;course_student&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;course_id&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;select * from &amp;#x22;courses&amp;#x22; where &amp;#x22;id&amp;#x22; = 1

select * from &amp;#x22;teachers&amp;#x22; where &amp;#x22;teachers&amp;#x22;.&amp;#x22;id&amp;#x22; in (5)

select 
    &amp;#x22;students&amp;#x22;.*, 
    &amp;#x22;course_student&amp;#x22;.&amp;#x22;course_id&amp;#x22; as &amp;#x22;pivot_course_id&amp;#x22;, 
    &amp;#x22;course_student&amp;#x22;.&amp;#x22;student_id&amp;#x22; as &amp;#x22;pivot_student_id&amp;#x22; 
from &amp;#x22;students&amp;#x22; 
inner join &amp;#x22;course_student&amp;#x22; 
on &amp;#x22;students&amp;#x22;.&amp;#x22;id&amp;#x22; = &amp;#x22;course_student&amp;#x22;.&amp;#x22;student_id&amp;#x22; 
where &amp;#x22;course_student&amp;#x22;.&amp;#x22;course_id&amp;#x22; in (1)&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h3 id=&quot;how-to-save-data-to-database&quot;&gt;How to save data to database&lt;/h3&gt;
&lt;p&gt;Laravel Factory 提供了一种很好的方式来 Mock 测试数据，一旦我们定义好 Model 的 Factory 规则，我们就能轻松的在开发阶段模拟出一个关系完整的数据。这比起我们手动为前端制造测试数据要方便和可靠得多，如下面的例子将为每一个课程分配一个教师和不确定数量的学生：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// database/seeders/CourseSeeder.php&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$students &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Student&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;all&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$teachers &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Teacher&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;all&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Course&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;factory&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;count&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;10&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;make&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;each&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($course) &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($students, $teachers) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    $course&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;teacher&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;associate&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($teachers&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;random&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;());&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    $course&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;save&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    $course&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;students&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;attach&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($students&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;random&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;rand&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;9&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)));&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;count(10)-&gt;make()-&gt;each(function ($course) use ($students, $teachers) {
    $course-&gt;teacher()-&gt;associate($teachers-&gt;random());
    $course-&gt;save();
    $course-&gt;students()-&gt;attach($students-&gt;random(rand(0, 9)));
});&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;最后我们通过运行 &lt;code&gt;php artisan migrate --seed&lt;/code&gt;，Laravel 会自动同步所有的数据库迁移文件并按照 Laravel Factory 定义的规则生成一个关系完备的测试数据。
&lt;img src=&quot;https://images.godruoyi.com/posts/202401/24/osykfvtIkmCEmCBxBIH2YFsNjNoTiCcMnvcWYHAe.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;laravel-route&quot;&gt;Laravel Route&lt;/h2&gt;
&lt;p&gt;在 Laravel 中我们还可以非常方便的管理应用的路由；Laravel 的路由是集中式路由，所有的路由全部写在一两个文件中；Laravel 的 Route 给开发者暴露了一套简单的 API，而通过这些 API 我们就能轻松的注册一个符合行业标准的 RSETful 风格的路由，如我们为我们课程注册的路由：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Route&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;apiResource&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;courses&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;CourseController&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;Route::apiResource(&amp;#x27;courses&amp;#x27;, CourseController::class);&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Laravel 会自动帮我们注册 5 条路由如下所示，包括用于新增操作的 POST 请求，用于删除的 DELETE 请求等：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202401/24/FDrbUWmLlLaZQDeAyvqu9kHOu8a2hNLv1znhBghf.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Laravel 路由虽然是非常优秀的设计，但它却不是最高效的设计。Laravel 用&lt;a href=&quot;https://github.com/laravel/framework/blob/10.x/src/Illuminate/Routing/Router.php#L62&quot;&gt;一个数组&lt;/a&gt;保存你注册过的所有路由；在进行路由匹配时，Laravel 会用你当前请求的 pathinfo 来匹配已经注册的所有路由；当你的路由数量超级多时，最坏情况下你需要 O(n) 次才能找出匹配的路由。不过这点复杂度比起注册路由&amp;#x26;启动服务的开销几乎可以忽略不计，并且一个应用也不会有数量过多的路由，加之 Laravel 还单独提供了 &lt;code&gt;artisnan route:cache&lt;/code&gt; 命令来缓存路由的注册和匹配。我猜这也是为什么 Laravel 不需要实现其他优秀的路由算法如 &lt;a href=&quot;https://en.wikipedia.org/wiki/Radix_tree&quot;&gt;Radix Tree&lt;/a&gt; 的原因吧。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;create-course&quot;&gt;Create Course&lt;/h2&gt;
&lt;p&gt;接下来我们来看在 Laravel 中是如何优雅的保存数据，这部分的记录你可以参考下面这几个 commit：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/godruoyi/laravel-best-practice/commit/fbff7fd71a6b9162b76f9547046468413bbaff4e&quot;&gt;feat: create course&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/godruoyi/laravel-best-practice/commit/204589dede30ce1d7cf98195e06a52d4159c243a&quot;&gt;chore: switch to use pest&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/godruoyi/laravel-best-practice/commit/ef5c8f549f011d2b5333bee16ad3e6f082141085&quot;&gt;add testing for create course endpoint&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们知道在进行数据操作前，都需要先对数据进行校验。而 Laravel 提供的 FormRequest 就可以非常方便的做到这一点；你可以在 FormRequest 中定义前端传入的每一个字段的验证规则。如是否必须，ID 是否应该在数据库中存在等：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; StoreCourseRequest&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#6CB6FF&quot;&gt; FormRequest&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; rules&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; array&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;            &apos;name&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;required|string|max:255&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;            &apos;description&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;nullable|string&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;            &apos;content&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;nullable|string&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;            &apos;teacher_id&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;required|exists:teachers,id&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;            &apos;students&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;nullable|array&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;            &apos;students.*&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;sometimes|int|exists:students,id&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        ];&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt; &amp;#x27;required|string|max:255&amp;#x27;,
            &amp;#x27;description&amp;#x27; =&gt; &amp;#x27;nullable|string&amp;#x27;,
            &amp;#x27;content&amp;#x27; =&gt; &amp;#x27;nullable|string&amp;#x27;,
            &amp;#x27;teacher_id&amp;#x27; =&gt; &amp;#x27;required|exists:teachers,id&amp;#x27;,
            &amp;#x27;students&amp;#x27; =&gt; &amp;#x27;nullable|array&amp;#x27;,
            &amp;#x27;students.*&amp;#x27; =&gt; &amp;#x27;sometimes|int|exists:students,id&amp;#x27;,
        ];
    }
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;如果你尝试传入一些无效的数据，Laravel 会直接帮我们验证并返回错误信息，如下面的 teacher_id 在数据库中并不存在。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;shell&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;shell&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; echo&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; -n&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;{&quot;name&quot;: &quot;hello&quot;, &quot;teacher_id&quot;: 9999}&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; http&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; post&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; http://127.0.0.1:8000/api/courses&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; -b&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;    &quot;errors&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;        &quot;teacher_id&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;            &quot;The selected teacher id is invalid.&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        ]&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;    &quot;message&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &quot;The selected teacher id is invalid.&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;$ echo -n &amp;#x27;{&amp;#x22;name&amp;#x22;: &amp;#x22;hello&amp;#x22;, &amp;#x22;teacher_id&amp;#x22;: 9999}&amp;#x27; | http post http://127.0.0.1:8000/api/courses -b
{
    &amp;#x22;errors&amp;#x22;: {
        &amp;#x22;teacher_id&amp;#x22;: [
            &amp;#x22;The selected teacher id is invalid.&amp;#x22;
        ]
    },
    &amp;#x22;message&amp;#x22;: &amp;#x22;The selected teacher id is invalid.&amp;#x22;
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;得益于 Laravel 强大的的辅助函数和丰富的 API，在下面的代码中我们甚至可以做到一行代码就完成课程的创建及依赖关系的更新。不过这些都属于「&lt;strong&gt;茴&lt;/strong&gt;」字的几种写法，在真实开发中我们应该选择适合团队并且简单易懂的。但我觉得正是这种最求极值的体验让每个用了 Laravel 的人都爱上了它。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// app/Http/Controllers/CourseController.php&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; store&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;StoreCourseRequest&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $request)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    $course &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; tap&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;        Course&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;create&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($request&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;validated&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()), &lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        fn&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($course) =&gt; $course&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;students&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;sync&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($request&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;students)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    );&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; response&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;compact&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;course&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;), &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;201&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;validated()), 
        fn ($course) =&gt; $course-&gt;students()-&gt;sync($request-&gt;students)
    );

    return response()-&gt;json(compact(&amp;#x27;course&amp;#x27;), 201);
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&quot;storage-helper&quot;&gt;Storage Helper&lt;/h2&gt;
&lt;p&gt;除了上面用到的 tap 辅助函数，Laravel 另一个优秀的地方是为我们提供了超级多的辅助函数；有操作数组的 Arr，操作字符串的 Str，操作集合的 Collection，操作时间的 Carbon 等。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;collect&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;([&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;alice@gmail.com&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;bob@yahoo.com&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;carlos@gmail.com&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;])&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    -&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;countBy&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;fn&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($email) =&gt; &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Str&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;of&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($email)&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;after&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;@&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;toString&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;())&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    -&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;all&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(); &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// [&apos;gmail.com&apos; =&gt; 2, &apos;yahoo.com&apos; =&gt; 1]&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;countBy(fn ($email) =&gt; Str::of($email)-&gt;after(&amp;#x27;@&amp;#x27;)-&gt;toString())
    -&gt;all(); // [&amp;#x27;gmail.com&amp;#x27; =&gt; 2, &amp;#x27;yahoo.com&amp;#x27; =&gt; 1]&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;这里还有一个有趣的现象：在 Laravel 中，辅助函数通常会放在一个名叫 &lt;code&gt;Support&lt;/code&gt; 的文件下面的；而这在其他框架中通常会被叫做 &lt;code&gt;utils&lt;/code&gt;。在我看来如果单比命名，&lt;code&gt;support&lt;/code&gt; 在这里要优雅得多；并且 Laravel 的源代码中到处都充满这这种匠人式的设计；不管是函数的命名、注释、甚至是什么时候该空行，都有着自己的设计思考在里面。在 PSR2 代码规范中，还有专门的 Laravel 格式化风格。&lt;/p&gt;
&lt;p&gt;写了这么久的代码，我不知道我写的代码到底够不够好，但好在是能嗅到一点点坏代码的味道了，而这一切都全部得益于 Laravel。&lt;/p&gt;
&lt;p&gt;举个例子，你可以随便点开一个框架的源代码文件(如&lt;a href=&quot;https://github.com/laravel/framework/blob/10.x/src/Illuminate/Foundation/Http/Kernel.php&quot;&gt;Kernel.php&lt;/a&gt;)，看看它的命名，看看它方法的设计。我觉得这些技能在所有语言中都是通用的。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;protected&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; sendRequestThroughRouter&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($request)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;    $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;app&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;instance&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;request&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, $request);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;    Facade&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;clearResolvedInstance&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;request&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;    $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;bootstrap&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Pipeline&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;app))&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;                -&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;send&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($request)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;                -&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;through&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;app&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;shouldSkipMiddleware&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; [] &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;middleware)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;                -&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;dispatchToRouter&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;());&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;app-&gt;instance(&amp;#x27;request&amp;#x27;, $request);

    Facade::clearResolvedInstance(&amp;#x27;request&amp;#x27;);

    $this-&gt;bootstrap();

    return (new Pipeline($this-&gt;app))
                -&gt;send($request)
                -&gt;through($this-&gt;app-&gt;shouldSkipMiddleware() ? [] : $this-&gt;middleware)
                -&gt;then($this-&gt;dispatchToRouter());
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&quot;testing&quot;&gt;Testing&lt;/h2&gt;
&lt;p&gt;Laravel 为我们提供了另一个优秀的设计就是测试。它为我们提供了种类众多的测试，包括 HTTP 测试、浏览器测试(行为测试)、单元测试、数据库测试等。作为后端开发，测试应该是所有环节中最重要的一部分；我们可以不用为每个函数都编写单元测试，但对于暴露出去的每一个 API，都应该有足够的 Feature 测试来覆盖大部分可能的情况。&lt;/p&gt;
&lt;p&gt;在 Laravel 中我们可以非常方便的为每一个 API 编写功能测试，如下面我们为创建课程编写的 HTTP 测试：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;uses&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;RefreshDatabase&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;it&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;create course fails if the teacher is not exist&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; () {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    $course &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;        &apos;name&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;Laravel&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;        &apos;description&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;The Best Laravel Course&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;        &apos;teacher_id&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// teacher not exist&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    ];&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;    $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;postJson&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;route&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;courses.store&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;), $course)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        -&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;assertStatus&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;422&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        -&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;assertJsonValidationErrors&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;([&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;teacher_id&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;it&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;create course successfully with 1 students&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; () {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;    Teacher&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;factory&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;create&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;([&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;name&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;Godruoyi&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;    Student&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;factory&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;create&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;([&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;name&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;Bob&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;    $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;assertDatabaseCount&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Teacher&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;    $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;assertDatabaseCount&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Student&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    $course &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;        &apos;name&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;Laravel&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;        &apos;description&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;The Best Laravel Course&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;        &apos;teacher_id&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;        &apos;students&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    ];&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;    $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;postJson&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;route&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;courses.store&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;), $course)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        -&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;assertStatus&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;201&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;    expect&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Course&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;find&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        -&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;students&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;toHaveCount&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        -&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;students&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;first&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;toBe&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;Bob&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        -&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;teacher&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;toBe&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;Godruoyi&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt; &amp;#x27;Laravel&amp;#x27;,
        &amp;#x27;description&amp;#x27; =&gt; &amp;#x27;The Best Laravel Course&amp;#x27;,
        &amp;#x27;teacher_id&amp;#x27; =&gt; 1, // teacher not exist
    ];

    $this-&gt;postJson(route(&amp;#x27;courses.store&amp;#x27;), $course)
        -&gt;assertStatus(422)
        -&gt;assertJsonValidationErrors([&amp;#x27;teacher_id&amp;#x27;]);
});

it(&amp;#x27;create course successfully with 1 students&amp;#x27;, function () {
    Teacher::factory()-&gt;create([&amp;#x27;name&amp;#x27; =&gt; &amp;#x27;Godruoyi&amp;#x27;]);
    Student::factory()-&gt;create([&amp;#x27;name&amp;#x27; =&gt; &amp;#x27;Bob&amp;#x27;]);

    $this-&gt;assertDatabaseCount(Teacher::class, 1);
    $this-&gt;assertDatabaseCount(Student::class, 1);

    $course = [
        &amp;#x27;name&amp;#x27; =&gt; &amp;#x27;Laravel&amp;#x27;,
        &amp;#x27;description&amp;#x27; =&gt; &amp;#x27;The Best Laravel Course&amp;#x27;,
        &amp;#x27;teacher_id&amp;#x27; =&gt; 1,
        &amp;#x27;students&amp;#x27; =&gt; [1],
    ];

    $this-&gt;postJson(route(&amp;#x27;courses.store&amp;#x27;), $course)
        -&gt;assertStatus(201);

    expect(Course::find(1))
        -&gt;students-&gt;toHaveCount(1)
        -&gt;students-&gt;first()-&gt;name-&gt;toBe(&amp;#x27;Bob&amp;#x27;)
        -&gt;teacher-&gt;name-&gt;toBe(&amp;#x27;Godruoyi&amp;#x27;);
});&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202401/24/cifU9s56OI8P0crZseOdrssBDmX97fFIubNmdUyq.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;update--select--delete&quot;&gt;Update &amp;#x26; Select &amp;#x26; Delete&lt;/h2&gt;
&lt;p&gt;接下来我们来看如何在 Laravel 中实现查询/删除/更新操作，这部分的记录你可以参考下面这几个 Commit：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/godruoyi/laravel-best-practice/commit/884eb7dd24d4fa32ac698698f431a037f566877f&quot;&gt;feat: create course and related testing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/godruoyi/laravel-best-practice/commit/37c9d5fa7ab2fc731f3643c9348a462ead58d8e9&quot;&gt;feat: show course and testing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/godruoyi/laravel-best-practice/commit/1ab9331d6bb9377f956dfd9be104cb6c1a8b9df4&quot;&gt;feat: update course and testing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/godruoyi/laravel-best-practice/commit/4ed0bc72ee965e9787b6ba001d6f14f2926e044e&quot;&gt;feat: delete course and testing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/godruoyi/laravel-best-practice/commit/c3f83395356d5d3e9f1c67f3b638acc10084ca99&quot;&gt;feat: use laravel resources&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; index&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Request&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $request)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    $courses &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Course&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;when&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($request&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;name, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;fn&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($query, $name) =&gt; $query&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;where&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;name&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;like&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;%{&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$name&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;}%&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        -&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;withCount&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;students&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        -&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;with&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;teacher&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        -&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;paginate&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($request&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;per_page &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;??&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 10&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; CourseCollection&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($course);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; show&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Course&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $course)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; CourseResource&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($course&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;load&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;teacher&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;students:id,name&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;name, fn ($query, $name) =&gt; $query-&gt;where(&amp;#x27;name&amp;#x27;, &amp;#x27;like&amp;#x27;, &amp;#x22;%{$name}%&amp;#x22;))
        -&gt;withCount(&amp;#x27;students&amp;#x27;)
        -&gt;with(&amp;#x27;teacher&amp;#x27;)
        -&gt;paginate($request-&gt;per_page ?? 10);

    return new CourseCollection($course);
}

public function show(Course $course)
{
    return new CourseResource($course-&gt;load(&amp;#x27;teacher&amp;#x27;, &amp;#x27;students:id,name&amp;#x27;));
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;在 Laravel 中可以高效的使用 Eloquent ORM 实现各种查询；如上面的例子中我们使用了 &lt;code&gt;withCount&lt;/code&gt; 来查询课程的学生数量、用 &lt;code&gt;with&lt;/code&gt; 加载课程对应的教师；还可以指定生成的 SQL 查询只包含某几个字段如 &lt;code&gt;students:id,name&lt;/code&gt;。我们还使用了 Laravel Resource 来格式化最终的输出格式，这样做的原因是很多情况下我们不希望直接将数据库的字段暴露出去，你甚至还能在 Laravel Resource 中按不同的角色显示不同的字段，如下面的 &lt;code&gt;secret&lt;/code&gt; 字段只有当用户是 admin 时才返回：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; toArray&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Request&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $request)&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; array&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;        &apos;id&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;id,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;        &apos;name&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;name,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;        &apos;email&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;email,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;        &apos;secret&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;when&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($request&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;user&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;isAdmin&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(), &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;secret-value&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;        &apos;created_at&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;created_at,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;        &apos;updated_at&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;updated_at,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    ];&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt; $this-&gt;id,
        &amp;#x27;name&amp;#x27; =&gt; $this-&gt;name,
        &amp;#x27;email&amp;#x27; =&gt; $this-&gt;email,
        &amp;#x27;secret&amp;#x27; =&gt; $this-&gt;when($request-&gt;user()-&gt;isAdmin(), &amp;#x27;secret-value&amp;#x27;),
        &amp;#x27;created_at&amp;#x27; =&gt; $this-&gt;created_at,
        &amp;#x27;updated_at&amp;#x27; =&gt; $this-&gt;updated_at,
    ];
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&quot;abstract-api&quot;&gt;Abstract API&lt;/h2&gt;
&lt;p&gt;Laravel 另一个优雅的地方是给开发者提供了很多优秀的组件，如 Cache、Filesystem、Queue、View、Auth、Event、Notifaction 等。这些组件都用一个共通的设计：即开发者只需要面对一套高度抽象的 API 而不用关心具体的实现。举个例子，Laravel Cache Store 的部分 API 定义如下：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;interface&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; Store&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; get&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($key);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; put&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($key, $value, $seconds);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;interface Store
{
    public function get($key);
    public function put($key, $value, $seconds);
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;在使用 Cache 时，我们基本不用关心到底用的是文件缓存还是 Redis 缓存；在使用队列时也不用关心用的是 sync 队列还是专业的 MQ 如 Kafka。这在日常开发中十分有用，因为你不需要在本地配置各种复杂的服务。你可以在开发阶段在 &lt;code&gt;.env&lt;/code&gt; 文件中将你的缓存驱动改为本地磁盘，将你的队列驱动改为本地同步队列；当你完成所有开发后，只需要在 staging/prod 环境修改 &lt;code&gt;.env&lt;/code&gt; 的值就可以了，你几乎不需要做什么额外的工作。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202401/24/iIBmKchNzfAfD69yj87DpC0PXZUrqvuVAY8bOsfH.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;laravel-core---container&quot;&gt;Laravel Core - Container&lt;/h2&gt;
&lt;p&gt;Laravel Container 是整个 Laravel 框架中最核心的部分，所有的一切都是建立在它之上的。&lt;/p&gt;
&lt;p&gt;我们知道容器只有两个功能：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;装东西(bind)&lt;/li&gt;
&lt;li&gt;从容器里取东西(get)
所有用到容器的框架其本质都是在框架启动的时候疯狂的往容器里装东西，容器里面的东西越多，容器提供的功能越大。如 Java 的 Spring 会在编译时为 Sprint Container 填充不同的对象，在使用时就能向容器获取不同的值。Laravel Container 也类似；Laravel 还巧妙的提供了 Service Provider 的方式来往容器里装东西，它的定义如下：&lt;/li&gt;
&lt;/ol&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;interface&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; ServiceProvider&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    /**&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;     * Register any application services.&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;     */&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; register&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; void&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    /**&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;     * Bootstrap any application services.&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;     */&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; boot&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; void&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;interface ServiceProvider
{
    /**
     * Register any application services.
     */
    public function register(): void

    /**
     * Bootstrap any application services.
     */
    public function boot(): void
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;每个 Service Provider 在注册阶段都会向 container 中设置不同的值；如 CacheServiceProvider 会向容器中注册 Cache 对象，后续在使用 &lt;code&gt;Cache::get&lt;/code&gt; 时就使用的是这里注册的 Cache 对象，在注册阶段不应该向容器中获取值，因为此时服务可能还没有 Ready；启动阶段一般用来控制如何启动你的服务，如你可以在这个阶段中 Connect to Server、Start engin 等等。Laravel 默认会注册 20 多个 Service Provider，每个 Service Provider 都为 Laravel 提供了一种新的能力：如 Cookie/Session/DB/Filesystem 等。它的所有的核心组件都是通过这种方式注册的，正是因为如此众多的 Service Provider 才使得 Laravel Container 更加强大。&lt;/p&gt;
&lt;p&gt;我最喜欢 Laravel Container 的一点是它支持获取任何对象，即使容器里没有，它也能给你造一个。Laravel Container 支持自动帮你构造容器中不存在的对象，如果这构造这个对象时还依赖另外的对象，Laravel 会尝试递归的创建它，举个例子：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; A&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; __construct&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; B&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $b) {}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; B&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; __construct&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; C&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $c) {}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; C&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; __construct&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $name &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;Hello C&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$a &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Container&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;())&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;A&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;expect&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($a)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    -&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;not&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;toBeNull&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    -&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;b&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;not&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;toBeNull&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    -&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;b&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;not&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;toBeNull&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    -&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;b&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;toBe&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;Hello C&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;get(A::class);

expect($a)
    -&gt;not-&gt;toBeNull()
    -&gt;b-&gt;not-&gt;toBeNull()
    -&gt;b-&gt;c-&gt;not-&gt;toBeNull()
    -&gt;b-&gt;c-&gt;name-&gt;toBe(&amp;#x27;Hello C&amp;#x27;);&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;正是因为这个特性，所以在 Laravel 的绝大多数方法参数中，你可以随意的注入任意数量的参数；这也是我最喜欢的一点。Laravel 会自动帮我们从容器中获取它，如果容器不存在，则会尝试初始化它。如我们上面的 CURD 的例子中，Request 对象就是 Laravel 自动注入的，你还可以在后面注入任意数量的参数：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; CourseController&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#6CB6FF&quot;&gt; Controller&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; index&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Request&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $request, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;A&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $a, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;/* arg1, arg2, ... */&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;        // ...&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;class CourseController extends Controller
{
    public function index(Request $request, A $a, /* arg1, arg2, ... */)
    {
        // ...
    }
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&quot;laravel--pipeline&quot;&gt;Laravel  Pipeline&lt;/h2&gt;
&lt;p&gt;Laravel 另一个优秀的设计是 Pipeline ；Laravel 的 Pipeline 贯穿了整个框架的生命周期，可以说整个框架都是在一个流水线的管道里启动起来的。而 Laravel Pipeline 的实现也很有趣；我们知道在常见的 Pipeline 设计中，大多会通过 for 循环来实现，而 Laravel 则采用的是最简单却又最复杂的实现 &lt;code&gt;array_reduce&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;我们知道 array_reduce 可以将一组数据串起来执行，如：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;array_reduce&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;([&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;], &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;fn&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($carry, $item) =&gt; $carry &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $item) &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// 6&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt; $carry + $item) // 6&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;但当 array_reduce 遇到全是闭包的调用时，情况就复杂了：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$pipelines &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; array_reduce&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;([&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Middleware1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Middleware2&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;/* ... */&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;], &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($stack, $pipe) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($passable) &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($stack, $pipe) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        try&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;            if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;is_callable&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($pipe)) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;                return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $pipe($passable, $stack);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;            } &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;elseif&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; is_object&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($pipe)) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;                [$name, $parameters] &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;parsePipeString&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($pipe);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;                $pipe &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;getContainer&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;make&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($name);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        &lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;                $parameters &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; array_merge&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;([$passable, $stack], $parameters);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;            } &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;                $parameters &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; [$passable, $stack];&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        &lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;            return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $pipe(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;...&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$parameters);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        } &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;catch&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Throwable&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $e) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;            return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;handleException&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($passable, $e);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    })&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($passable) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;router&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;dispatch&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($passable);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;})&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// send request through middlewares and then get response&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$response &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $pipelines($request); &lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;parsePipeString($pipe);
                $pipe = $this-&gt;getContainer()-&gt;make($name);
        
                $parameters = array_merge([$passable, $stack], $parameters);
            } else {
                $parameters = [$passable, $stack];
            }
        
            return $pipe(...$parameters);
        } catch (Throwable $e) {
            return $this-&gt;handleException($passable, $e);
        }
    })
}, function ($passable) {
    return $this-&gt;router-&gt;dispatch($passable);
})

// send request through middlewares and then get response
$response = $pipelines($request); &quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;上面的代码其实是 Laravel 中间件的核心代码，也是 Laravel 启动流程的核心实现；虽然加入了各种样的闭包后导致函数阅读起来十分痛苦，但它的本质其实很简单；就是像洋葱一样将所有的中间件包起来，然后让请求从最外层一层一层的穿过它，每一层都可以决定是否继续向下执行，而最后的心脏部分是最终要执行的操作。举个简单的例子，我们可以将一段文本通过各种过滤后再保存进数据库，如：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Pipeline&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    -&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;send&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;&amp;#x3C;p&gt;This is the HTML content of a blog post&amp;#x3C;/p&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    -&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;through&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;([&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;        ModerateContent&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;        RemoveScriptTags&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;        MinifyHtml&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    ])&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    -&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $content) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Post&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;create&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;([&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;            &apos;content&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $content,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;            ...&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        ]);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    });&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;send(&amp;#x27;&lt;p&gt;This is the HTML content of a blog post&lt;/p&gt;&amp;#x27;)
    -&gt;through([
        ModerateContent::class,
        RemoveScriptTags::class,
        MinifyHtml::class,
    ])
    -&gt;then(function (string $content) {
        return Post::create([
            &amp;#x27;content&amp;#x27; =&gt; $content,
            ...
        ]);
    });&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&quot;laravel-comnication&quot;&gt;Laravel Comnication&lt;/h2&gt;
&lt;p&gt;Laravel 的强大离不开社区的支持，这十年来 Laravel 官方已经发布了 &lt;a href=&quot;https://laravel.com/&quot;&gt;20 多种周边生态&lt;/a&gt;，这里摘抄一个来自&lt;a href=&quot;https://twitter.com/xiqingongzi&quot;&gt;@白宦成&lt;/a&gt;关于 Laravel 和其他&lt;a href=&quot;https://wiki.bestony.com/#%E6%A1%86%E6%9E%B6%E5%AF%B9%E6%AF%94&quot;&gt;框架的对比图&lt;/a&gt;。&lt;/p&gt;







































































































































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;strong&gt;项目&lt;/strong&gt;&lt;/th&gt;&lt;th&gt;&lt;strong&gt;Laravel&lt;/strong&gt;&lt;/th&gt;&lt;th&gt;&lt;strong&gt;Rails&lt;/strong&gt;&lt;/th&gt;&lt;th&gt;&lt;strong&gt;Django&lt;/strong&gt;&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;ORM&lt;/td&gt;&lt;td&gt;有&lt;/td&gt;&lt;td&gt;有&lt;/td&gt;&lt;td&gt;有&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;数据库迁移&lt;/td&gt;&lt;td&gt;有&lt;/td&gt;&lt;td&gt;有&lt;/td&gt;&lt;td&gt;有&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;发送邮件&lt;/td&gt;&lt;td&gt;&lt;del&gt;&lt;a href=&quot;https://laravel.com/docs/10.x/mail&quot;&gt;Mailables&lt;/a&gt;&lt;/del&gt;&lt;/td&gt;&lt;td&gt;&lt;del&gt;&lt;a href=&quot;https://guides.rubyonrails.org/action_mailer_basics.html&quot;&gt;ActionMailer&lt;/a&gt;&lt;/del&gt;&lt;/td&gt;&lt;td&gt;&lt;del&gt;&lt;a href=&quot;https://docs.djangoproject.com/zh-hans/4.2/topics/email/&quot;&gt;SendMail&lt;/a&gt;&lt;/del&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;接收邮件&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;td&gt;&lt;del&gt;&lt;a href=&quot;https://guides.rubyonrails.org/action_mailbox_basics.html&quot;&gt;Action Mailbox&lt;/a&gt;&lt;/del&gt;&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;管理框架&lt;/td&gt;&lt;td&gt;&lt;del&gt;&lt;a href=&quot;https://nova.laravel.com/&quot;&gt;Nova&lt;/a&gt;&lt;/del&gt;&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;td&gt;&lt;del&gt;&lt;a href=&quot;https://docs.djangoproject.com/zh-hans/4.2/ref/django-admin/&quot;&gt;Django Admin&lt;/a&gt;&lt;/del&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;单页管理&lt;/td&gt;&lt;td&gt;&lt;del&gt;&lt;a href=&quot;https://laravel.com/docs/10.x/folio&quot;&gt;Folio&lt;/a&gt;&lt;/del&gt;&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;td&gt;&lt;del&gt;&lt;a href=&quot;https://docs.djangoproject.com/zh-hans/4.2/ref/contrib/flatpages/&quot;&gt;flatpages&lt;/a&gt;&lt;/del&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;系统检查框架&lt;/td&gt;&lt;td&gt;&lt;del&gt;&lt;a href=&quot;https://github.com/laravel/pulse&quot;&gt;Pluse&lt;/a&gt;&lt;/del&gt;&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;td&gt;&lt;del&gt;&lt;a href=&quot;https://docs.djangoproject.com/zh-hans/4.2/topics/checks/%7C&quot;&gt;checks&lt;/a&gt;&lt;/del&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Sitemap&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;td&gt;&lt;del&gt;&lt;a href=&quot;https://docs.djangoproject.com/zh-hans/4.2/ref/contrib/sitemaps/&quot;&gt;Sitemap&lt;/a&gt;&lt;/del&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;RSS &amp;#x26; Atom&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;td&gt;&lt;del&gt;&lt;a href=&quot;https://docs.djangoproject.com/zh-hans/4.2/ref/contrib/syndication/&quot;&gt;Feed&lt;/a&gt;&lt;/del&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;多站点框架&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;td&gt;&lt;del&gt;&lt;a href=&quot;https://docs.djangoproject.com/zh-hans/4.2/ref/contrib/sites/&quot;&gt;Sites&lt;/a&gt;&lt;/del&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;前端处理&lt;/td&gt;&lt;td&gt;&lt;del&gt;&lt;a href=&quot;https://laravel.com/docs/10.x/vite&quot;&gt;Asset Bundling&lt;/a&gt;&lt;/del&gt;&lt;/td&gt;&lt;td&gt;&lt;del&gt;&lt;a href=&quot;https://guides.rubyonrails.org/asset_pipeline.html&quot;&gt;Asset Pipeline&lt;/a&gt;&lt;/del&gt;&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;WebSocket&lt;/td&gt;&lt;td&gt;&lt;del&gt;&lt;a href=&quot;https://laravel.com/docs/5.8/broadcasting#introduction&quot;&gt;Broadcasting&lt;/a&gt;&lt;/del&gt;&lt;/td&gt;&lt;td&gt;&lt;del&gt;&lt;a href=&quot;https://guides.rubyonrails.org/action_cable_overview.html&quot;&gt;Action Cable&lt;/a&gt;&lt;/del&gt;&lt;/td&gt;&lt;td&gt;&lt;del&gt;&lt;a href=&quot;https://channels.readthedocs.io/en/latest/&quot;&gt;Django Channels&lt;/a&gt;&lt;/del&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;队列&lt;/td&gt;&lt;td&gt;&lt;del&gt;&lt;a href=&quot;https://laravel.com/docs/5.8/queues&quot;&gt;Queues&lt;/a&gt;&lt;/del&gt;&lt;/td&gt;&lt;td&gt;&lt;del&gt;&lt;a href=&quot;https://guides.rubyonrails.org/active_job_basics.html&quot;&gt;Active Job&lt;/a&gt;&lt;/del&gt;&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;文本编辑器&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;td&gt;&lt;del&gt;&lt;a href=&quot;https://guides.rubyonrails.org/action_text_overview.html&quot;&gt;Action Text&lt;/a&gt;&lt;/del&gt;&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;GIS&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;td&gt;&lt;del&gt;&lt;a href=&quot;https://docs.djangoproject.com/zh-hans/4.2/ref/contrib/gis/&quot;&gt;DjangoGIS&lt;/a&gt;&lt;/del&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;信号调度框架&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;td&gt;&lt;del&gt;&lt;a href=&quot;https://docs.djangoproject.com/zh-hans/4.2/topics/signals/&quot;&gt;Signals&lt;/a&gt;&lt;/del&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;支付框架&lt;/td&gt;&lt;td&gt;&lt;del&gt;&lt;a href=&quot;https://laravel.com/docs/master/cashier-paddle&quot;&gt;Cashier&lt;/a&gt;&lt;/del&gt;&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;浏览器测试&lt;/td&gt;&lt;td&gt;&lt;del&gt;&lt;a href=&quot;https://laravel.com/docs/master/dusk&quot;&gt;Dusk&lt;/a&gt;&lt;/del&gt;&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;td&gt;&lt;del&gt;&lt;a href=&quot;https://guides.rubyonrails.org/testing.html#system-testing&quot;&gt;System Testing&lt;/a&gt;&lt;/del&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;自动化部署工具&lt;/td&gt;&lt;td&gt;&lt;del&gt;&lt;a href=&quot;https://laravel.com/docs/master/envoy&quot;&gt;Envoy&lt;/a&gt;&lt;/del&gt;&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Redis 调度&lt;/td&gt;&lt;td&gt;&lt;del&gt;&lt;a href=&quot;https://laravel.com/docs/master/horizon&quot;&gt;Horizon&lt;/a&gt;&lt;/del&gt;&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;完整用户系统&lt;/td&gt;&lt;td&gt;&lt;del&gt;&lt;a href=&quot;https://jetstream.laravel.com/introduction.html&quot;&gt;Jetstream&lt;/a&gt;&lt;/del&gt;&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Feature Flag&lt;/td&gt;&lt;td&gt;&lt;del&gt;&lt;a href=&quot;https://laravel.com/docs/master/pennant&quot;&gt;Pennant&lt;/a&gt;&lt;/del&gt;&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Code Style Fixer&lt;/td&gt;&lt;td&gt;&lt;del&gt;&lt;a href=&quot;https://laravel.com/docs/master/pint&quot;&gt;Pint&lt;/a&gt;&lt;/del&gt;&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;搜索框架&lt;/td&gt;&lt;td&gt;&lt;del&gt;&lt;a href=&quot;https://laravel.com/docs/master/scout&quot;&gt;Scout&lt;/a&gt;&lt;/del&gt;&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;OAuth&lt;/td&gt;&lt;td&gt;&lt;del&gt;&lt;a href=&quot;https://laravel.com/docs/master/socialite&quot;&gt;Socialite&lt;/a&gt;&lt;/del&gt;&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;系统分析&lt;/td&gt;&lt;td&gt;&lt;del&gt;&lt;a href=&quot;https://laravel.com/docs/master/telescope&quot;&gt;Telescope&lt;/a&gt;&lt;/del&gt;&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;td&gt;无&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;除了官方，社区本身已有非常多的第三方扩展；有快速生成 Admin 管理后台的各种 Generater、有操作 Excel 的 &lt;a href=&quot;https://github.com/SpartnerNL/Laravel-Excel&quot;&gt;SpartnerNL/Laravel-Excel&lt;/a&gt;、有高效操作图片的 &lt;a href=&quot;https://github.com//Intervention//image&quot;&gt;Intervention/image&lt;/a&gt;、还有最近要被纳入默认测试框架的 &lt;a href=&quot;https://github.com/pestphp/pest&quot;&gt;Pest&lt;/a&gt; 以及在屎一样的 API 之上构建出来的最好用的微信 SDK &lt;a href=&quot;https://github.com/w7corp/easywechat&quot;&gt;EasyWechat&lt;/a&gt;。你几乎能在 PHP 生态中找到任何你想找的轮子。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;说到这儿，不得不说 PHP 生态中了一个强大的存在 &lt;a href=&quot;https://symfony.com/&quot;&gt;Symfony&lt;/a&gt;。Symfony 完全是另一个可以和 Laravel 媲美的框架，甚至在很多设计上比 Laravel 还要超前；并且 Laravel 的核心组件如路由/Request/Container 都是构建在 Symfony 之上的。但 Symfony 的推广没有 Laravel 那么好运，Symfony 发布到现在已经 12 年了，仍然处于不温不火的地位(国内看的话)，我想大概是没有一个像 Taylor Otwell 一样即会写代码还会营销的 KOL 吧。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;正是因为这些强大的社区支持帮助 Laravel 变得更加强大，也正是因为这些繁荣的生态保护着 PHP 一步一步走到现在。有些开发者可能觉得 PHP 已经走向衰亡了，并且十分鄙视 PHP 着门语言。我其实很不明白作为一名工程师为什么我们会瞧不上某一门语言？每一门语言都有着自己天然的优势，PHP 作为一门脚本语言在 WEB 开发这块儿有着极快的开发速度，加上上手难度低，工资不高，对于初创型企业来何尝不为一个好的选择呢。我不会因为写 Python 就觉得 PHP 屁都不如，也不因为写 Rust 就觉得 Go 狗都不如；在我看来，语言只是实现产品的一种方式，不同的语言在不同的领域有自己的优势，我们应该学习不止一门语言，并尽量了解每一门语言的优缺点，在完成开发时选择自己以及团队合适的，而不是只会写 Java 就觉得其他语言啥都不是。&lt;/p&gt;
&lt;h2 id=&quot;不足&quot;&gt;不足&lt;/h2&gt;
&lt;p&gt;Laravel 为人垢弊的问题就是太慢了，一个普通的应用一个 RTT 可能也要 100～200 ms；当遇到稍微大一点的并发请求时，CPU 的负载就奔着 90% 去了。为了解决 Laravel 速度太慢这一问题，Laravel 团队在 2021 年的时候推出了 &lt;a href=&quot;https://github.com/laravel/octane&quot;&gt;Laravel/Octane&lt;/a&gt;，如果你对 Laravel Octane 感兴趣，也可以看看我之前写的文章 — &lt;a href=&quot;https://godruoyi.com/posts/laravel-octane&quot;&gt;Laravel Octane 初体验&lt;/a&gt;。加持了 Laravel Octane 的应用，我们可以把请求响应做到 20ms 以内。&lt;/p&gt;
&lt;p&gt;不过我觉得 Laravel 的不足不在性能，毕竟 PHP 作为脚本语言，就算我们把它优化到极致，也不可能达到类似 Go 那么高的吞吐率，如果真的是为了性能，那为什么不选择其他更适合的语言呢？&lt;/p&gt;
&lt;p&gt;在我看来最大的不足是繁重的社区生态；Laravel 之前只有 Blade 模版引擎，其语法和其他模版引擎大同小异，学起来很容易上手；后来 Laravel 推出了 &lt;a href=&quot;https://laravel-livewire.com/&quot;&gt;Livewire&lt;/a&gt; 和 &lt;a href=&quot;https://inertiajs.com/&quot;&gt;Inertiajs&lt;/a&gt;。Livewire 和 Inertiajs 都是一种类前端框架，它们提供了一种更加高效的方式来管理前端页面，并且能更好的和 Laravel 整合在一起。但是它却带来了更高的学习成本和更多人力资源的浪费。本来我们只需要熟悉标准的 Vue/React API 就好了，现在却不得不学习一种新的语法，而这些语法是构建在我们熟悉的 API 之上的；有时候你原始的 API 你知道怎么写，但是新框架的新语法让你不得不查看更多的文档甚至源码，你不得不花更多的时间去适配它；而当你的团队有新人接手这些项目时，他也得跟你走一样的路，并且 Laravel 团队说不定哪天还会弃用它们(如 &lt;a href=&quot;https://github.com/laravel-mix/laravel-mix&quot;&gt;Laravel-Mix&lt;/a&gt;)。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这里还有个例子是 Laravel 在之前推出了 &lt;a href=&quot;https://bootcamp.laravel.com/livewire/creating-chirps&quot;&gt;Laravel Bootcamp&lt;/a&gt; 用来教新人怎么快速上手 Laravel，但这之前只推出了两个版本，即 Livewire 和 Inertia，好在是&lt;a href=&quot;https://x.com/PovilasKorop/status/1570810285320978436?s=20&quot;&gt;被社区大佬及时反应&lt;/a&gt;后才在再后来加上了最原始的 Blade 支持。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Laravel 官方还推出了 &lt;a href=&quot;https://laravel.com/docs/10.x/sail&quot;&gt;Laravel Sail&lt;/a&gt;、&lt;a href=&quot;https://herd.laravel.com/&quot;&gt;Laravel Herd&lt;/a&gt; 还有更早之前推出现在被弃用的 &lt;a href=&quot;https://github.com/laravel/homestead&quot;&gt;Laravel Homestead&lt;/a&gt; 等本地开发环境工具；而部署工具 Laravel 推出了 &lt;a href=&quot;https://forge.laravel.com/&quot;&gt;Laravel Forge&lt;/a&gt;、&lt;a href=&quot;https://vapor.laravel.com/&quot;&gt;Laravel Vapor&lt;/a&gt; 还有 &lt;a href=&quot;https://envoyer.io/&quot;&gt;Laravel Envoyer&lt;/a&gt;；如果你作为一个 Laravel 新人你知道用什么搭建本地开发环境吗？又用什么部署你的 Laravel 应用吗？说实话我用了 Laravel 这么久我也不知道。我更建议大家的是如果你对 Laravel 感兴趣，不要一来就接触 Laravel 这些复杂的概念，老老实实的在本地安装好 PHP/Nginx/PostgreSQL 或者 Docker；而如果你要还要用它写前端页面，老老实实的用原生框架如 Vue/React/Bootstrap 甚至 Blade  才是更好的选择。&lt;/p&gt;
&lt;p&gt;Laravel 还有很优秀的设计我没有在这篇文章中指出来，如果你对 Laravel 感兴趣或者想写出一手还不错的代码，我真的建议你看一看 Laravel 的源码，看一看他的设计，我觉得这些设计在所有的语言中都是通用的，enjoy。&lt;/p&gt;
&lt;h2 id=&quot;参考&quot;&gt;参考&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/laravel&quot;&gt;The Laravel Framework | GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://bootcamp.laravel.com/introduction&quot;&gt;https://bootcamp.laravel.com/introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://learnku.com/articles/5206/the-use-of-php-built-in-function-array-reduce-in-laravel&quot;&gt;Laravel 管道流原理 | Godruoyi Laravel China 社区&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://learnku.com/articles/5180/laravel-middleware-principle&quot;&gt;Laravel 中间件原理 | Godruoyi  Laravel China 社区&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/nativephp&quot;&gt;NativePHP 的技术原理和实现细节 | Godruoyi&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ixiqin.com/2019/02/06/why-should-a-modern-framework-have-migration-function/&quot;&gt;为什么一个现代化的框架应该具备 Migration 功能 - 白宦成&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/laravel-octane&quot;&gt;Laravel Octane 初体验 | Godruoyi&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://martinjoo.dev/laravel-pipelines&quot;&gt;Laravel Pipelines | Martin Joo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>一条甲鱼</title><link>https://godruoyi.com/posts/a-turtle/</link><guid isPermaLink="true">https://godruoyi.com/posts/a-turtle/</guid><description>对于看家护院来说，两条狗就已经够了；丈母娘就经常对老丈人说：「喂那么多狗，狗吃的比人还多，简直是浪费粮食」。老丈人虽然不是地主，但余粮还是有，几条狗也是养得起的</description><pubDate>Sat, 04 Nov 2023 15:21:48 GMT</pubDate><content:encoded>&lt;p&gt;老丈人在自家的鱼塘里钓了一个甲鱼。&lt;/p&gt;
&lt;p&gt;说是甲鱼，但若你说是乌龟，我也是信的。「头大的就是甲鱼」，老丈人说道。「乌龟是长不大的」，阿宝补充说。我其实是不忍心杀来吃的，我想它也不愿意被我们吃，不然也不至于一直把头缩进壳里。我更意愿把它养在院坝边上的池塘里，哪里有一个倾斜的大石头，平时是用来洗鞋子洗衣服。我想它很愿意躺在上面嗮太阳。但它晒太阳时会不会被狗叼走呢。&lt;/p&gt;
&lt;p&gt;老丈人收拾鱼时，黄标和大白就睡在他旁边；时不时的摇摇尾巴，或者用嘴巴咬咬腿上的格蚤。黑儿不见了。听老丈人说是被哪些骑摩托车的狗贩子打了去，怕是回不来了。我挺喜欢黑儿的；它是最忠诚的狗。不管老丈人开着三轮车去哪里，它都永远跟在后面。现在它不见了，我猜老丈人还是有点感情的吧。但农村的猫狗要是不见了，是不会在电线杆上贴告示重金求狗的。乡里人想的是，说不定那天它就回来了，要是一个月还不回来，那回不回来已经不重要了，他们早已经忘记了对方。&lt;/p&gt;
&lt;p&gt;对于看家护院来说，两条狗就已经够了；丈母娘就经常对老丈人说：「喂那么多狗，狗吃的比人还多，简直是浪费粮食」。老丈人虽然不是地主，但余粮还是有，几条狗也是养得起的。不过有些狗越养越懒，要是有一天变为流浪狗了，它甚至都不能自力更生，少有能自食其力者，不是偷这家的蛋，就是偷哪家的鸡，搞得附近几个村子鸡犬不宁。想想他的祖先狼，狗都觉得惭愧。&lt;/p&gt;
&lt;p&gt;好，就写到这儿吧。&lt;/p&gt;
&lt;h2 id=&quot;老丈人系列&quot;&gt;老丈人系列&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/a-grape-trellis/&quot;&gt;葡萄架&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/dads-tricycle/&quot;&gt;老丈人的三轮车&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>NativePHP 的技术原理和实现细节</title><link>https://godruoyi.com/posts/nativephp/</link><guid isPermaLink="true">https://godruoyi.com/posts/nativephp/</guid><description>NativePHP 在这里的价值就是提供了一套相对完整的 API，通过操作这些 API 我们就能非常方便的和 Electron APP 进行交付。而且我们不需要关心项目打包的具体细节，也不需要手动对接这些 API；NativePHP 已经非常深度的把他们集成到了 Laravel 环境中，我们可以高效的在 Laravel 中使用这些魔法而不用关心具体的细节实现。</description><pubDate>Tue, 25 Jul 2023 08:37:52 GMT</pubDate><content:encoded>&lt;p&gt;这次的 LaraconUS 发布很多很有意思的项目，比如我在&lt;a href=&quot;https://twitter.com/godruoyi/status/1681875841603735552?s=20&quot;&gt;这里&lt;/a&gt;提到的 Laravel Prompts/Laravel Folio/Laravel Herd/Pest 2 以及 &lt;del&gt;&lt;a href=&quot;https://twitter.com/beyondcode&quot;&gt;beyondcode&lt;/a&gt;&lt;/del&gt; 这两天发布的比较重量级的产品 —— NativePHP 等。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;需要强调的是这次发布的诸多产品中只有 Laravel Prompts/Laravel Herd 属于官方出品，其他均为社区项目。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这篇文章主要想探讨一下 NativePHP 的实现细节、使用了哪些技术、它的生命周期和工作原理等，如果文章中有任何纰漏，欢迎留言指正。&lt;/p&gt;
&lt;p&gt;在使用 NativePHP 之前，&lt;a href=&quot;https://nativephp.com/docs/1/getting-started/installation#installation&quot;&gt;官方文档&lt;/a&gt;第一步就是需要在 Laravel 项目中安装 &lt;code&gt;nativephp/electron&lt;/code&gt;：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;shell&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;shell&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; composer&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; nativephp/electron&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; php&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; artisan&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; native:install&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; php&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; artisan&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; native:serve&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;$ composer require nativephp/electron

$ php artisan native:install
$ php artisan native:serve&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;安装成功后即可通过 &lt;code&gt;native:install&lt;/code&gt; 安装所需的 Node 依赖并通过 &lt;code&gt;native:serve&lt;/code&gt; 启动 Native APP。从 Electron 这个名字就可以大概猜出，目前的 NativePHP 生态中，主要是使用 &lt;a href=&quot;https://www.electronjs.org/&quot;&gt;Electron&lt;/a&gt; 来进行 APP 打包。我们先顺着 &lt;code&gt;native:serve&lt;/code&gt; 看看整个 Native APP 是如何启动起来的。&lt;/p&gt;
&lt;h2 id=&quot;boot-nativephp&quot;&gt;Boot NativePHP&lt;/h2&gt;
&lt;p&gt;命令 &lt;code&gt;native:serve&lt;/code&gt; 是一个标准的 Laravel Command，它的核心逻辑类似于 CD 到 &lt;code&gt;resources/js&lt;/code&gt; 目录并执行 &lt;code&gt;yarn run dev&lt;/code&gt; 来启动 Navite APP。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202307/25/QsDfZUrldJsGwgarc2pNoStqHVmhMFMofOqoDDdX.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;p&gt;NativePHP Electron 这个包下面的 &lt;code&gt;resources/js&lt;/code&gt; 目录是一个完整的前端工程，它主要使用 &lt;a href=&quot;https://electron-vite.org/&quot;&gt;electron-vite&lt;/a&gt; 来编译及调试 Electron 项目，package.json 的部分编译代码如下所示：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;scripts&quot;: {
  &quot;start&quot;: &quot;electron-vite preview&quot;,
  &quot;dev&quot;: &quot;electron-vite dev --watch&quot;,
  &quot;build&quot;: &quot;electron-vite build&quot;,
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;整个前端工程除了使用 electron-vite 来启动 Electron App 外，并没有做其他额外的事情。从他的入口文件 main/index.js 你可以看到它主要是调用 &lt;code&gt;nativephp-electron&lt;/code&gt; 这个前端插件来启动 NativePHP APP。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202307/25/5Mtz5dZtBG07lIQqo2jzgb81wetJKloRxkpwwXTo.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;p&gt;NativePHP 的 Electron binding 的全部功能都是在这个前端插件 &lt;a href=&quot;https://github.com/NativePHP/electron-plugin&quot;&gt;electron-plugin&lt;/a&gt; 里实现的，主要的流程包括：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;通过 express 启动一个 API Server&lt;/li&gt;
&lt;li&gt;通过 &lt;code&gt;PHP -S 127.0.0.1&lt;/code&gt; 启动 PHP Server&lt;/li&gt;
&lt;li&gt;通过 &lt;code&gt;artisan&lt;/code&gt; 运行 Laravel 数据迁移&lt;/li&gt;
&lt;li&gt;通过 &lt;code&gt;artisan&lt;/code&gt; 运行 Laravel WebSocket&lt;/li&gt;
&lt;li&gt;通过 &lt;code&gt;artisan&lt;/code&gt; 运行 Laravel Queue&lt;/li&gt;
&lt;li&gt;启动定时任务&lt;/li&gt;
&lt;li&gt;发送 Booted 通知&lt;/li&gt;
&lt;li&gt;添加事件监听&lt;/li&gt;
&lt;li&gt;添加 Terminate 事件&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;start-express-api-server&quot;&gt;Start Express API Server&lt;/h3&gt;
&lt;p&gt;其中最核心的是通过 &lt;a href=&quot;https://expressjs.com/&quot;&gt;expressjs&lt;/a&gt; 框架启动一个 API Server，在这个 Server 中定义了许多和 APP 交付的 RESTful API；比如操作剪辑版、窗口管理、菜单管理等。当我们想在 Laravel 系统中操作 Electron APP 时，实际上操作的就是这些 API。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202307/25/fckOx54WOIfoGaFeSnAXaRtU7PlQRQPsgEfAbW4x.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;p&gt;举个例子，在 Laravel 中你可以直接通过 Window Facade 快速的设置窗口大小，这个操作本质上会发起一个对 Express Api Server 的 POST 请求；Express Server 在收到这个请求后，会通过 Electron 的 BrowserWindow 对象设置 APP 窗口大小。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Native\Laravel\Facades\Window&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// POST window/open {width: 800, height: 800}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Window&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;open&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;width&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;800&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;height&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;800&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// Electron&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; window&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; BrowserWindow&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;    width&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;windowState&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;width&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; parseInt&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;width&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;    height&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; windowState&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;height&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; parseInt&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;height&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;})&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;width(800)-&gt;height(800);

// Electron
const window = new BrowserWindow({
    width: windowState?.width || parseInt(width),
    height: windowState?.height || parseInt(height),
})&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&quot;start-php-server&quot;&gt;Start PHP Server&lt;/h2&gt;
&lt;p&gt;Express API Server 启动完成后，&lt;a href=&quot;https://github.com/NativePHP/electron-plugin&quot;&gt;Electron Plugin&lt;/a&gt;会尝试启动 PHP Server。这里非常简陋的使用了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;php -S 127.0.0.1:$phpProt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;的形式来启动 PHP 服务。因为这个项目还未正式发布 1.0 版本，所以目前的这个临时过度我认为是可以接受的。期待后期社区添加专业的 Web Server 支持。&lt;/p&gt;
&lt;p&gt;注意这里每启动的一个 Server 如 API Server、PHP Server 都会是一个单独的进程；&lt;a href=&quot;https://github.com/NativePHP/electron-plugin&quot;&gt;Electron Plugin&lt;/a&gt; 会收集这些进程的 PID，待关闭 APP 时会一并把这些所有进程 KILL 掉。&lt;/p&gt;
&lt;p&gt;还需要注意的是由于每个进程都是单独启动的，也没有使用 &lt;a href=&quot;http://supervisord.org/&quot;&gt;Supervisor&lt;/a&gt; 一类的进程管理工具，当某个进程意外退出时，可能会导致你打包的整个 APP 不可用。&lt;/p&gt;
&lt;h2 id=&quot;add-event-listeners&quot;&gt;Add Event Listeners&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/NativePHP/electron-plugin&quot;&gt;Electron Plugin&lt;/a&gt;  插件在主要的 API Server 及 PHP Server 都启动完成后，会注册大量的事件。这些事件主要是为了捕获 APP 端的状态变化。比如用户重新设置了窗口大小、用户打开了一个 URL 等；而这些事件全都会通过 RESTful API 发往 PHP Server。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;javascript&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;javascript&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;window.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;on&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;resized&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, () &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;  notifyLaravel&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;_native/api/events&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    event: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;Native&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#F47067&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;Laravel&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#F47067&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;Events&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#F47067&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;Windows&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#F47067&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;WindowResized&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    payload: [id, window.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;getSize&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()[&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;], window.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;getSize&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()[&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;]]&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;  })&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt; {
  notifyLaravel(&amp;#x27;_native/api/events&amp;#x27;, {
    event: &amp;#x27;Native\\Laravel\\Events\\Windows\\WindowResized&amp;#x27;,
    payload: [id, window.getSize()[0], window.getSize()[1]]
  })
});&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;举个例子，上面的程序会监听 Electron BrowserWindow 的 &lt;code&gt;reseized&lt;/code&gt; 事件，当 Electron APP 触发这个事件后，会向 PHP Server 发起一个 Post 请求，请求的路由 &lt;code&gt;_native/api/events&lt;/code&gt; 被定义在 &lt;a href=&quot;https://github.com/NativePHP/laravel&quot;&gt;NativePHP/laravel&lt;/a&gt; 这个 composer 包当中，这个包会随着你在刚开始安装 &lt;code&gt;nativephp/electron&lt;/code&gt; 一并安装。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;javascript&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;javascript&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;await&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; axios.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;post&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;  `http://127.0.0.1:${&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;state&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;phpPort&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;}/_native/api/events`&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;  payload,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;  {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    headers: {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;      &quot;X-NativePHP-Secret&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: state.randomSecret,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;await axios.post(
  &amp;#x60;http://127.0.0.1:${state.phpPort}/_native/api/events&amp;#x60;,
  payload,
  {
    headers: {
      &amp;#x22;X-NativePHP-Secret&amp;#x22;: state.randomSecret,
    },
  }
);&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;PHP Server 中这个更新 Events API 的功能很简单，就是将传入的 Event 初始化并触发事件；这样一来整个 Laravel 系统都会感知来自 Electron APP 的任何状态变化。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// route.php&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Route&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;group&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;([&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;middleware&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; PreventRegularBrowserAccess&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;], &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; () {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;    Route&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;post&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;_native/api/booted&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;NativeAppBootedController&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;    Route&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;post&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;_native/api/events&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;DispatchEventFromAppController&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;})&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;withoutMiddleware&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;\App\Http\Middleware\VerifyCsrfToken&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// DispatchEventFromAppController.php&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; DispatchEventFromAppController&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; __invoke&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Request&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $request)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        $event &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $request&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;event&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;class_exists&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($event)) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;            $event &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $event(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;...&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$request&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;payload&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, []));&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;            event&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($event);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; response&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;([&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;            &apos;success&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        ]);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt; PreventRegularBrowserAccess::class], function () {
    Route::post(&amp;#x27;_native/api/booted&amp;#x27;, NativeAppBootedController::class);
    Route::post(&amp;#x27;_native/api/events&amp;#x27;, DispatchEventFromAppController::class);
})-&gt;withoutMiddleware(\App\Http\Middleware\VerifyCsrfToken::class);

// DispatchEventFromAppController.php
class DispatchEventFromAppController
{
    public function __invoke(Request $request)
    {
        $event = $request-&gt;get(&amp;#x27;event&amp;#x27;);
        if (class_exists($event)) {
            $event = new $event(...$request-&gt;get(&amp;#x27;payload&amp;#x27;, []));

            event($event);
        }

        return response()-&gt;json([
            &amp;#x27;success&amp;#x27; =&gt; true,
        ]);
    }
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;所有事件注册完成后，Electron APP 就算启动完成了。不过这并&lt;strong&gt;不会&lt;/strong&gt;打开任何窗口，我们必须得在 Laravel 中注册你想打开的窗口。随着你执行 &lt;code&gt;native:install&lt;/code&gt; 已经在 NativeAppServiceProvider 中注册了根目录 &lt;code&gt;/&lt;/code&gt; 为默认打开的窗口，如果你的 PHP Server 运行在 &lt;code&gt;127.0.0.1:8080&lt;/code&gt; 上，那 &lt;code&gt;http://127.0.0.1/&lt;/code&gt; 就会是 Election APP 默认打包的页面，至于 Election 如何将一个网页打包为 APP 不在这篇文章的讨论范围内，我们只需要知道更定一个 URL 地址，Election 就能将它整个打包为一个 APP。&lt;/p&gt;
&lt;p&gt;我们也可以通过 Window 设置为默认打开的窗口为登陆页面，这将使用 &lt;code&gt;http://127.0.0.1/login&lt;/code&gt;  路由为默认的打包窗口。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// NativeAppServiceProvider.php&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Window&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;open&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;/login&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;))&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;width&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;800&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;height&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;400&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;url(url(&amp;#x27;/login&amp;#x27;))-&gt;width(800)-&gt;height(400);&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202307/25/trxWkNC4ENAFglMnNpH15QPo7wtAbWoXeJUOvR5I.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;why-nativephp&quot;&gt;Why NativePHP&lt;/h2&gt;
&lt;p&gt;如你所想，由于 Electron APP 是运行在 JavaScript 环境的，而 Laravel 是运行在 PHP 环境的，在 PHP 环境中我们不能直接操作 Electron APP。NativePHP 在这里的价值就是提供了一套相对完整的 API，通过操作这些 API 我们就能非常方便的和 Electron APP 进行交付。而且我们不需要关心项目打包的具体细节，也不需要手动对接这些 API；NativePHP 已经非常深度的把他们集成到了 Laravel 环境中，我们可以高效的在 Laravel 中使用这些魔法而不用关心具体的细节实现。&lt;/p&gt;
&lt;p&gt;可以理解为 NativePHP 为我们提供了一套统一的 API 去操作 Native APP，我们不需要关心 Native APP 的底层是使用的 Electron 还是 &lt;a href=&quot;https://tauri.app/&quot;&gt;Tauri&lt;/a&gt;，NativePHP 都会适配这些第三方的打包工具，并为他们实现一套完整的 API Server。不管是现在已经发布的 &lt;a href=&quot;https://github.com/NativePHP/electron-plugin&quot;&gt;Electron Plugin&lt;/a&gt; 还是即将发布的 Tauri Plugin，作为开发者我们都只需要站在更高的纬度使用 NativePHP 就好了。未来 NativePHP 会不止适配 Laravel，还会适配如 Symfony 等其他框架，这给我们使用 PHP 开发 Native APP 提供了一个很好的机会。不过就目前发布的 &lt;a href=&quot;https://github.com/NativePHP/electron-plugin&quot;&gt;Electron Plugin&lt;/a&gt; 插件来说，想要适配其他框架可能还很麻烦，如果后续仍然使用这个前端 Package 来同时适配多个 PHP 框架，可能还需要做比较大的重构比如单独抽离一个 Event Core、Core Server API 等等。&lt;/p&gt;
&lt;p&gt;感激社区，PHP IS DEAD.&lt;/p&gt;</content:encoded></item><item><title>佐玩的面试挂了</title><link>https://godruoyi.com/posts/zolplay-interview-failed/</link><guid isPermaLink="true">https://godruoyi.com/posts/zolplay-interview-failed/</guid><description>佐玩的面试挂了，我很伤心。</description><pubDate>Sat, 22 Jul 2023 14:18:53 GMT</pubDate><content:encoded>&lt;p&gt;佐玩的面试挂了，我很伤心。&lt;/p&gt;
&lt;p&gt;其实我有信心能过的，毕竟我对自己的 Laravel 还是很有自信。但是很遗憾，打脸了，我和预期可能还差很远。我开始想要是能进这家公司的话，我又能愉快的写 Laravel 了，又能在全是大佬的氛围下学到更多了。但现在看来我的自己好好规划下后面的路。&lt;/p&gt;
&lt;p&gt;我之所以想改变的原因是因为现在的工作我觉得我学不到太多的东西，或者说付出的时间和得到的收获并不匹配，我担心这样下去自己会丧失竞争力，所以才想着改变。而这短暂的尝试发现自己能力可能并没有达到自己预期的样子，我得静下心来想想怎么提升自己。&lt;/p&gt;
&lt;p&gt;首先要做的是花更多的时间学习英语。学好英语我后面才有更多的选择，才敢把选择的范围放大。现在的我基本每天都只在 Duolingo 打个卡，平时交流也是用 OpenAI 翻译，这一年下来虽说有所进步但一直没有什么质的变化；口语还是稀烂，英语口语课也不去上，也听不太懂别人说什么，更没有勇气参加英文面试。&lt;/p&gt;
&lt;p&gt;第二件我觉得需要做的就是从现有的工作中深入学习，挖到更多值得学习的内容。比如公司项目一个请求的生命周期，整体的部署架构，用到的中间件；如何模仿公司的项目架构自己搭建一个一样的；目前负责这一块儿的完整的业务逻辑。从这些总能学到更多我以前忽视的东西。既然没有其他更好的变化，我就得着保持目前工作的稳定。&lt;/p&gt;
&lt;p&gt;第三件需要做的是保持激情，时刻对新的东西充满热爱，敢于尝试。学习更多的新技术并尝试做点不同的 demo 出来。多输出，哪怕是在推上发一些小的看法和学习的心得。其次就是对中意的公司或者项目保持关注，多贡献自己的 PR 哪怕是改个单词。我觉得只要是对一个项目付出别人十倍的时间，并提交各种不同的 PR 或帮助解决不同的问题，总有一天你会被项目 OWNER 看到，说不定就会收到面试机会。这也算是参与开源的小私心吧。&lt;/p&gt;
&lt;p&gt;这世界充满了比你优秀的人，你只要一直保持认真，他就输了。&lt;/p&gt;
&lt;p&gt;21 Jul 2023 at 23:50&lt;/p&gt;</content:encoded></item><item><title>老家的变化</title><link>https://godruoyi.com/posts/the-changes-in-my-hometown/</link><guid isPermaLink="true">https://godruoyi.com/posts/the-changes-in-my-hometown/</guid><description>我的老家在贵州的一个山沟里，那里有四五个小村庄坐落在几座大山脚下，中间有一些田和一条小河，还有条水泥马路把它们串起来形成一个长约 3 公里的跑道；还有新安的路灯；我们家门口就有一盏路灯。</description><pubDate>Sun, 26 Mar 2023 12:48:51 GMT</pubDate><content:encoded>&lt;p&gt;我的老家在贵州的一个山沟里，那里有四五个小村庄坐落在几座大山脚下，中间有一些田和一条小河，还有条水泥马路把它们串起来形成一个长约 3 公里的跑道；还有新安的路灯；我们家门口就有一盏路灯。&lt;/p&gt;
&lt;p&gt;前几天接到老妈的电话，说邻居家有人过世了，我必须得回去一趟。平时村子里的红白喜事我多半是不会回去的，偶尔回去一次可能会听到「连波回来了啊，请到假了吗」的嘲笑声，这时我多半会拿出回家之前买的二十五一包的中华，化解尴尬的同时希望能堵住他的嘴。&lt;/p&gt;
&lt;p&gt;我不知道这规矩是什么时候开始的，但规矩就是不管谁家要办酒，周围的人每家都自少要回来一个人帮忙，哪怕你父母就在老家。「人家在上海的都回来了，你在重庆这么近不回来不像话」老妈在电话里这样给我说道。我也问过他们家里有人还大远处赶回来的人，得到的回答基本都是「那有什么办法呢」。&lt;/p&gt;
&lt;p&gt;我觉得办法肯定是有的，乡里人好面子这事情也是真的，大家回去的目的多是为了得到一个好名声，这些好名声通常会在长辈闲谈时体现出来：谁家办酒所有人都来了，王二好像又没回来，以后他家办酒大家都不去帮忙，&lt;strong&gt;让他抬丧都找不到人&lt;/strong&gt;。而说是帮忙，其实大部分是来打牌的，谁家打牌的人多，也侧面说明了他家的人脉广。主人还不要忘记照顾好打牌的人，热茶香烟，炭火宵夜都是要有的。&lt;/p&gt;
&lt;p&gt;噢还有女人们，她们总是最忙的人。她们会在厨房从白天忙到晚上，而帮忙的人们是不进厨房的。待到女人们把宵夜煮好用茶盆端到牌桌上，帮忙的人也不会停下手里的工作，而是一边看牌一边吃。吃完后碗多半你是找不到的，毕竟这东西放在牌桌上碍事，它应该放在其他任何顺手就能放的地方。&lt;/p&gt;
&lt;p&gt;我问过她们这样累吗，她们好像麻木了，世界就是这样的。&lt;/p&gt;
&lt;p&gt;老家越来越好了，可我第一次有了不愿回去的想法。&lt;/p&gt;</content:encoded></item><item><title>2022 年终总结</title><link>https://godruoyi.com/posts/review-of-2022/</link><guid isPermaLink="true">https://godruoyi.com/posts/review-of-2022/</guid><description>这一年，国家站在战争发起的一方，人们嘲笑被刺杀的首相，孕妇会因为新冠拒诊而流产，火灾会活活烧死逃生通道被焊死的孩子们，清零的大巴在夜间翻车，诸如等等，以至于忍无可忍的人们举起了白纸</description><pubDate>Wed, 04 Jan 2023 09:55:30 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;多年后我会向我女儿讲述这一年发生的各种事，就像已故的爷爷给我讲述「人民公社」时代的故事一样。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这一年，国家站在战争发起的一方，人们嘲笑被刺杀的首相，孕妇会因为新冠拒诊而流产，火灾会活活烧死逃生通道被焊死的孩子们，清零的大巴在夜间翻车，诸如等等，以至于忍无可忍的人们举起了白纸……&lt;/p&gt;
&lt;p&gt;但就具体的个体而言，即使这世界充满了太多的糟粕，我的故事还是时而平淡、时而惊喜的往前走着。&lt;/p&gt;
&lt;h2 id=&quot;life&quot;&gt;Life&lt;/h2&gt;
&lt;p&gt;惊喜的是年初的时候老婆怀孕了，我俩有了共同的希望都在小心翼翼的呵护着；我很庆幸因为全职远程的缘故，可以有更多的时间陪着她，可以一起看电视、一起散步、一起看我打游戏。由于散步的地方多了，以至于三个月后每个地方都能找到她孕吐时期留下的残留物。度过了艰难的孕早期，我们在七月离开了重庆前往贵州的老家，在那里我们呆了一个多月。我太喜欢在老家的那段时间了；房子后有山，可以去找点什么吃的，荔枝、板栗、猕猴桃；村前有河，和钱红们抓的鱼熬的汤老婆现在都在说好吃；还有那承包养殖小龙虾不成变成的耦田，每个从城里回去的姑娘总会来护两朵荷花在胸前拍照留恋；晚饭后还可以围绕村村通的水泥路溜达一圈，要是时间凑巧的话，西下的太阳散发出的肉眼可见的光，照在马路边栏杆旁农户种的茄子黄瓜和手拉着手散步的你们身上，又有什么比这样的日子更值得向往和留恋呢。况且在农村也能把钱挣了。&lt;/p&gt;
&lt;p&gt;年底的时候老婆给我生了个闺女，从此我变成一个有软肋的人了。在生产的前几天，老婆阳了；住院后，我也阳了。所以那两天的晚上，是一个待产的孕妇照顾她那发烧发冷的丈夫，而她的丈夫在白天醒来后才会软绵绵的端水打饭；好在这个妈妈足够坚强，甚至于不打无痛也能在一个半小时左右顺下我们的闺女。当她发消息告诉我：「生了，是个妹妹，没打无痛」时；我眼泪都流出来了，一半是因为激动，一半是因为心痛。&lt;/p&gt;
&lt;p&gt;有了闺女后，我开始担心这个黑暗吃人的世界，我担心你墙上的影子看多了，就以为它原本就是那样。父本淡泊，该如何保护你，让你健康快乐的长大呢？有时候甚至在想：学好外语吧闺女，以后逃离这个丑陋的国度，顺便带上父母一起离开；有时候又想，世界本来就是这样，不能让人处处满意，当你遇到什么不开心的事时，试着想想它好的一面，乐观的看待这个世界会给我们带来更多的快乐，毕竟生活是自己的，快乐也是自己的。&lt;/p&gt;
&lt;h2 id=&quot;something-else&quot;&gt;Something Else&lt;/h2&gt;
&lt;p&gt;去年的这个时候我还在新公司 OnBoarding，一晃我已经全职远程一年啦。我很喜欢这样的工作形式，我感觉所有的时间都是自己的，可以早上十点开始上班，中午躺在床上睡个自然醒的午觉，醒来后阅读下 Slack 同事们的讨论，单测写完后提两个 PR。有时候一张 Ticket 要几天或者几个月才能完成，当然不是因为偷懒呢，更多的是需要等待别的团队的同事先完成其他的任务。但就技术而言，我这一年并没有多大的提升，因为他们会尽量把要做的任务拆分为一个个可独立开发和测试的最小单元，并且非常完整及详细的描述这个任务应该怎么做。这导致你在做这个任务的时候接触的面十分受限。我猜这也是为什么连 Senior Engineer 离职也不需要写交接文档的原因。不过就像同事说的那样，还可以在其他软技能如开发流程、组件的使用(AWS/DataDog/Jira/GoogleWorkspace)、公司架构的摸索(MonoRepo/Thrift/gRPC)、英语口语、邮件沟通等方面找到弥补的。&lt;/p&gt;
&lt;p&gt;今年下半年开始我一直在 Duolingo 上学习英语，到现在已经坚持 180 天了，虽不太确定这样会有多大的效果，但起码 Twitter 上的英语推我大抵是看得懂了，在和同事沟通时也不再像年初那样每次都要用 DeepL 翻译了，不过口语和听力还是很差，每次站会看着同事们用英语你来我去的交流时，我好想把他的英语能力复制一份到我脑里。对于我们开发工程师来说，我真的建议每一个人学好英语，然后把你的雇主从某一个具体的城市扩大到全国甚至全世界。当你把你的工作目标放到全世界后，哪里又有什么 35 岁危机呢？况且从  Linkedin  上的招聘来看，只要你英语达到能正常交流的程度，技术匹配，薪资都能在 $6K+ 左右，我希望我能在五年内达到这一目标。&lt;/p&gt;
&lt;h2 id=&quot;last&quot;&gt;Last&lt;/h2&gt;
&lt;p&gt;写到这儿 2022 年就算过去了，也不再展望来年，只愿每一位身体健康。&lt;/p&gt;
&lt;p&gt;2023-01-04 17:31&lt;/p&gt;</content:encoded></item><item><title>一九三九年春在太原</title><link>https://godruoyi.com/posts/in-the-spring-of-1939-in-taiyuan/</link><guid isPermaLink="true">https://godruoyi.com/posts/in-the-spring-of-1939-in-taiyuan/</guid><description>只有时候，从野外吹来的风，使你嗅到一点着自由的气息。很细致，很新鲜，很温暖，并且很有生气。在这种感觉里，你可以想到，黔南已解封了，草已经发芽了，戏已经演完了</description><pubDate>Mon, 19 Sep 2022 12:48:32 GMT</pubDate><content:encoded>&lt;p&gt;自由被关在城外了。&lt;/p&gt;
&lt;p&gt;只有时候，从野外吹来的风，使你嗅到一点着自由的气息。很细致，很新鲜，很温暖，并且很有生气。在这种感觉里，你可以想到，黔南已解封了，草已经发芽了，戏已经演完了！&lt;/p&gt;
&lt;p&gt;但我却出不了门。&lt;/p&gt;
&lt;p&gt;一整天，我所看见的，是白色的墙，白色的土，和穿着白色衣裳在街头守望的兵。我气闷而且窒息。连行动也被强度的限制着了。出门。要通行证；到街上去，要健康证。并且七点钟已经开始戒严了。为了免掉那些白色同志对你采取攻击式，端起枪来，并且对准你的脑袋，我只好一个入关在屋子里。&lt;/p&gt;
&lt;p&gt;而我的屋子，又恰巧临着街。一整夜，我全听见「这是为你好」的声音，这在深夜里，特别加重了恐怖的氛围。&lt;/p&gt;</content:encoded></item><item><title>2021 年终总结</title><link>https://godruoyi.com/posts/review-2021/</link><guid isPermaLink="true">https://godruoyi.com/posts/review-2021/</guid><description>不过这只是属于我自己的看法罢了，每个位置都能发挥他的价值，有些鸟是关不住的。</description><pubDate>Fri, 28 Jan 2022 10:31:19 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;劝君更尽一杯酒，西出阳关有故人。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;11 月底从公司离职后，我变成了一个自由人；这个时间点离职很尴尬，一是距离过年还有两个月，二是不太好找工作；果不其然半个月下来面试都垮掉了，想去的诸如腾讯、字节、吉利等大一点的公司，都失败了。今年年初的时候其实已拿到两家的 offer，只是嫌工资低大小周没去。如今时过境迁，养尊处优的过了大半年后处处碰壁，才晓得在重庆这个互联网行业不太景气的城市，选择的机会是越来越小。&lt;/p&gt;
&lt;p&gt;在公司的最后几个月里，几乎每个月都要吃一两顿散伙饭，你可以从这条 &lt;a href=&quot;https://twitter.com/godruoyi/status/1363702719513468930&quot;&gt;Twitter 时间线&lt;/a&gt; 看出个大概。而未离职的大家刷题的刷题，考公的考公，也有朋友今年成功上岸的，甚是厉害。我对于进体制内一直不感兴趣；一是我的性格不适合这样的工作，就算考过可能连政审也通不过，二是钱太少。当然最重要的是我害怕就如大学刚毕业时进入体制内的朋友说的那样：“我这辈子就这样了！”。不过这只是属于我自己的看法罢了，每个位置都能发挥他的价值，有些鸟是关不住的；从朋友圈看的话🐶，也有朋友在公务员这个位置干的风生水起的。只是我在这个岗位上看不到我的价值，你没法说服我同意你的看法，我也没法说服你认同我的看法，相互尊重就好啦。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202201/28/FpL2zEiuWL423r9NHRG1mAr3dOQUbi0TrfeijGaD.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I have to remind myself that some birds aren’t meant to be caged. Their feathers are just too bright. And when they fly away, the part of you that knows it was a sin to lock them up does rejoice.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;今年还想反思的就是自己的表达能力，很多时候心里是这样想的，说出来就变成另外一种意思了，并且自己还没意识到说错了。因为这个问题，没少跟媳妇吵架，也因为这个问题，没交到几个珍惜的朋友。我现在都还后悔几年前因为自己表达能力的问题失去了一个知心好友的机会，虽然现在我们也算是朋友，但我感觉始终有隔阂再也难成长起来了。&lt;/p&gt;
&lt;p&gt;去年定的目标今年看来就勉强完成了锻炼身体这一项，从 iWatch 的统计看的话，今年大部分时间都合上了三环记录。下图是我今年每月运动次数统计（绝大部分都是走路上班）。虽然自我感觉良好，但身体是真的大不如前了；现在一个普通的感冒靠自愈一个星期也好不了，还得吃药做雾化。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202201/28/njzO0FH1twfHjeopx0dknkc9ZTP4JKcwY4Hky5xm.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;p&gt;还有件值得高兴的事是我终于有自己的车了。原本这辆车是超预算了的，为了诱惑老婆让我买这辆，我天天在她面前用迈克唱 Dangerous 的调子唱&lt;a href=&quot;https://xingyue.geely.com/xingyuel&quot;&gt;星越 L&lt;/a&gt; 星越 L 才得偿如愿，接下来就可以开车去兜风咯。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202201/28/SBxXIcpGSbMBGdlCj5SdV9H9Cty03ABsfg0D0k15.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;p&gt;年底的时候我终于找到了我曾梦想的工作，Full Time Remote Work，做到了如推特网友说的那样，在国内拿美刀，希望新的一年我能做出一些有意思的事情。&lt;/p&gt;
&lt;h2 id=&quot;todo&quot;&gt;TODO&lt;/h2&gt;
&lt;p&gt;参考我在推特上已经发表的 &lt;a href=&quot;d&quot;&gt;Keywords that describe 2022 for me&lt;/a&gt; 为来年细化一个目标吧。&lt;/p&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;&gt; Golang&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;&gt; Rust (&lt;a href=&quot;https://doc.rust-lang.org/book/#the-rust-programming-language&quot;&gt;The Rust Programming Language&lt;/a&gt; &amp;#x26; &lt;a href=&quot;https://doc.rust-lang.org/stable/rust-by-example/&quot;&gt;Rust By Example&lt;/a&gt; &amp;#x26; Write a Bitcask)&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;&gt; Kubernetes&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;&gt; Blog (At least one article per month)&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;&gt; Books&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;&gt; Life&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;&gt; A Child&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;&gt; English&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;&gt; Write A Blog With Laravel Best Practices&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;往年总结&quot;&gt;往年总结&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/2020-year-end-review&quot;&gt;2020 年终总结&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/continue-refueling-in-2019&quot;&gt;2019 年度总结&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;他们的-2021&quot;&gt;他们的 2021&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;潘鸟的 2021 &lt;a href=&quot;https://sppan24.cn/archives/2021&quot;&gt;https://sppan24.cn/archives/2021&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://freek.dev/2155-a-recap-of-2021&quot;&gt;A recap of 2021 - Freek Van der Herten’s blog on PHP, Laravel and JavaScript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/saveweb/review-2021&quot;&gt;Review 2021 Awesome Lists&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>刷抖音</title><link>https://godruoyi.com/posts/tiktok/</link><guid isPermaLink="true">https://godruoyi.com/posts/tiktok/</guid><description>从前听人说： 中国人人人具有三种博士的资格：拿筷子博士、吃瓜子博士、刷抖音博士。</description><pubDate>Fri, 12 Nov 2021 03:10:04 GMT</pubDate><content:encoded>&lt;p&gt;从前听人说： 中国人人人具有三种博士的资格：拿筷子博士、吃瓜子博士、刷抖音博士。&lt;/p&gt;
&lt;p&gt;而三种技术中最进步最发达的，要算刷抖音。近来抖音大王的畅销，便是其老大的证据。据关心此事的人说，抖音大王一类的视频 APP，最近市上流行的有许多种类。最初是某大公司”用推荐算法” 创制的，后来有什么“某手公司” 、“某红书公司” ……等种种出品陆续产出。到现在差不多无论哪个穷乡僻处的小产品上，都有几 TB 的视频陈列而倾销着了。现代中国人的精通刷抖音术，由此盖可想见。&lt;/p&gt;
&lt;p&gt;我对于此道，一向非常短拙，说出来有伤于中国人的体面，但对自家人不妨谈谈。我从来不曾自动地找求某音来刷。但到人家作客，受人劝诱时; 或者在酒席上、重庆的茶楼上，也会不由自主的把手机拿出来，而当发现一个消息也没有后，也便点开抖音滑了起来。我必须注意选择，选那较短、搞笑、而或乡村田野的视频，而对于那些奇装异服的、好嗨哟一类的，我多半是拒绝的。而每每到了饭点，或被人惊扰时，才发现已经过了半小时。碰到这种时候，我就下个决心，从此戒绝抖音。戒绝之法，大抵是站起身来，喝一口茶漱一漱口，点起一支香烟，或者把手机推开些，以示不再对它发生关系。然而过了几分钟，与别人谈了几句话，不知不觉之间，手机就已经在手上了。等到自己觉察破戒的时候，往往已经看了好几个视频，而这时候会想，不要紧的、在刷几分钟，后面在戒的了。这样，吃了非戒不可，戒了非吃不可；吃而复戒，戒而复吃，我为它受尽苦痛。这使我现在想起了抖音觉得害怕。&lt;/p&gt;
&lt;p&gt;但我看别人，精通此技的很多。我以为中国人的三种博士才能中，刷抖音的才能最可叹佩。常见闲散的少爷们，一只手指间夹着一支香烟，一只手握着一个手机，烟雾缭绕，且滑且笑，且笑且走。从容自由，真是「交关写意」！他们精心的挑选自己喜欢的视频，遇到不满意的，只消“格”地一滑，再把嘴里的烟“啪”地一吐，而在那里嚼食着新的食粮了。那手指真象一具精巧灵敏的机器，不绝地塞进视频去，不绝地咀嚼着，……全不费力，可以永无罢休。&lt;/p&gt;
&lt;p&gt;发明刷抖音的人，真是一个了不起的天才！这是一种最有效的“消闲”法。要“消磨岁月”，除了抽鸦片以外，没有比刷抖音更好的方法了。&lt;/p&gt;
&lt;p&gt;在世间一切事物之中，想来想去，只有抖音。所以我说发明刷抖音的人是了不起的天才。而能尽量地享用抖音的中国人，在消闲一道上，真是了不起的积极的实行家！试看理发店、地铁上抖音大王的畅销，试听小面馆里、公交车上、家庭中满屋的流行音乐，便可想见中国人在此之道上消磨去的时间，每年统计起来为数一定可惊。将来此道发展起来，恐怕是全中国也可消灭在朗朗笑声中呢。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;我本来见抖音害怕，写到这里，觉得更加害怕了。&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://baike.baidu.com/item/%E5%90%83%E7%93%9C%E5%AD%90/698877&quot;&gt;《吃瓜子》&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>一包烟</title><link>https://godruoyi.com/posts/a-pack-of-cigarettes/</link><guid isPermaLink="true">https://godruoyi.com/posts/a-pack-of-cigarettes/</guid><description>读小学的时候我有机会通过自己的劳动获得一包烟，但我拒绝了。</description><pubDate>Fri, 29 Oct 2021 07:45:49 GMT</pubDate><content:encoded>&lt;p&gt;读小学的时候我有机会通过自己的劳动获得一包烟，但我拒绝了。&lt;/p&gt;
&lt;p&gt;那是小学三四年级，正值播种，我和小伙伴叫鸡一起去大田坎排秧。什么是排秧呢？排秧不及栽秧，排秧是把牙签长的秧苗一根根的插到细田里；而栽秧要等其发芽长大后，再一根根的把它们扯起来，捆成一把一把的甩在田里，再三五两根的插在田中。排秧这活路要是用一个大劳力来做的话，就显得大材小用了，所以种田较多的农户一般都是喊些孩子来帮忙。&lt;/p&gt;
&lt;p&gt;我们去了一天，还在农户家睡了一晚。忘记了是开工前还是收工后，农户一人给我们发了个咸鸭蛋，还有一包烟。农户把烟递过来时，我一边摆手一边说：我不抽烟。2002 年，应该是我爸抽的那种 1 块 5 一包的黄果树吧。农户问了一遍后也没有勉强，就递给了下一个小伙伴，看着小伙伴都收下那包烟后，我好像感觉我做错了，我内心甚至幻想农户再来问我一次，我就能得到这包烟了，虽然我不抽，但我可以带回去给爸啊。通过自己的劳动换来的礼物，爸爸肯定会喜欢的。&lt;/p&gt;
&lt;p&gt;但幻想的场景并没有出现，在回去的路上，伙伴就说我为什么不接下那包烟，拿回去给你爸爸抽多好。本来就有点后悔的我更加抑郁了。回到家后的第二天，我在菜土里跟爸爸说了这件事：我真该给你拿回来的。没想到我说完反被老爸骂了：你不抽烟就不该拿别人的，不要以为别个都拿了你不拿你就做错了，老子还买不起一包烟让你在外面拿回来吗。被骂完后我的抑郁一扫而空，又高兴得是一个孩子了。&lt;/p&gt;
&lt;p&gt;这么多年过去了，我还对这件事印象深刻；虽然老爸没读过什么书，只知务农和种烟，但他却从来没让我的三观走歪过。&lt;/p&gt;</content:encoded></item><item><title>我的阅读清单</title><link>https://godruoyi.com/posts/my-books/</link><guid isPermaLink="true">https://godruoyi.com/posts/my-books/</guid><description>我的天空里没有太阳，全是黑暗，但并不黑，因为有东西代替了太阳</description><pubDate>Mon, 18 Oct 2021 08:11:09 GMT</pubDate><content:encoded>&lt;p&gt;记录我目前正在看的或已经看完的部分书籍(纸质)，并记录 &lt;del&gt;当时&lt;/del&gt; （现在觉得当时）可能存在的感想。&lt;/p&gt;
&lt;h2 id=&quot;2023&quot;&gt;2023&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;《小米创业思考》&lt;/li&gt;
&lt;li&gt;《WhatIf》&lt;/li&gt;
&lt;li&gt;《一只特立独行的猪》&lt;/li&gt;
&lt;li&gt;《万寿寺》&lt;/li&gt;
&lt;li&gt;《微服务架构设计模式》&lt;/li&gt;
&lt;li&gt;《性能之巅》&lt;/li&gt;
&lt;li&gt;《凤凰架构》&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;2022&quot;&gt;2022&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;《白鹿原》&lt;/li&gt;
&lt;li&gt;《邓小平时代》&lt;/li&gt;
&lt;li&gt;《娱乐至死》&lt;/li&gt;
&lt;li&gt;《松本行弘的程序世界》&lt;/li&gt;
&lt;li&gt;《精通正则表达式》&lt;/li&gt;
&lt;li&gt;《黑客与画家》&lt;/li&gt;
&lt;li&gt;《我的二本学生》&lt;/li&gt;
&lt;li&gt;《草房子》&lt;/li&gt;
&lt;li&gt;《窗边的小豆豆》&lt;/li&gt;
&lt;li&gt;《苏菲的世界》&lt;/li&gt;
&lt;li&gt;《人性的弱点》&lt;/li&gt;
&lt;li&gt;《自己动手实现Lua》&lt;/li&gt;
&lt;li&gt;《未来世界的幸存者》&lt;/li&gt;
&lt;li&gt;《乌合之众》&lt;/li&gt;
&lt;li&gt;《结网》&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;2021&quot;&gt;2021&lt;/h2&gt;
&lt;h3 id=&quot;已完成&quot;&gt;已完成&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;《图解TCP/IP》&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《Linux程序设计第4版》&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《CODE（编码）》
磁生电，电生磁，方生万物；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《态度》
看完以至于我在做一些特别 LOW 的事情时都在想：要是吴军老师来做这件事的话肯定不会这样做。然后我就换一种方式了，虽然可能会更差。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《动物庄园》
一群猪的革命。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《两周自制脚本语言》
设计还行，但有点过时了，14年出版的书了，现在设计脚本语言的话可看「自己动手实现Lua」。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《Go 语言专家编程》
名字很浮夸，但内容却是很实在，值得一看。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《Go 语言高级编程》&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《数据密集型应用设计》&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《Redis 设计与实现》
虽然书中使用的 Redis 版本比较久，但如书名一样真真实实的讲解了各种数据结构的细节实现。期待作者的新书。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《黄金时代》
第一次读王小波的书，感觉还可，后面在买点来读读。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《四个春天》
这两年最喜欢的同名电影的衍生物，超合我胃。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《浪潮之巅（上下册）》
版本买低了，很多预言已经成真。推荐买新版，书很不错。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《刀剑神域01》
没动漫好看，动漫看起来更刺激。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《文城》
不是很好看，你要是喜欢余华老师的话还是可以一读，见&lt;a href=&quot;https://godruoyi.com/posts/wencheng&quot;&gt;读文城有感&lt;/a&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《读库2103》
多抓鱼拼单买的，其中「我在北京送外卖」和「我在上海开出租」是很不错的。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《三体》
我是一直努力向上的虫子，见&lt;a href=&quot;https://godruoyi.com/posts/three-body&quot;&gt;读三体有感&lt;/a&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;2020&quot;&gt;2020&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;《Go 语言设计与实现》
作者是一个大神，写的很深奥，一直「想」通过通俗易懂的文字翻译一篇他的博客。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《深入理解计算机系统》
神书，推荐每一个从业者阅读。我看了两年，目前看到第三章。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《每天5分钟玩转Kubernetes》
每天玩5分钟的话，还是玩不转，建供参考。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《计算机是怎么跑起来的》&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《程序是怎么跑起来的》&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《网络是怎么链接的》
这三部曲比较通熟易懂，适合如何；但一些细节没有解释清楚。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《且听风烟》
且当作一个小故事来读吧，没有什么特别的营养。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《通往奴役之路》
读不下去了，准备换本。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《硅谷革命》
回忆苹果公司的疯狂往事，见读硅谷革命有感。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《在细雨中呼喊》&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《第七天》
那是另一个天堂，在人死后。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《码农翻身》
用故事的方式讲技术最好的书籍，虽然细节不太关注，但原理给你说得明明白白。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《重构改善代码的既有设计》
搁浅中。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《MySQL技术内幕，InnoDB 存储引擎》
搁浅中。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;2019&quot;&gt;2019&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;《你不知道的 JavaScript（上/中卷）》&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《程序员的自我修养 链接、装载与库》
一书了解程序的本质。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《计算机网络自顶向下方法》&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《程序员修炼之道从小工到专家》
初读太过理论了，可能当时工作年限较小，很多地方觉得无所谓。应抽时间再读一遍。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《许三观卖血记》
究竟是谁，在逼我卖血？&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;2016&quot;&gt;2016&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;《MySQL必知必会》
MySQL 入门必备，不过现在试图/存储过程等概念用的少了，可做了解。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《JavaScript 高级程序设计第三版》
JavaScript 圣经级书籍，讲的很全面，可用作系统学习 JS 的参考书。未看完。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《Head First 设计模式》
设计比较好的入门书籍，有一定工作年限在看更好理解。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《深入PHP：面向对象、模式与实践（第3版）》
面向对象程序设计，内容一般般。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《PHP与MySQL程序设计（第4版）》
入门书籍，内容较为简单，学到的东西不多。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《Thinking in Java》
当初学 Java 时买的，后来转 PHP 就送给我朋友了；朋友后来又转前端了，下次遇到他我要让他还我，我还没看呢。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;2011&quot;&gt;2011&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;《白夜行》
我的天空里没有太阳，全是黑暗，但并不黑，因为有东西代替了太阳。看完《平凡的世界》再来看这部，我心中的信念更强烈了，没有什么能够阻挡，我对生活的向往，哪怕是我在工地搬砖，我也能够看到我的太阳。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《人生》
生活总是这样，不能让人处处都满意。但我们还要热情地活下去。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《平凡的世界》
有生以来影响我最大的一本书，四年大学最有价值的事就是在同学那里借了五十块钱在地摊上买下这本书。他在我最需要的时间里，告诉我不管处于什么样的环境，不管以后是在工地搬砖还是深坑挖煤，在面对生活时依然保持乐观继续学习。
看着书时我大概写了 20 来篇日记，直到晓霞走时我竟完全不敢看下去，我抑郁了好几天，我不知道少平听到这消息会怎么办。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《活着》
这是第一部读余华老师的作品，酣暢淋漓，简直一口气就能读完。好像活着就是为了活着而已，若大的苦难，皆可乐观。余华老师现在的作品没有能超越这部的。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《边城》
讲些什么忘记了，看来印象不深，只记得有个小女孩有个老人，插画很美。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《霍乱时期的爱情》
说实话我觉得这书一般般，一位享受完荣华富贵的女子在年老丈夫死后，回头选择了最求自己一生的人。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《忏悔录》
不知道写的什么了，只看了上部，没警醒我需要忏悔的地方；现在在家垫台灯。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《悲伤逆流成河》
还是被结局所震撼了，居然全都写死了。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《围城》
围来围去大可不必，若你吃到一颗好吃的鸡蛋，你会去寻找下蛋的母鸡吗？&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《穆斯林的葬礼》
其他忘了，只记得最好的生日礼物是：「海上生明月，天涯共此时」。还有一句：随珠和璧，明月清风。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;《追风筝的人》
说实话感觉这书没那么神，可能是我当时不知道在那小巷子里发生的是什么事吧。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;2008&quot;&gt;2008&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;《钢铁是怎样炼成的》
高一时期期末考试第一名学校的奖励，后来把他赠给表弟了，不知道他是否看完。那时冬妮娅是我心中唯一的女神。&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>讲个鬼故事</title><link>https://godruoyi.com/posts/the-ghost/</link><guid isPermaLink="true">https://godruoyi.com/posts/the-ghost/</guid><description>鬼到底是有的是没有的？传说上有人见过，还跟鬼说过话，还有人在后边被鬼追赶过，吊死鬼一见了人就贴在墙上。但没有一个人捉住一个鬼给大家看看。</description><pubDate>Mon, 11 Oct 2021 02:34:06 GMT</pubDate><content:encoded>&lt;p&gt;鬼到底是有的是没有的？传说上有人见过，还跟鬼说过话，还有人在后边被鬼追赶过，吊死鬼一见了人就贴在墙上。但没有一个人捉住一个鬼给大家看看。&lt;/p&gt;
&lt;p&gt;连波先生讲了他看见过鬼的故事给大家听：&lt;/p&gt;
&lt;p&gt;“那是在丝绵……” 连波先生说，“十六年前……”&lt;/p&gt;
&lt;p&gt;那时候连波先生刚上初中，下午放学后要是带的米不够做饭了，连波先生就会回家去吃。两地相隔 4-5 里路，最快要走四五十分钟。几里路不算远，但必得经过一处山坳，山脚山顶都无人家，传言山脚小溪旁的白橡树上还有人在哪里上过吊，如今枯了的树干更显得荒凉。&lt;/p&gt;
&lt;p&gt;连波先生有时候吃饭吃的晚了，六七点才回学校的事也常有。有一天连波先生就回得很晚，天空模模糊糊，能看见行人，但也不至于太黑。&lt;/p&gt;
&lt;p&gt;连波先生向着归路走得很起劲时，往山坡处一看，远远有一个白影。&lt;/p&gt;
&lt;p&gt;连波先生不相信鬼的，乡里有人谈起鬼时，连波先生常常不屑一顾，只认为那是他自己产生的幻觉，所以对于这个白影，连波先生是不怕的。&lt;/p&gt;
&lt;p&gt;走了不几步，那远处的白影没有了，在看突然又有了，走了不几步，那远处的白影没有了，正和鬼一样，鬼不就是变幻无常的吗？&lt;/p&gt;
&lt;p&gt;连波先生有点踌躇了，到底向前走呢？还是回过头来走，但去学校的路就此一条了。&lt;/p&gt;
&lt;p&gt;连波先生仍是向前走，到底要看一看鬼是什么样，虽然那时候也怕了。&lt;/p&gt;
&lt;p&gt;连波先生那时候有了一双回力鞋，鞋底还算厚实。连波先生决心快步走过去，等走到一公里处的人家户去，鬼就不敢追来了，鬼是不敢去人多的地方的。&lt;/p&gt;
&lt;p&gt;连波先生就开始大步走了起来，大腿和大脑向着两个不同的方向。&lt;/p&gt;
&lt;p&gt;连波先生走到鬼消失的地方时，那路边蹲着的白影噢的一声叫起来，随着就站起来，连波先生定眼看去，他却是个人。&lt;/p&gt;
&lt;p&gt;连波先生说在他走过那段路时，他是害怕的，好像若那东西在上面布置好陷阱等他，自己反而会遭殃的，所以走的很快。&lt;/p&gt;
&lt;p&gt;原来是个男子在路边蹲着屙屎。&lt;/p&gt;
&lt;p&gt;连波先生说到这里就笑了起来。&lt;/p&gt;
&lt;p&gt;“鬼也是要方便的，一方便就立刻变成人了。”&lt;/p&gt;
&lt;p&gt;我想，倘若鬼是常常能遗矢也是好的，因为给了他一个作人的机会。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;本文&lt;del&gt;改编&lt;/del&gt;自「萧红 - 回忆鲁迅先生（节选）」。参考 &lt;a href=&quot;https://baike.baidu.com/item/%E4%B8%89%E9%81%BA%E7%9F%A2/4409312&quot;&gt;三遗矢&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded></item><item><title>文城</title><link>https://godruoyi.com/posts/wencheng/</link><guid isPermaLink="true">https://godruoyi.com/posts/wencheng/</guid><description>人生就是自己的往事和他人的序章，时代的洪流推着每个人做出各自的选择。这是一个荒蛮的年代，结束的尚未结束，开始的尚未开始。</description><pubDate>Thu, 23 Sep 2021 05:50:30 GMT</pubDate><content:encoded>&lt;p&gt;最近比较颓，晚上地下城游戏疲劳用完后，我躺在沙发上看起了仅剩一点的文城。&lt;/p&gt;
&lt;p&gt;在看这本书前，我听过一期梁文道和余华老师的播客，从播客的内容上知道，这本书作者没有想要表达什么，也没有想要反应什么，只是叙事。少了写「活着」和「兄弟」时需要考虑反映什么，讽刺什么的压力。这本书在叙起事来， 一如既往的游刃有余，或者可以说是听马行空。&lt;/p&gt;
&lt;p&gt;书分为前后两半部分，前半部分主要讲林祥福；看完这部分后，总觉得差点什么，对结局也不是很满意，还有很多人物最后的去留也没交代清楚就草草结尾。后半部主要是讲小美，这部分从小美的视角把所有的事情都交待出来，看着看着感觉是一个爱情故事。直到小美冻死在雪地前，我都抱有一丝她见到了林祥福抱着女儿对她笑的场景；但很遗憾没有，作者不带一点儿拖泥带水的把她写死了，也有可能她选择冻死来逃避，一边是女儿和自己可能向往的生活，一边是自己的丈夫。林祥福看着小美的尸体从眼前抬过，死的人看不见，活的人也看不见；林祥福苦苦寻找了十多年的小美，直至他死后，也没能见上一面，他们之间隔的最近的距离，是田二兄弟抬着林祥福的尸体回家时，在小美的坟前歇息的距离。不到一米，但两个死了的人，看不见，活着的人，也看不见。余华老师真是把苦难和悲情写的淋淋尽致。&lt;/p&gt;
&lt;p&gt;你要是还没看，就把它当作一个故事看就行了。不然要是曹雪芹老师醒过来，你还要问她什么时候出一部超越「红楼梦」的作品吗？其他莫多想，打个 7.2 分。&lt;/p&gt;
&lt;p&gt;推荐你收听下面两期播客：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.xiaoyuzhoufm.com/episode/60dc5cdce13dd0c6808ab6e6&quot;&gt;播客：余华×梁文道，当代作家的责任是什么？ - 小宇宙&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.xiaoyuzhoufm.com/episode/6059250654fee091656154f2?s=eyJ1IjoiNWU4ODE2NWJlMDM2YzQzMTc5NTIyN2Y5In0%3D%0A&quot;&gt;播客：余华《文城》，苦字有几种写法？- 小宇宙&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>说一说公众号</title><link>https://godruoyi.com/posts/talk-about-the-official-account/</link><guid isPermaLink="true">https://godruoyi.com/posts/talk-about-the-official-account/</guid><description>我不相信我错过的东西真的就那么有价值，我也不相信真的能用碎片时间从工号上学到什么新知识</description><pubDate>Thu, 19 Aug 2021 05:37:06 GMT</pubDate><content:encoded>&lt;p&gt;很久就想写一篇公众号相关的感想了，刚好在推特上刷到了&lt;a href=&quot;https://twitter.com/yihong0618/status/1428176270446059524&quot;&gt;这条时间线&lt;/a&gt;，我就来简单写写；如果因此得罪了大佬们，请勿怪罪。因为我只是想谈谈写作这件事。&lt;/p&gt;
&lt;p&gt;最近在学 Go，关注了很多国内的大佬，时间久了，就会发现这个社区太不健康了。有一些成长期很优质的博主，慢慢的就都往公号去了。你若翻看他们以前写的博客文章，还是能学到很多东西的。而现在好好的博客需要关注公号才能继续查看，公号的内容还要等几天才&lt;strong&gt;有可能&lt;/strong&gt;同步到博客。而对于文章质量，一开大佬们可能花一两天写一篇文章，这是超级负责的，往往这样的文章也是有保证的。&lt;/p&gt;
&lt;p&gt;慢慢的，你会发现文章质量越来越低，都是诸如什么你不知道系列，脱坑系列，xxx 技巧，吊打面试官，xxx 一篇就够了等等。而这类文章，看完后或许会豁然开朗，或许会轻哦一声，当时以为看到的知识都会了，而过几天就全忘了，因为这样的文章除了增加阅读量外，能吸收到的东西太少了。而也是这类文章，能吸引越来越多的关注，越来越多的关注促使作者更多写这类文章，周而复始，生生不息。再往后，作者开始转载别的工号的文章，开始相互推荐，开始接广告，朋友圈里极客时间的广告满天飞；而另人伤心的是不止一个作者这样，更另人伤心的是读者们都好像觉得这很正常。所以是作者们变了吗？或许是吧，也或许是环境变了吧。&lt;/p&gt;
&lt;p&gt;我不喜欢被这样的事情所影响，所以从今年初开始，我取关了几乎所有的技术号，我不相信我错过的东西真的就那么有价值，我也不相信真的能用碎片时间从工号上学到什么新知识；如果要说真能学到，那应该是能把深奥的原理像讲故事一样讲出来的公众号，这方面我只知道 &lt;a href=&quot;https://mp.weixin.qq.com/s/Cpxiq7EuMtfAC8f4BmVIOQ&quot;&gt;2018 年前的码农翻身&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;我在社区认识一个很不错的小伙，还相赠了书，他送了我一本「Go 语言编程之旅」，我送了他一本 &lt;a href=&quot;https://book.douban.com/subject/35144587/&quot;&gt;Go 专家编程&lt;/a&gt;。刚结识的时候，他正开了一个新的工众号，准备开始技术写作，而他今年的目标就是，公号有 500 个粉丝。现在看来，估计已经达到了，不得不说还是很腻害。我并不是想说在公号上写作有什么不好，而是很纳闷，我们写作到底是为了什么，是为了涨粉为了出名，还是写作本身？若一开始就是想好了靠写作公号张粉，涨粉后变现；那写作本身反到变的不重要了。因为涨粉的方式有很多，好的内容，亦或好的技巧；如公号互推、相互装载、各种微信群等。当你把技巧运用的越来越熟练后，你就偏离写作越来越远了。但若你只是把粉丝数作为衡量自己文章质量的一个标准，那也是很令人赞叹的；令人伤心的是，就目前的情况看，大家都更愿意去提升涨粉的技巧，而不是文章内容本身。&lt;/p&gt;
&lt;p&gt;还有一个大佬，相信国内的 Go 语言开发者都看过他的博客，其中的「Go 语言设计与实现」系列更是经典，他就是 &lt;a href=&quot;https://draveness.me/&quot;&gt;面向信仰编程&lt;/a&gt; 的作者 &lt;a href=&quot;https://twitter.com/draven0xff&quot;&gt;Draven&lt;/a&gt;。如果你看过他的博客文章，你会发现每一篇都很硬核，有理有据，这才是技术博客应有的样子。他也有同名的公众号，翻翻历史你会发现里面一篇水文一篇广告都没有。更让人愉悦的事，作者先是有了博客，博客人气很旺，文章老是被转载后才开通的公号（参考&lt;a href=&quot;https://draveness.me/2019-summary/#%E7%A4%BE%E4%BA%A4%E7%BD%91%E7%BB%9C&quot;&gt;2019 年总结&lt;/a&gt;）；更难得可贵的是，即使作者公众号粉丝数超过 2w（参考&lt;a href=&quot;https://draveness.me/2020-summary/#%E7%A4%BE%E4%BA%A4%E7%BD%91%E7%BB%9C&quot;&gt;2020 年总结&lt;/a&gt;），他依然保持初心，从不恰饭，要知道如果按一块钱一个阅读量算，随随便便一篇水文，也是好几千啊。保持初心，不忘始终，这才是值得我们学习的最佳案例呀。&lt;/p&gt;
&lt;p&gt;说了这么多，并不是说用公众号写作的就不好，而是更想让大家知道，你究竟为什么而写作？愿你我能在正确的道路上，持之以恒。&lt;/p&gt;</content:encoded></item><item><title>为什么我们需要数据库事务</title><link>https://godruoyi.com/posts/why-do-we-need-database-transactions/</link><guid isPermaLink="true">https://godruoyi.com/posts/why-do-we-need-database-transactions/</guid><description>事务一直是简化这些问题的首选机制。他为上层应用程序提供一个可靠性保障：将多个读写操作组合成一个逻辑单元来执行，要么全部成功，要么全部失败。应用程序在处理这些问题时将不再关心一半成功一半失败的情况，也不再拘泥于下层各种不可靠的系统。</description><pubDate>Tue, 03 Aug 2021 07:41:41 GMT</pubDate><content:encoded>&lt;p&gt;日常工作中我们可能会遇到如下的问题，在未引入数据库事务这一特性前，应用程序在处理这些问题时总显得过于复杂，如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;数据库在写入一半数据时崩溃&lt;/li&gt;
&lt;li&gt;订单数据保存一半后网络链接中断&lt;/li&gt;
&lt;li&gt;多个客户端可能会同时写入数据库&lt;/li&gt;
&lt;li&gt;多个客户端之间的条件竞争可能会扰乱整个应用程序&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;而事务一直是简化这些问题的首选机制。他为上层应用程序提供一个可靠性保障：将多个读写操作组合成一个逻辑单元来执行，要么全部成功，要么全部失败。应用程序在处理这些问题时将不再关心一半成功一半失败的情况，也不再拘泥于下层各种不可靠的系统，因为大多数数据库系统都会从多个维度（事务的ACID）来保证数据的正确性。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;计算机发展到今天，我们一直在不可靠的环境中构建可靠的系统。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;事务的-acid&quot;&gt;事务的 ACID&lt;/h2&gt;
&lt;p&gt;ACID 并不是什么高大上的术语；而是数据库系统在实现事务时为保证其正确可靠而必须满足的几个约束，不同的约束提供了不同的保障。&lt;/p&gt;
&lt;h3 id=&quot;atomicity原子性&quot;&gt;Atomicity（原子性）&lt;/h3&gt;
&lt;p&gt;原子性并不是指把事务当作一个整体来运行，既运行过程中不可中断、不可切换；而是指事务再遇到出错时，能终止事务，丢弃该事务的所有变更。事务一旦终止，实现这一特性的数据库会保证数据恢复到原始状态，应用程序不在担心各种可能存在的中间结果，只用专注于处理成功或失败两种状态。&lt;/p&gt;
&lt;h3 id=&quot;consistency一致性&quot;&gt;Consistency（一致性）&lt;/h3&gt;
&lt;p&gt;一致性主要是指数据状态的一致性，考虑这样一个例子：从 A 账户转入 100 元到 B 账户，最终的一致性状态是指 A 账户的支出和 B 账户的收入达到收支平衡。&lt;/p&gt;
&lt;p&gt;而在 DDIA 这一书里，作者认为一致性是应用程序的属性，不应该由数据库来实现。拿上面例子来说，账户 A 转出 100 元但由于程序问题账户 B 却收入 200 元，虽然最终状态是收支不平衡，但这并不影响数据库会按正确的方式来保存这些数据。状态是否一致的判断，应该交由应用程序去处理，数据库系统只会正确的保存你给他的所有数据，而不会关心数据本身（参考 &lt;a href=&quot;http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.87.2812&amp;#x26;rep=rep1&amp;#x26;type=pdf&quot;&gt;ACID 中的 C 是被扔进去拼凑的单词&lt;/a&gt;）。&lt;/p&gt;
&lt;h3 id=&quot;isolation隔离性&quot;&gt;Isolation（隔离性）&lt;/h3&gt;
&lt;p&gt;当多个 client 同时操作同一数据时，就可能会出现并发问题（race conitions）。事务的隔离性要求并发执行的事务之间互不干扰，如果在一个事务中进行多次写入，则另一个事务要么看到她全部写入结果，要么什么都看不到。&lt;/p&gt;
&lt;p&gt;下面的例子就不满足事务的隔离性：User1 先后进行了两次写入，在她未提交前，User1 读取到了部分新增的数据。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202108/03/TOkq5GTlht12ZxSelk36r3vnfOWAjAegEO0ks1n0.png&quot; alt=&quot;违反隔离性&quot;&gt;
&lt;strong&gt;图 1.0 违反隔离性：一个事务读取另一个事务的未提交的写入&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;为什么不能读取一个事务未提交的写入？是因为一个未提交的写入，其后续可能会被终止，终止后该事务的所有写入都会被回滚；若一个事务读取到了另一个事务未提交的变更，而该事务回滚后，程序将得到一个完全不应该存在的值。&lt;/p&gt;
&lt;h3 id=&quot;durability持久性&quot;&gt;Durability（持久性）&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;事务的持久性是一个承诺，即一旦事务成功提交，即使发生硬件故障或数据库崩溃，写入的数据也不会丢失。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这只是一个美好性承诺，我们小心翼翼地祈祷不会出现如硬盘被偷、文件损坏等故障导致的数据丢失。虽然这有点杞人忧天，不过也说明了并不存在绝对 100% 的保证。&lt;/p&gt;
&lt;h2 id=&quot;事务的隔离级别&quot;&gt;事务的隔离级别&lt;/h2&gt;
&lt;p&gt;为了简化应用程序在面对并发时的各种问题，大部分关系型数据库都提供了不同的隔离级别。隔离级别并不是一个什么高深的概率，只是数据库系统在简化并发问题时的抽象。不同的隔离级别对应不同的保障，保障系数越高，相应的性能就越低。在选择不同的隔离级别前，我们应该思考该隔离级别提供了什么样的保障？相同的代码在不同的隔离级别下可能会存在什么问题？不同的隔离级别可能会带来什么问题？而不是一味为了应付面试而了解的诸如脏读、幻读、不可重复读、MVCC 等抽象的概念。&lt;/p&gt;
&lt;h2 id=&quot;read-committed&quot;&gt;Read Committed&lt;/h2&gt;
&lt;p&gt;正如名字一样，在 Read Committed（读已提交）隔离级别下，一个事务只能读取已提交的数据（对照上面隔离性时的例子）。如下面的例子中，在 User1 未提交前，User2 前两次读取的结果都相同，而当 User1 提交后，User2 就能读取到提交后的数据。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202108/03/xh2K3QGq0kVQ4G1vUFgB7ZGCupRMK2pI0N9M4goS.png&quot; alt=&quot;Read Committed&quot;&gt;
&lt;strong&gt;图 2.0 Read Committed User2 在 User1 提交后才能看到新值&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;read-committed-的实现&quot;&gt;Read Committed 的实现&lt;/h3&gt;
&lt;p&gt;Read Committed 是 Oracle 11g、PostgreSQL 的默认隔离级别，通常采用加锁来防止并发写入（写写）。一个事务在尝试更新（写入）对象时，必须先获得该对象的锁，同一时刻只能有一个事务持有该对象的锁，未获得锁的事务需要一直等待，直到持有锁的事务提交或终止。此种方式相当于将两个并发写请求通过加锁的方式串联起来，使得同一时刻最多只允许一个事务进行写入，也就不会存在数据竞争等情况。&lt;/p&gt;
&lt;p&gt;几乎所有的数据库系统都允许多个事务并发读取（读读），并发读取数据时并不会对资源加锁，在 Read Committed 和快照隔离级别下，写操作也不会柱塞读操作。&lt;/p&gt;
&lt;p&gt;若存在读写并发时（读写），写操作的事务会记录所操作资源的两个版本，一个是原始值，一个是修改后的新值；读事务写事务提交前，都只能读取到原始值，而看不到新值。只有当写事务提交后，读事务才能读取到他提交后的新值。&lt;/p&gt;
&lt;p&gt;如图 2.1 所示，写操作把 ID 为 1 的记录从 18 更新到 28，但未提交；其内部可以简单的理解为有一个指向上一个版本的「链接」，通过这个「链接」就能获取上一次已提交的值。读操作在检索到这一对象时(where id = 1)，由于最新值 28 是 UnCommitted，将通过「链接」获取上一次已提交的值作为查询的返回值，既返回 18。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202108/03/uiq6yVja58bw5FnIJO9aVLCOCDFJrtDBwOVMm2fH.png&quot; alt=&quot;Read Committed&quot;&gt;
**图 2.1 Read Committed **&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;通过这种方式，Read Committed 就能保障读取到的数据，一定是已经提交了的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;read-committed-带来的问题&quot;&gt;Read Committed 带来的问题&lt;/h3&gt;
&lt;p&gt;Read Committed 相较于其他隔离级别，不但提供了较好的性能，并且能够满足绝大多数的应用场景，但这并不代表它就是完美的。如下面的例子，在一个事务中，程序筛选满足条件的记录数量，若数量大于 0，再获取相应的数据集合，并返回记录条数和数据集本身。&lt;/p&gt;
&lt;p&gt;考虑到当事务执行完第一个查询条件后，另外的事务新增了几条数据并提交，由于在 Read Committed 隔离级别下，事务能读取到另一事物已提交的更新，这将导致后面一次查询出来的数据集条数和第一次查询的 count 不匹配（不可重复读问题）。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;start transaction;
count = select count(1) from t where foo = bar

if count &gt; 0 {
    return {
        count,
        datas: select x from t where foo = bar
    }
}
commit;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Read Committed 认为这种问题是可以被接受的，也没打算解决这一问题；因为当你重新执行一遍事务，你可能会得到正确的数据。&lt;/p&gt;
&lt;h2 id=&quot;snapshot-isolation快照隔离&quot;&gt;Snapshot Isolation（快照隔离）&lt;/h2&gt;
&lt;p&gt;快照隔离（Snapshot Isolation）相比于 Read Committed 提供了更严谨的保障，在 Read Committed 的基础上，还能解决上述的不可重复读现象。这也是 MySQL InnoDB 的默认隔离级别，在 MySQL 中快照隔离被称为&lt;strong&gt;可重复读&lt;/strong&gt;(repeatable read)，其名字在不同的数据库系统实现中有不同的叫法，我们只需要知道其具体原理即可。&lt;/p&gt;
&lt;h3 id=&quot;snapshot-isolation-的实现&quot;&gt;Snapshot Isolation 的实现&lt;/h3&gt;
&lt;p&gt;快照隔离在处理多事务并发写入（写写）和多事务并发读取（读读）时，采用与 Read Committed 一样的机制，既允许「读与读」并发而「写与写」互斥。参考 [Read Committed 的实现](Read Committed 的实现)。&lt;/p&gt;
&lt;p&gt;在处理多事务并发读写时（读写），不同于 Read Committed，快照隔离通常会保留所操作资源的多个版本，并在每个版本中记录更新数据时的事务 ID（事务 ID 在事务开始时由数据库系统分配，通常是单调递增的）。如图 3.0 所示，记录了 ID 为 1 的数据更新历史，其值先后被更新为 0 -&gt; 6 -&gt; 15 -&gt; 18 -&gt; 28，其事务 ID 依次为 1、3、5、7、9；最后一次更新操作暂未提交。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202108/03/E44vsatw2Nw3XlU5MmtBPWgJIDUFpI9nadnHc03e.png&quot; alt=&quot;多版本并发控制&quot;&gt;
&lt;strong&gt;图 3.0 多版本控制&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;读操作在读取数据时，会过滤事务 ID 大于自身的版本。假设有一个读事务正在读取 ID 为 1 的这条记录，其 txid 为 6，由于程序运行较慢，该记录已经向前提交了两个版本，既上图的 txid 为 7、9 的两次提交；则读操作在查询时，只会获取 txid&amp;#x3C;=6 并且已提交的版本作为查询的返回值，所以查询将返回 &lt;code&gt;{txid:5, value: 15}&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;通过这个机制，事务在整个生命周期内进行的多次查询，都将使用同一个版本的数据，即使查询的对象已经提交了多个版本，查询时都将使用事务开始时的数据。相当于在事务启动的时候就生成了一个一致性快照，但这个快照并不是一个数据备份，其并没有 Copy 数据的开销，而是在运行时通过 txid 动态计算的不同版本。&lt;/p&gt;
&lt;h3 id=&quot;snapshot-isolation-带来的问题&quot;&gt;Snapshot Isolation 带来的问题&lt;/h3&gt;
&lt;p&gt;快照隔离和 Read Committed 都通过「写与写互斥」来解决多事务并发写入的问题，但在某些场景下这种方式并不能保障数据的正确性，其中最主要的就是丢失更新问题（Lost Update）。&lt;/p&gt;
&lt;p&gt;如图 3.1，两个并发请求开始读取到的 counter 都是 42，应用程序将值自增后在更新到数据库，最后保存的结果却为 42，User1 的更新被覆盖了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202108/03/7Gg9fRNx3X2q7uvjHwmffk2YxiWwvotqs426GNQr.png&quot; alt=&quot;Lost Update&quot;&gt;
&lt;strong&gt;图 3.1 Lost Update&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;更新丢失准确来说不算是数据库的问题，也不应该要求数据库做出这方面的保障，毕竟数据库在保存数据时，并不知道数据本身的合法性。通常，更新丢失有以下几种解决办法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;原子写&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;update t set count = count + 1 where id = 1
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;排它锁（FOR UPDATE）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;通过在 SQL 语句后面指定 FOR UPDATE 来锁定查询条件返回的记录数，在事务未提交期间，其他&lt;strong&gt;查询&amp;#x26;写入&lt;/strong&gt;必须等待。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;start
select count from t where id = 1 for update

count ++
update t set count = count where id = 1

commmit
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;serializable&quot;&gt;Serializable&lt;/h2&gt;
&lt;p&gt;最后一种隔离级别是可序列化，可序列化隔离通常被认为是最强的隔离级别。他将多个并发执行的事务串行化，一个事务必须等待之前的任务处理完成后才能接着处理。这种隔离级别可以防止所有可能的竞争条件。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Serializable 的实现&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Serializable 通常采用两阶段锁（two-phase locking，2PL）的方式来实现。他允许多事务并发读取，既读与读之间互不干涉。但如果要对某一对象进行写入时，需要等待该对象上的所有&lt;strong&gt;读&amp;#x26;写&lt;/strong&gt;事务完成后，才能写入；如果要对写入的对象进行读取时，需要等待写入事务提交或终止后，才能读取。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Serializable 带来的问题&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;由于两阶段锁在遇到写操作时，都会对资源进行加锁，并且写操作还会柱塞读操作。所以 Serializable 带来的性能十分低下。并且还可能会发生死锁和写放大等现象，毕竟在生产环境中，当其中一个服务读写变慢时，他就有可能会拖坏整个应用的吞吐率，并逐渐扩大至整个程序不可用。这也是 Serializable 即使提供了更好的隔离级别却很少使用的原因。&lt;/p&gt;
&lt;h2 id=&quot;总结&quot;&gt;总结&lt;/h2&gt;
&lt;p&gt;数据库事务为我们提供了一个良好的抽象，让开发人员不在担心各种不可靠的环境，不在关心各种模凌两可的状态。有一点需要知道，事务不是天然存在的，我们不要想当然的以为他能够处理好所有的问题，而不考虑他在不同场景下可能带来的影响。&lt;/p&gt;
&lt;p&gt;虽然文章标题叫做「&lt;a href=&quot;https://godruoyi.com/posts/why-do-we-need-database-transactions&quot;&gt;为什么我们需要数据库事务&lt;/a&gt;」，但其实作者大部分篇幅都在写隔离级别，因为我发现再解释完为什么后，还需要接着解释 Why，那姑且就这样吧。如果你觉得文章对你有帮助，你也可以订阅作者的博客 &lt;a href=&quot;https://blog.godruoyi.com/feeds&quot;&gt;RSS&lt;/a&gt; 或直接访问作者博客 &lt;a href=&quot;https://godruoyi.com/&quot;&gt;二愣的闲谈杂鱼&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&quot;参考&quot;&gt;参考&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://book.douban.com/subject/30329536/&quot;&gt;Designing Data-Intensive Applications - 豆瓣&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%95%B0%E6%8D%AE%E5%BA%93%E4%BA%8B%E5%8A%A1&quot;&gt;数据库事务 - 维基百科&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://twitter.com/joe_hellerstein/status/588376556545777664&quot;&gt;ACID C was put in just for laughs - Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.quora.com/Why-doesnt-NoSQL-support-an-ACID-property&quot;&gt;Why doesn’t NoSQL support an ACID property?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://time.geekbang.org/column/article/68963&quot;&gt;事务隔离：为什么你改了我还看不见？- 极客时间&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://t.co/aFu2Vn42FI?amp=1&quot;&gt;技术分享：Distributed Lock Manager （陈皓）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/EGGe14IpEsho75ntjeR3OA&quot;&gt;Redis作者「不懂」分布式锁？&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/why-do-we-need-database-transactions&quot;&gt;原文链接 - 为什么我们需要数据库事务？&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>人的一生应该怎样度过</title><link>https://godruoyi.com/posts/how-should-one-spend-one-s-life/</link><guid isPermaLink="true">https://godruoyi.com/posts/how-should-one-spend-one-s-life/</guid><description>今天突然发现我还有个 wordpress 站点，进去看才发现不知什么时候写了这样的一篇。这应该是在中移物联网的时候写下的，现把他搬到我的 Blog。</description><pubDate>Thu, 03 Jun 2021 03:22:32 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;今天突然发现我还有个 wordpress 站点，进去看才发现不知什么时候写了这样的一篇。原文发布于 2016-07，这应该是在中移物联网的时候写下的，现把他搬到我的博客。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;从毕业到现在差不多快一年了，现在的我在一家二流公司里做一个 codeer。团队大于有 20 人左右，我只是其中的一个小角色，默默的编着码，时不时问问一些特别弱智的问题，虽然坐的位置还算显眼，然而这并没有什么卵用，你在还是走都不会跟团队造成任何影响；相当于工地上的小工。而不论是生活中还是工作上，我都不喜欢表露，宁愿自己一个人呆着，也不愿去和别人谈天谈地，十足的内向。虽然也毕业一年了，但不管是思想上还是这张脸，都写着幼稚两大字。社会经验不足，社会技能不足，不爱交流，还爱幻想，我真的是受够我自己了，这样的我还一天老谈人的一生应该怎样度过，真的是日乐购了。&lt;/p&gt;
&lt;p&gt;记得在大学 4210 教室听毕业宣传大会的时候，我还在和永智谈体制化，然而那就是谈谈而已；现在的我已经被体制给化了，每天傻逼似的上班下班，出门前天才亮，回家天都黑了，还一天自以为是的以为过得很充足，还以为这样下去就能像柯察金一样，“他不因虚度年华而悔恨”。于是我开始不满这个社会，不满这个国家，我不满他没有给我后盾，不满他让我每个月的五号都在计算：还了信用卡还有 3000，除去 1500 的生活费还剩 1500，再存 1000 块钱 …. 。还傻逼似的以为这样下去生活会愈来愈好的，然而我就连房租都交不起，我还要怎么像柯察金一样为人类的伟大胜利而奋斗终生。。醒醒吧，你又在信用卡上刷了 50 块钱去买菜了。&lt;/p&gt;
&lt;p&gt;今天看到我老家邻居（同龄人，初中辍学/包公头）回家开始修房子了，我又开始慌了，读了这么多年的书，终究还是要和钱过得去。别人的孩子初中毕业就出去鬼混了，现在回到家，修了一个窝，就近找了点事，能照顾父母，能安度余生，可怜的连波还在重庆花 800 块钱租着房子，更不奢求照顾自己年迈的父母了。有时候就在想，城市里生活和乡下生活到底差了什么呢，为什么我要拼了命的往城市里钻。&lt;/p&gt;
&lt;p&gt;这就是你的生活， 连波。&lt;/p&gt;
&lt;p&gt;搬出路遥来安慰安慰我吧，&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;生活就是这样， 不能让人处处满意， 但我们还要热情的生活下去， 人活一生， 值得爱的东西很多， 不要因为一个不满意， 就灰心。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;p&gt;可以看到当时的情绪还是挺激动的；刚毕业那年，我欠下了很多信用卡，大约 1W 左右，后面陆陆续续花了一年才还完。而又看到小时候的玩伴初中未毕业就外出务工，现在「混」得有模有样的，心理难免有点失落。现在看来才发现书上说的都对：当他回首往事时，他不因虚度年华而悔恨，也不因碌碌无为而羞耻。&lt;/p&gt;</content:encoded></item><item><title>三体</title><link>https://godruoyi.com/posts/three-body/</link><guid isPermaLink="true">https://godruoyi.com/posts/three-body/</guid><description>是地球人与三体人的技术水平差距大呢，还是蝗虫与咱们人的技术水平差距大？</description><pubDate>Thu, 20 May 2021 09:53:37 GMT</pubDate><content:encoded>&lt;p&gt;这本书已经在家里的客厅躺了一两个月了，最后的一小部分的内容终于在公司摸鱼中看完了。整体评价的话，第二部最好看，其次是第一部，最差是第三部。&lt;/p&gt;
&lt;p&gt;最喜欢的内容来自第一部；在三体世界用智子封锁死地球的基础科学并大举侵犯的前提下，两位科学及汪淼和丁仪陷入了深深的绝望和等死。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;你带我们来这儿干什么？ 汪淼问。&lt;/p&gt;
&lt;p&gt;看虫子。大史点上一支斯坦顿上校送的雪茄说，同时用雪茄指指面前的麦田。&lt;/p&gt;
&lt;p&gt;汪淼和丁仪这才发现，田野被厚厚的一层蝗虫覆盖了，每根麦秆上都爬了好几只，地面上，更多的蝗虫在蠕动着，看去像是一种黏稠的液体。&lt;/p&gt;
&lt;p&gt;这地方也有蝗灾了？汪淼赶走田埂一小片地上的蝗虫，坐了下来。&lt;/p&gt;
&lt;p&gt;像沙尘暴一样，十年前就有了，不过今年最厉害。&lt;/p&gt;
&lt;p&gt;那又怎么样？大史，什么都无所谓了。丁仪带着未消的醉意说。&lt;/p&gt;
&lt;p&gt;我只想请二位想一个问题：&lt;strong&gt;是地球人与三体人的技术水平差距大呢，还是蝗虫与咱们人的技术水平差距大？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;看看吧，这就是虫子，它们的技术与我们的差距，远大于我们与三体文明的差距。人类竭尽全力消灭它们，用尽各种毒剂，用飞机喷撒，引进和培养它们的天敌，搜寻并毁掉它们的卵，用基因改造使它们绝育；用火烧它们，用水淹它们，每个家庭都有对付它们的灭害灵，每个办公桌下都有像苍蝇拍这种击杀它们的武器……这场漫长的战争伴随着整个人类文明，现在仍然胜负未定，虫子并没有被灭绝，它们照样傲行于天地之间，它们的数量也并不比人类出现前少。&lt;/p&gt;
&lt;p&gt;虫子从来就没有被真正战胜过。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;当我看到这里的时候，十分震惊。是啊，就算比你再优秀的人，也不是可望不可及的；只要你在一点点的进步，终将会像虫子一样傲行于天地之间。&lt;/p&gt;</content:encoded></item><item><title>我是如何走向技术这条路的</title><link>https://godruoyi.com/posts/my-technical-journey/</link><guid isPermaLink="true">https://godruoyi.com/posts/my-technical-journey/</guid><description>每个人都有自己的故事，我的故事并不有趣，也不励志，但我愿意把他写出来，你的呢？</description><pubDate>Mon, 17 May 2021 06:11:42 GMT</pubDate><content:encoded>&lt;p&gt;2011 年 9 月，在姐姐的陪同下，我来到了隔壁山城——重庆，准备入学报道。那是作者第一次离省。&lt;/p&gt;
&lt;p&gt;从家乡的小县城出发，坐上早上发往重庆的大班车，在来来回回的在山路上开了 4、5 个小时，接近正午时，班车停在了菜园坝长途汽车站。车站看起来一点也不像&lt;del&gt;繁华&lt;/del&gt;都市应有的样子，普遍不高的建筑物加上到处吆喝的小贩，给我一种从一个县城到达另一个县城的错觉；但当我前脚刚跨下大巴后，一股热浪带着风被我吸来，我第一次体会到了山城的热情。出发前爸妈说的原来是真的，重庆是一座火城。&lt;/p&gt;
&lt;p&gt;记得在高三教学楼一楼电脑室填完高考志愿出来后，我还不知道所选的计算机专业会有怎么样的就业前景；当时听同学说计算机是红牌专业，也不知道这是危险还是火热的意思。比起那些选学校选专业有老师参谋家长建议的同学来说，我是幸运的；所有的一切，我自己说了算；爸妈也没法给我任何类型的帮助，在他们看来，能考上师范大学就心满意足了。但他们不知道的是，我不想呆在本省，我不想小学、初中、高中、大学都呆被困在同一个省，这样很大概率我毕业后还得留在这里，我想去外面看看；但碍于自己的软弱，又不想离家太远，就只敢选择于川渝之间，别的太远，一是不想去，而是钱不够。&lt;/p&gt;
&lt;p&gt;高中时期我也算得上一个优等生，在县重点中学最高排名能挤进年级前 50。而到了大学后，作业靠抄，考试靠蒙，连体育也能挂科，活生生坠落成一个狗样。除了上课，大部分时间都在玩游戏，从 CF(穿越火线)、DNF(地下城与勇士)到 LOL(英雄联盟)；中午玩几把再睡、下午从课后玩到晚上、晚上玩到断网，甚至断网了还要来几把单机 CS，不放过任何可以游戏的时间。&lt;/p&gt;
&lt;p&gt;这样浑浑噩噩过了三年，直到大四上学期，校方「勾结」某培训机构来学校免费讲课，主要是讲 Java EE。培训机构也真会挑时间：大四这一年，基本没得课程安排，大部分同学都会离校参加社会实习；但是前面三年都在游戏中度过的我们，根本没有任何资本找到实习工作；少有学的好的同学，也只停留学校传授的粗枝烂叶上；所以培训机构这时候进场，自然而然给大家提供了另一个选择。&lt;/p&gt;
&lt;p&gt;机构老师讲的，很大不同于大学教授；机构老师讲的是实实在在的东西，诸如环境变量的搭建，Eclipse 的使用，如何用 Servlet/JSP 开发网页等；不但听得明白，下课后还能自己动手试错；而大学老师所教的，三年过去了我都不知道教的是什么。和大部分学校一样，计算机专业的学生首次接触的基本都是 C 语言，教材是那本误人子弟的谭浩强版；不但老师讲得云里雾里，教材上的知识也牛头不对马嘴。一般上三四节理论课，再上一节实验课，理论课上讲语法，实验课让你上机运行。作者深深记得第一次 C 语言上机课时，在集成环境中写完 Hello World 后输出一段文字后的无助；这能干什么，这他妈有什么用？完全找不到任何乐趣所言。还不如后面选修的《网页设计》，在这门课上，我第一次知道原来将一个 .txt 文件改为 .html 文件后，就能直接用浏览器查看效果了，并且还可以通过 H1，H2 及一些简单的 CSS 来控制样式。&lt;/p&gt;
&lt;p&gt;一个多月后，培训机构带走了同寝室的两个小伙伴，剩下三个伙伴中，肉杰已经找到了工作，就在学校附近的一家大公司，偏向市场方向；&lt;a href=&quot;https://sppan24.github.io/&quot;&gt;鸟&lt;/a&gt;是我们寝室技术最好的，已在学校附近找到了 Java 实习，月薪 2K。只剩下一个我，空气瞬间安静了下来。有时候早上一觉醒来，寝室里一个人都没有，我坐在床上一手抓头发一手抓着床边的栏杆，望着正前方的门发呆。我不知道干什么，我好压抑，我想找人说话，我真想大吼一声。我变成了那种明知在浪费青春而又无能为力的废人。我一刻也不想呆在寝室，游戏也不愿意打了，有时候带着本书，在图书馆看一早，有时候在后校门租个自行车，漫无目的的游荡着；有一次骑到了一个小村里，拐上右手边的一个山坡，上面有户人家，旁边有个鱼塘，鱼塘里有一些荷花；我看了看鱼塘，又看了看路上背着背篼的行人，又倒回来回校了。我也不知道我在追求什么。&lt;/p&gt;
&lt;p&gt;一连好几天，我都漫无目的的过着。&lt;a href=&quot;https://sppan24.github.io/&quot;&gt;鸟&lt;/a&gt;中午休息的时候会回学校，看到毫无斗志的我，告诉我不能再这样颓废下去，问我要不要学学 Java，并发了一套朴乾老师的 Java Web 视频给我，让我跟着学；到这一刻，我都不知道我接下来的人生是怎样的，我设想过无法的可能，可能在某个公司干着可有可无的工作，可能在工地搬砖，可能永远都是能被无成本替换的那一批。&lt;/p&gt;
&lt;p&gt;从那天起，我早上 7 点起来开始看视频，有时候快到吃午饭了，才去洗漱。中午去食堂吃完饭回来后肯定要打一局游戏，不过已经开始克制只打一局。然后午休，1.30 左右鸟去上班了，我又起来看视频，看到下午饭点快到了，再去吃饭，吃完饭回来免不了的又要打两把游戏，接着又开始看视频，看到晚上 10.30 左右。这时肉杰才提着公文包醉轩轩的回来。&lt;/p&gt;
&lt;p&gt;就这样修炼一个月后，我开始投简历了。很庆幸，就第二天收到了一家公司的面试邀请。这家公司位于渝中区鹅岭公园附近，随着电梯的层层上升，我越来越激动起来。这是我第一次面试。和前台简单聊了几句后，面试官出现了，结结巴巴的自我介绍结束后，面试官问了一些很简单的技术问题，我回答得都还不错；接着我被推进到下一流程，一个 HR 小姐姐走了过来；她开始向我说起了公司的企业文化，福利待遇，职业发展，就业空间；并告诉我因为你的基础不扎实，需要进行六个月的岗前培训，培训期间无费用，但得从六个月转正后的薪资里扣除。我耐心的听她慢慢讲完，并没有任何疑问后，HR 小姐姐给了我一张表让我填写，我拿着那表就笑了。除了名字一栏填写真实的外，其他的我都在乱写；正好旁边有个朋友也在填表，我问他你也是来面试的吗？得到他肯定的答复后我告诉他：这家是外包公司，不要听他们的和他们签合同。小哥半信半疑的看着我，说完我把填好的表交给前台，头也不回的走了，身后传来前台姐姐「等等，还没结束」的声音，我并没有理他。下楼后，我看到一个&lt;a href=&quot;https://images.godruoyi.com/posts/202105/17/qV788OmEZhe62AGUNXDIgfpP07XRxIkuozDUVgVu.png&quot;&gt;背着书包的小孩&lt;/a&gt;，那一刻我竟然无比的开心。&lt;/p&gt;
&lt;p&gt;回到学校后，慢慢又开始烦躁起来了，天真的我才发现，事情并没那么简单；视频是看完了，自认为学的也差不多了，也投了那么多简历，但就是收不到面试邀请；曾以为学完了就能顺顺利利的找到实习，但事总与愿违；而面试受挫加简历石沉大海，使我无心学习越发烦躁；一星期后我选择收拾行李回家看看，在心里仿佛有声音告诉我，即使再难的不知所措，回家后都能又见一春。我拖上了零几年老爸打工带回来的红色小拉箱，去往菜园坝汽车站坐车。在菜园坝车站附近花 60 块找了个旅社，被吆喝住宿的阿姨带着爬上一个狭小的楼梯，再穿过一个五块钱灯光的走廊，走廊尽头左手边第一间单间正在那静静等着它的新主人。房间四周无窗，空气弥漫着一股精液腐烂的味道，灯开后，才能看到门前有一张小桌子，左手边有一张床，床上铺了一个两边已经坏掉的凉席，感觉一米五的凉席只有中间一米可用，床上的被子凌乱的躺在床尾，让我想到一句诗描述的那样：布衾多年冷似铁，娇儿恶卧踏里裂。&lt;/p&gt;
&lt;p&gt;我坐在床上，开着门，让风尽量的多进来一些。不一会我接到一个电话，天极传媒邀请我明天参加面试；接完电话的我长舒一口气，要是这电话晚一天打来，我就已经在回家的路上了。听隔壁传来的阵阵呻吟声，我在期待着明天的面试。&lt;/p&gt;
&lt;p&gt;面试当天就结束了，问得都比较简单，实习月薪 2000，两天后上班。在这里，一开始面试我的面试官成了我的恩师，后来他告诉我，你其实是技术最菜的那一个，但在你身上我看到了年轻时的样子。我也不知道老师年轻的时候是什么样子，可能是那稚嫩的脸上表现出的阳光和对新知识的向往吧。从那时候起，我算是真正踏上了技术这条路。而时隔五六年后，我依然没能企及老师的高度。&lt;/p&gt;
&lt;p&gt;这就是我的入职经历，每个人都有他自己的故事，我的故事并不有趣，也不励志，但我愿意把他写出来，你的呢？&lt;/p&gt;</content:encoded></item><item><title>老丈人的三轮车🛺</title><link>https://godruoyi.com/posts/dads-tricycle/</link><guid isPermaLink="true">https://godruoyi.com/posts/dads-tricycle/</guid><description>老丈人有一辆三轮车🛺，每次不开车回去时，我都能坐上老丈人的三轮车。以前，我和阿宝会在镇上下大巴，老丈人就从家开着三轮车来接我们。</description><pubDate>Thu, 06 May 2021 02:40:42 GMT</pubDate><content:encoded>&lt;p&gt;老丈人有一辆三轮车 🛺，每次不开车回去时，我都能坐上老丈人的三轮车。以前，我和阿宝会在镇上下大巴 🚌，老丈人就从家开着三轮车来接我们。阿宝坐在前面——和老司机坐在一起，不知道在和他老爹嘀咕些什么。我坐在三轮车后面的车厢里。天气好的时候，我在后面还可以站起来，一只手比着向前进的动作，像老毛阅兵一样，俯视着自己的大好河山，就差喊一句同志们辛苦了；一只手牢牢抓住车边儿上的栏杆；下坡的时候，老丈人像没踩刹车一样，轰隆隆的任凭车滑下去，我就得把另外一只问候同志们好的手收回来，死死抓住边儿上的栏杆。而天气不好的时候，我就得坐在老丈人准备的小凳子上，一只手抓着栏杆，一只手打着伞。伞不能打太高了，不然要被吹翻；也不能太低了，不然脖子淤着不好受。我不喜欢打伞，打伞后我就不能看到沿途的风景了。&lt;/p&gt;
&lt;p&gt;老丈人的三轮车是油点混合的，动力非常 &lt;del&gt;强劲&lt;/del&gt; ；回去的路上开始很平稳，用电动方式就能载得动我们仨呢；而后面有一段坡度很陡的路，每次走到这里的时候，老丈人就会停下来，让站在车厢里的我把油门打开；然后老丈人切换为油模式，轰轰轰的发动机始转动，载着我们一点点的往坡上爬；如果旁边恰好有过路的行人，你会发现他走的居然比我们还快。我站在后面提心吊胆的，为了重心前移，还不得不半个身子往前倾，生怕爬到一半爬不上去而倒退。&lt;/p&gt;
&lt;p&gt;老丈人家了养了三只狗，一只黑儿 🐕‍🦺，一只黄标 🐕，一只好像没得名字，就暂且叫他大白吧 🐩。每当老丈人要开车外出时，这三狗子就会追着车子一起走。其中尤其黑儿跑得最快，走的最远。以前黄标和大白都不追车的，只有黑儿，老丈人开车去哪里，他就跟着去哪里，不管多远的路程。有时候会被路上的其他狗一直追一只咬，黑儿就夹着尾巴使劲儿跑；有时候泡在三轮车后面、有时候跑在前面、有时候又跑在轮子附近；赢得老丈人一句「狗日的黑儿，你走开点」。&lt;/p&gt;
&lt;video controls=&quot;&quot; width=&quot;350&quot;&gt;
  &lt;source src=&quot;https://github.com/user-attachments/assets/e8f5255a-1668-47f5-8a8c-f0e050d201ea&quot; type=&quot;video/mp4&quot;&gt;
&lt;/video&gt;
&lt;p&gt;我喜欢这样的生活，&lt;/p&gt;
&lt;h2 id=&quot;老丈人系列&quot;&gt;老丈人系列&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/a-grape-trellis/&quot;&gt;葡萄架&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/a-turtle/&quot;&gt;一条甲鱼&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Laravel Octane 初体验</title><link>https://godruoyi.com/posts/laravel-octane/</link><guid isPermaLink="true">https://godruoyi.com/posts/laravel-octane/</guid><description>Laravel 框架一直很优秀，但是他在性能方面却一直为人诟病。框架的 boot 时间可能比业务处理时间还长；而 Laravel Octane 则通过启动 Application 一次，常驻内存的方式来加速我们的应用。</description><pubDate>Thu, 29 Apr 2021 09:51:32 GMT</pubDate><content:encoded>&lt;p&gt;Laravel Octane 已经发布好几周了，虽说目前还处于 beta 状态，也挡不住开发者对他的热爱，一个月不到，其在 GitHub 的 star 数量已超过 2K；部分&lt;a href=&quot;https://twitter.com/freekmurze/status/1384929538367492098&quot;&gt;开发者&lt;/a&gt;已将他们的项目运行在 Laravel Octane 之上。&lt;/p&gt;
&lt;p&gt;如果你还在观望，也可等等&lt;del&gt;一两周后的稳定版&lt;/del&gt; &lt;a href=&quot;https://twitter.com/taylorotwell/status/1392186038395756556&quot;&gt;I’ve tagged Laravel Octane 1.0 - Taylor Otwell&lt;/a&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We will likely go ahead and tag Octane 1.0 as stable next week &lt;a href=&quot;https://twitter.com/taylorotwell/status/1387388409380057089&quot;&gt;@Taylor Otwell on Twitter&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;为了体验一把加速的魔力，作者已拿一个简单的 H5 项目在生产环境下试了试水，除了一些乱七八糟的问题，其他的都令作者激动不已，客户还表示我们的平台好快啊，下次还找你。&lt;/p&gt;
&lt;h2 id=&quot;laravel-octane-的组成&quot;&gt;Laravel Octane 的组成&lt;/h2&gt;
&lt;p&gt;Laravel Octane 内置了两个高性能的应用服务：&lt;a href=&quot;https://swoole.co.uk/&quot;&gt;Swoole&lt;/a&gt; 和 &lt;a href=&quot;https://roadrunner.dev/&quot;&gt;RoadRunner&lt;/a&gt;，正如官方文档介绍的：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Octane boots your application once, keeps it in memory, and then feeds it requests at supersonic speeds.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我们知道，Laravel 框架一直很优秀，但是他在性能方面却一直为人诟病。框架的 boot 时间可能比业务处理时间还长，并且随着项目第三方 service provider 的增多，其启动速度越来越不受控。而 Laravel Octane 则通过启动 Application 一次，常驻内存的方式来加速我们的应用。&lt;/p&gt;
&lt;p&gt;Laravel Octane 需要 PHP8.0 支持，如果你是在 macOS 下工作，你可以参考这篇文章来更新你的 PHP 版本 &lt;a href=&quot;https://stitcher.io/blog/php-8-upgrade-mac&quot;&gt;Upgrade to PHP 8 with Homebrew on Mac&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&quot;octane-简单示列&quot;&gt;Octane 简单示列&lt;/h2&gt;
&lt;p&gt;虽说官方文档已经描述的很详细，不过作者这里还是通过一个简单的示列项目来演示。&lt;/p&gt;
&lt;h3 id=&quot;create-laravel-application&quot;&gt;Create Laravel Application&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;➜ laravel new laravel-octane-test

 _                               _
| |                             | |
| |     __ _ _ __ __ ___   _____| |
| |    / _` | &apos;__/ _` \ \ / / _ \ |
| |___| (_| | | | (_| |\ V /  __/ |
|______\__,_|_|  \__,_| \_/ \___|_|

Creating a &quot;laravel/laravel&quot; project at &quot;./laravel-octane-test&quot;
Installing laravel/laravel (v8.5.16)
...
Application ready! Build something amazing.
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;install-laravel-octane&quot;&gt;Install Laravel Octane&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$ composer require laravel/octane
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;安装成功后，读者可以直接执行 &lt;code&gt;artisan octane:install&lt;/code&gt; 来安装依赖；Octane 将提示你想使用的 server 类型。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;➜ php artisan octane:install

 Which application server you would like to use?:
  [0] roadrunner
  [1] swoole
 &gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你选择的是 RoadRunner，程序将会自动帮你安装 RoadRunner 所需的依赖；而如果你选择的是 Swoole，你只需要确保你已经手动安装了 PHP swoole 扩展。&lt;/p&gt;
&lt;h2 id=&quot;使用-roadrunner-server&quot;&gt;使用 RoadRunner Server&lt;/h2&gt;
&lt;p&gt;RoadRunner 的使用过程不尽人意，作者在安装过程中总会出现一些官方文档忽视的错误。&lt;/p&gt;
&lt;h3 id=&quot;下载-rr-可执行文件失败&quot;&gt;下载 rr 可执行文件失败&lt;/h3&gt;
&lt;p&gt;在执行 &lt;code&gt;octane:install&lt;/code&gt; 安装 RoadRunner 依赖时，作者本机根本无法通过 GitHub 下载 rr 可执行文件，提示的错误如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;In CommonResponseTrait.php line 178:

HTTP/2 403  returned for &quot;https://api.github.com/repos/spiral/roadrunner-binary/releases?page=1&quot;.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你也遇到了这样的错误，建议直接去 &lt;a href=&quot;https://github.com/spiral/roadrunner-binary/releases&quot;&gt;RoadRunner 官网&lt;/a&gt; 下载对应平台的 rr 可执行文件及 .rr.yaml 配置文件并放到项目根目录。如 macOS 平台的可执行文件及配置文件地址：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/spiral/roadrunner-binary/releases/download/v2.1.0/roadrunner-2.1.0-darwin-amd64.zip&quot;&gt;https://github.com/spiral/roadrunner-binary/releases/download/v2.1.0/roadrunner-2.1.0-darwin-amd64.zip&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/spiral/roadrunner-binary/blob/v2.1.0/.rr.yaml&quot;&gt;https://github.com/spiral/roadrunner-binary/blob/v2.1.0/.rr.yaml&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最后记得修改 rr 的可执行权限及 RoadRunner 的 Worker starting command。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;chmod +x ./rr
&lt;/code&gt;&lt;/pre&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;yaml&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;yaml&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;server&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;  # Worker starting command, with any required arguments.&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;  #&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;  # This option is required.&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;  command&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;php artisan octane:start --server=roadrunner --host=127.0.0.1 --port=8000&quot;&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;server:
  # Worker starting command, with any required arguments.
  #
  # This option is required.
  command: &amp;#x22;php artisan octane:start --server=roadrunner --host=127.0.0.1 --port=8000&amp;#x22;&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h3 id=&quot;ssl_valid-key-file-sslserverkey-does-not-exists&quot;&gt;ssl_valid: key file ‘/ssl/server.key’ does not exists&lt;/h3&gt;
&lt;p&gt;RoadRunner 的配置文件中，默认开启了 ssl 配置， 若你不需要启用 https 访问，可注释 http.ssl 配置。&lt;/p&gt;
&lt;h3 id=&quot;error-while-dialing-dial-tcp-1270017233&quot;&gt;Error while dialing dial tcp 127.0.0.1:7233&lt;/h3&gt;
&lt;p&gt;RoadRunner 默认开启 temporal 特性，其 listen 端口为 7233，若你不想启用该特性，可注释 temporal 配置。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;yaml&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;yaml&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;# Drop this section for temporal feature disabling.&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;temporal&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;# Drop this section for temporal feature disabling.
temporal:&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;blockquote&gt;
&lt;p&gt;关于 temporal 的信息可查看官网 &lt;a href=&quot;https://github.com/temporalio/sdk-php&quot;&gt;temporalio/sdk-php: Temporal PHP SDK&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;executable-file-not-found-in-path&quot;&gt;Executable file not found in $PATH&lt;/h3&gt;
&lt;p&gt;这种情况一般是配置文件中未制定程序执行路径，请检查以下配置。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Server.command&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;修改为 RoadRunner worker 的启动命令，如：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;php artisan octane:start —server=roadrunner —host=127.0.0.1 —port=8000
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Service.some_service_*.comment&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果你不想使用该特性，注释该配置。至此，作者的 RoadRunner &lt;strong&gt;终于&lt;/strong&gt;启动起来了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202104/29/XgEntLO1b4ouj3TG9Hv3T7H63GNQibLQBwNzH7sx.png&quot; alt=&quot;Laravel Octane RoadRunner&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;ab-test-for-roadrunner&quot;&gt;AB Test For RoadRunner&lt;/h3&gt;
&lt;p&gt;作者用自己的笔记本(2018-13inch/2.3GHz/16GB)做了一个简单的 AB Test，框架代码未做任何改动，为 Laravel 默认的 welcome 页面。&lt;/p&gt;
&lt;p&gt;经过改变不同的并发参数和请求数，得到的结果都如下图所示上下轻微波动，其 QPS 基本维持在 230/s 左右。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;➜  ~ ab -n 2000 -c 8 http://127.0.0.1:8000/
Server Software:
Server Hostname:        127.0.0.1
Server Port:            8000

Document Path:          /
Document Length:        17490 bytes

Concurrency Level:      8
Time taken for tests:   8.418 seconds
Complete requests:      2000
Failed requests:        0
Total transferred:      37042000 bytes
HTML transferred:       34980000 bytes
Requests per second:    237.59 [#/sec] (mean)
Time per request:       33.671 [ms] (mean)
Time per request:       4.209 [ms] (mean, across all concurrent requests)
Transfer rate:          4297.28 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        3   11   4.6     11      29
Processing:     3   20  34.8     15     270
Waiting:        3   18  34.8     12     270
Total:          7   31  35.2     25     284
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;默认情况下，Laravel 的 welcome 页面会先经过 web 中间件，最后在渲染 blade 页面；而 web 中间件包含大量 Cookie 和 Session 操作：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;protected&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $middlewareGroups &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;    &apos;web&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;        \App\Http\Middleware\EncryptCookies&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;        \Illuminate\Session\Middleware\StartSession&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;        \Illuminate\View\Middleware\ShareErrorsFromSession&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;        \App\Http\Middleware\VerifyCsrfToken&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;        \Illuminate\Routing\Middleware\SubstituteBindings&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    ],&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;];&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt; [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],
];&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;所以作者重新定义了一个测试路由，该路由不包含任何中间件（全局除外），并只输出一个 Hello World。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// RouteServiceProvider.php&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; boot&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    require&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; base_path&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;routes/test.php&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// test.php&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Route&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;/_test&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; () {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;Hello World&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;// RouteServiceProvider.php
public function boot()
{
    require base_path(&amp;#x27;routes/test.php&amp;#x27;);
}

// test.php
Route::get(&amp;#x27;/_test&amp;#x27;, function () {
    return &amp;#x27;Hello World&amp;#x27;;
});&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;再次测试后如下，可以看到其 QPS 已经达到官方宣传标准 2300/s（难道官方测试也是这样 Remove All Middleware?）。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ab -n 2000 -c 8 http://127.0.0.1:8000/_test

Server Software:
Server Hostname:        127.0.0.1
Server Port:            8000

Document Path:          /_test
Document Length:        11 bytes

Concurrency Level:      8
Time taken for tests:   0.867 seconds
Complete requests:      2000
Failed requests:        0
Total transferred:      374000 bytes
HTML transferred:       22000 bytes
Requests per second:    2307.81 [#/sec] (mean)
Time per request:       3.466 [ms] (mean)
Time per request:       0.433 [ms] (mean, across all concurrent requests)
Transfer rate:          421.45 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.1      0       3
Processing:     1    3   8.8      2     143
Waiting:        1    3   8.8      2     142
Total:          1    3   8.8      2     143
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上述测试过程中，作者本机的资源限制如下。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;~ ulimit -n
256
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;使用-swoole-server&quot;&gt;使用 Swoole Server&lt;/h2&gt;
&lt;p&gt;Swoole server 的使用就要顺畅多了；通过 pecl 安装好 PHP swoole 扩展后，无需任何配置就能启动。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202104/29/4fZ6XuboeSdtVIjCYrG6Pk9tXE9tc20oywd0Z5kA.png&quot; alt=&quot;Laravel Swoole&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;ab-test-for-swoole-server&quot;&gt;AB Test For Swoole Server&lt;/h3&gt;
&lt;p&gt;作者用同样的配置对 swoole server 进行 AB Test，结果如下，其 QPS 也基本维持在 230/s 左右。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ab -n 2000 -c 8 http://127.0.0.1:8000/_test

Server Software:        swoole-http-server
Server Hostname:        127.0.0.1
Server Port:            8000

Document Path:          /
Document Length:        17503 bytes

Concurrency Level:      8
Time taken for tests:   8.398 seconds
Complete requests:      2000
Failed requests:        0
Total transferred:      37130000 bytes
HTML transferred:       35006000 bytes
Requests per second:    238.15 [#/sec] (mean)
Time per request:       33.592 [ms] (mean)
Time per request:       4.199 [ms] (mean, across all concurrent requests)
Transfer rate:          4317.61 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        3   11   6.6     10     102
Processing:     4   20  50.3     12     442
Waiting:        2   18  50.3     11     441
Total:          7   30  50.9     23     450
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;无中间件路由测试结果如下，可以看到其 QPS 已达到了 1650/s。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ ab -n 2000 -c 8 http://127.0.0.1:8000/_test

Server Software:        swoole-http-server
Server Hostname:        127.0.0.1
Server Port:            8000

Document Path:          /_test
Document Length:        21 bytes

Concurrency Level:      8
Time taken for tests:   1.212 seconds
Complete requests:      2000
Failed requests:        0
Total transferred:      528000 bytes
HTML transferred:       42000 bytes
Requests per second:    1650.63 [#/sec] (mean)
Time per request:       4.847 [ms] (mean)
Time per request:       0.606 [ms] (mean, across all concurrent requests)
Transfer rate:          425.55 [Kbytes/sec] received
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;从 AB Test 结果来看，两种 Server 的性能基本持平；但由于是在本地开发环境测试，未考虑到的因素较多，测试结果仅供参考。&lt;/p&gt;
&lt;h2 id=&quot;部署上线&quot;&gt;部署上线&lt;/h2&gt;
&lt;p&gt;Laravel Octane 虽然提供了 start 命令用于启动 Server，但该命令只能在前台运行（不支持 -d）；在部署到生产环境时，常见的办法还是利用 &lt;a href=&quot;http://supervisord.org/&quot;&gt;Supervisor&lt;/a&gt; 来进行进程管理。读者可以参考 &lt;a href=&quot;https://github.com/laravel/sail/blob/1.x/runtimes/8.0/supervisord.conf&quot;&gt;Laravel Sail&lt;/a&gt; 的 Supervisor 配置。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[program:php]
command=/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan serve --host=127.0.0.1 --port=80
user=sail
environment=LARAVEL_SAIL=&quot;1&quot;
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;后续持续交付时，可通过 Jenkins 连接到服务节点，使用 &lt;code&gt;octane:reload&lt;/code&gt; 命令重新加载服务。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;stage(&quot;部署 ${ip}&quot;) {
    withCredentials([sshUserPrivateKey(credentialsId: env.HOST_CRED, keyFileVariable: &apos;identity&apos;)]) {
        remote.user = &quot;${env.HOST_USER}&quot;
        remote.identityFile = identity
        sshCommand remote: remote, command: &quot;php artisan config:cache &amp;#x26;&amp;#x26; php artisan route:cache &amp;#x26;&amp;#x26; php artisan octane:reload&quot;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不过这里需要注意的是，当你更新了 Composer 依赖，如新增了一个第三方包时，你最好在生产环境重启下 Laravel Octane。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo supervisorctl -c /etx/supervisorctl.conf restart program:php
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;否则可能会出现如 Class “Godruoyi\Snowflake\Snowflake” not found 的错误。&lt;/p&gt;
&lt;h2 id=&quot;laravel-octane-是线程安全的吗&quot;&gt;Laravel Octane 是线程安全的吗？&lt;/h2&gt;
&lt;p&gt;在回答这个问题之前，我们先来看看 Laravel Octane 的请求处理流程。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202104/29/GUz7CA0H2G9SR2GXbiWHvvkQfkuyeUr5lXsMTE2b.png&quot; alt=&quot;Laravel Octane&quot;&gt;&lt;/p&gt;
&lt;p&gt;随着 Server 的启动，程序会创建指定数量的 Worker 进程。当请求到来时，会从可用的 Worker 列表中选取一个并交由他处理。每个 Worker 同一时刻只能处理一个请求，在请求处理过程中，对资源（变量/静态变量/文件句柄/链接）的修改并不会存在竞争关系，所以 Laravel Octane 时线程(进程)安全的。&lt;/p&gt;
&lt;p&gt;这其实和 FPM 模型是一致的，不同的地方在于 FPM 模型在处理完一个请求后，会销毁该请求申请的所有内存；后续请求到来时，依然要执行完整的 PHP 初始化操作（参考 &lt;a href=&quot;https://tadimy.gitbooks.io/php-internals/content/php-fpm-start-up.html&quot;&gt;PHP-FPM 启动分析&lt;/a&gt;）。而 Laravel Octane 的初始化操作是随着 Worker Boot 进行的，在整个 Worker 的生命周期内，只会进行一次初始操作（程序启动的时候）。后续请求将直接复用原来的资源。如上图，Worker Boot 完成后，将会初始化 Laravel Application Container，而后续的所有请求，都将复用该 App 实例。&lt;/p&gt;
&lt;h2 id=&quot;laravel-octane-工作原理&quot;&gt;Laravel Octane 工作原理&lt;/h2&gt;
&lt;p&gt;Octane 只是一个壳，真正处理请求都是由外部的 Server 处理的。不过 Octane 的设计还是值得一说的。&lt;/p&gt;
&lt;p&gt;从源码也可以看出，随着 Worker 的 Boot 完成，Laravel Application 已被成功初始化。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// vendor/laravel/octane/src/Worker.php&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; boot&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;array&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $initialInstances &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; [])&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; void&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;    $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;app &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $app &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;appFactory&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;createApplication&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;        array_merge&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;            $initialInstances,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;            [&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Client&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;client],&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        )&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    );&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;    $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;dispatchEvent&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($app, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; WorkerStarting&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($app));&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;app = $app = $this-&gt;appFactory-&gt;createApplication(
        array_merge(
            $initialInstances,
            [Client::class =&gt; $this-&gt;client],
        )
    );

    $this-&gt;dispatchEvent($app, new WorkerStarting($app));
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;在处理后续到来的请求时，Octane 通过 &lt;code&gt;clone $this-&gt;app&lt;/code&gt; 获取一个沙箱容器。后续的所有操作都是基于这个沙箱容器来进行的，不会影响到原有的 Container。在请求结束后，Octane 会清空沙箱容器并 unset 不再使用的对象。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; handle&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Request&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $request, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;RequestContext&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $context)&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; void&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;    CurrentApplication&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($sandbox &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; clone&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;app);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    try&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        $response &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $sandbox&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;make&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Kernel&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;handle&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($request); &lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    } &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;catch&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Throwable&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $e) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;        $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;handleWorkerError&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($e, $sandbox, $request, $context, $responded);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    } &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;finally&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        $sandbox&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;flush&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;        unset&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($gateway, $sandbox, $request, $response, $octaneResponse, $output);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;        CurrentApplication&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;app);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;app);

    try {
        $response = $sandbox-&gt;make(Kernel::class)-&gt;handle($request); 

    } catch (Throwable $e) {
        $this-&gt;handleWorkerError($e, $sandbox, $request, $context, $responded);
    } finally {
        $sandbox-&gt;flush();

        unset($gateway, $sandbox, $request, $response, $octaneResponse, $output);

        CurrentApplication::set($this-&gt;app);
    }
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;blockquote&gt;
&lt;p&gt;再次注意，由于同一个 Worker 进程同一时刻只能处理一个请求，故这里是不存在竞争的，即使是对 static 变量的修改，也是安全的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;注意事项--第三方包适配&quot;&gt;注意事项 &amp;#x26; 第三方包适配&lt;/h2&gt;
&lt;p&gt;由于同一个 Worker 的多个请求会共享同一个容器实例，所以在向容器中注册单例对象时，应该特别小心。如下面的例子：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; register&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;    $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;app&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;singleton&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Service&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($app) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Service&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($app[&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;request&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    });&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;app-&gt;singleton(Service::class, function ($app) {
        return new Service($app[&amp;#x27;request&amp;#x27;]);
    });
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;例子中采用 singleton 注册一个单例对象 Service，当该对象在某个 Provider 的 Boot 方法被初始化时，应用容器中将始终保持着唯一的 Service 对象；后续 Worker 在处理的其他请求时，从 Service 中获取的 request 对象将是相同的。&lt;/p&gt;
&lt;p&gt;解决方法是你可以换一种绑定方式，或者使用闭包。最值得推荐的办法是只传入你需要的请求信息。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; App\Service&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;app&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;bind&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Service&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($app) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Service&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($app[&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;request&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;app&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;singleton&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Service&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($app) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Service&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;fn&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; () =&gt; $app[&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;request&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// Or...&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$service&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;method&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($request&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;input&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;name&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;));&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;app-&gt;bind(Service::class, function ($app) {
    return new Service($app[&amp;#x27;request&amp;#x27;]);
});

$this-&gt;app-&gt;singleton(Service::class, function ($app) {
    return new Service(fn () =&gt; $app[&amp;#x27;request&amp;#x27;]);
});

// Or...

$service-&gt;method($request-&gt;input(&amp;#x27;name&amp;#x27;));&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;强烈推荐读者阅读官方提出的&lt;a href=&quot;https://github.com/laravel/octane#container-injection&quot;&gt;注意事项&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&quot;参考&quot;&gt;参考&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Upgrade to PHP 8 with Homebrew on Mac &lt;a href=&quot;https://stitcher.io/blog/php-8-upgrade-mac&quot;&gt;https://stitcher.io/blog/php-8-upgrade-mac&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Laravel Octane &lt;a href=&quot;https://github.com/laravel/octane&quot;&gt;https://github.com/laravel/octane&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Laravel Sail &lt;a href=&quot;https://github.com/laravel/sail&quot;&gt;https://github.com/laravel/sail&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;FastCgi 与 PHP-fpm 之间的关系 &lt;a href=&quot;https://godruoyi.com/posts/the-relationship-between-fastcgi-and-php-fpm&quot;&gt;https://godruoyi.com/posts/the-relationship-between-fastcgi-and-php-fpm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;PHP-FPM vs Swoole &lt;a href=&quot;https://developpaper.com/php-fpm-vs-swoole/&quot;&gt;https://developpaper.com/php-fpm-vs-swoole/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Swoole 编程须知 &lt;a href=&quot;https://wiki.swoole.com/#/getting_started/notice&quot;&gt;https://wiki.swoole.com/#/getting_started/notice&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Go 语言的雪花算法实现</title><link>https://godruoyi.com/posts/golang-snowflake/</link><guid isPermaLink="true">https://godruoyi.com/posts/golang-snowflake/</guid><description>An Lock Free ID Generator for Golang based on Snowflake Algorithm (Twitter announced).</description><pubDate>Thu, 15 Apr 2021 08:30:29 GMT</pubDate><content:encoded>&lt;p&gt;An &lt;strong&gt;Lock Free&lt;/strong&gt; ID Generator for Golang implementation &lt;a href=&quot;https://github.com/godruoyi/go-snowflake&quot;&gt;View on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/logos/201908/13/_1565672621_LPW65Pi8cG.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;p&gt;Snowflake is a network service for generating unique ID numbers at high scale with some simple guarantees.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The first bit is unused sign bit.&lt;/li&gt;
&lt;li&gt;The second part consists of a 41-bit timestamp (milliseconds) whose value is the offset of the current time relative to a certain time.&lt;/li&gt;
&lt;li&gt;The 10 bits machineID(5 bit workid + 5 bit datacenter id), max value is 2^10 -1 = 1023.&lt;/li&gt;
&lt;li&gt;The last part consists of 12 bits, its means the length of the serial number generated per millisecond per working node, a maximum of 2^12 -1 = 4095 IDs can be generated in the same millisecond.&lt;/li&gt;
&lt;li&gt;The binary length of 41 bits is at most 2^41 -1 millisecond = 69 years. So the snowflake algorithm can be used for up to 69 years, In order to maximize the use of the algorithm, you should specify a start time for it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The ID generated by the snowflake algorithm is not guaranteed to be unique. For example, when two different requests enter the same machine at the same time, and the sequence generated by the node is the same, the generated ID will be duplicated.&lt;/p&gt;
&lt;p&gt;So if you want use the snowflake algorithm to generate unique ID, You must ensure: The sequence-number generated in the same millisecond of the same node is unique.&lt;/p&gt;
&lt;p&gt;Based on this, we created this package and integrated multiple sequence-number providers into it.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AtomicResolver (base sync/atmoic)&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Each provider only needs to ensure that the serial number generated in the same millisecond is different. You can get a unique ID.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;feature&quot;&gt;Feature&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;✅ Lock Free&lt;/li&gt;
&lt;li&gt;🎈 Zero configuration, out of the box&lt;/li&gt;
&lt;li&gt;🚀 Concurrency safety&lt;/li&gt;
&lt;li&gt;🌵 Support private ip to machineid&lt;/li&gt;
&lt;li&gt;🐡 Support custom sequence resolver&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;installation&quot;&gt;Installation&lt;/h2&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;shell&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;shell&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; go&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; get&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; github.com/godruoyi/go-snowflake&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;$ go get github.com/godruoyi/go-snowflake&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&quot;useage&quot;&gt;Useage&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;simple to use.&lt;/li&gt;
&lt;/ol&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;package&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; main&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;fmt&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;github.com/godruoyi/go-snowflake&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; main&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    id &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; snowflake.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;ID&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    fmt.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;Println&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(id)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    // 1537200202186752&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;package main

import (
    &amp;#x22;fmt&amp;#x22;

    &amp;#x22;github.com/godruoyi/go-snowflake&amp;#x22;
)

func main() {
    id := snowflake.ID()
    fmt.Println(id)
    // 1537200202186752
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Specify the MachineID.&lt;/li&gt;
&lt;/ol&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;package&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; main&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;fmt&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;github.com/godruoyi/go-snowflake&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; main&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    snowflake.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;SetMachineID&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    // Or set private ip to machineid, testing...&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    // snowflake.SetMachineID(snowflake.PrivateIPToMachineID())&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    id &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; snowflake.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;ID&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    fmt.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;Println&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(id)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;package main

import (
    &amp;#x22;fmt&amp;#x22;

    &amp;#x22;github.com/godruoyi/go-snowflake&amp;#x22;
)

func main() {
    snowflake.SetMachineID(1)

    // Or set private ip to machineid, testing...
    // snowflake.SetMachineID(snowflake.PrivateIPToMachineID())

    id := snowflake.ID()
    fmt.Println(id)
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Specify start time.&lt;/li&gt;
&lt;/ol&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;package&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; main&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;fmt&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;time&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;github.com/godruoyi/go-snowflake&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; main&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    snowflake.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;SetStartTime&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(time.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;Date&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;2014&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;9&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, time.UTC))&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    id &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; snowflake.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;ID&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    fmt.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;Println&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(id)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;package main

import (
    &amp;#x22;fmt&amp;#x22;
    &amp;#x22;time&amp;#x22;

    &amp;#x22;github.com/godruoyi/go-snowflake&amp;#x22;
)

func main() {
    snowflake.SetStartTime(time.Date(2014, 9, 1, 0, 0, 0, 0, time.UTC))
    id := snowflake.ID()
    fmt.Println(id)
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;Parse ID.&lt;/li&gt;
&lt;/ol&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;package&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; main&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;fmt&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;time&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;github.com/godruoyi/go-snowflake&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; main&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    id &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; snowflake.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;ID&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    sid &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; snowflake.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;ParseID&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(id)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    fmt.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;Println&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(sid.ID)             &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// 132271570944000000&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    fmt.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;Println&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(sid.MachineID)      &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// 0&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    fmt.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;Println&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(sid.Sequence)       &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// 0&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    fmt.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;Println&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(sid.Timestamp)      &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// 31536000000&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    fmt.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;Println&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(sid.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;GenerateTime&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()) &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// 2009-11-10 23:00:00 +0000 UTC&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;package main

import (
    &amp;#x22;fmt&amp;#x22;
    &amp;#x22;time&amp;#x22;

    &amp;#x22;github.com/godruoyi/go-snowflake&amp;#x22;
)

func main() {
    id := snowflake.ID()
    sid := snowflake.ParseID(id)

    fmt.Println(sid.ID)             // 132271570944000000
    fmt.Println(sid.MachineID)      // 0
    fmt.Println(sid.Sequence)       // 0
    fmt.Println(sid.Timestamp)      // 31536000000
    fmt.Println(sid.GenerateTime()) // 2009-11-10 23:00:00 +0000 UTC
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&quot;best-practices&quot;&gt;Best practices&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;⚠️⚠️ All SetXXX method is thread-unsafe, recommended you call him in the main function.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;package&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; main&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;fmt&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;time&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;net/http&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;github.com/godruoyi/go-snowflake&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; main&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    snowflake.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;SetMachineID&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// change to your machineID&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    snowflake.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;SetStartTime&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(time.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;Date&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;2014&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;9&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, time.UTC))&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    http.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;HandleFunc&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;/order&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, submitOrder)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    http.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;ListenAndServe&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;:8090&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;nil&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; submitOrder&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#E36209;--shiki-dark:#F69D50&quot;&gt;w&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; http&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;ResponseWriter&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#E36209;--shiki-dark:#F69D50&quot;&gt;req&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;http&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;Request&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    orderId &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; snowflake.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;ID&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    // save order&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;package main

import (
    &amp;#x22;fmt&amp;#x22;
    &amp;#x22;time&amp;#x22;
    &amp;#x22;net/http&amp;#x22;

    &amp;#x22;github.com/godruoyi/go-snowflake&amp;#x22;
)

func main() {
    snowflake.SetMachineID(1) // change to your machineID
    snowflake.SetStartTime(time.Date(2014, 9, 1, 0, 0, 0, 0, time.UTC))

    http.HandleFunc(&amp;#x22;/order&amp;#x22;, submitOrder)
    http.ListenAndServe(&amp;#x22;:8090&amp;#x22;, nil)
}

func submitOrder(w http.ResponseWriter, req *http.Request) {
    orderId := snowflake.ID()
    // save order
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&quot;advanced&quot;&gt;Advanced&lt;/h2&gt;
&lt;p&gt;Custom sequence resolver. you can customize the sequence-number resolver by following way:&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;package&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; main&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;fmt&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;time&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;github.com/godruoyi/go-snowflake&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; yourSequenceNumber&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#E36209;--shiki-dark:#F69D50&quot;&gt;ms&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; int64&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;uint16&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// usage&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;snowflake.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;SetSequenceResolver&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(yourSequenceNumber)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;snowflake.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;ID&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;package main

import (
    &amp;#x22;fmt&amp;#x22;
    &amp;#x22;time&amp;#x22;

    &amp;#x22;github.com/godruoyi/go-snowflake&amp;#x22;
)

func yourSequenceNumber(ms int64) (uint16, error) {

}

// usage

snowflake.SetSequenceResolver(yourSequenceNumber)
snowflake.ID()&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;</content:encoded></item><item><title>Blog 3 周年</title><link>https://godruoyi.com/posts/the-third-anniversary-of-the-blog/</link><guid isPermaLink="true">https://godruoyi.com/posts/the-third-anniversary-of-the-blog/</guid><description>2021-04-04 号，Blog 就满了 3 周年了。作者已经忘记了 Blog 具体上线时间了，只记得 Timeline 里，最早的文章发布于 2018-04-04。</description><pubDate>Fri, 02 Apr 2021 05:12:22 GMT</pubDate><content:encoded>&lt;p&gt;2021-04-04 号，博客就满了 3 周年了。作者已经忘记了博客具体上线时间了，只记得 &lt;a href=&quot;https://godruoyi.com/timeline&quot;&gt;Timeline&lt;/a&gt; 里，最早的文章发布于 2018-04-04。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202104/02/JW18cNvV8nqwpmWZbwIwaLYO1BuXDhfzx8rq6OuS.png&quot; alt=&quot;blog-3-周年&quot;&gt;&lt;/p&gt;
&lt;p&gt;三年前因为朋友推荐的便宜服务器，120/年，可续费三次；心想搭个博客吧，三年后看看自己能折腾成什么样子；而一晃三年真过去了，自己才写了不到 50 篇文章，博客也没有变成想象中成功的样子。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202104/02/ZljyAUGF5wWMNoFjKKLCqefVecxzpps9wbGyZmcf.png&quot; alt=&quot;全部文章&quot;&gt;
截图于 2021-03-26&lt;/p&gt;
&lt;h2 id=&quot;google-analytics&quot;&gt;Google Analytics&lt;/h2&gt;
&lt;p&gt;Google Analytics 显示去年一年的 uv 才不到 1000 人（博客上线很长一段时间后才开始支持 Google Analytics，所以前面 10 个月都没有数据记录）。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202104/02/u7zuHCVT31DKbvmlotZYeL3r1L4I1laRNG1bCMwo.png&quot; alt=&quot;google uv&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;pv&quot;&gt;PV&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202104/02/Go9ELrByvQuWPEWSl0fGAJz9qMQuq79Yv39t0mMp.png&quot; alt=&quot;google pv&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;最常访问路径&quot;&gt;最常访问路径&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202104/02/y7lDL5PR8KkUvND9AzT87HdSuhA6Hc6dPDaQbArE.png&quot; alt=&quot;网页标题和屏幕类&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这时我又想起了那段话：「我没有成名，没能兑现向热尼娅许下的诺言，但是还有一点我没有失信：我在一堆缴获的汽车当中，以廉价买了一部旧奥佩尔。我换上一套簇新的衣服，开着奥佩尔向大剧院驶去」。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;当然，这是我记录生活记录感悟的地方，太多人关注说不定还影响自己内心的想法；只希望 10 年 20 年后，博客还在，回头再看看这些年的变化时，能对着屏幕会心一笑。&lt;/p&gt;
&lt;p&gt;前几天得到了老婆的支持，又准备续三年的服务了。&lt;/p&gt;</content:encoded></item><item><title>Go 语言的词法分析和语法分析(2)—Import声明的解析</title><link>https://godruoyi.com/posts/go-lexical-analysis-and-syntax-analysis-2-parsing-of-import-declarations/</link><guid isPermaLink="true">https://godruoyi.com/posts/go-lexical-analysis-and-syntax-analysis-2-parsing-of-import-declarations/</guid><description>Go 语言编译原理，import 声明的解析。上篇文章 Go 语言的词法分析和语法分析(1)作者阐述了渐渐式词法分析的过程；通过这一步，源文件已被转化为 Syntax.File 结构体，其属性情况如下</description><pubDate>Thu, 25 Mar 2021 07:21:21 GMT</pubDate><content:encoded>&lt;p&gt;上篇文章 &lt;a href=&quot;https://godruoyi.com/posts/golang-lexer-and-parser-1&quot;&gt;Go 语言的词法分析和语法分析(1)&lt;/a&gt; 作者阐述了渐渐式词法分析的过程；通过这一步，源文件已被转化为 Syntax.File 结构体，其属性情况如下：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202103/25/2L6DRYf2lNnwVD56raqPKuxhmQQSbNQcAG2pxV5Z.png&quot; alt=&quot;Syntax.File结构体&quot;&gt;
图 2.0 Syntax.File 结构体&lt;/p&gt;
&lt;p&gt;File 结构体中的 PkgName 属性已被初始化为 &lt;code&gt;syntax.Name{Value: “main”}&lt;/code&gt;。Go 语言扫描器 Scanner 再处理完包名 PkgName 后，下一次解析将扫描出 import token；此时 *scanner 的 b, r, e 属性如下图所示：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202103/25/ng8JzjoPKR3nwKPJNGm1om0tbJDVneoF0YKTDap7.png&quot; alt=&quot;import token&quot;&gt;
图 2.1 import token&lt;/p&gt;
&lt;p&gt;同样，通过 &lt;code&gt;buf[b:r]&lt;/code&gt; 计算出当前扫描的 token 为 import 字符串，对比 Go 关键字列表后将 tok 设置为内置 _Import，此时 *scanner 内部各属性如下图所示：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202103/25/NiW0twBvLRF33mamaWisA5gTqDlDPNg7xOF0AieP.png&quot; alt=&quot;scanner&quot;&gt;
图 2.2 scanner content&lt;/p&gt;
&lt;h2 id=&quot;importdecl-解析&quot;&gt;ImportDecl 解析&lt;/h2&gt;
&lt;p&gt;然后扫描器将开始&lt;strong&gt;循环&lt;/strong&gt;解析源文件的 import 声明，直至扫描到的 token 不等于 _Import，并把 import 的 path 部分（如 net/http, main）等保存到扫描器的 DeclList 数组（slice） 中。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; p.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;got&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(_Import) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    f.DeclList &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; p.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;appendGroup&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(f.DeclList, p.importDecl)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#E36209;--shiki-dark:#F69D50&quot;&gt;p &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;parser&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;got&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#E36209;--shiki-dark:#F69D50&quot;&gt;tok&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; token&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;bool&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; p.tok &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; tok {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        p.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;next&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; false&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;for p.got(_Import) {
    f.DeclList = p.appendGroup(f.DeclList, p.importDecl)
}

func (p *parser) got(tok token) bool {
    if p.tok == tok {
        p.next()
        return true
    }
    return false
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Import 有多种不同格式的声明，常见文法有：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; . path&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;/package&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; alias path&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;/package&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; path&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;/package&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; _ path&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;/package&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; by group&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    . path&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;/package&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    alias path&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;/package&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    path&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;/package&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    _ path&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;/package&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;import . path/package
import alias path/package
import path/package
import _ path/package

// by group
import (
    . path/package
    alias path/package
    path/package
    _ path/package
)&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Go 语言内部用 ImportDecl 结构体来描述 import 声明，其结构如下：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;ImportDecl &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;struct&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    Group &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;Group&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;       // 分组，同一括号下的为同一组&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    LocalPkgName &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;Name&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt; // 别名&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    Path &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;BasicLit&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;     // 路径，如 net/http&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;ImportDecl struct {
    Group *Group       // 分组，同一括号下的为同一组
    LocalPkgName *Name // 别名
    Path *BasicLit     // 路径，如 net/http
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;在进行 import 词法解析时， Go 语言会尝试初始化 ImportDecl 结构体并为其各属性赋值；下面作者针对不同情况来查看赋值后的 ImportDecl 结构体内容。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;假设申明语句为 &lt;code&gt;import &quot;net/http&quot;&lt;/code&gt;，解析完该语句后结构体内容如下：&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202103/25/CyBYhPqOD869kZdRrtOjTzPI27jXg6yZFVdwQcYM.png&quot; alt=&quot;常见的 Import 声明&quot;&gt;
图 2.3 常见的 Import 声明&lt;/p&gt;
&lt;p&gt;结构体中的 PkgName 属性（包别名）被设置为 &lt;code&gt;syntax.Name{Value: &quot;&quot;}&lt;/code&gt;，Group 被设置为 nil，Path 部分被设置为 &lt;code&gt;syntax.BasicLit{Value: &quot;net/http&quot;}&lt;/code&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;假设申明语句为 &lt;code&gt;import alias &quot;net/http&quot;&lt;/code&gt;，解析完该语句后结构体内容如下：&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202103/25/LSbM30j1EG4nXkM088bc95eUmidja8pmc5n8TTCi.png&quot; alt=&quot;别名类型的 import 声明&quot;&gt;
图 2.4 别名类型的 import 声明&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;假设申明语句为 &lt;code&gt;import . &quot;net/http&quot;&lt;/code&gt;，解析完该语句后结构体内容如下：&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202103/25/tXHDTQgVjY3v2qxlCBaIGGEHfC4KtmZJIdNPzPza.png&quot; alt=&quot;2.5 Dot 格式的 import 声明&quot;&gt;
图 2.5 Dot 格式的 import 声明&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;假设申明语句为下面分组&lt;/li&gt;
&lt;/ul&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;fmt&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;net/http&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;import (
    &amp;#x22;fmt&amp;#x22;
    &amp;#x22;net/http&amp;#x22;
)&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;解析完该语句后结构体内容如下（多个 *ImportDecl 指向同一 Group）：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202103/25/kp1L9KvydguJVSozmYru3wRJtG1ajcxpkYBdmEK3.png&quot; alt=&quot;Import Group&quot;&gt;
图 2.6 Import Group&lt;/p&gt;
&lt;p&gt;至此，Import 语法申明已基本解析完毕，接下来 Go 语言将解析 &lt;code&gt;const, val, func, type&lt;/code&gt; 等 &lt;em&gt;TopLevelDecl&lt;/em&gt; 级别的声明；毕竟 Go 语言的顶级申明就只有这六个关键字，全部解析完毕后，Go 语言的词法语法分析就完成了。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;package&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;import&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;const&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;var&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;type&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;package
import
const
var
type
func&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;针对上篇文章 &lt;a href=&quot;https://godruoyi.com/posts/golang-lexer-and-parser-1&quot;&gt;Go 语言的词法分析和语法分析(1)&lt;/a&gt; 中的 main.go 文件，进过这一步解析后，syntax.File 结构体信息如下：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202103/25/CdljsZGp8Pt7tO9Y2MtsKqs7TNynhl5nBit8fGnd.png&quot; alt=&quot;import声明解析后的结构体信息&quot;&gt;
图 2.7 import声明解析后的结构体信息&lt;/p&gt;
&lt;h2 id=&quot;topleveldecl&quot;&gt;TopLevelDecl&lt;/h2&gt;
&lt;p&gt;下面是 Go 语言处理完 import 声明后开始处理其他 TopLevelDecl 的函数代码，代码所在位置 &lt;a href=&quot;https://github.com/golang/go/blob/master/src/cmd/compile/internal/syntax/parser.go#L399&quot;&gt;src/cmd/compile/internal/syntax/parser.go:399&lt;/a&gt;。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// { ImportDecl &quot;;&quot; }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; p.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;got&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(_Import) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    f.DeclList &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; p.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;appendGroup&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(f.DeclList, p.importDecl)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// { TopLevelDecl &quot;;&quot; }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; p.tok &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;!=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; _EOF {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    switch&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; p.tok {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    case&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; _Const:&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;        // 处理常量&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    case&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; _Type:&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;        // 处理类型定义&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    case&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; _Var:&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;        // 处理变量&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    case&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; _Func:&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;        // 处理函数&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    default&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        p.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;syntaxError&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;non-declaration statement outside function body&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;// { ImportDecl &amp;#x22;;&amp;#x22; }
for p.got(_Import) {
    f.DeclList = p.appendGroup(f.DeclList, p.importDecl)
}

// { TopLevelDecl &amp;#x22;;&amp;#x22; }
for p.tok != _EOF {
    switch p.tok {
    case _Const:
        // 处理常量
    case _Type:
        // 处理类型定义
    case _Var:
        // 处理变量
    case _Func:
        // 处理函数
    default:
        p.syntaxError(&amp;#x22;non-declaration statement outside function body&amp;#x22;)
    }
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;从这里也能看出，import 声明只能放在 package 声明后面，其他顶级声明前面；因为在 import 声明解析完成后，扫描器将进入下一个 for 循环继续解析其他 TopLevelDecl，若这时再遇到 _Import token，程序将走到 switch 的 default 分支，从而发生编译错误。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;./prog.go:11:1: syntax error: non-declaration statement outside function body
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;接下来阅读&quot;&gt;接下来阅读&lt;/h2&gt;
&lt;p&gt;下篇文章将探讨 Go 语言对常量的解析，这部分作者将在后面的文章中继续输出。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/golang-lexer-and-parser-1&quot;&gt;Go 语言的词法分析和语法分析(1)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;当前 &lt;a href=&quot;https://godruoyi.com/posts/go-lexical-analysis-and-syntax-analysis-2-parsing-of-import-declarations&quot;&gt;Go 语言的词法分析和语法分析(2)-Import申明的解析&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Go 语言的词法分析和语法分析(1)</title><link>https://godruoyi.com/posts/golang-lexer-and-parser-1/</link><guid isPermaLink="true">https://godruoyi.com/posts/golang-lexer-and-parser-1/</guid><description>Go 语言编译原理——说一下 Go 语言中的词法分析和语法分析，尽量不涉及具体的源码探索；</description><pubDate>Mon, 22 Mar 2021 07:02:41 GMT</pubDate><content:encoded>&lt;p&gt;这篇文章是在看完 Go 语言设计与实现前两章词法分析及语法分析后的总结，作者尽量站在宏观的角度，说一下 Go 语言中的词法分析和语法分析，尽量不涉及具体的源码探索。&lt;/p&gt;
&lt;h2 id=&quot;词法分析&quot;&gt;词法分析&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;词法分析（lexical analysis）是 &lt;a href=&quot;https://zh.wikipedia.org/wiki/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6&quot;&gt;计算机科学&lt;/a&gt; 中将字符序列转换为&lt;strong&gt;标记&lt;/strong&gt;（token）序列的过程——&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E8%AF%8D%E6%B3%95%E5%88%86%E6%9E%90&quot;&gt;词法分析 - 维基百科，自由的百科全书&lt;/a&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202103/22/gxI2xaLqVqwSfnjzOAalfmYKKIu5idtFYKREkDsW.png&quot; alt=&quot;Go 语言编译原理——词法分析&quot;&gt;
图 1.0 词法分析&lt;/p&gt;
&lt;p&gt;可以简单的理解为将源代码按一定的转换规则，翻译为字符序列的过程。&lt;/p&gt;
&lt;p&gt;如给定的如下转换规则：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;c&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;c&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;%%&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;package      &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;printf&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;PACKAGE &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;import       &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;printf&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;IMPORT &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;func         &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;printf&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;FUNC &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;\.           &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;printf&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;DOT &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;\{           &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;printf&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;LBRACE &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;\}           &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;printf&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;RBRACE &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;\(           &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;printf&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;LPAREN &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;\)           &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;printf&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;RPAREN &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;\&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;           printf(&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;QUOTE &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#F47067&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;           printf(&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;[0-9]+       printf(&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;NUMBER &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;[a-zA-Z_]+   printf(&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;IDENT &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#F47067&quot;&gt;%%&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;%%
package      printf(&amp;#x22;PACKAGE &amp;#x22;);
import       printf(&amp;#x22;IMPORT &amp;#x22;);
func         printf(&amp;#x22;FUNC &amp;#x22;);
\.           printf(&amp;#x22;DOT &amp;#x22;);
\{           printf(&amp;#x22;LBRACE &amp;#x22;);
\}           printf(&amp;#x22;RBRACE &amp;#x22;);
\(           printf(&amp;#x22;LPAREN &amp;#x22;);
\)           printf(&amp;#x22;RPAREN &amp;#x22;);
\&amp;#x22;           printf(&amp;#x22;QUOTE &amp;#x22;);
\n           printf(&amp;#x22;\n&amp;#x22;);
[0-9]+       printf(&amp;#x22;NUMBER &amp;#x22;);
[a-zA-Z_]+   printf(&amp;#x22;IDENT &amp;#x22;);
%%&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;其中第一条规则表示将源代码中的 &lt;code&gt;package&lt;/code&gt; 翻译为 &lt;code&gt;PACKAGE &lt;/code&gt;，其他类似；若用该规则来翻译如下的源代码：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;package&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; main&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;    &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;fmt&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; main&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    fmt.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;Println&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;Hello&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;package main

import (
    &amp;#x22;fmt&amp;#x22;
)

func main() {
    fmt.Println(&amp;#x22;Hello&amp;#x22;)
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;则输出的 Token 序列为：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;PACKAGE  IDENT

IMPORT  LPAREN
	QUOTE IDENT QUOTE
RPAREN

FUNC  IDENT LPAREN RPAREN  LBRACE
	IDENT DOT IDENT LPAREN QUOTE IDENT QUOTE RPAREN
RBRACE
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出的 Token 序列已经看不出是什么语言了；当然，这种纯字符的 Token 对后续的分析帮助不大，一般分析器都会把他封装为一个结构体，以包含更多信息。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; tokenx&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; struct&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    tok &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;token&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;   // 解析的 Token 序列（如上的 IDENT）&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    lim &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;  // 原始值（如 main）&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;type tokenx struct {
    tok token   // 解析的 Token 序列（如上的 IDENT）
    lim string  // 原始值（如 main）
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&quot;语法分析&quot;&gt;语法分析&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202103/22/cu0uJeewgtcWhNDXusRY9WnwNU40KVYFnpeZmMwy.png&quot; alt=&quot;Go 语言编译原理——语法分析&quot;&gt;
图 2.0 语法分析&lt;/p&gt;
&lt;p&gt;语法分析是将词法分析的输出当作输入，按照给定的语法格式进行分析并确定其语法结构的一种过程。&lt;/p&gt;
&lt;p&gt;举个列子，假设输入序列为 S，对应的语法规则为：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;S -&gt; E + F
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其含义为输入序列 S 可以表示为两个子项 E、F 相加；而 E 可以表示为：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;E -&gt; E + F
  -&gt; F
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其含义为 E 可以分解为两个子项 E、F 相加；或分解为一个单独的 F 项；&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;F -&gt; num{1, 2, 3, 4, 5}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;而 F 表示数字集合，假设只有上述数字集合，即 F ∈ {1, 2, 3, 4, 5}。&lt;/p&gt;
&lt;p&gt;则下面的字符序列都是有意义的，即&lt;strong&gt;给定的字符序列 S，是否能从语法规则中被推导出来&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;1 + 1
1 + 2
2 + 3 + 4
1 + 2 + 3 + 4
1 + 3 + 2 + 4 + 5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;假设给定的字符序列 S 为 2 + 3 + 4，按上述的规则 S -&gt; E + F，分步推到如下（按最右推导，即从最右边字符开始）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;S -&gt; E + F // 原规则&lt;/li&gt;
&lt;li&gt;S -&gt; E + 1 // 将 F 解析为数字 1，1 ≠ 4 继续回溯&lt;/li&gt;
&lt;li&gt;S -&gt; E + 2 // 2 ≠ 4 继续回溯&lt;/li&gt;
&lt;li&gt;S -&gt; E + 3 // 3 ≠ 4 继续回溯&lt;/li&gt;
&lt;li&gt;S -&gt; E + 4 // ok，数字 4 已最小不可分解&lt;/li&gt;
&lt;li&gt;S -&gt; (E + F) + 4 // 将 E 按推导为 E + F 格式&lt;/li&gt;
&lt;li&gt;S -&gt; (E + 1) + 4 // 将 F 解析为数字 1，1 ≠ 3 继续回溯&lt;/li&gt;
&lt;li&gt;S -&gt; (E + 2) + 4 // 将 F 解析为数字 2，2 ≠ 3 继续回溯&lt;/li&gt;
&lt;li&gt;S -&gt; (E + 3) + 4 // ok&lt;/li&gt;
&lt;li&gt;S -&gt; (F + 3) + 4 // 将 E 按推导为 F 格式&lt;/li&gt;
&lt;li&gt;S -&gt; (1 + 3) + 4 // 将 F 解析为数字 1，1 ≠ 2 继续回溯&lt;/li&gt;
&lt;li&gt;S -&gt; (2 + 3) + 4 // ok&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;即通过给定的规则，推导出字符串 S1，该字符串和原始字符序列 S 相等，语法分析即认为该输入序列是合法的。&lt;/p&gt;
&lt;p&gt;而下面的字符序列是无效的，即语法错误；&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;7 + 1 // 不合法，不可能推出数字 7
1 + 2 + 6 // 不合法，不可能推出数字 6
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到，语法分析器将采用递归的思想，一层一层分析，直到子项不可再分；具体的推导过程推荐观看 &lt;a href=&quot;https://www.bilibili.com/video/BV17W41187gL?p=39&quot;&gt;编译原理 — 中科大_哔哩哔哩 P39&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&quot;go-语言的词法分析--语法分析&quot;&gt;Go 语言的词法分析 &amp;#x26; 语法分析&lt;/h2&gt;
&lt;p&gt;Go 语言中的词法分析和语法分析是放在一起进行的；经过这一步，最终将源代码生成抽象语法树。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202103/22/U9MApMQhkRK20tskuzkDCuVlJampwKFdN8KqFOpa.png&quot; alt=&quot;Go 语言编译原理——词法&amp;#x26;语法分析&quot;&gt;
图 3.0 Go语言的词法分析与语法分析&lt;/p&gt;
&lt;p&gt;作者将通过下面的列子来探索 Go 语言的词法分析过程。假设目标文件夹中有两个文件 hello.go 和 main.go，其内容如下；&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// main.go&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;package&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; main&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;fmt&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; main&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    fmt.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;Println&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(Hello)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// hello.go&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;package&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; main&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; Hello &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &quot;Hello world&quot;&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;// main.go
package main

import &amp;#x22;fmt&amp;#x22;

func main() {
    fmt.Println(Hello)
}

// hello.go
package main

var Hello string = &amp;#x22;Hello world&amp;#x22;&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Go 语言的编译器入口是在 &lt;a href=&quot;https://github.com/golang/go/blob/go1.16.2/src/cmd/compile/internal/gc/main.go#L148&quot;&gt;src/cmd/compile/main.go&lt;/a&gt;；在进行一些初始化准备工作后，编译器获取待解析的文件数组，并将其交给 Go 语言解析器负责解析。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; filenames &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; []&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;string&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;    &quot;main.go&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;    &quot;hello.go&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// src/cmd/compile/internal/gc/main.go:578&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;parsefiles&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(filenames)&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;var filenames = []string{
    &amp;#x22;main.go&amp;#x22;,
    &amp;#x22;hello.go&amp;#x22;,
}

// src/cmd/compile/internal/gc/main.go:578
parsefiles(filenames)&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;解析器会利用多个 goroutine 来并发解析源文件，其 goroutine 数量取决于处理器的核心数 + 10，主要作用是为了控制同时打开的文件数量。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// Limit the number of simultaneously open files.&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;sem &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; make&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;chan&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; struct&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{}, runtime.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;GOMAXPROCS&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;10&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; _, filename &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; range&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; filenames {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    go&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#E36209;--shiki-dark:#F69D50&quot;&gt;filename&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; string&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        sem &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;&amp;#x3C;-&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; struct&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{}{}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        defer&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() { &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;&amp;#x3C;-&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;sem }()&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        &lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        file &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; syntax.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;Parse&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(os.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;Open&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(filename))&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }(filename)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// wait&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;// Limit the number of simultaneously open files.
sem := make(chan struct{}, runtime.GOMAXPROCS(0)+10)

for _, filename := range filenames {
    go func(filename string) {
        sem &lt;- struct{}{}
        defer func() { &lt;-sem }()
        
        file := syntax.Parse(os.Open(filename))
    }(filename)
}
// wait&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/golang/go/blob/3979fb9af9ccfc0b7ccb613dcf256b18c2c295f0/src/cmd/compile/internal/gc/noder.go#L53&quot;&gt;syntax.Parse&lt;/a&gt; 解析器会将每一个源文件解析为一个 source 结构体，其简化的格式如下：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; source&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; struct&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    buf []&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;byte&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;  // 源代码字节数组&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    ch  &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;rune&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    // 指向当前解析的字符&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    b,r,e &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;   // 指向buf数组的开始/当前扫描字符/结束指针&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;type source struct {
    buf []byte  // 源代码字节数组
    ch  rune    // 指向当前解析的字符
    b,r,e int   // 指向buf数组的开始/当前扫描字符/结束指针
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;解析器在初始化时，会将 buf 初始化为一个 4k 大小的空 slice，并将第一个字节设置为空格 &lt;code&gt;&apos; &apos;&lt;/code&gt;，如图所示：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202103/22/3ACspPvk1u4cCFeB6dLv3CZKacD2WAWitOy1zaIj.png&quot; alt=&quot;Go 语言编译原理——词法&amp;#x26;语法分析&quot;&gt;
图 3.1 buf初始化&lt;/p&gt;
&lt;p&gt;然后解析器开始一个字符一个字符的读取并分析，但由于 buf 目前是空的，第一次解析时会尝试读取目标文件内容到 buf 中；针对上面的 main.go 文件，读取后 buf 内容为：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202103/22/DoaQcreU3H1hJvaceePAbJldHXOxkRvXObbaga4x.png&quot; alt=&quot;file&quot;&gt;
图 3.2 读取文件内容&lt;/p&gt;
&lt;p&gt;接下来，解析器会尝试从 buf 中一个字符一个字符的读取，再遇到 &lt;code&gt;&apos; &apos;, \n, \t, \r&lt;/code&gt; 等字符时分离。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; s.ch &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; s.ch &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;\t&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; s.ch &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;\n&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; &amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; !&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;nlsemi &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;||&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; s.ch &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;\r&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    s.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;nextch&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;for s.ch == &amp;#x27; &amp;#x27; || s.ch == &amp;#x27;\t&amp;#x27; || s.ch == &amp;#x27;\n&amp;#x27; &amp;#x26;&amp;#x26; !nlsemi || s.ch == &amp;#x27;\r&amp;#x27; {
    s.nextch()
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;针对图 3.2 的 buf 内容，第一次操作结束后，扫描器中的 b,r,e 指针如下图所示：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;b: begin 开始位置；r: read 当前读取到的位置；e: end 结束位置。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202103/22/BUhsJNgRCQLKkhiq7sEeiLxUWcFc8uv6iqDCdpsj.png&quot; alt=&quot;Go 语言编译原理——词法&amp;#x26;语法分析&quot;&gt;
图 3.3 解析第一个 token&lt;/p&gt;
&lt;p&gt;解析器通过 b,r 指针计算（类似 &lt;code&gt;buf[b:r]&lt;/code&gt;）出本次解析获得的字符串 &lt;code&gt;package&lt;/code&gt;；再和 Go 预定义的关键字列表对比后，最终将设置当前 sacnner 扫描器的 tok 属性设置为 _Package。&lt;/p&gt;
&lt;p&gt;第一次解析结束后，扫描器的各个属性如下图所示：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202103/22/JbNNKN7Pns5IhAkmdnjlaHKXtvBJ4vkYAZg6btI2.png&quot; alt=&quot;Go 语言编译原理——词法&amp;#x26;语法分析&quot;&gt;
图 3.4 扫描器scanner基本属性情况&lt;/p&gt;
&lt;p&gt;Go 语言的词法分析是渐渐试的，在获得第一个 token 后，解析器尝试初始化 &lt;a href=&quot;https://github.com/golang/go/blob/3979fb9af9ccfc0b7ccb613dcf256b18c2c295f0/src/cmd/compile/internal/syntax/nodes.go#L36&quot;&gt;SourceFile&lt;/a&gt;；每一个 SourceFile 对应一个 go 文件，其中包含包的名称，变量、常量、函数等的全部定义，其简化结构如下：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;type&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; File&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; struct&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    PkgName  &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;Name&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;  // package name&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    DeclList []&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;Decl&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt; // 包含包名/变量/常量/函数等的定义&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;type File struct {
    PkgName  *Name  // package name
    DeclList []Decl // 包含包名/变量/常量/函数等的定义
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Go 源码中使用 &lt;a href=&quot;https://github.com/golang/go/blob/3979fb9af9ccfc0b7ccb613dcf256b18c2c295f0/src/cmd/compile/internal/syntax/parser.go#L376&quot;&gt;fileOrNil&lt;/a&gt; 方法初始化 file，解析器将检查第一个 token 是否为 pakcage，否则将报错；毕竟所有的 go 文件都是以 package 开头的。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// 伪代码&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; fileOrNil&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    f &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;File&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; tok &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;!=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; _Package {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        p.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;syntaxError&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;package statement must be first&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    f.PkgName &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ?&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;// 伪代码
func fileOrNil() {
    f := new(File)

    if tok != _Package {
        p.syntaxError(&amp;#x22;package statement must be first&amp;#x22;)
    }

    f.PkgName = ?
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;接下来解析器将设置当前 file 的包名，但此时解析器并不知道具体的包名是什么；不过根据语法规则解析器知道 package 关键字后面一定是跟的包名，只需要解析出下一个 token，该 token 就是对应的包名称。&lt;/p&gt;
&lt;p&gt;第二次解析结束后，扫描器中的 b,r,e 指针如下图所示：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202103/22/dau6aCGnFgaehulzA8jOMhJkY4ikvBW8C7LN1Maa.png&quot; alt=&quot;Go 语言编译原理——词法&amp;#x26;语法分析&quot;&gt;
图 3.5 第二次解析结束&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在需要什么的时候，显示的解析什么，这也是 Go 解析器渐渐试解析的体现。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;同样，根据 b,r 指针，计算出当前解析获得的字符串为 &lt;code&gt;main&lt;/code&gt;；该字符串非内置 token，Go 语言将设置当前 sacnner 扫描器的 tok 及 limi 属性为 _Name 及 main，其中 lit 是被扫描符号的文本表示。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;_Name 一般表示变量名/常量名/方法名等，可以理解为非内置关键字的文本类型。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;第二次解析结束后，scanner 的各个属性如下图所示：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202103/22/zHxU5niPSF9EUUnoqvpeqIXF7cwhIJaHhjjtxY7W.png&quot; alt=&quot;Go 语言编译原理——词法&amp;#x26;语法分析&quot;&gt;
图 3.6 扫描器scanner基本属性情况&lt;/p&gt;
&lt;p&gt;检查 _Package 及设置当前文件 PkgName 的伪代码如下：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;f &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;File&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; tok &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;!=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; _Package {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    p.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;syntaxError&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;package statement must be first&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;tok &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; parser.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;next&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// 伪代码 get next token&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; tok &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;!=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; _Name {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    // 验证：包名必须是一个 name 类型的 token, &lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    // 可以理解为非内置关键字的文本类型&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;f.PkgName &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; &amp;#x26;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;Name&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{Value: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;main&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;} &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// 伪代码&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;f := new(File)

if tok != _Package {
    p.syntaxError(&amp;#x22;package statement must be first&amp;#x22;)
}

tok := parser.next() // 伪代码 get next token
if tok != _Name {
    // 验证：包名必须是一个 name 类型的 token, 
    // 可以理解为非内置关键字的文本类型
}

f.PkgName = &amp;#x26;Name{Value: &amp;#x22;main&amp;#x22;} // 伪代码&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;到目前为止，解析器已经成功的初始化 &lt;a href=&quot;https://github.com/golang/go/blob/3979fb9af9ccfc0b7ccb613dcf256b18c2c295f0/src/cmd/compile/internal/syntax/nodes.go#L36&quot;&gt;syntax.File&lt;/a&gt; 结构并为其 PkgName 属性赋值，如图 3.7：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202103/22/Yi1h8k6auMkUzcpjilweO87FRS5y5dkTwZsNR3Z9.png&quot; alt=&quot;Go 语言编译原理——词法&amp;#x26;语法分析&quot;&gt;
图 3.7 syntax.File 属性&lt;/p&gt;
&lt;p&gt;接下来，解析器将开始分析 Go 语言的 import 关键字，这部分作者将在后面的文章中继续输出。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// { ImportDecl &quot;;&quot; }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; p.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;got&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(_Import) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    f.DeclList &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; p.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;appendGroup&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(f.DeclList, p.importDecl)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    p.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;want&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(_Semi)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;// { ImportDecl &amp;#x22;;&amp;#x22; }
for p.got(_Import) {
    f.DeclList = p.appendGroup(f.DeclList, p.importDecl)
    p.want(_Semi)
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&quot;接下来阅读&quot;&gt;接下来阅读&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;当前 &lt;a href=&quot;https://godruoyi.com/posts/golang-lexer-and-parser-1&quot;&gt;Go 语言的词法分析和语法分析(1)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/go-lexical-analysis-and-syntax-analysis-2-parsing-of-import-declarations&quot;&gt;Go 语言的词法分析和语法分析(2)-Import申明的解析&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;参考&quot;&gt;参考&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://draveness.me/golang/docs/part1-prerequisite/ch02-compile/golang-lexer-and-parser/&quot;&gt;解析器眼中的 Go 语言 | Go 语言设计与实现&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E8%AF%AD%E6%B3%95%E5%88%86%E6%9E%90&quot;&gt;语法分析 - 维基百科，自由的百科全书&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E8%AF%8D%E6%B3%95%E5%88%86%E6%9E%90&quot;&gt;词法分析 - 维基百科，自由的百科全书&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/chai2010/go-ast-book&quot;&gt;《Go语法树入门——开启自制编程语言和编译器之旅》&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV17W41187gL?p=39&quot;&gt;编译原理 — 中科大_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>金瓶梅</title><link>https://godruoyi.com/posts/jin-ping-mei/</link><guid isPermaLink="true">https://godruoyi.com/posts/jin-ping-mei/</guid><description>今儿用微信听书听金瓶梅，听到有一段特别有意思，心想用程序来描述出来肯定也不错。</description><pubDate>Fri, 19 Feb 2021 08:23:15 GMT</pubDate><content:encoded>&lt;p&gt;今儿用微信听书听金瓶梅，听到有一段特别有意思，心想用程序来描述出来肯定也不错。&lt;/p&gt;
&lt;p&gt;下面是背景描述：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;王婆给西门庆定的挨光十记，满足这十条，就能妥妥的挨光了。&lt;/p&gt;
&lt;p&gt;武大郎为人软弱，每天早出晚归，只知道做买卖，所以潘金莲一般不出门；
老身（王干娘，以下为第一人称）没事，有机会过去就和她闲坐聊天，她有事儿呢，也请我帮忙。
大官人（西门庆）如干此事，便买来一匹蓝绸、一匹白绸、一匹白绢，十两好绵交给我，我去让她帮我挑个日子我请裁缝来做衣服。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;package&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; main&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; main&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    _, 答应替我做 &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; 向她借黄历挑缝制衣服的日子&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; !&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;答应替我做 {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        return&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;    println&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;这光便一分了&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; !&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;愿意到我家来做&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        return&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;    println&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;这光便二分了&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; !&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;愿意留在这儿吃饭&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        return&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;    println&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;这光便三分了&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    // 这天你不要来，直至第三天晌午前后，你整整齐齐打扮了来，以咳嗽为号。&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; 我邀你进来喝茶她就要走&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; 不让她走&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;            panic&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;强扭的瓜要长蛆&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        return&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;    println&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;这光便四分了&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    // 进来坐下后，我介绍你就是让我做衣服的官人，&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    // 便夸你的许多好处，你便夸她针线活好。&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; 她若不理你&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        return&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;    println&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;这光便五分了&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    // 为了感谢娘子为你做衣服我提议让你作东请我们吃饭，你并拿钱让我去买酒肉。&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; 她起身离去&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        return&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;    println&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;这光便六分了&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    // 我拿上银子，临出门时对她说：有劳娘子相待官人坐一坐。&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; 她起身离去&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        return&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;    println&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;这光便七分了&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    // 买完东西回来后我让娘子「且吃一杯儿酒，难得这官人坏钱」。&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; 不肯一起吃饭&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;||&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; 她起身离去&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        return&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;    println&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;这光便八分了&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    // 待他吃得酒浓时说得入港时，我便推道没了酒，你并拿出银子让我去买酒。&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; 她起身离去&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        return&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;    println&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;这光便九分了&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    // 独处一室后，大官人却不可燥暴，别动手动脚，把事儿打搅了。&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    // 你用袖子把桌子上的筷子拂下去，然后假装捡筷子，并在她脚踝上捏一捏。&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; 她闹起来&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        return&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;    println&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;此事十分光了&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;//&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;		&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;    println&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;🈲，未满18岁请在大人陪同下观看&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;    println&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;🈲，未满18岁请在大人陪同下观看&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;    println&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;🈲，未满18岁请在大人陪同下观看&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;package main

func main() {
    _, 答应替我做 := 向她借黄历挑缝制衣服的日子()
    if !答应替我做 {
        return
    }
    println(&amp;#x22;这光便一分了&amp;#x22;)

    if !愿意到我家来做() {
        return
    }
    println(&amp;#x22;这光便二分了&amp;#x22;)

    if !愿意留在这儿吃饭() {
        return
    }
    println(&amp;#x22;这光便三分了&amp;#x22;)

    // 这天你不要来，直至第三天晌午前后，你整整齐齐打扮了来，以咳嗽为号。

    if 我邀你进来喝茶她就要走() {
        if 不让她走() {
            panic(&amp;#x22;强扭的瓜要长蛆&amp;#x22;)
        }
        return
    }
    println(&amp;#x22;这光便四分了&amp;#x22;)

    // 进来坐下后，我介绍你就是让我做衣服的官人，
    // 便夸你的许多好处，你便夸她针线活好。

    if 她若不理你() {
        return
    }
    println(&amp;#x22;这光便五分了&amp;#x22;)

    // 为了感谢娘子为你做衣服我提议让你作东请我们吃饭，你并拿钱让我去买酒肉。

    if 她起身离去() {
        return
    }
    println(&amp;#x22;这光便六分了&amp;#x22;)

    // 我拿上银子，临出门时对她说：有劳娘子相待官人坐一坐。

    if 她起身离去() {
        return
    }
    println(&amp;#x22;这光便七分了&amp;#x22;)

    // 买完东西回来后我让娘子「且吃一杯儿酒，难得这官人坏钱」。

    if 不肯一起吃饭() || 她起身离去() {
        return
    }
    println(&amp;#x22;这光便八分了&amp;#x22;)

    // 待他吃得酒浓时说得入港时，我便推道没了酒，你并拿出银子让我去买酒。

    if 她起身离去() {
        return
    }
    println(&amp;#x22;这光便九分了&amp;#x22;)

    // 独处一室后，大官人却不可燥暴，别动手动脚，把事儿打搅了。
    // 你用袖子把桌子上的筷子拂下去，然后假装捡筷子，并在她脚踝上捏一捏。

    if 她闹起来() {
        return
    }

    println(&amp;#x22;此事十分光了&amp;#x22;) //
		
    println(&amp;#x22;🈲，未满18岁请在大人陪同下观看&amp;#x22;)
    println(&amp;#x22;🈲，未满18岁请在大人陪同下观看&amp;#x22;)
    println(&amp;#x22;🈲，未满18岁请在大人陪同下观看&amp;#x22;)
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;顺道编译运行下：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; go&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; main.go&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;这光便有一分了&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;这光便二分了&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;这光便三分了&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;这光便四分了&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;这光便五分了&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;这光便六分了&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;这光便七分了&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;这光便八分了&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;这光便九分了&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;此事十分光了&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;🈲️，未满18岁请在大人陪同下观看&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;🈲️，未满18岁请在大人陪同下观看&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;🈲️，未满18岁请在大人陪同下观看&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;$ go run main.go
这光便有一分了
这光便二分了
这光便三分了
这光便四分了
这光便五分了
这光便六分了
这光便七分了
这光便八分了
这光便九分了
此事十分光了
🈲️，未满18岁请在大人陪同下观看
🈲️，未满18岁请在大人陪同下观看
🈲️，未满18岁请在大人陪同下观看&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;</content:encoded></item><item><title>2020 年终总结</title><link>https://godruoyi.com/posts/2020-year-end-review/</link><guid isPermaLink="true">https://godruoyi.com/posts/2020-year-end-review/</guid><description>二愣和阿宝今年过年没回家。这是二愣第一次不回家过年。小两口坐在重庆小家的沙发上，嗑着坚果、喝着果汁、嘻嘻哈哈的看着春晚。</description><pubDate>Thu, 11 Feb 2021 15:42:01 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;二愣和阿宝今年过年没回家。这是二愣第一次不回家过年。小两口坐在重庆小家的沙发上，嗑着坚果、喝着果汁、嘻嘻哈哈的看着春晚。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;对于我来说，过完农历三十才叫新年呐。看着 bear 里只埋了标题的年终总结，压的我看春晚都看不起劲。&lt;/p&gt;
&lt;p&gt;怎么说，今年也是不平凡中平凡的一年；不平凡是因为今年确实发生了很多事；小宝走了、阿宝怀孕了、二愣生病了、富贵儿走了、连我的小摩托也被人顺走了。而平凡则是即使我们发生了再多的事，就像拿去年和前年来比，也并没有什么是大不了的。而疫情？时逢乱世我对这并没有什么想说的，我也不关心多少人感染了多少人又离世了。只要我及我的亲人健康平安，其他的都随缘吧！我就是这大千世界平凡而普通的人。&lt;/p&gt;
&lt;h2 id=&quot;工作--技术&quot;&gt;工作 &amp;#x26; 技术&lt;/h2&gt;
&lt;p&gt;去年立下的 flag，现在回过头看，似乎大部分都未完成。年中的时候，由于公司转型，我开始接触 go 语言。用了它做了一两个项目后，愈发感觉 go 语言骨子里的简单。在保持原生多并发支持前提下，并没有额外增加面向对象、继承、范型等特性。虽说写的代码量可能变多了，但比起简单和高效这完美的平衡来说，已经足够了。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;del&gt;我其实并不看好范型，这会严重增加语言的复杂程度，保持这样的简单就好了。&lt;/del&gt;
自从看了 Rust 后，我收回以前的观点，我现在觉得 Go 应该早点支持范型的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;虽说 go 语言确实简单，但深入到原理后，就越来越多的特性需要学习的了。随着使用的加深，我现在甚至在开始学习计算机系统相关了。诸如内存管理、内存分配、编译原理。大部分工作了几年的程序员，估计都会回过头去捨起自己当初丢下的东西吧。&lt;/p&gt;
&lt;p&gt;今年开源了 2～3 个开源库，新增了 300 多个 Star。并在时隔两年后，终于完善了 OCR 的 2.0 版本，虽说没什么人用，但对得起自己当初开源的初衷了。&lt;/p&gt;
&lt;h2 id=&quot;todo-2021&quot;&gt;Todo 2021&lt;/h2&gt;
&lt;p&gt;目标还是要定，即使我完全不按这样做。等到明年再来写总计的时候，也好有个照应。&lt;/p&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;&gt; 学习Rust&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;&gt; 深入学习 Golang&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;&gt; 看完 CSAPP&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;&gt; 锻炼身体&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最后一句，还是要多说话、多说话，自信一点。&lt;/p&gt;
&lt;h2 id=&quot;往年总结&quot;&gt;往年总结&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/continue-refueling-in-2019&quot;&gt;2019 年终总结&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>九华山的和尚</title><link>https://godruoyi.com/posts/monks-on-mount-jiuhua/</link><guid isPermaLink="true">https://godruoyi.com/posts/monks-on-mount-jiuhua/</guid><description>当二愣拿着从体检中心领取的早餐站在门口时，一位身穿黄色大褂的和尚出现在他面前。圆顶方袍、颈带佛珠，符合二愣心中对和尚的一切想象。他一手搓着佛珠，一手做着阿弥陀佛，操着流利的普通话：</description><pubDate>Thu, 04 Feb 2021 03:33:39 GMT</pubDate><content:encoded>&lt;p&gt;当二愣拿着从体检中心领取的早餐站在门口时，一位身穿黄色大褂的和尚出现在他面前。圆顶方袍、颈带佛珠，符合二愣心中对和尚的一切想象。他一手搓着佛珠，一手做着阿弥陀佛，操着流利的普通话：&lt;/p&gt;
&lt;p&gt;小伙子，我看你这么阳光明媚，满脸红光，20 几了啊？&lt;/p&gt;
&lt;p&gt;那几天二愣刚剪了个短发，看起来着实眉清目秀。但心怀警惕的二愣说了就当没说的回答道：&lt;/p&gt;
&lt;p&gt;20 多岁。&lt;/p&gt;
&lt;p&gt;你们年轻人不要一直搞手机啊，我看你站在哪里玩手机，我叫了三遍你都没有回应。&lt;/p&gt;
&lt;p&gt;和尚这样说，让二愣觉得好似有什么好事朝我游过来后又流走了似的，愈发珍贵起来。&lt;/p&gt;
&lt;p&gt;哦我刚在跟我老婆说我检查完了。好像一定要让他知道，我和别人那些低头玩手机的不一样，但又有什么不一样呢，二愣也说不出来。&lt;/p&gt;
&lt;p&gt;我看你脸带阳光、面色和蔼，还长着一对连字眉，一般人都没有你这样的眉毛。但你额头淡黑，今年运气不怎么好吧。&lt;/p&gt;
&lt;p&gt;二愣今年运气确实不佳，丢了猫、生了病、掉了宝。&lt;/p&gt;
&lt;p&gt;佛渡有缘人，遇见就是缘。你最近要提防小人，交朋友这些切忌不要交尖嘴猴腮之人、鼻子塌的人。这些人会挡住你往后的鸿运。今年后，你将会大运不断，你也会遇到生命中的贵人，但是切记切记不要被小人影响。&lt;/p&gt;
&lt;p&gt;那一刻我仿佛感到一束阳光照了下来，感觉过往的再多不是，都已烟消云散。连忙双手合十学着阿弥陀佛的样子答道：&lt;/p&gt;
&lt;p&gt;借你吉言，借你吉言。&lt;/p&gt;
&lt;p&gt;父母不在身边吧，她们年纪也大了，抽时间多回去看看，他们养大你也不容易。&lt;/p&gt;
&lt;p&gt;但你眼眶干涩淡红，心脏是不太好吧？&lt;/p&gt;
&lt;p&gt;二愣心里一惊，他只是肝和肺不太好，心脏应没问题吧！&lt;/p&gt;
&lt;p&gt;和尚边说边走，好像下一句说完了就要匆忙赶路走了的样子。&lt;/p&gt;
&lt;p&gt;我是九华山的和尚，来华岩寺参加佛事，佛渡有缘人。&lt;/p&gt;
&lt;p&gt;他又说了一次，佛渡有缘人。但二愣还沉迷阳光普照无法自拔。居然还用手比划了一个您忙您先走的手势。&lt;/p&gt;
&lt;p&gt;和尚又说了一句，佛渡有缘人，我会向佛祖表达你的善意，并掏出了自己的收款码。&lt;/p&gt;
&lt;p&gt;最后，二愣掏出了手机，扫了扫道士的收款码，径直离去。他又长了点心，但他好像也没后悔。&lt;/p&gt;
&lt;p&gt;和尚为什么要随声携带收款码这个东西。&lt;/p&gt;</content:encoded></item><item><title>axios 的错误处理</title><link>https://godruoyi.com/posts/axios-error-handling/</link><guid isPermaLink="true">https://godruoyi.com/posts/axios-error-handling/</guid><description>Axios 是目前使用最为广泛的 http 请求工具包，在进行错误处理时，基于框架提供的拦截器，我们可以快速的实现错误处理。</description><pubDate>Thu, 21 Jan 2021 08:06:17 GMT</pubDate><content:encoded>&lt;p&gt;Axios 是目前使用最为广泛的 http 请求工具包，在进行错误处理时，基于框架提供的拦截器，我们可以快速的实现错误处理。&lt;/p&gt;
&lt;h2 id=&quot;axios-interceptor&quot;&gt;axios interceptor&lt;/h2&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;javascript&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;javascript&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;axios.interceptors.response.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#E36209;--shiki-dark:#F69D50&quot;&gt;response&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; response;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#E36209;--shiki-dark:#F69D50&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    // 如4xx/5xx等基本错误的处理&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;    alert&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;全局错误处理&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Promise&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;reject&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(error); &lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;axios.interceptors.response.use(function (response) {
    return response;
}, function (error) {
    // 如4xx/5xx等基本错误的处理
    alert(&amp;#x27;全局错误处理&amp;#x27;)

    return Promise.reject(error); 
});&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;但 axios 提供的 response 拦截器是全局的，若我们想对某个具体请求进行错误处理时，情况就稍微有点复杂了。&lt;/p&gt;
&lt;p&gt;如有一个投票接口，由于后端限制了投票次数，当投票超限时我们需要单独处理；返回的响应如下：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;HTTP&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1.0&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 429&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Too Many Requests&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;&quot;error_code&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;4291011&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;今日投票次数超限&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;HTTP/1.0 429 Too Many Requests

{&amp;#x22;error_code&amp;#x22;:4291011,&amp;#x22;message&amp;#x22;:&amp;#x22;今日投票次数超限&amp;#x22;}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;可能对于部分前端同学来说，处理方式是直接在 response 拦截器加上相应的条件判断就好了：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;javascript&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;javascript&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;axios.interceptors.response.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#E36209;--shiki-dark:#F69D50&quot;&gt;response&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; response;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#E36209;--shiki-dark:#F69D50&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (error.response.status &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 429&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (error.response.data.error_code &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 4291011&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;            // 单独处理投票错误&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        } &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (error.response.data.error_code &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 4291011&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;				    // 作品票数异常，需先通过滑动验证码&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;				}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    // 处理其他 如4xx/5xx等基本错误的处理&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Promise&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;reject&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(error); &lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;axios.interceptors.response.use(function (response) {
    return response;
}, function (error) {
    if (error.response.status == 429) {
        if (error.response.data.error_code == 4291011) {
            // 单独处理投票错误
        } else if (error.response.data.error_code == 4291011) {
				    // 作品票数异常，需先通过滑动验证码
				}
    }

    // 处理其他 如4xx/5xx等基本错误的处理
    return Promise.reject(error); 
});&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;但是这样做却存在很多问题&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;随着各种错误码的增多，拦截器需要处理的情况越来越多，最终充满着大量的 if-else&lt;/li&gt;
&lt;li&gt;具体的错误码应该和具体发起请求的代码放在一起，一来方便查看，二来好扩展及定位&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以我们可以把错误处理逻辑移到调用代码处，如下：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;javascript&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;javascript&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;axios.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;post&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;/vote/1&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#E36209;--shiki-dark:#F69D50&quot;&gt;response&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    // success&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}).&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;catch&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#E36209;--shiki-dark:#F69D50&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    let&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; code &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; error.response.data.error_code&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (code &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 4291011&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;        alert&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;投票超限&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    } &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;elseif&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (code &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 4030001&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;        alert&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;作品票数异常，需先通过滑动验证码&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;axios.post(&amp;#x27;/vote/1&amp;#x27;).then(function (response) {
    // success
}).catch(function (error) {
    let code = error.response.data.error_code
    if (code == 4291011) {
        alert(&amp;#x27;投票超限&amp;#x27;)
    } elseif (code == 4030001) {
        alert(&amp;#x27;作品票数异常，需先通过滑动验证码&amp;#x27;)
    }
});&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;但这样又存在一个问题，由于 axios 拦截器的代码会比 catch 先执行，所以当执行到 catch 时，实际上 response 拦截器的代码已全部执行完成，所以会先后弹出「全局错误处理」-&gt; 「投票超限」。&lt;/p&gt;
&lt;p&gt;这显然不是我们想要的，我们希望当我们单独处理错误后，先执行具体的业务错误处理，最后在执行全局的错误处理。&lt;/p&gt;
&lt;p&gt;如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;针对这种没有 catch 的情况，当请求错误后，我们希望由全局错误进行处理。&lt;/li&gt;
&lt;/ul&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;javascript&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;javascript&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;axios.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;post&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;/vote/1&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#E36209;--shiki-dark:#F69D50&quot;&gt;response&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    // success&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;})&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;axios.post(&amp;#x27;/vote/1&amp;#x27;).then(function (response) {
    // success
})&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;blockquote&gt;
&lt;p&gt;这默认是可行的，不需要做额外的准备工作。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;当我们使用 catch 后，我们希望应该先处理自定义的错误，最后在处理全局错误。&lt;/li&gt;
&lt;/ul&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;javascript&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;javascript&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;axios.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;post&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;/vote/1&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;).&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#E36209;--shiki-dark:#F69D50&quot;&gt;response&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    // success&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}).&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;catch&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#E36209;--shiki-dark:#F69D50&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    // custom error &lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;})&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt; {
    // custom error 
})&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;然而目前这样是行不通的，看了 &lt;a href=&quot;https://github.com/axios/axios/blob/fe52a611efe756328a93709bbf5265756275d70d/lib/core/Axios.js#L27&quot;&gt;axios request&lt;/a&gt; 方法源码后得知，框架在发起请求时，并没有给我提供相应的钩子；所以在 Promise 执行到 catch 时，拦截器里的代码一定已经执行过了。&lt;/p&gt;
&lt;h2 id=&quot;axios-config&quot;&gt;axios config&lt;/h2&gt;
&lt;p&gt;我们只能依赖 axios 提供的 config 来完成这个特性，如下所示：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;javascript&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;javascript&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;axios.interceptors.response.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#E36209;--shiki-dark:#F69D50&quot;&gt;response&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; response;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#E36209;--shiki-dark:#F69D50&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    error.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;globalErrorProcess&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; () {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        switch&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.response.status) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;            case&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 401&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// 处理基本 401 错误&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;                break&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;            case&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 404&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// 处理基本 404 错误&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;                break&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;            case&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 403&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// 处理基本 403 错误&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;                break&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;                      // 处理其他4xx/5xx等基本错误的处理&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Promise&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;reject&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(error.config.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;hasOwnProperty&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;catch&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; error.config.catch &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Promise&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;reject&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(error);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; error.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;globalErrorProcess&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;axios.interceptors.response.use(function (response) {
    return response;
}, function (error) {
    error.globalErrorProcess = function () {
        switch (this.response.status) {
            case 401: // 处理基本 401 错误
                break;
            case 404: // 处理基本 404 错误
                break;
            case 403: // 处理基本 403 错误
                break;
                      // 处理其他4xx/5xx等基本错误的处理
        }

        return Promise.reject(this);
    };

    if(error.config.hasOwnProperty(&amp;#x27;catch&amp;#x27;) &amp;#x26;&amp;#x26; error.config.catch == true) {
        return Promise.reject(error);
    }

    return error.globalErrorProcess()
});&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;我们定义一个全局的错误处理器，并把他赋给 error 对象的 globalErrorProcess 方法。接着判断当前请求 config 是否启用 catch，若启用，默认不进行任何错误处理，交由调用方自行负责；否则用全局错误处理。&lt;/p&gt;
&lt;p&gt;在使用时，若需要自定义捕获错误，可显示传递一个 config，相应请求方法的 API 如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;axios.request(config)&lt;/li&gt;
&lt;li&gt;axios.get(url[, config])&lt;/li&gt;
&lt;li&gt;axios.delete(url[, config])&lt;/li&gt;
&lt;li&gt;axios.head(url[, config])&lt;/li&gt;
&lt;li&gt;axios.options(url[, config])&lt;/li&gt;
&lt;li&gt;axios.post(url[, data[, config]])&lt;/li&gt;
&lt;li&gt;axios.put(url[, data[, config]])&lt;/li&gt;
&lt;li&gt;axios.patch(url[, data[, config]])&lt;/li&gt;
&lt;/ul&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;javascript&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;javascript&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;axios.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;post&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;https://api.github.com/xxx&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;null&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, {catch: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}).&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#E36209;--shiki-dark:#F69D50&quot;&gt;response&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    console.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(response);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}).&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;catch&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#E36209;--shiki-dark:#F69D50&quot;&gt;error&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    let&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; code &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; error.response.data.error_code&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (code &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 4291011&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;        // 今日投票次数太多，显示关注公众号二维码&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    } &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (code &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 4031011&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;        // 不允许的投票时间段，&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    } &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (code &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 4291012&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;        // 作品票数异常，需先通过滑动验证码&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; error.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;globalErrorProcesser&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;axios.post(&amp;#x27;https://api.github.com/xxx&amp;#x27;, null, {catch: true}).then(function (response) {
    console.log(response);
}).catch(function (error) {
    let code = error.response.data.error_code

    if (code == 4291011) {
        // 今日投票次数太多，显示关注公众号二维码
    } else if (code == 4031011) {
        // 不允许的投票时间段，
    } else if (code == 4291012) {
        // 作品票数异常，需先通过滑动验证码
    }

    return error.globalErrorProcesser()
});&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;最后别忘了显示的调用全局错误处理，否则是不会懒觉到其他异常处理的。&lt;/p&gt;
&lt;p&gt;如果你有更好的方案，欢迎留言一起探讨。&lt;/p&gt;
&lt;h2 id=&quot;参考--讨论&quot;&gt;参考 &amp;#x26; 讨论&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://gist.github.com/saqueib/a495af17d7c0e2fd5c2316b0822ebac3&quot;&gt;Global error handling using axios interceptor · GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/48990632/how-to-manage-axios-errors-globally-or-from-one-point&quot;&gt;javascript - How to manage axios errors globally or from one point - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://learnku.com/articles/53763&quot;&gt;一种 Laravel 异常上下文解决方案 | Laravel China 社区&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Go 语言限流器的实现</title><link>https://godruoyi.com/posts/golang-throttle/</link><guid isPermaLink="true">https://godruoyi.com/posts/golang-throttle/</guid><description>今天在看 GitHub 趋势榜的时候看到一个小巧的 go 框架 chi，看了下他限流器中间件的实现，觉得还挺有趣的</description><pubDate>Fri, 15 Jan 2021 09:41:08 GMT</pubDate><content:encoded>&lt;p&gt;今天在看 GitHub 趋势榜的时候看到一个小巧的 go 框架 &lt;a href=&quot;https://github.com/go-chi/chi&quot;&gt;chi&lt;/a&gt;，看了下他限流器中间件的实现，觉得还挺有趣的。&lt;/p&gt;
&lt;p&gt;我们知道 Laravel 的 Throttle 中间件是用计数器实现的，每次请求计数器 +1，可以大概限制每一分钟允许多少个请求。所以在看源码前我就在想：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;这个限流器是不是也用的计数器实现的？&lt;/li&gt;
&lt;li&gt;他是怎么解决某一时间区间内实际请求数大于限制条件这一问题的？&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;然而看完后才醒悟，golang 中直接使用 channel 来实现即简单又可靠。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;程序启动的时候，向一个带缓冲的 chan 发送一个空结构体 &lt;code&gt;struct{}{}&lt;/code&gt;，简化版如下：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; main&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    tokens &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; make&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;chan&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; struct&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{}, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;10&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    // 随程序启动，创建一个带缓存的 chan&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    for&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; i &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;; i &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 10&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;; i &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;++&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        tokens &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;&amp;#x3C;-&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; struct&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{}{}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    http.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;HandleFunc&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#E36209;--shiki-dark:#F69D50&quot;&gt;rw&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; http&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;ResponseWriter&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#E36209;--shiki-dark:#F69D50&quot;&gt;r&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;http&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;Request&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        select&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        case&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; &amp;#x3C;-&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;tokens:&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;            rw.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;Write&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;([]&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;byte&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;success&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;            break&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        default&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;            rw.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;Write&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;([]&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;byte&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;Too many request&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    })&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    http.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;ListenAndServe&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;:9911&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;nil&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;func main() {
    tokens := make(chan struct{}, 10)

    // 随程序启动，创建一个带缓存的 chan
    for i = 0; i &lt; 10; i ++ {
        tokens &lt;- struct{}{}
    }

    http.HandleFunc(&amp;#x22;/&amp;#x22;, func(rw http.ResponseWriter, r *http.Request) {
        select {
        case &lt;-tokens:
            rw.Write([]byte(&amp;#x22;success&amp;#x22;))
            break
        default:
            rw.Write([]byte(&amp;#x22;Too many request&amp;#x22;))
        }
    })

    http.ListenAndServe(&amp;#x22;:9911&amp;#x22;, nil)
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;程序启动完成后，针对每一个请求，通过 select 从缓冲通道 tokens 里获取数据；因为一开始缓冲通道已初始化 10 条数据，所以对于前 10 个请求，肯定能从 tokens 里获取到数据。&lt;/p&gt;
&lt;p&gt;而对于后续的请求，由于此时通道里也没有任何数据，所以程序默认选择 default 分支执行代码，会从而返回 Too manay request。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;➜&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;  ~&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; curl&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; 127.0.0.1:9911/&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;success&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;➜&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;  ~&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; curl&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; 127.0.0.1:9911/&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;success&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;➜&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;  ~&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; curl&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; 127.0.0.1:9911/&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;Too&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; many&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; request&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;➜  ~ curl 127.0.0.1:9911/
success
➜  ~ curl 127.0.0.1:9911/
success

...

➜  ~ curl 127.0.0.1:9911/
Too many request&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;当然这样实现的话这个限流器就只能处理前 10 个请求了，为了能真正达到同一时刻只有指定数量的请求能被处理，只需在从通道里获取到数据后，处理完具体的业务逻辑，再返还给通道即可。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;select&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;case&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; btok &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; &amp;#x3C;-&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;tokens:&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    rw.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;Write&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;([]&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;byte&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;success&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    tokens &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;&amp;#x3C;-&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; btok &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// join to chan&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    break&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;default&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    rw.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;Write&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;([]&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;byte&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;Too many request&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;select {
case btok := &lt;-tokens:
    rw.Write([]byte(&amp;#x22;success&amp;#x22;))
    tokens &lt;- btok // join to chan
    break
default:
    rw.Write([]byte(&amp;#x22;Too many request&amp;#x22;))
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;这只是一个简单的实现，CHI 的这个中间件还添加了超时、cancel 和 backlog 的支持，具体可参考&lt;a href=&quot;https://github.com/go-chi/chi/blob/master/middleware/throttle.go&quot;&gt;chi/throttle.go at master · go-chi/chi · GitHub&lt;/a&gt;。&lt;/p&gt;</content:encoded></item><item><title>去医院的一天</title><link>https://godruoyi.com/posts/a-day-at-the-hospital/</link><guid isPermaLink="true">https://godruoyi.com/posts/a-day-at-the-hospital/</guid><description>下午请了半天假，今天又是去医院的一天。  爬上门诊部二楼开始排队取号时，看了看时间，才下午两点半。前面的夫妻预约取号按成了预约挂号，还指着已经满号的医生问我为什么按不动，我说满号了，挂不起，他说我取号怎...</description><pubDate>Mon, 23 Nov 2020 09:49:54 GMT</pubDate><content:encoded>&lt;p&gt;下午请了半天假，今天又是去医院的一天。&lt;/p&gt;
&lt;p&gt;爬上门诊部二楼开始排队取号时，看了看时间，才下午两点半。前面的夫妻预约取号按成了预约挂号，还指着已经满号的医生问我为什么按不动，我说满号了，挂不起，他说我取号怎么操作啊，我默默的帮他按了下 Home 按钮并指它这个才是取号。&lt;/p&gt;
&lt;p&gt;看着他操作了半天后，屏幕提示请在一个小时后取票，我并不知道为什么，心想可能是他刚挂的号需要等一个小时吧。结果我轮到我依然是一样的提示。&lt;/p&gt;
&lt;p&gt;得，昨晚预约的是 16.30 到 17.00，看起来只能在 15:00 左右的时候才能取到号了。心想要不是试下窗口吧，说不定能取到。&lt;/p&gt;
&lt;p&gt;窗口排队的人是真的多，好不容易排到我了，结果时间还是没到，还是不让取号，只差几分钟，工作人员都在无私的坚持自己的职业道德。可能是我没对他微笑吧！&lt;/p&gt;
&lt;p&gt;&lt;code&gt;15.30&lt;/code&gt; 后，我又在取号机前排起了队。但是这次按着按着取号机就卡住不动了，左边的大叔钱都扣了，屏幕还是一直转，一动不动；我问了旁边扫地的阿姨这排机器该找谁维护，他说去老年科背后找一卡通的人。好不容易人挤人的过去了，一卡通的说这是网络的问题，他们不是技术人员无能为力，让我等一会再试，交了钱的去 1、2 号窗口人工处理。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;心想他们可能经常遇到这问题，就像开发对测试说这是网络问题，你重新试试一样。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我又去一楼的机器打印，排到一个大姐姐后面，看着他一步步操作，输入密码，插入银行卡，至于为什么要插入银行卡，社保里有钱的人可能永远也体会不到。操作一切都还顺利，成功的拿到取号凭据。轮到我把社保卡插进去后，就开始转圈了，一分钟过去了，还是在转圈。。&lt;/p&gt;
&lt;p&gt;我放弃了，转而走向了二楼呼吸科分诊台门口，看到窗口排队的还是这么多，想想算了，直接去找分诊台的医生吧！趴在分诊台的柜台上，灵机一动的给医生看了我线上支付的凭据后，他终于取到号了；而接下来又开始了漫长的排队等待。&lt;/p&gt;
&lt;p&gt;三个小时过去了，他还在排队，他可真笨啊，早不这样干。&lt;/p&gt;</content:encoded></item><item><title>Ocr 2.0 Release（拖延症）</title><link>https://godruoyi.com/posts/procrastination/</link><guid isPermaLink="true">https://godruoyi.com/posts/procrastination/</guid><description>三年前在 Github 发布了一个图片识别的 PHP 扩展包 godruoyi/ocr，主要功能是整合几大厂商的图片识别接口，方便统一调用。</description><pubDate>Fri, 20 Nov 2020 08:53:41 GMT</pubDate><content:encoded>&lt;p&gt;三年前在 Github 发布了一个图片识别的 PHP 扩展包 godruoyi/ocr，主要功能是整合几大厂商的图片识别接口，方便统一调用。&lt;/p&gt;
&lt;p&gt;当初设计都比较简单，基本是能用就行；&lt;/p&gt;
&lt;p&gt;&lt;del&gt;一年后觉得自己技术牛逼了，知识过硬了&lt;/del&gt; ，打算重新发布个版本，已更新下那难看的 last commit，随便慰藉自己当初开源的初衷 &lt;del&gt;吗&lt;/del&gt; 。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202011/20/XxGFVtWBpE1oZrXOKMilhvyEAC37wY5Rz54ICxE8.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;p&gt;然后大刀阔斧的开整，结果婚结完了，朋友朋友的婚都结完了、两年过去了，我才重构了 20%。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202011/20/0sOuuR4nm7niUNczcpwxX0akfS03wOeWWRzM45gs.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;p&gt;趁着现在大家都去旅游去了而我还在公司上班的打工命，噼里啪啦完成了 2.0 的改造，算是完成了今年的一番壮举吧。&lt;/p&gt;
&lt;p&gt;其实当初还规划了在 2.0 添加响应过滤的功能，原型都写好了：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Godruoyi\OCR\Support\Response&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$application&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;aliyun&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;filters&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Response&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $response) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    $body &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $response&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;toArray&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $body[&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;Response&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;][&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;IdNum&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;??&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; null&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;})&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;idcard&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;...&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;); &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// will return string|null&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;aliyun-&gt;filters(function (Response $response) {
    $body = $response-&gt;toArray();
    return $body[&amp;#x27;Response&amp;#x27;][&amp;#x27;IdNum&amp;#x27;] ?? null;
})-&gt;idcard(&amp;#x27;...&amp;#x27;); // will return string|null&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;然鹅没想到完美的错误处理方式，那还不如不做，交给开发者自己来魔改吧。阿宝就经跟常我说：「你的拖延症啥时候改改啊」。&lt;/p&gt;
&lt;p&gt;就连这篇文章都是几天前写的，然后写了一半，还得现在来补齐。啥时候能一鼓作气，再而兴，三而旺呢。&lt;/p&gt;</content:encoded></item><item><title>Bus Dispatcher &amp; Event Dispatcher</title><link>https://godruoyi.com/posts/laravel-bus-dispatcher-and-event-dispatcher/</link><guid isPermaLink="true">https://godruoyi.com/posts/laravel-bus-dispatcher-and-event-dispatcher/</guid><description>Laravel 提供的 Bus 总线组件主要为了解决什么问题？我们平时使用的 event、dispatch 等方法和他有什么关系？他和 Event 组件提供的 Dispatcher 有什么区别？</description><pubDate>Wed, 04 Nov 2020 05:17:23 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Laravel 提供的 Bus 总线组件主要为了解决什么问题？我们平时使用的 event、dispatch 等方法和他有什么关系？他和 Event 组件提供的 Dispatcher 有什么区别？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Bus 总线和 Event 的组件都提供了 Dispatcher 服务，但主要区别如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Bus 总线提供的 Dispatcher 可以 dispatch 任何 command&lt;/li&gt;
&lt;li&gt;Event 组件提供的 Dispatcher 只能 dispatch 已经注册的 event&lt;/li&gt;
&lt;li&gt;都提供了 dispatch new 和 dispatch to queue 的支持&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;EventDispatcher dispatch 的 event 若事先未注册，则不会有任何反应。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;bus-dispatcher&quot;&gt;Bus Dispatcher&lt;/h2&gt;
&lt;p&gt;完整应用了 &lt;a href=&quot;https://godruoyi.com/posts/laravel-pipeline-flow-principle&quot;&gt;管道流的原理&lt;/a&gt;，可以将任意的 command 对象通过该总线发射出去。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$busDispatcher&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;pipeThrough&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($piples)&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;dispatch&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($command);&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;pipeThrough($piples)-&gt;dispatch($command);&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;如下面的例子，用户注册成功后，通过一系列的检查操作后，发送邮件。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;app&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;Illuminate\Contracts\Bus\Dispatcher&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;pipeThrough&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;([&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;    &apos;Check1&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;Check2&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;Check3&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;])&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;dispatch&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($user);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// need have `handler` method.&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; User&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; handle&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;        // send email.&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;pipeThrough([
    &amp;#x27;Check1&amp;#x27;, &amp;#x27;Check2&amp;#x27;, &amp;#x27;Check3&amp;#x27;
])-&gt;dispatch($user);

// need have &amp;#x60;handler&amp;#x60; method.
class User
{
    public function handle()
    {
        // send email.
    }
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;blockquote&gt;
&lt;p&gt;如果上面的 command 实现自 ShouldQueue，那么发送邮件这一步操作将会放到队列中去执行。&lt;/p&gt;
&lt;p&gt;⏰ 此处的 handle 方法参数可自动注入你想要的任何对象。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;通常我们不会将「处理器」和 command 放在同一个类中，可以通过下面的方式指定单独的处理类（发送邮件）。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;手动指定处理器后， handle 方法不可在自动注入参数。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;app&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;Illuminate\Contracts\Bus\Dispatcher&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;pipeThrough&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;([&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;    &apos;Check1&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;Check2&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;Check3&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;])&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;([&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;    &apos;App\User&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;App\SendEmailHandler&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;])&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;dispatch&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($user);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; SendEmailHandler&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; handle&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;User&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $user)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;        // send emial for user.&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;pipeThrough([
    &amp;#x27;Check1&amp;#x27;, &amp;#x27;Check2&amp;#x27;, &amp;#x27;Check3&amp;#x27;
])-&gt;map([
    &amp;#x27;App\User&amp;#x27; =&gt; &amp;#x27;App\SendEmailHandler&amp;#x27;
])-&gt;dispatch($user);

class SendEmailHandler
{
    public function handle(User $user)
    {
        // send emial for user.
    }
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;也可以直接调用 dispatchNow 来立即发送，这种发式无论 command 是否实现自 ShouldQueue，都不会经过队列；并且你还可以指定额外的处理器来覆盖默认的处理操作。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;app&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;Illuminate\Contracts\Bus\Dispatcher&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;pipeThrough&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;([&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;    &apos;Check1&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;Check2&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;Check3&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;])&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;dispatchNow&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($user, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; \App\SendReadpackHandler&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;pipeThrough([
    &amp;#x27;Check1&amp;#x27;, &amp;#x27;Check2&amp;#x27;, &amp;#x27;Check3&amp;#x27;
])-&gt;dispatchNow($user, new \App\SendReadpackHandler);&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;Laravel 还为我们提供了一个叫做 PendingDispatch 的类，用来「延迟」具体的 dispatch 操作。其原理和 Bus Dispatcher 一样，只是利用 PHP 的 __destruct 当对象引用被销毁或程序退出时再执行真正的 dispatch 操作。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; __destruct&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;    app&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Dispatcher&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;dispatch&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;job);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;dispatch($this-&gt;job);
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;所以若你直接在命令行执行如下的代码，其实是没有任何输出的；但当你将其赋空时，程序将会正常执行。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;✗ tinker
Psy Shell v0.9.9 (PHP 7.3.14 — cli) by Justin Hileman
&gt;&gt;&gt;
&gt;&gt;&gt; $dispatch = dispatch(new \App\Jobs\SendEmail(&apos;send email to lianbo&apos;))
=&gt; Illuminate\Foundation\Bus\PendingDispatch {#3076}
&gt;&gt;&gt;
&gt;&gt;&gt; $dispatch = null
Ok, received
=&gt; null
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;event-dispatcher&quot;&gt;Event Dispatcher&lt;/h2&gt;
&lt;p&gt;Event 组件提供的 dispatch 只能转发已经注册到事件服务上的 event，如下所示：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Event&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;listen&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($eventName, $listener);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$eventDispatcher&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;dispatch&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($eventName); &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// ok&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$eventDispatcher&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;dispatch&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($otherName); &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// null&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;dispatch($eventName); // ok
$eventDispatcher-&gt;dispatch($otherName); // null&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;所以我们经常使用 &lt;code&gt;event($eventName)&lt;/code&gt; 来发射事件。&lt;/p&gt;
&lt;p&gt;总的来说，Event Dispatcher 的功能远不如 Bus 组件那么强大，Bus Dispatcher 基于 Laravel 强大的 Container，我们可以 dispatch 任何对象并自动注入我们需要的依赖。&lt;/p&gt;</content:encoded></item><item><title>连波的春天</title><link>https://godruoyi.com/posts/the-spring-of-linbo/</link><guid isPermaLink="true">https://godruoyi.com/posts/the-spring-of-linbo/</guid><description>我意愿我老来的时候，在大山脚下有个房子，和阿宝种点包谷茄子，再养一头狗</description><pubDate>Tue, 03 Nov 2020 04:06:42 GMT</pubDate><content:encoded>&lt;p&gt;“一家人的日常读起来就像是在看是枝裕和的电影，温情柔软，却笑中带泪。”&lt;/p&gt;
&lt;p&gt;拍摄于贵州独山的这部剧，一开头就感觉那么亲切，老太太唱的进酒歌，仿佛就是儿时从我妈那儿听到的一样。&lt;/p&gt;
&lt;p&gt;儿子过完年又去北京上班了，看着他上了出租车，父亲掏出钱提前付了车费，母亲看着出租车掉个头慢慢开去。&lt;/p&gt;
&lt;p&gt;在教两老人用微信语音时，两老笑的不亦乐乎。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202011/03/UnJtSqpoVUf6PmPWPiaL98Vw2O98M3Zbe07BjHyA.jpeg&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;p&gt;孩子们走后偌大的一个房子，还好有燕子叽叽喳喳。我能想到当燕子归来后，老头那种小孩似的开心的表情。&lt;/p&gt;
&lt;p&gt;老太说，我能陪你一两年是一两年，人到暮年，我们最大的陪伴，就只有老伴了吧。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;向往这样的日子&lt;/p&gt;
&lt;p&gt;我意愿我老来的时候&lt;/p&gt;
&lt;p&gt;在大山脚下有个房子&lt;/p&gt;
&lt;p&gt;和阿宝种点包谷茄子&lt;/p&gt;
&lt;p&gt;再养一头狗，像电影「百鸟朝凤」里面的妞妞一样，我在田里干活，她给我送饭。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;read-more&quot;&gt;Read more&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://movie.douban.com/review/9896481/&quot;&gt;四个春天&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.douban.com/note/242582123/&quot;&gt;导演饭叔 - 我妈&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.douban.com/note/265025263/&quot;&gt;导演饭叔 - 我爸&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Go 语言中的 init 函数（译）</title><link>https://godruoyi.com/posts/the-go-init-function/</link><guid isPermaLink="true">https://godruoyi.com/posts/the-go-init-function/</guid><description>有时候，在创建 golang 应用程序的时候，我们经常需要在程序初始化的时候执行某些操作。如初始化数据库的连接、载入本地配置文件等。</description><pubDate>Fri, 30 Oct 2020 09:42:11 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;本文译自 &lt;a href=&quot;https://tutorialedge.net/golang/the-go-init-function/&quot;&gt;The Go init Function&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;有时候，在创建 golang 应用程序的时候，我们经常需要在程序初始化的时候执行某些操作。如初始化数据库的连接、载入本地配置文件等。&lt;/p&gt;
&lt;p&gt;而在 Go 语言中，这些操作都是通过  &lt;code&gt;init()&lt;/code&gt; 函数来完成的。在本篇教程中，我们将探讨如何更优雅（fame and glory）的使用 &lt;code&gt;init()&lt;/code&gt; 函数，或许能帮助你构建你的下一个 go 项目。&lt;/p&gt;
&lt;h2 id=&quot;go-中的初始化函数&quot;&gt;Go 中的初始化函数&lt;/h2&gt;
&lt;p&gt;在 Go 语言中，&lt;code&gt;init()&lt;/code&gt; 函数的功能非常强大，并且比其他语言更容易使用。&lt;code&gt;init()&lt;/code&gt; 函数可以在 package 的 block 中使用，并且无论该包被导入多少次，&lt;code&gt;init()&lt;/code&gt; 函数将始终仅被调用一次。&lt;/p&gt;
&lt;p&gt;现在，请看下面的列子，&lt;code&gt;init&lt;/code&gt;  函数定义的内容只被执行了一次。这种特性能使用我们有效的建立数据库连接、注册各种各样的服务，或执行您只想执行一次的其他任务。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;package&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; main&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; init&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;  fmt.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;Println&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;This will get called on main initialization&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; main&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;  fmt.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;Println&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;My Wonderful Go Program&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;package main

func init() {
  fmt.Println(&amp;#x22;This will get called on main initialization&amp;#x22;)
}

func main() {
  fmt.Println(&amp;#x22;My Wonderful Go Program&amp;#x22;)
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;请注意，在上面的示例中，我们并没有显示的调用 &lt;code&gt;init()&lt;/code&gt; 函数，Go 语言会隐式地为我们处理执行，因此上述程序应提供如下所示的输出：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; go&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; test.go&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;This&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; will&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; get&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; called&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; on&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; main&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; initialization&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;My&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Wonderful&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Go&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Program&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;$ go run test.go
This will get called on main initialization
My Wonderful Go Program&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;利用这个特性，我们可以做一些很棒的事情，例如变量初始化。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;package&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; main&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;fmt&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; name &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;string&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; init&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    fmt.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;Println&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;This will get called on main initialization&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    name &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &quot;Elliot&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; main&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    fmt.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;Println&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;My Wonderful Go Program&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    fmt.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;Printf&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;Name: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#F47067&quot;&gt;%s\n&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, name)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;package main

import &amp;#x22;fmt&amp;#x22;

var name string

func init() {
    fmt.Println(&amp;#x22;This will get called on main initialization&amp;#x22;)
    name = &amp;#x22;Elliot&amp;#x22;
}

func main() {
    fmt.Println(&amp;#x22;My Wonderful Go Program&amp;#x22;)
    fmt.Printf(&amp;#x22;Name: %s\n&amp;#x22;, name)
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;在上述示例中，与必须显示的调用定义的函数相比，&lt;code&gt;init()&lt;/code&gt; 函数会更受欢迎。当运行上述程序时，变量名 name 的值已经被初始化。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; go&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; test.go&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;This&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; will&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; get&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; called&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; on&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; main&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; initialization&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;My&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Wonderful&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Go&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Program&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;Name:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Elliot&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;$ go run test.go
This will get called on main initialization
My Wonderful Go Program
Name: Elliot&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&quot;multiple-packages&quot;&gt;Multiple Packages&lt;/h2&gt;
&lt;p&gt;让我们来看看一个更复杂的场景，它更接近于生产环境。想象一下，我们的应用程序中有 3 个不同的 Go 包，分别是 main、broker、database。&lt;/p&gt;
&lt;p&gt;在每一个包中，我们都可以指定一个 init() 函数，比如用于设置 Kafka 或 MySQL 的连接池。当我们调用 database 包中的函数时，它将自动调用 init 函数并初始化连接池。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;🐜：你不能依赖多个 init() 函数的执行顺序。与其如此，还不如把精力花费在其他逻辑的编写上。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;init-的初始化顺序&quot;&gt;Init 的初始化顺序&lt;/h2&gt;
&lt;p&gt;对于更复杂的系统，你的每个包中可能包含多个文件。每一个文件都可能有自己的 init 函数。那么 Go 是如何初始化这些包的顺序的呢？&lt;/p&gt;
&lt;p&gt;当涉及到初始化顺序时，需要考虑一些事项。 Go 通常按照声明的顺序进行初始化，但如果声明的变量依赖另外包中的变量或方法，那将
但是会在它们可能依赖的任何变量之后显式初始化。 这意味着，假设您在同一软件包中有两个文件 a.go 和 b.go，若 a.go 中任何内容的初始化都取决于 b.go 中的内容，则 a.go 将首先被初始化。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;🐜：可以在官方文档中找到有关Go中初始化顺序的更深入概述：&lt;a href=&quot;https://golang.org/ref/spec#Package_initialization&quot;&gt;The Go Programming Language Specification - The Go Programming Language&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;来看下面的列子：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// source: https://stackoverflow.com/questions/24790175/when-is-the-init-function-run&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; WhatIsThe &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; AnswerToLife&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; AnswerToLife&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 42&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; init&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    WhatIsThe &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 0&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; main&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; WhatIsThe &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        fmt.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;Println&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;It&apos;s all a lie.&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// 输出这行&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;// source: https://stackoverflow.com/questions/24790175/when-is-the-init-function-run
var WhatIsThe = AnswerToLife()

func AnswerToLife() int {
    return 42
}

func init() {
    WhatIsThe = 0
}

func main() {
    if WhatIsThe == 0 {
        fmt.Println(&amp;#x22;It&amp;#x27;s all a lie.&amp;#x22;) // 输出这行
    }
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;在这种情况下，您会看到 AnswerToLife 函数将在 init 函数之前运行，因为 WhatIsThe 变量是在调用我们的 init 函数之前声明的。&lt;/p&gt;
&lt;h2 id=&quot;同一文件多个-init-函数&quot;&gt;同一文件，多个 init 函数&lt;/h2&gt;
&lt;p&gt;如果我们在同一个 Go 文件里定义多个 init() 函数会怎样？一开始我认为这是不可能的，但 Go 确实支持在一个文件中拥有 2 个独立的 init() 函数。&lt;/p&gt;
&lt;p&gt;这些 init() 函数按声明顺序依次调用。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;目前同一个 go 文件中是支持多个 init 函数的，不止 2 个。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;package&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; main&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;fmt&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; initCounter &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;int&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; init&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    initCounter &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;++&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; init&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    initCounter &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;++&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; init&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    initCounter &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;++&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; init&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    initCounter &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;++&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; init&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    initCounter &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;++&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; main&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    fmt.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;Println&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(initCounter) &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;//5&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;package main

import &amp;#x22;fmt&amp;#x22;

var initCounter int

func init() {
    initCounter ++
}
func init() {
    initCounter ++
}
func init() {
    initCounter ++
}
func init() {
    initCounter ++
}

func init() {
    initCounter ++
}

func main() {
    fmt.Println(initCounter) //5
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;但我们为什么要将 init 拆分为多个呢？这样做有什么好处？其实对于复杂的系统，这使得我们可以将复杂的初始化过程分解为多个易于理解的 init 函数。从本质上讲，它使我们可以避免在单个 init 函数中包含一大推代码。需要注意的是，同一文件中的多个 init 函数是按声明时的顺序初始化的，这点在拆分时要特别小心。&lt;/p&gt;
&lt;h2 id=&quot;结论&quot;&gt;结论&lt;/h2&gt;
&lt;p&gt;至此，关于 init() 函数的基本介绍就结束了。一旦你掌握了包初始化的使用，你可能会发现它对掌握基于 Go 项目的结构化艺术很有用。&lt;/p&gt;
&lt;p&gt;原文地址 [[&lt;a href=&quot;https://tutorialedge.net/golang/the-go-init-function/#conclusion&quot;&gt;The Go init Function | TutorialEdge.net&lt;/a&gt;]]&lt;/p&gt;</content:encoded></item><item><title>美国中情局保藏的前苏联段子</title><link>https://godruoyi.com/posts/former-soviet-jokes-kept-by-the-cia/</link><guid isPermaLink="true">https://godruoyi.com/posts/former-soviet-jokes-kept-by-the-cia/</guid><description>今天给大家讲个美国中情局保藏的前苏联段子</description><pubDate>Tue, 29 Sep 2020 10:26:48 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;今天给大家讲个美国中情局保藏的前苏联段子。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;恐怖的兔子&quot;&gt;恐怖的兔子&lt;/h2&gt;
&lt;p&gt;两只兔子在街上碰面。&lt;/p&gt;
&lt;p&gt;兔子甲：“出了什么事？你怎么这么急慌慌的？”&lt;/p&gt;
&lt;p&gt;兔子乙：“你没听说吗？传的很凶呢，所有的骆驼都要被阉割。”&lt;/p&gt;
&lt;p&gt;兔子甲：“你又不是骆驼。”&lt;/p&gt;
&lt;p&gt;兔子乙：“等人家把你抓住、阉了，你再去想法证明自己不是骆驼吧。”&lt;/p&gt;
&lt;h2 id=&quot;言论自由&quot;&gt;言论自由&lt;/h2&gt;
&lt;p&gt;一个美国人向俄国人解释为什么说美国是一个真正言论自由的国家。美国人说：&lt;/p&gt;
&lt;p&gt;“我可以走到白宫前，高喊让里根下地狱！”&lt;/p&gt;
&lt;p&gt;俄国人很不以为然：&lt;/p&gt;
&lt;p&gt;“你这算什么，我也可以走到红场上高喊，让里根下地狱”！&lt;/p&gt;
&lt;h2 id=&quot;共产主义与科学&quot;&gt;共产主义与科学&lt;/h2&gt;
&lt;p&gt;一位群众问党支书：&lt;/p&gt;
&lt;p&gt;“共产主义是谁发明的，共产主义者还是科学家？”&lt;/p&gt;
&lt;p&gt;支书回答：“这还用问，当然是共产主义者了！”&lt;/p&gt;
&lt;p&gt;群众：“我猜也是。如果是科学家发明的，他们一定会先拿狗实验一下。”&lt;/p&gt;
&lt;h2 id=&quot;知识的源头&quot;&gt;知识的源头&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://twitter.com/haoel/status/1310831913813176320&quot;&gt;https://twitter.com/haoel/status/1310831913813176320&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bbc.com/zhongwen/simp/world-45652110&quot;&gt;https://www.bbc.com/zhongwen/simp/world-45652110&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>停电</title><link>https://godruoyi.com/posts/tingdian/</link><guid isPermaLink="true">https://godruoyi.com/posts/tingdian/</guid><description>前段时间贵州大雨，电力局提前发了短信，说可能会停一两个小时的电  我们家是用电来烘烟的，没电了，万把块钱一烘的烟，就全部烂掉了  爸去街上抬发电机  但是发电机太大，至少需要三个人  找不到人  姐夫说用钱请人...</description><pubDate>Fri, 28 Aug 2020 09:11:03 GMT</pubDate><content:encoded>&lt;p&gt;前段时间贵州大雨，电力局提前发了短信，说可能会停一两个小时的电&lt;/p&gt;
&lt;p&gt;我们家是用电来烘烟的，没电了，万把块钱一烘的烟，就全部烂掉了&lt;/p&gt;
&lt;p&gt;爸去街上抬发电机&lt;/p&gt;
&lt;p&gt;但是发电机太大，至少需要三个人&lt;/p&gt;
&lt;p&gt;找不到人&lt;/p&gt;
&lt;p&gt;姐夫说用钱请人也的去抬啊&lt;/p&gt;
&lt;p&gt;可能是先去街上试了下&lt;/p&gt;
&lt;p&gt;差零件&lt;/p&gt;
&lt;p&gt;联系海军开车送去黄都购买&lt;/p&gt;
&lt;p&gt;买回，上好，什么筛子又坏了&lt;/p&gt;
&lt;p&gt;后面还是没抬回发电机&lt;/p&gt;
&lt;p&gt;电停了&lt;/p&gt;
&lt;p&gt;那就在烘烤烟的地方守着&lt;/p&gt;
&lt;p&gt;一守就是半天&lt;/p&gt;
&lt;p&gt;一个人或坐着、站着、蹲着、却始终无能为力&lt;/p&gt;
&lt;p&gt;看着这幸苦办弄出来的&lt;/p&gt;
&lt;p&gt;也只能往心里面吞&lt;/p&gt;
&lt;p&gt;操他妈的&lt;/p&gt;
&lt;p&gt;后联系到电力相关的人&lt;/p&gt;
&lt;p&gt;爸跟他们吼起来了&lt;/p&gt;
&lt;p&gt;我知道他为什么吼&lt;/p&gt;
&lt;p&gt;换成是领导也要被吼&lt;/p&gt;
&lt;p&gt;12 小时后，电来了&lt;/p&gt;
&lt;p&gt;烟，却全部烂了&lt;/p&gt;</content:encoded></item><item><title>换一种方式，交换两个变量的值</title><link>https://godruoyi.com/posts/another-way-to-do-it-is-to-swap-the-values-of-the-two-variables/</link><guid isPermaLink="true">https://godruoyi.com/posts/another-way-to-do-it-is-to-swap-the-values-of-the-two-variables/</guid><description>你知道如何不实用临时变量交换两个变量的值吗</description><pubDate>Mon, 24 Aug 2020 15:02:22 GMT</pubDate><content:encoded>&lt;p&gt;在日常编程中，我们经常涉及到交换两个变量的值，常见实现如下。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; inplaceSwap&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#E36209;--shiki-dark:#F69D50&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#E36209;--shiki-dark:#F69D50&quot;&gt;b&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; int&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    c &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; a&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    a &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; b&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    b &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; c&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; a, b&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; main&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;    println&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;inplaceSwap&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// output 2 1&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;func inplaceSwap(a, b int) (int, int) {
    c := a
    a = b
    b = c

    return a, b
}

func main() {
    println(inplaceSwap(1,2))
}

// output 2 1&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;blockquote&gt;
&lt;p&gt;当然，你也可以使用 &lt;code&gt;a, b = b, a&lt;/code&gt; 来快速交换两个变量的值。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;利用中间变量，&lt;strong&gt;快速简单&lt;/strong&gt;又快速的实现了变量的交换。但今天发现一种高逼格的实现，虽然并没什么用，如下：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; inplaceSwap&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--shiki-light:#E36209;--shiki-dark:#F69D50&quot;&gt;b&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; int&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;*int&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;*int&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    *&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;b &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;a &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;^&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;b&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    *&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;a &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;a &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;^&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;b&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    *&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;b &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;a &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;^&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;b&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; a, b&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; main&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    a &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    b &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 2&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;    inplaceSwap2&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;a, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;b)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    fmt.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;Printf&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;a=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#F47067&quot;&gt;%v&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;, b=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#F47067&quot;&gt;%v\n&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, a, b) &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// a=2, b=1&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;func inplaceSwap(*a, *b int) (*int, *int) {
    *b = *a ^ *b
    *a = *a ^ *b
    *b = *a ^ *b

    return a, b
}

func main() {
    a := 1
    b := 2

    inplaceSwap2(&amp;#x26;a, &amp;#x26;b)

    fmt.Printf(&amp;#x22;a=%v, b=%v\n&amp;#x22;, a, b) // a=2, b=1
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;在此过程中，没有用到任何临时变量，而是由下面这特性完成的。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;对于任意向量 a，有 a ^ a = 0&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我们假设变量 a 的内存地址为 0x80，变量 b 的内存地址为 0x90，则换算成二进制为：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;a &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; 0x&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;80&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;b &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; 0x&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;90&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// a = 1000 0000&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// b = 1001 0000&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// a ^ b = 0001 0000&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;a := 0x80
b := 0x90

// a = 1000 0000
// b = 1001 0000

// a ^ b = 0001 0000&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;则对于上面的三行运算&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// 第一行&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;b &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;a &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;^&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;b &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// 1000 0000 ^ 1001 0000 =&gt; *b = 0001 0000&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// 第二行，这行计算完成后，变量 a 的内存地址已成功换为变量 b 的地址&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;a &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;a &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;^&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;b &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// 1000 0000 ^ 0001 0000 =&gt; *a = 1001 0000&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// 第三行，这行计算完成后，变量 b 的内存地址已成功换为变量 a 的地址&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;b &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;a &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;^&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;b &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// 1001 0000 ^ 0001 0000 =&gt; *b = 1000 0000&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt; *b = 0001 0000

// 第二行，这行计算完成后，变量 a 的内存地址已成功换为变量 b 的地址
*a = *a ^ *b // 1000 0000 ^ 0001 0000 =&gt; *a = 1001 0000

// 第三行，这行计算完成后，变量 b 的内存地址已成功换为变量 a 的地址
*b = *a ^ *b // 1001 0000 ^ 0001 0000 =&gt; *b = 1000 0000&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;至此，通过上三行代码，我们成功的将两个变量的内存地址交换，且没有用到零时变量。&lt;/p&gt;
&lt;p&gt;这种交换的方式并没有性能上的优势，它仅仅是一个智力游戏。 — 深入理解计算机系统&lt;/p&gt;
&lt;p&gt;但是这种有一个严重的弊端，当变量 a 和 b 是同一个变量时，将会导致其值为 0。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;go&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;a &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; 0x&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;80&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;b &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; 0x&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;80&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// a ^ b = 10000000 ^ 10000000 = 00000000&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// 这将导致变量 b 的指针地址指向的值为 0&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// *b = *a ^ *b   =&gt;   *b = 0&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// 而 a 和 b 指向的同一个地址，故 &amp;#x26;a = 0，&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// 后面即使再多操作，也只是在操作 0，本质不会发生变化。&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;   *b = 0

// 而 a 和 b 指向的同一个地址，故 &amp;#x26;a = 0，
// 后面即使再多操作，也只是在操作 0，本质不会发生变化。&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;hr&gt;</content:encoded></item><item><title>秋老虎</title><link>https://godruoyi.com/posts/qiu-laohu/</link><guid isPermaLink="true">https://godruoyi.com/posts/qiu-laohu/</guid><description>家里面的老人说立秋后的第一场雨就是要强降温的前奏，跟着就会反一波热浪，重庆称为「秋老虎」（意思就是强降温之后再来一波强升温达到 38° 39°那种天气，还是有点热的哦），跟着就会越来越冷身上越来越厚进入到了寒...</description><pubDate>Thu, 20 Aug 2020 05:36:27 GMT</pubDate><content:encoded>&lt;p&gt;家里面的老人说立秋后的第一场雨就是要强降温的前奏，跟着就会反一波热浪，重庆称为「秋老虎」（意思就是强降温之后再来一波强升温达到 38° 39°那种天气，还是有点热的哦），跟着就会越来越冷身上越来越厚进入到了寒风萧萧的冬季~。&lt;/p&gt;
&lt;p&gt;距离动手术拿掉小福贵的那天已经过去了 57 天，身体已几乎恢复了正常，也回到岗位一个月零 9 天了，为了避免自己在这个时候生病裙子几乎很少穿了，在公司尽量避开了空调的所有风口。同事如果找不到遥控器的情况下，没话说，~准在我这儿！这 57 天我已经吃了 3 只大母鸡，2 大袋鸡蛋（记不住多少个反正两位数），碍于是手术初期避免伤口拉伤也没有做任何锻炼运动（当然我是一个坦然的人，你可以直接说我懒我不介意的），于是我的体重从 53kg 上升到了 56kg，可真是了不起，可我自己依然很自信的觉得自己很瘦？？？ 买了 3 条裤子退了 3 条裤子，买了 1 双凉鞋也退了 1 双凉鞋。不久的将来我可能就可以和收件的快递成为朋友了吧！&lt;/p&gt;
&lt;p&gt;今年立秋后的重庆一直晴朗，雨很少。感觉都下到了我们的邻居四川去了，连续好几天的大雨涨洪不得了，记得 18 年那年夏天住在华新街，而工作在渝中区的嘉滨路，大概是下午 15.00 左右吧吹起了狂风，当时微博上有网友拍了一个观音桥步行街的一颗大树直接被大风连根拔起，后来大雨就来了，刮倒了好多电线杆和树，全城太多地方水电气受到了影响；人家政府的也忙不过来呀，于是下班了的时候雨停了但是交通瘫痪了全都堵在了路上，还好那一年住的地方距离上班大致上隔了一座渝澳大桥，看到这个场面于是我选择了走路回去。可能走了 40 分钟左右到家了。路上遇到全是被风雨打乱的电杆和树枝，大桥上，公交车师傅们拿着工具在捅桥上的排水洞，有些乘客堵着在车里难受，也出来在桥站着吹风看着嘉陵江，也许有些人很少这样看过吧，每天都在各自忙各自的，此时也不为一番享受。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;那是我和大牛交往的第 2 年，那时租的三钢厂家属楼，是九几年的老房子没电梯，没独立厨房，也没独立的客厅在 5 楼。暴风雨后各自都安全到家了，家里停电了我们晚饭就煮了碗鱼汤面吃下了，没电脑玩手机也没法充电，虽然才下过一场大雨但是雨停后热浪依旧存在，我俩就只有憨憨的坐在窗户边的榻榻上背靠着衣柜门（没沙发衣柜门凉快），大牛一直拿着蒲扇给我打扇，等呀等呀来来回来大牛都洗了 2 次冷水澡还是热，我毕竟是个女生我是不可能洗冷水澡了，到了晚上 11.00 了还没来电，大牛就让我先睡觉明天还得上早班，于是我躺下了他在一边坐着一直给我扇着，慢慢的… 慢慢的…我就睡着了…&lt;/p&gt;</content:encoded></item><item><title>为什么中国的传统行业没人继承</title><link>https://godruoyi.com/posts/why-no-one-to-inherit-chinese-traditional-industry/</link><guid isPermaLink="true">https://godruoyi.com/posts/why-no-one-to-inherit-chinese-traditional-industry/</guid><description>今天在坐公交车上班的时候，看到重庆电视台推送的视频，一位小男孩在跟着自己的爸爸学京剧打鼓</description><pubDate>Fri, 07 Aug 2020 01:55:44 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;今天在坐公交车上班的时候，看到重庆电视台推送的视频，一位小男孩在跟着自己的爸爸学京剧打鼓。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;爸爸剧组有演出的时候，有时就会带上小男孩；表演者在台上唱着京剧，孩子在台子左边敲着小鼓，有时候一坐就是两小时。小男孩的爸妈说，孩子从小就喜欢打鼓，也打得挺好的，所以剧组经常带着一起出去表演，有时还会让他上台。&lt;/p&gt;
&lt;p&gt;我看到这个视频就在想，小孩子的想法真天真啊；打鼓这件事，充其量是孩子对这件事充满好奇，毕竟小孩的好奇心和想法我们是无法捉摸的。但作为家长，应该理性的引导孩子的兴趣，当然我不是觉得打鼓有什么不好，不，我就是觉得打鼓不好。在这个时代，打鼓能给你带来什么。作为成年人，我想的不是什么继承中国传统文化之类的鬼话。我门应该关注的是他能给我带来什么；当你孩子真的从事这份行业的时候，在总加速师一尊大哥大革命似加速的今天，能养活自己吗？能养活孩子吗？能养活年迈的父母吗？&lt;/p&gt;
&lt;p&gt;这是大脑里突然呈现出小时候父母常说的一句话：“这能当饭吃吗”。这话真的是要长大了有压力了才能理解他的含义。小孩子才靠兴趣做事情，大人应该考虑得失了。&lt;/p&gt;
&lt;p&gt;这估计也是为什么中国那么多优秀的传统行业，却没有人愿意继承；因为这些大部分的传统行业，没办法给我们带来理想的报酬，只能是在自身条件充裕的情况下，当作额外的兴趣在延续罢了，始终难入主业。&lt;/p&gt;
&lt;p&gt;再隔十年后，传统行业可能已不复存在了，但又有什么关系呢！说不定那时早已城春草木深了。&lt;/p&gt;</content:encoded></item><item><title>阿宝的感悟</title><link>https://godruoyi.com/posts/my-dear-abao-feeling/</link><guid isPermaLink="true">https://godruoyi.com/posts/my-dear-abao-feeling/</guid><description>阿宝一觉睡醒后的感悟，她就是这么可爱（配图为最近最喜欢的电影，这也是阿宝老了的样子，肯定的）。</description><pubDate>Thu, 06 Aug 2020 06:58:46 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;阿宝一觉睡醒后的感悟，她就是这么可爱（配图为最近最喜欢的电影，这也是阿宝老了的样子，肯定的）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;午休的时候被窗户外的车流声吵醒了，点开手机看了一眼 13:49 分了，外面的知了声持续不断，抬头瞧了瞧对面的男同事人不见了，办公室的胥总也不在，左边的女同事依旧睡得很香。&lt;/p&gt;
&lt;p&gt;这个烈日的午觉后劲让我脑壳有点昏，于是起身去接了杯水喝，回工位后发现没抽纸了。于是放下了水杯出了办公室去了前台拿了包抽纸，前台和两边办公区域的灯光很明亮，伙伴们都在忙自己的事情，包扣前台的妹妹也在小心翼翼的复印一张看着有点质感的证书（也许是某个新员工的毕
业证吧）！这时一股凉凉涌上心头，我不如他们一天粘贴复制的数据中心，不如前台，更不如产品部门的人，技术部的就别提了吧！做着最难的职位，拿着最低的工资。&lt;/p&gt;
&lt;p&gt;八月一号后为了想保住工资，每天发 100-200 条微信给客户推荐产品，以前5000 元一年现在 1999 一年也没人搭理我。离开前台后我到了走廊一阵热浪向我的身体扑来，妈呀！好热。换工作这个想法到此为止吧，，，&lt;/p&gt;
&lt;p&gt;拿着纸的我回到了自己的办公室，推开了办公室的门，在黑黢黢的办公室打开了明亮的灯，刺醒了睡得很香的同事。&lt;/p&gt;
&lt;p&gt;被我刺瞎的同事醒了后我对她说了句话:如果价格低都没人买，我们下个月就没法替家里省点电了。。。。。。。。。。&lt;/p&gt;
&lt;p&gt;。。。。。。。。over ! !&lt;/p&gt;</content:encoded></item><item><title>相比如今公众号等大热的文字创作平台，你如何看待 Blog</title><link>https://godruoyi.com/posts/what-do-you-think-of-blog-compared-with-the-popular-platform-of-text-creation-such-as-public-account/</link><guid isPermaLink="true">https://godruoyi.com/posts/what-do-you-think-of-blog-compared-with-the-popular-platform-of-text-creation-such-as-public-account/</guid><description>摘抄一段 Hsiaoming Yang 关于 Blog 的看法，在丝经济大行其道的今天，你是不是拥有自己的信息来源呢？</description><pubDate>Sat, 23 May 2020 03:51:06 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;在丝经济大行其道的今天，你是不是拥有自己的信息来源呢？原文摘抄自利器对 Typlog 作者的专访，推荐你前往这里查看完整的 &lt;a href=&quot;https://liqi.io/lepture/&quot;&gt;访谈记录&lt;/a&gt; （可能需翻墙）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我喜欢个人博客，尤其是在这粉丝经济大行其道之时。我喜欢看作者们的生活思考或者技能分享，感受一个个活生生的人。看个人的真实想法，而非替粉丝的说话。现在大家越来越不怎么更新了，越发显得珍贵起来。&lt;/p&gt;
&lt;p&gt;我尝订阅过几个公众号，后来都取消了。偶尔看看朋友圈，多数文章的标题都在叫唤着「快点我快点我」，但是并没有什么好看的；又有喜欢的朋友分享的文章，点开一看，不好意思，文章已经被屏蔽了，所以现在很少会看公众号文章。这并不是说公众号里没有好的内容，但是大多数文章都是糟糕的排版，各种表情包以及不相关的图片穿插于并没有什么价值的文字之间，所以多数时候也就懒得点开了。&lt;/p&gt;
&lt;p&gt;有时在它处看到一篇还不错的文章，但是作者一直在文章里隔三差五地推销一下他的公众号，打断阅读思路，又在每篇文章里放上一个丑陋的二维码，所以越发反感公众号了。&lt;/p&gt;
&lt;p&gt;中文世界最大的悲剧便是封闭。从国家到公司，从公司到个人，都在打造围墙，建起一个又一个圈子。因为封闭，没有长尾效应，大家的注意力都在当下，一个热点驱逐着另一个热点，没有积淀的走马观花。更悲哀的是整个世界都在向中国学习。&lt;/p&gt;
&lt;p&gt;我不了解为何大家会去阅读公众号，我并不觉得有什么不能错过的内容。如果想获得的是知识，那读书是更好的选择。当然我也不觉得有什么错，每个人都有自己的习惯嘛。&lt;/p&gt;
&lt;p&gt;我自己不用国内这些平台写作，一是想自己掌握自己的数据，二是不想去猜各个网站的敏感词都是哪些。我的博客是 &lt;a href=&quot;https://lepture.com/&quot;&gt;Just lepture&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;上面的链接可能需要代理，你也可以点击下面的备用链接。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/Fca2ZL79aH45rP50HBktJw&quot;&gt;https://mp.weixin.qq.com/s/Fca2ZL79aH45rP50HBktJw&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://liqi.io/lepture/&quot;&gt;原文 https://liqi.io/lepture/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.lvbby.com/p/md/1812310124343741311003&quot;&gt;https://www.lvbby.com/p/md/1812310124343741311003&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>矛盾的 50% 定律</title><link>https://godruoyi.com/posts/the-50-rule-of-contradiction/</link><guid isPermaLink="true">https://godruoyi.com/posts/the-50-rule-of-contradiction/</guid><description>大概是这样的，一个楚国的商人，说自己的卖的矛无坚不摧，然后又说自己的盾坚不可摧，路人让其「以子之矛，陷子之盾，何如？」，结果商人弗能应也。</description><pubDate>Sat, 09 May 2020 14:10:40 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;我们都知道矛和盾的故事，但你要是不知道，那我先同步下认知线。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;大概是这样的，一个楚国的商人，说自己的卖的矛无坚不摧，然后又说自己的盾坚不可摧；路人让其「以子之矛，陷子之盾，何如？」，结果商人弗能应也。&lt;/p&gt;
&lt;p&gt;然后今天爬楼梯的时候我就在想。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果我的矛把我的盾捅破了，我就连世界上最坚固的盾都能刺穿；&lt;/li&gt;
&lt;li&gt;若是我的盾防下了矛的攻击，我就连世界上最锋利的矛都刺不穿我的盾；&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;那是不是怎么说都有 50% 是对的呢，只要你不连在一起看这件事就行了。&lt;/p&gt;
&lt;p&gt;姑且就叫他矛盾的 50% 定律吧。&lt;/p&gt;</content:encoded></item><item><title>硅谷革命</title><link>https://godruoyi.com/posts/the-revolution-in-silicon-valley/</link><guid isPermaLink="true">https://godruoyi.com/posts/the-revolution-in-silicon-valley/</guid><description>Macintosh 则不同，它背后的驱动力主要还是来自艺术价值，它无视外部竞争，目的是要开发出一款非凡卓越的产品</description><pubDate>Sat, 07 Mar 2020 11:19:10 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;终于看完了两年前就买下的这本书。很难想象，当初的这群狂热分子，竟然是这样做出世上最伟大的产品的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202003/07/_1583578626_MU8lTqVTh3.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;p&gt;真挺羡慕的，能置身那样的时代，参与划时代的产品开发。那时候的团队氛围和对产品艺术的追求，&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Macintosh 则不同，它背后的驱动力主要还是来自艺术价值，它无视外部竞争，目的是要开发出一款非凡卓越的产品。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;但放到现在来看，周围已经没有那样现实扭曲磁场能力的人了。并且当平台不够强大时，可能连物质生活都满意不了，还说什么理想呢。&lt;/p&gt;
&lt;p&gt;或者说一些数据吧。&lt;/p&gt;
&lt;p&gt;1981 年的乔布斯，开着保时捷，Macintosh 团队年薪较低的工程师 22000 美元。&lt;/p&gt;</content:encoded></item><item><title>2020 继续加油</title><link>https://godruoyi.com/posts/continue-refueling-in-2019/</link><guid isPermaLink="true">https://godruoyi.com/posts/continue-refueling-in-2019/</guid><description>大年三十了，终于写完了这篇总结，给来年定个目标，给今年一个肯定；希望以后的每年都有总结可写，有美好的故事发送，加油！</description><pubDate>Thu, 23 Jan 2020 16:14:23 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;眼看着就要过除夕啦，是时候把在 Bear 里只埋了标题的这篇文章写完啦。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;总的来说，今年比 2018 年要过的充实，无论从工作中还是生活上，都越来越向理想的目标近了。&lt;/p&gt;
&lt;h2 id=&quot;搬家&quot;&gt;搬家&lt;/h2&gt;
&lt;p&gt;年初的时候，终于搬进了我们的新家。虽然房子是一年前就买下来的，但当时并没有装修，搁置了一年终于搬进来了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202001/23/_1579795650_lYwRR1LN5D.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;p&gt;搬进来就意味着要装修，装修这件事可是真累人；原本打算写篇文章纪念的，结果如你看到的，又只写了个开头。&lt;/p&gt;
&lt;h2 id=&quot;峨眉山&quot;&gt;峨眉山&lt;/h2&gt;
&lt;p&gt;5 月份的时候，我们去峨眉山看日出，这可能是今年最受屡的一次旅行了，不但天气不好下起了小雨，还忍受着累、饿、困、冷、信念崩塌等各种 debuff 缠身。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/201905/22/_1558528125_LN0WQoeMqm.jpeg&quot; alt=&quot;峨眉山&quot;&gt;&lt;/p&gt;
&lt;p&gt;为了纪念这次不一样的出行，我还特意写了一篇文章：&lt;a href=&quot;https://godruoyi.com/posts/emei-out&quot;&gt;峨眉山的日出&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;换个角度看问题&quot;&gt;换个角度看问题&lt;/h2&gt;
&lt;p&gt;8 月份的时候，作为技术支持，随着商务的同事一起去酉阳蝶石花谷；虽说并没有我什么事，但我还是想写写。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202001/23/_1579795743_PXHMjaJvrK.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;p&gt;作为码农，不应该只关注属于自己模块的功能实现，最好把自己当作为一枚 project owner（虽说自己只是一个 programmer），并了解整个项目的各个周期。&lt;/p&gt;
&lt;p&gt;就技术上来说，要学会像测试人员一样，清楚的知道系统的业务逻辑，并尝试自行 review 同伴的代码。如果你是一枚后段工程师，也可以尝试 review 下前端同伴的代码，看看他们都用了些什么黑科技，也能更方便的进行接口设计。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;产品、原型、设计、架构、技术选型、编码、CICD&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 等。&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;产品、原型、设计、架构、技术选型、编码、CICD 等。&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;拿工具说，若你们团队正在使用自动构建的工具，那你知道他的工作原理吗？要是让你自己搭建一个类似的工具，或是写个类似的 workflow，如何操作呢？&lt;/p&gt;
&lt;p&gt;从产品来看的话，尝试了解下客户的真实需求，了解下业类成熟的解决方案。并不是所有需求都需要编码才能实现的，有时候引导下客户，说不定技术上很难解决的问题，客户其实根本就不咳er。&lt;/p&gt;
&lt;h2 id=&quot;三亚&quot;&gt;三亚&lt;/h2&gt;
&lt;p&gt;快年底的时候，终于完成了多年的一个愿望————带着爸妈去看海。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202001/23/_1579795751_6xINeZajzX.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;p&gt;原打算带他们去北京的，因为在我看来，老一辈的人对北京都有一种说不清的情节。但当询问了爸妈的意见后，他们居然选择了三亚。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202001/23/_1579795842_e9xwGRBs86.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;p&gt;这趟旅行很开心，回程的路上老妈还有点宁宁不舍，多半是因为我没让她买景区里她看中的纪念品；下次带老人出去玩的时候，站在他们的角度多想想，别管它是假的还是贵的了。&lt;/p&gt;
&lt;h2 id=&quot;技术&quot;&gt;技术&lt;/h2&gt;
&lt;p&gt;技术方面，今年的技术并没有什么大的提升。虽说从年中开始，就开始尝试用 kubernetes 来部署生产环境应用。但在我看来，不过是在腾讯云提供的上层建筑上修修改改，对底层的逻辑一知半会，如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Service 的工作原理&lt;/li&gt;
&lt;li&gt;Kubernetes 的负载均衡是如何实现的&lt;/li&gt;
&lt;li&gt;容器内的网络、DNS是如何工作的&lt;/li&gt;
&lt;li&gt;master 节点的调度原理&lt;/li&gt;
&lt;li&gt;PV、PVB 的设计&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;但也不到不说，从开始使用 kubernetes 以来，一些以前不清楚的逻辑也慢慢清晰出来。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;容器的工作原理&lt;/li&gt;
&lt;li&gt;如何写一个 Dockerfile&lt;/li&gt;
&lt;li&gt;用 namespace 和 cgroup 如何实现一个容器&lt;/li&gt;
&lt;li&gt;镜像的分层设计及文件的联合挂载&lt;/li&gt;
&lt;li&gt;Deployment 是如何控制 POD 按期望的方式运行的&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;其他技术，由于今年交付项目周期太紧，涉及的就太少了，但对 Laravel 的使用程度正在逐步提升，希望明年能成为一个贡献者。&lt;/p&gt;
&lt;h2 id=&quot;todo-2020&quot;&gt;TODO 2020&lt;/h2&gt;
&lt;p&gt;在过去的 2019 年，工作方面我自认为还是比较努力的，但年底突然醒悟，发现有很多方面做的都不是很完善，总结下今年的不足，给来年定几个 flag 吧。&lt;/p&gt;
&lt;ul class=&quot;contains-task-list&quot;&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;&gt; 看 5 本非技术相关的书籍&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;&gt; 远门旅行一次&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;&gt; 爬华山&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;&gt; 做一次技术分享&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;&gt; 和 50 个陌生人说话，不低于 3 分钟&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;&gt; 学习理解别人的意思，站在别人的认知程度去解释问题&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;&gt; 学习 Linux&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;&gt; 学会炒两个新菜（大菜）&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;&gt; 学习一门副业&lt;/li&gt;
&lt;li class=&quot;task-list-item&quot;&gt;&lt;input type=&quot;checkbox&quot; disabled=&quot;&quot;&gt; 开始作为一个内容创造者，并发布 50 个视频&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最后一点，还是要多说话，多说话，多说话，自信一点。&lt;/p&gt;</content:encoded></item><item><title>基于雪花算法的 PHP ID 生成器</title><link>https://godruoyi.com/posts/php-id-generator-based-on-snowflake-algorithm/</link><guid isPermaLink="true">https://godruoyi.com/posts/php-id-generator-based-on-snowflake-algorithm/</guid><description>Snowflake 是 Twitter 内部的一个 ID 生算法，可以通过一些简单的规则保证在大规模分布式情况下生成唯一的 ID 号码</description><pubDate>Tue, 13 Aug 2019 05:07:09 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Snowflake 是 Twitter 内部的一个 ID 生算法，可以通过一些简单的规则保证在大规模分布式情况下生成唯一的 ID 号码。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;其组成为：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;第一个 bit 为未使用的符号位。&lt;/li&gt;
&lt;li&gt;第二部分由 41 位的时间戳（毫秒）构成，他的取值是当前时间相对于某一时间的偏移量。&lt;/li&gt;
&lt;li&gt;第三部分和第四部分的 5 个 bit 位表示数据中心和机器ID，其能表示的最大值为 2^5 -1 = 31；&lt;/li&gt;
&lt;li&gt;最后部分由 12 个 bit 组成，其表示每个工作节点&lt;strong&gt;每毫秒&lt;/strong&gt;生成的序列号 ID，同一毫秒内最多可生成 2^12 -1 即 4095 个 ID。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;需要注意的是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在分布式环境中，5 个 bit 位的 datacenter 和 worker 表示最多能部署 31 个数据中心，每个数据中心最多可部署 31 台节点。&lt;/li&gt;
&lt;li&gt;41 位的二进制长度最多能表示 2^41 -1 毫秒即 69 年，所以雪花算法最多能正常使用 69 年，为了能最大限度的使用该算法，你应该为其指定一个开始时间。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;由上可知，雪花算法生成的 ID 并不能保证唯一，如当两个不同请求同一时刻进入相同的数据中心的相同节点时，而此时该节点生成的 sequence 又是相同时，就会导致生成的 ID 重复。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;所以要想使用雪花算法生成唯一的 ID，就需要保证同一节点同一毫秒内生成的序列号是唯一的。基于此，我们在 SDK 中集成了多种序列号提供者：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;RandomSequenceResolver（随机生成）&lt;/li&gt;
&lt;li&gt;RedisSequenceResolver （基于 redis psetex 和 incrby 生成）&lt;/li&gt;
&lt;li&gt;LaravelSequenceResolver（基于 redis psetex 和 incrby 生成）&lt;/li&gt;
&lt;li&gt;SwooleSequenceResolver（基于 swoole_lock 锁）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不同的提供者只需要保证&lt;strong&gt;同一毫秒生成的序列号不同&lt;/strong&gt;，就能得到唯一的 ID。&lt;/p&gt;
&lt;h2 id=&quot;要求&quot;&gt;要求&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;PHP &gt;= 7.0&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://getcomposer.org/&quot;&gt;Composer&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;安装&quot;&gt;安装&lt;/h2&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;shell&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;shell&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; composer&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; godruoyi/php-snowflake&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; -vvv&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;$ composer require godruoyi/php-snowflake -vvv&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&quot;使用&quot;&gt;使用&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;简单使用.&lt;/li&gt;
&lt;/ol&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$snowflake &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; \Godruoyi\Snowflake\Snowflake&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$snowflake&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// 1537200202186752&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;id();
// 1537200202186752&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;指定数据中心ID及机器ID.&lt;/li&gt;
&lt;/ol&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$snowflake &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; \Godruoyi\Snowflake\Snowflake&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($datacenterId, $workerId);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$snowflake&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;();&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;id();&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;指定开始时间.&lt;/li&gt;
&lt;/ol&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$snowflake &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; \Godruoyi\Snowflake\Snowflake&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$snowflake&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;setStartTimeStamp&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;strtotime&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;2019-09-09&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1000&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$snowflake&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;();&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;setStartTimeStamp(strtotime(&amp;#x27;2019-09-09&amp;#x27;)*1000);

$snowflake-&gt;id();&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&quot;高级&quot;&gt;高级&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;在 Laravel 中使用&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;因为 SDK 相对简单，我们并没有提供 Laravel 的扩展包，你可通过下面的方式快速集成到 Laravel 中。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// App\Providers\AppServiceProvider&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Godruoyi\Snowflake\Snowflake&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Godruoyi\Snowflake\LaravelSequenceResolver&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; AppServiceProvider&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#6CB6FF&quot;&gt; ServiceProvider&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    /**&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;     * Register any application services.&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;     *&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;     * &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;@return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; void&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;     */&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; register&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;        $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;app&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;singleton&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;snowflake&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; () {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;            return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Snowflake&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;())&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;                -&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;setStartTimeStamp&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;strtotime&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;2019-10-10&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1000&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;                -&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;setSequenceResolver&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; LaravelSequenceResolver&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;app&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;cache&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;store&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()));&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        });&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;app-&gt;singleton(&amp;#x27;snowflake&amp;#x27;, function () {
            return (new Snowflake())
                -&gt;setStartTimeStamp(strtotime(&amp;#x27;2019-10-10&amp;#x27;)*1000)
                -&gt;setSequenceResolver(new LaravelSequenceResolver($this-&gt;app-&gt;get(&amp;#x27;cache&amp;#x27;)-&gt;store()));
        });
    }
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;自定义序列号解决器&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;你可以通过实现 Godruoyi\Snowflake\SequenceResolver 接口来自定义序列号解决器。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; YourSequence&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; implements&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#6CB6FF&quot;&gt; SequenceResolver&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    /**&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;     *  {&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;@inheritdoc&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;     */&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; sequence&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $currentTime)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;          // Just test.&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; mt_rand&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// usage&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$snowflake&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;setSequenceResolver&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; YourSequence&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$snowflake&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;();&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;setSequenceResolver(new YourSequence);
$snowflake-&gt;id();&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;你也可以直接使用闭包：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$snowflake &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; \Godruoyi\Snowflake\Snowflake&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$snowflake&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;setSequenceResolver&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($currentTime) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    static&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $lastTime;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    static&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $sequence;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($lastTime &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $currentTime) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        ++&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$sequence;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    $lastTime &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $currentTime;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $sequence;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;})&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;id&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;();&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;setSequenceResolver(function ($currentTime) {
    static $lastTime;
    static $sequence;

    if ($lastTime == $currentTime) {
        ++$sequence;
    }

    $lastTime = $currentTime;

    return $sequence;
})-&gt;id();&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;hr&gt;
&lt;p&gt;如果您在使用过程中遇到任何问题，欢迎提交 「&lt;a href=&quot;https://github.com/godruoyi/php-snowflake&quot;&gt;PR&lt;/a&gt;」。&lt;/p&gt;</content:encoded></item><item><title>少平和晓霞的故事</title><link>https://godruoyi.com/posts/the-story-of-shao-ping-and-xiaoxia/</link><guid isPermaLink="true">https://godruoyi.com/posts/the-story-of-shao-ping-and-xiaoxia/</guid><description>生活的道路不断向前伸展。有时我会蓦地想到我们约定的那件事，尤其是在约期临近的几天里，我有一种强烈的忧悒和不安压在心头，仿佛我熬过的所有岁月，都是为了这次约会。</description><pubDate>Fri, 02 Aug 2019 13:11:18 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;原文发布于我的 qzone，仅以此纪念我逝去的晓霞。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我们中学时代最后一天的最后一堂课结束了。虽然前面仍有漫长而艰难的考试，但是中学的课程再也没有了，
往后将是讲座、讨论会、或参加就某一问题的学术研究，全是成年人的字眼。&lt;/p&gt;
&lt;p&gt;热尼娅·鲁米扬采娃朝教室里探了探头：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;谢廖沙，打扰你一会儿行吗？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我走到走廊里。在这不寻常的日子里，热尼娅也显得与往常不太一样了。她的衣着象往常一样有些古怪：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;那条去年就显小了的连衣裙，短得遮不住膝盖&lt;/li&gt;
&lt;li&gt;外面套着一件瘦小得连胸前都扣不拢的毛衣，里面衬着洗旧了的白绸衫&lt;/li&gt;
&lt;li&gt;脚穿一双圆头平底童皮鞋——这身打扮象是从她妹妹那儿拿来的&lt;/li&gt;
&lt;li&gt;她那头浓密的淡灰色的柔发虽用许多发卡和小梳子勉强别住，但还是散落了下来，遮住了她的前额和面颊&lt;/li&gt;
&lt;li&gt;而且有一缕额发时常垂落到她那短小的鼻子上，总惹得她气恼地把它撩开&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;与往常不同的是，她面颊上泛出一层淡淡的红晕，那双忽而严肃正经、忽而漫不经心的灰色大眼睛里，闪耀着生气勃勃、亲切动人的光芒。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;谢廖沙，我有句话想跟你说：咱们俩十年以后再见面吧？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;热尼娅从来不开玩笑，所以我也一本正经地问：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;为什么？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;因为我想知道，你将来能成为一个什么样的人。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;热尼娅把那缕垂在短鼻子上的头发甩开&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;知道吗，这几年来，我一直很喜欢你。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;热尼娅，你为什么早不对我说呢？我问。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;早说有什么用？当时你那么喜欢尼娜！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我顿时感到，一种莫名的沮丧和忧悒袭上心头，仿佛失去了什么一般。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;那我们何时何地再见面呢？ 我又问。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;十年以后的五月二十九日，晚上八点。在大剧院正中的两根圆柱之间。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;要是那儿的圆柱配不成双怎么办？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;那儿只有八根圆柱，谢廖扎……到那时，我就是一个著名的天文学家。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;她又郑重、自豪、十分自信地补充了一句：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;如果我变化很大，你就凭报上登出的照片来认我吧。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;那时候，我也会成名。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;刚说到这儿，我倏地停住了——我根本没想过，将来我会在哪个领域成名，甚至连报考哪个系我还没决定呢。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;不管怎么说，我一定开着自己的小汽车去……。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这个回答实在可笑，然而我却找不出更合适的话来。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;好极了，热尼娅笑了，那你就开着带我去满城兜风……&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;光阴荏苒，转瞬已过多年。 一九四四年夏，我住在野战医院，从收音机里听到了授予空军少校热尼娅·鲁米扬采娃苏联英雄称号的命令。&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;生活的道路不断向前伸展。有时我会蓦地想到我们约定的那件事，尤其是在约期临近的几天里，我有一种强烈的忧悒和不安压在心头，仿佛我熬过的所有岁月，都是为了这次约会。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我没有成名，没能兑现向热尼娅许下的诺言，但是还有一点我没有失信：&lt;/p&gt;
&lt;p&gt;我在一堆缴获的汽车当中，以廉价买了一部旧奥佩尔。我换上一套簇新的衣服，开着奥佩尔向大剧院驶去。&lt;/p&gt;
&lt;p&gt;假如那次真的能见到热尼娅，我就会对她这么说，我经过无数次的彷徨，终于找到了自己的道路，&lt;/p&gt;
&lt;p&gt;我把汽车停在街心花园旁边，向卖花女人买了一束铃兰，朝大剧院正中央的两根圆柱走去。那儿果真有八根柱子。
我在那儿伫立片刻，把铃兰献给了一位脚穿运动鞋，身材纤瘦的灰眼睛姑娘，然后驱车回家去了。&lt;/p&gt;
&lt;p&gt;我真想让时光在霎那间停止流逝，让我回顾一下那逝去的年华和我自己，让我看一眼那身穿短连衣裙、外套绒衫的少女；&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;找回那颗蒙昧无知的少年的心，这颗心曾轻易地错过了决定命运的时刻，轻易的错过了我一生中本来可以获得的欢乐和幸福-----致晓霞&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded></item><item><title>Nginx 配置二级目录支持</title><link>https://godruoyi.com/posts/nginx-configures-secondary-directory-support/</link><guid isPermaLink="true">https://godruoyi.com/posts/nginx-configures-secondary-directory-support/</guid><description>用最简单优雅的方式，配置你的二级目录支持</description><pubDate>Mon, 03 Jun 2019 03:25:20 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Nginx 配置二级目录并不像 apache 那么简单，但二级目录的配置在实际工作中又经常用到，现整理如下。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;在 &lt;code&gt;/etc/nginx&lt;/code&gt; 目录下创建文件夹  sublocations，用于存放所有二级目录的配置：&lt;/li&gt;
&lt;/ol&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;sudo&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; mkdir&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; -p&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; /etc/nginx/sublocations&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;sudo mkdir -p /etc/nginx/sublocations&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;在该目录下配置二级目录配置文件（如你想配置的二级目录为 example.com/bbs）：&lt;/li&gt;
&lt;/ol&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;nginx&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;nginx&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;# /etc/nginx/sublocations/bbs.conf&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;location&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; ^~&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; /bbs &lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    alias &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;/your/bbs/project/path&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    index &lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;index.php;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    try_files &lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$uri $uri/ @bbs;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    # 引入解析配置&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    include &lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;snippets/php_parse_for_sublocation.conf;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;location&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; @bbs &lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    rewrite&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; /bbs/(.*)$ /bbs/index.php?/$1 &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;last&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;# /etc/nginx/sublocations/bbs.conf

location ^~ /bbs {
    alias &amp;#x22;/your/bbs/project/path&amp;#x22;;
    index index.php;

    try_files $uri $uri/ @bbs;

    # 引入解析配置
    include snippets/php_parse_for_sublocation.conf;
}

location @bbs {
    rewrite /bbs/(.*)$ /bbs/index.php?/$1 last;
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;配置针对二级目录的解析文件，不同的语言可配置不同的 CGI 解析，下面是 PHP 的解析配置：&lt;/li&gt;
&lt;/ol&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;nginx&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;nginx&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;# /etc/nginx/snippets/php_parse_for_sublocation.conf&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;# pass PHP scripts to FastCGI server&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;location&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; ~&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; \.php$ &lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    fastcgi_pass &lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;unix:/var/run/php/php7.2-fpm.sock;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    fastcgi_index &lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;index.php;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    include &lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;fastcgi_params;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    # 注意这里的 SCRIPT_FILENAME&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    fastcgi_param &lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;SCRIPT_FILENAME $request_filename;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    fastcgi_intercept_errors &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;off&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    fastcgi_buffer_size &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;16k&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    fastcgi_buffers &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;4&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 16k&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    fastcgi_connect_timeout &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;300&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    fastcgi_send_timeout &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;300&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    fastcgi_read_timeout &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;300&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;# /etc/nginx/snippets/php_parse_for_sublocation.conf

# pass PHP scripts to FastCGI server
location ~ \.php$ {
    fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
    fastcgi_index index.php;
    include fastcgi_params;

    # 注意这里的 SCRIPT_FILENAME
    fastcgi_param SCRIPT_FILENAME $request_filename;

    fastcgi_intercept_errors off;
    fastcgi_buffer_size 16k;
    fastcgi_buffers 4 16k;
    fastcgi_connect_timeout 300;
    fastcgi_send_timeout 300;
    fastcgi_read_timeout 300;
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;最后在域名配置中引入二级目录配置即可&lt;/li&gt;
&lt;/ol&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;nginx&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;nginx&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;server&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    # ....&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    server_name &lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;example.com;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;	&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    location&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; / &lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        try_files &lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$uri $uri/ &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;404&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    # 引入二级目录配置&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    include &lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;sublocations/tianyou.conf;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    # ....&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;server {
    # ....

    server_name example.com;
	
    location / {
        try_files $uri $uri/ 404;
    }

    # 引入二级目录配置
    include sublocations/tianyou.conf;

    # ....
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;测试配置成功并重启服务后，就能通过二级目录 example.com/bbs 访问到对应的服务了。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;sudo&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; nginx&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; -t&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;         # 测试配置是否成功&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;sudo&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; nginx&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; -s&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; reload&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;  # 重启服务&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;sudo nginx -t         # 测试配置是否成功
sudo nginx -s reload  # 重启服务&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;更多 Nginx 的配置请参考：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/best-nginx-configuration-for-improved-security&quot;&gt;提高安全性的最佳 Nginx 配置&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/let-s-encrypt-generic-domain-name-certificate-application-and-configuration&quot;&gt;Let’s Encrypt 泛域名证书申请及配置&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/nginx-configuration-across-support&quot;&gt;Nginx 配置跨越支持&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Nginx 配置跨越支持</title><link>https://godruoyi.com/posts/nginx-configuration-across-support/</link><guid isPermaLink="true">https://godruoyi.com/posts/nginx-configuration-across-support/</guid><description>用你最美的姿态，去「跨域」那座山</description><pubDate>Mon, 27 May 2019 07:24:26 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;用你最美的姿态，去「跨域」那座山。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在日常的开放中，我们经常遇到跨域的问题，常用的处理方式都是在代码层添加 cors 支持，但若你有 Nginx 配置权限，在 Nginx 上处理跨域将使得程序异常简单和高效。&lt;/p&gt;
&lt;h2 id=&quot;代理&quot;&gt;代理&lt;/h2&gt;
&lt;p&gt;假设我们的前端域名为 &lt;code&gt;example.com&lt;/code&gt;，API 服务架设在 &lt;code&gt;api.example.com&lt;/code&gt; 域名下，那我们可以通过代理的形式来配置跨越请求，具体的配置为：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在  Nginx 的 example.com 虚拟主机文件中配置如下的代理&lt;/li&gt;
&lt;li&gt;配置成功重启后，前端即可用 example.com/api 的方式和 API 交互&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;# /etc/nginx/sites-enabled/example.com.conf

location /api/ {
    proxy_pass http://api.example.com/;    
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这种方式的原理是将 API 提供的服务，代理到前端域名的二级目录下，从而避免跨域。&lt;/p&gt;
&lt;h2 id=&quot;response-header&quot;&gt;Response Header&lt;/h2&gt;
&lt;p&gt;当然由于很多情况下我们不想将服务代理到前端域名二级目下，那可以通过在 Http Response 中添加 Header 来解决跨越，具体配置如下：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;nginx&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;nginx&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;# /etc/nginx/snippets/cors.conf;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($request_method &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;= &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;OPTIONS&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    add_header &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;Access-Control-Allow-Origin&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;*&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; always;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    add_header &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;Access-Control-Allow-Methods&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;GET, POST, OPTIONS, PUT, DELETE&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; always;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    add_header &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;Access-Control-Allow-Headers&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,Content-Disposition&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; always;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    add_header &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;Access-Control-Max-Age&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 1728000&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; always;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    add_header &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;Content-Length&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    add_header &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;Content-Type&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;text/plain; charset=utf-8&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 204&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($request_method &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;~* &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;(GET|POST|DELETE|PUT)&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    add_header &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;Access-Control-Allow-Origin&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;*&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; always;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;# /etc/nginx/snippets/cors.conf;

if ($request_method = &amp;#x27;OPTIONS&amp;#x27;) {
    add_header &amp;#x27;Access-Control-Allow-Origin&amp;#x27; &amp;#x27;*&amp;#x27; always;
    add_header &amp;#x27;Access-Control-Allow-Methods&amp;#x27; &amp;#x27;GET, POST, OPTIONS, PUT, DELETE&amp;#x27; always;
    add_header &amp;#x27;Access-Control-Allow-Headers&amp;#x27; &amp;#x27;DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,Content-Disposition&amp;#x27; always;
    add_header &amp;#x27;Access-Control-Max-Age&amp;#x27; 1728000 always;

    add_header &amp;#x27;Content-Length&amp;#x27; 0;
    add_header &amp;#x27;Content-Type&amp;#x27; &amp;#x27;text/plain; charset=utf-8&amp;#x27;;

    return 204;
}

if ($request_method ~* &amp;#x22;(GET|POST|DELETE|PUT)&amp;#x22;) {
    add_header &amp;#x27;Access-Control-Allow-Origin&amp;#x27; &amp;#x27;*&amp;#x27; always;
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;blockquote&gt;
&lt;p&gt;关于何时会发起 OPTIONS 请求及 OPTIONS 请求的内容，可参考阮老师的这篇文章—— &lt;a href=&quot;http://www.ruanyifeng.com/blog/2016/04/cors.html&quot;&gt;跨域资源共享 CORS 详解&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;然后在 API 服务域名下添加 CORS 支持即可&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;nginx&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;nginx&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;# /etc/nginx/sites-enabled/api.example.com.conf&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;location&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; / &lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    try_files &lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$uri $uri/ /index.php?$query_string;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;location&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; ~&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; \.php$ &lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    // 引入 &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;cors&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; 配置&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    include &lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;snippets/cors.conf;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    fastcgi_split_path_info &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;^(.+\.php)(/.+)$&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    fastcgi_pass &lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;unix:/var/run/php/php7.2-fpm.sock;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    ...&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    ...&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;# /etc/nginx/sites-enabled/api.example.com.conf

location / {
    try_files $uri $uri/ /index.php?$query_string;
}

location ~ \.php$ {
    // 引入 cors 配置
    include snippets/cors.conf;

    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
    ...
    ...
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;blockquote&gt;
&lt;p&gt;注意 &lt;code&gt;include snippets/cors.conf&lt;/code&gt; 这段代码的位置，若直接放在 location 中，是不起作用的，如下所示：&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;nginx&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;nginx&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;location&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; / &lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    include &lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;snippets/cors.conf;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    try_files &lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$uri $uri/ /index.php?$query_string;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;location / {
    include snippets/cors.conf;

    try_files $uri $uri/ /index.php?$query_string;
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;这是因为下面的 &lt;code&gt;try_files&lt;/code&gt; 将请求 Forward 到了 &lt;code&gt;location ~ \.php$&lt;/code&gt;  这个 block 下，在此之前添加的 &lt;code&gt;add_header&lt;/code&gt; 命令是无效的。&lt;/p&gt;
&lt;p&gt;enjoy ～_～&lt;/p&gt;</content:encoded></item><item><title>峨眉⼭的⽇出</title><link>https://godruoyi.com/posts/emei-out/</link><guid isPermaLink="true">https://godruoyi.com/posts/emei-out/</guid><description>上个周末，和⽼婆及阿婷们⼀起去峨眉⼭看⽇出。虽然结局很悲惨，过程也悲惨，却并⾮那么不如⼈意，写下来权当是安慰我们的这次旅⾏吧。</description><pubDate>Wed, 22 May 2019 12:03:18 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;上个周末，和⽼婆及阿婷们⼀起去峨眉⼭看⽇出。虽然结局很悲惨，过程也悲惨，却并⾮那么不如⼈意，写下来权当是安慰我们的这次旅⾏吧。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;出发&quot;&gt;出发&lt;/h2&gt;
&lt;p&gt;周末两天峨眉⼭的天⽓其实不那么好，持续着今年的阴⾬⻤鬼天⽓。但不知道什么原因驱使着我们，还义⽆反顾的选择沐浴。我猜不可能是爱，可能是积压了好久的⾏程需要释放吧。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/201905/22/_1558524742_ZVQ4p39w6K.jpeg&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;p&gt;从重庆出发，下午 13:30 左右我们就到了峨眉⼭⼭脚。四个⼈浑浑噩噩的吃完⼀顿饭，就坐⻋到 &lt;code&gt;五显岗&lt;/code&gt; 准备爬⼭了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/201905/22/_1558524766_PnNSXHNmta.jpeg&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;燃烧的卡路&quot;&gt;燃烧的卡路⾥&lt;/h2&gt;
&lt;p&gt;从 &lt;code&gt;五显岗&lt;/code&gt; ⾛了⼤约 2KM，才到我们今天爬⼭的起点 &lt;code&gt;清⾳阁&lt;/code&gt;。在门口的空地上，坐着很多「滑工」。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/201905/22/_1558527894_CIOHVduWfg.jpeg&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;p&gt;以前我觉得，爬⼭这种事⼉，怎么能去坐滑杆呢。⼀个是毫⽆意义，二是⾟苦底下抬滑竿的⼈。可现在⼀想啊，你付出了你的劳动，收获了相应甚⾄更多的报酬，那我还有什么值得怜悯的呢，就像外卖一样，我付钱点的外卖，&lt;del&gt;你凭什么让我说谢谢呢&lt;/del&gt;，每个辛勤劳动的人，都值得你为他说些谢谢，连波啊连波，你当时在想什么呢，说出这么混账的话。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;人啊，真是越活越没⼼没肺了&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;到 &lt;code&gt;清⾳阁&lt;/code&gt; 后，已经快下午 16:00 了，从这里开始，有两条⼭路可以进⼭；门口的老阿姨告诉我们，无论你们今天选什么线路，都是不可能徒步走到终点 「雷洞庭」的。因为即使走短线，也要近 8 个小时，按 20:00 点天黑算的话，我们要走到凌晨 00:00 才能到达目的地，需要走近 4 个小时的夜路。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我其实是一直想挑战「长线」的，但「长线」的路程大约有 40km，肯定是被老婆无情的拒绝了，她说你就是不知天高地厚。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;从清音阁开始到我们的终点「雷洞庭」大约有 20km 的距离。按照「正常」速度的话大约 4 个小时就能走到终点。但是我要是告诉你都是这种路的话，你还会选择往上爬吗？&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/201905/22/_1558527903_VB4nZqbF7c.jpeg&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;p&gt;40 分钟后，我们到达了第一个地点——「万年寺」，这时候天还没下雨，人还很燥热，体力还很充沛。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/201905/22/_1558524813_vaIxAwYHsE.jpeg&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;p&gt;下午 17:30 左右，我们爬上了「观心坡」，雨这时已经越下越大，两位女性同志的小腿开始颤抖，保温杯里的水已近底；店家的脉动要买 10 块钱一瓶，好在有夫妻住店，才以他们的名义打得一瓶开水。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;18:30&lt;/code&gt; 左右，我们在沿途的店家休息了下来，这个点儿，大部分店家都准备下山了；点了两碗蛋炒饭，店主的蛋炒饭是用韭菜混合炒的，这是吃过最香的炒饭了。再来了一瓶红牛，一只手电，骈了两根登山棍后，我们又继续上路了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/201905/23/_1558592173_eWbCIhpnJx.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;19:30&lt;/code&gt;，我们来到了「长老坪」，这里的视线比较好，但是店家已经关门下山了，我们连选择高价脉动的权利都丧失了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/201905/22/_1558527947_bnlqR2zG0R.jpeg&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;20:12&lt;/code&gt;，天已经黑下来了，你依旧能看到持续上升的阶梯，和我鬼畜一般的脸。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/201905/22/_1558527962_maRTAr7BlF.jpeg&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;21:00&lt;/code&gt; 左右，爬山还在进行，这时候必须得打手电了，背包里的棒棒糖早已吃完，吃了一块豆干后，每个人都像打了鸡血样，持续不断的前进着。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/201905/22/_1558527953_F8AjDehi5I.jpeg&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;p&gt;凌晨 &lt;code&gt;00:00&lt;/code&gt;，我们终于到了「洗象池」，虽说叫洗象池，但其实是一个寺庙，门口的路灯发着微弱的光，店里还有两位值夜班的阿姨。在寺里吃完最后一块豆干后，雨却越下越大了，等了近半个小时，却没有丝毫减弱的痕迹。向阿姨买了泡面，四个人狼吞虎咽的吃了起来，多希望有个火腿，然而寺庙好像只吃素。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/201905/22/_1558528206_wbEsHqy4my.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;p&gt;从这里到我们的终点雷洞庭还有 2 个多小时的路程，由于雨还在下，大家都动摇了起来。于是我们躺了下来。&lt;/p&gt;
&lt;p&gt;凌晨 &lt;code&gt;03:00&lt;/code&gt;，闹钟响了，按照昨晚的约定，要是没下雨的话，我们将继续起来赶路。但屋外还依旧下着大雨，墨迹了大半天，快四点的时候，四人终于满脸疲惫的上路了；这是天还很黑，雨还是很大。&lt;/p&gt;
&lt;p&gt;凌晨 &lt;code&gt;06:00&lt;/code&gt; ，天渐渐亮了，雨也慢慢停了下来。阿宝披着破洞百出的雨衣，在风中凌乱着；到了凌晨 &lt;code&gt;06:30&lt;/code&gt; 的时候，我们终于到了雷洞庭，吃了碗三两的小面，住进了昨晚未来得及入住的酒店。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/201905/22/_1558528121_h7EogmzXla.jpeg&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;金顶&quot;&gt;金顶&lt;/h2&gt;
&lt;p&gt;上午 &lt;code&gt;09:00&lt;/code&gt;，我们终于到了金顶，那天的金顶，风景格外美丽，连周围不安稳的风，都在四处炫耀着他的美。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/201905/22/_1558528125_LN0WQoeMqm.jpeg&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;p&gt;可能我们四个这辈子都不会想再经历这样的经验了，从第一天下午 &lt;code&gt;16:00&lt;/code&gt; 算起，到爬上金顶，除去中途躺下的三个小时，我们走了 10 多个小时。&lt;/p&gt;
&lt;p&gt;下山的时候我对阿宝说：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;阿宝，下次我们还要来，到时候直接坐车到雷洞庭，我再也不想爬这个山了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;遗憾肯定是有的，没有看到日出，没有看到云海，连猴子都没来抢阿宝红色的背包。但可能旅行就是这样吧。&lt;/p&gt;</content:encoded></item><item><title>唐玄奘的由来</title><link>https://godruoyi.com/posts/the-origin-of-xuanzang-in-the-tang-dynasty/</link><guid isPermaLink="true">https://godruoyi.com/posts/the-origin-of-xuanzang-in-the-tang-dynasty/</guid><description>这篇短文是前年（2017）年去婺源路上看 《西游记》时写的，现重新整理并发布到我的 Blog。  时阿阿平登位，天下太平，人才短缺。故大力招聘 `PHP` 。  重庆有一人，姓陈名萼，字光蕊，见到该招聘信息</description><pubDate>Thu, 28 Mar 2019 05:23:11 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;这篇短文是前年（2017）年去婺源路上看 《西游记》时写的，现重新整理并发布到我的博客。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;时阿阿瓶登位，天下太平，人才短缺。故大力招聘 &lt;code&gt;PHP&lt;/code&gt; 。&lt;/p&gt;
&lt;p&gt;重庆有一人，姓陈名萼，字光蕊，见到该招聘信息，告其母，前去长安面试，一面就过，七险一金，年底分红。&lt;/p&gt;
&lt;p&gt;皇帝御笔亲赐状元。游街三日，未曾想游到丞相门。 相有一女，小名温娇， 又名满堂娇，未婚。 正在抛球择夫， 见光蕊形似徐公（徐公，重庆之美丽者也），问其下人，实乃公务员。一球进洞，当日完婚。&lt;/p&gt;
&lt;p&gt;次日皇帝封光蕊为温州洲主， 随妻回家接母往温州上任。走了几天，行至万花店刘小二家落脚。母亲久病缠身，光蕊听闻外面买鱼。想着给母亲做一条炝锅鱼。 这鱼却闪闪发光，绝非等闲，赶紧放回洪江。&lt;/p&gt;
&lt;p&gt;母亲久病缠身不能前行，八百块租了间一室一厅。拜辞而去。明天上线，熬夜前行，到洪江渡口， 有船夫刘洪，李彪二人。看到夫人长得好看，行船至没人烟处，杀其夫，坠入江。温娇望其夫已死，欲跳江中，刘洪一把抓住并挟持道。你若从我，万事皆休，你若不从，哼哼哈嘿。&lt;/p&gt;
&lt;p&gt;时温娇已怀有身孕，无计可施，只得权时应承，顺了刘洪。 刘洪拿着 &lt;code&gt;offer&lt;/code&gt; 就前往温州任职。&lt;/p&gt;
&lt;p&gt;光蕊尸体坠入江低，被一只螃蟹发现，告龙王。龙王一看，救命恩人矣；原来龙王就是那条鱼。寻其灵魂，喝点汤水，活了，光蕊诉其缘由。龙王告知勿忧，将其放入冰箱里，待其夫妇团聚再出。&lt;/p&gt;
&lt;p&gt;话说温娇跟着刘洪在温州入职就定。而肚子也越来越大，一天，做一梦。梦说「此子必成大器，刘贼若回，必害其子」。温娇待孩子生下后，用栏栏装起，藏一血书，放入洪江，冲走了。&lt;/p&gt;
&lt;p&gt;孩子冲到金山寺，被一和尚捡到，唤乳名江流。&lt;/p&gt;
&lt;p&gt;十八年过后……&lt;/p&gt;
&lt;p&gt;孩子已经长大，和尚又为他消发修行，取发名玄奘！！&lt;/p&gt;</content:encoded></item><item><title>About Godruoyi</title><link>https://godruoyi.com/posts/about-godruoyi/</link><guid isPermaLink="true">https://godruoyi.com/posts/about-godruoyi/</guid><description>Godruoyi，15 年毕业于西南大学计算机专业，Laravel 爱好者；目前靠 Go 养家糊口，抽空学习 Rust，全职远程中</description><pubDate>Tue, 26 Mar 2019 18:00:48 GMT</pubDate><content:encoded>&lt;p&gt;Godruoyi，15 年毕业于西南大学计算机专业，Laravel 爱好者；目前靠 Go 养家糊口，抽空学习 Rust，全职远程中。由于看起来像二愣，说起话来也像二愣，朋友们都叫我二愣。&lt;/p&gt;
&lt;p&gt;喜欢捣鼓各种工具，但效率也不高；喜欢买书，一定是纸质书籍，爱看小文学，伪文艺青年，你也可以查看 &lt;a href=&quot;https://godruoyi.com/posts/my-books&quot;&gt;我的阅读清单&lt;/a&gt;；性格有点内向，喜欢写写写写，虽然文笔也不好，&lt;del&gt;有时候&lt;/del&gt;很多时候都爱 &lt;a href=&quot;https://godruoyi.com/categories/life&quot;&gt;发发闹骚&lt;/a&gt;，我的 GitHub &lt;a href=&quot;https://github.com/godruoyi&quot;&gt;https://github.com/godruoyi&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;如果你想了解我更多的信息，这里有一些我觉得写得还不错的文章，&lt;a href=&quot;http://godruoyi.com/posts#selected-articel&quot;&gt;http://godruoyi.com/posts#selected-articel&lt;/a&gt; 。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注：我使用 Bear 写每一篇博客，所有的博客内容均没有 AI 参与。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;折腾--其他&quot;&gt;折腾 &amp;#x26; 其他&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/godruoyi/ocr&quot;&gt;🧚🏾 The Best Image OCR SDK For BAT&lt;/a&gt;  集成腾讯、阿里、百度的文字识别 SDK&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/godruoyi/easy-container&quot;&gt;📦 一个简单的依赖注入容器（from Laravel Container）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/godruoyi/laravel-tips&quot;&gt;🍡 Laravel tips in terminal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/godruoyi/laravel-tips-raycast&quot;&gt;🥑 Laravel Tips In Raycast&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/php-id-generator-based-on-snowflake-algorithm&quot;&gt;❄️ 雪花算法的 PHP 实现&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/golang-snowflake&quot;&gt;🌨️ 雪花算法的 Golang 实现&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/godruoyi/wakeup&quot;&gt;🌰 Wakeup&lt;/a&gt; Use AI to create admirable images to surprise your every morning&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://godruoyi.com/posts/the-resetful-api-design-specification&quot;&gt;📰 RESTful API 设计规范&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/godruoyi/readog&quot;&gt;🐶 Readog is a browser extension that can save your links to any platform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/godruoyi/gblog&quot;&gt;🎈 An beautiful blog built with Astro&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/godruoyi/imageflow&quot;&gt;🐝 An Raycast extension that allows you to process images using a customizable workflow&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;关于这个博客&quot;&gt;关于这个博客&lt;/h2&gt;
&lt;p&gt;六年前我将我的博客迁移到 Laravel &amp;#x26; Vue 平台，更早的博客文章在迁移过程中丢失，你可以从这里看到&lt;a href=&quot;https://godruoyi.com/posts/the-about-gblog/&quot;&gt;这里&lt;/a&gt;当时的记录。 最近我又将博客迁移到 Astro 平台(见&lt;a href=&quot;https://godruoyi.com/posts/blog-3/&quot;&gt;Blog 的延续&lt;/a&gt;)，评论数据又丢了，好在是博客内容现在已经全部托管到 GitHub，这次该是丢不了了。&lt;/p&gt;
&lt;p&gt;如果你喜欢这个博客，你可以参考 &lt;a href=&quot;https://github.com/godruoyi/gblog&quot;&gt;这个仓库&lt;/a&gt; 一键部署一个你自己的。&lt;/p&gt;
&lt;h2 id=&quot;联系我&quot;&gt;联系我&lt;/h2&gt;
&lt;p&gt;最近经常在刷推，你也可以在下面的网站或联系方式中找到我。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Email: &lt;a href=&quot;mailto:godruoyi@gmail.com&quot;&gt;godruoyi@gmail.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;WechatID: Youji_&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://learnku.com/users/5359&quot;&gt;Laravel China&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>容器的工作原理和隔离机制</title><link>https://godruoyi.com/posts/how-the-container-works-and-the-isolation-mechanism/</link><guid isPermaLink="true">https://godruoyi.com/posts/how-the-container-works-and-the-isolation-mechanism/</guid><description>容器的本质是一个进程，进程与进程之间相互隔离造就了容器与容器互不影响的特性。在启动一个容器（即创建一个进程时），通过 Namespace 技术实现容器的隔离、通过  Cgroups 来实现容器的资源控制。</description><pubDate>Sun, 21 Oct 2018 11:50:15 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;由于自己最近在学习 @张磊 老师的 &lt;a href=&quot;https://time.geekbang.org/column/intro/116&quot;&gt;Kubernetes&lt;/a&gt; 专栏，以下内容均来自每节专栏学习后的感悟、总结。张老师的&lt;a href=&quot;https://time.geekbang.org/column/intro/116&quot;&gt;Kubernetes&lt;/a&gt;专栏通俗易懂，欢迎大家跟我一起加入学习。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;容器的本质&quot;&gt;容器的本质&lt;/h2&gt;
&lt;p&gt;容器的本质是一个&lt;strong&gt;进程&lt;/strong&gt;，进程与进程之间相互隔离造就了容器与容器互不影响的特性。在启动一个容器（即创建一个进程时），通过 &lt;code&gt;Namespace&lt;/code&gt; 技术实现容器的隔离、通过&lt;code&gt;Cgroups&lt;/code&gt; 来实现容器的资源控制。&lt;/p&gt;
&lt;h2 id=&quot;namespace&quot;&gt;Namespace&lt;/h2&gt;
&lt;p&gt;容器进程的创建通过 &lt;code&gt;Linux&lt;/code&gt; 平台下的 &lt;code&gt;clone&lt;/code&gt; 方法创建，在调用该方法创建进程时，通过指定额外的 &lt;code&gt;Namespace&lt;/code&gt; 参数，使得刚创建的进程属于一个独立的空间。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; pid&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; clone&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;main_function,&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; stack_size,&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; CLONE_NEWPID&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; SIGCHLD,&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; NULL&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;int pid = clone(main_function, stack_size, CLONE_NEWPID | SIGCHLD, NULL)&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;指定额外参数 &lt;code&gt;CLONE_NEWPID&lt;/code&gt; 创建的新进程，有一个自己的 &lt;em&gt;独立进程空间&lt;/em&gt;，在这个空间里，它的进程 ID 为 1。它既看不到其在宿主机的真正进程、也看不到其他容器的进程。&lt;/p&gt;
&lt;p&gt;其实这都是假象，创建的该进程在宿主机上是真实存在的，并且也受宿主机的管理和控制，也享受宿主机的资源。但在该进程内部，它处于一个独立的空间，只看得到该进程一个资源，让其误以为自己处于一个密闭的 &lt;code&gt;盒子&lt;/code&gt; 内。这&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;这，就是 Linux 容器最基本的实现原理了！&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;你可能也注意到了，虽然通过 &lt;code&gt;Linux Namespace&lt;/code&gt; 技术实现了进程的相互隔离，但这种隔离机制只是为不同的容器进程指定不同的 &lt;code&gt;namespace&lt;/code&gt;，但不同的容器进程其实在宿主机上是真实存在的，并且也使用相同的宿主机内核。这也是容器在和虚拟化技术相比下，&lt;strong&gt;隔离得不够彻底&lt;/strong&gt;的原因。&lt;/p&gt;
&lt;h2 id=&quot;cgroups&quot;&gt;Cgroups&lt;/h2&gt;
&lt;p&gt;容器进程创建好后，若不进行其他处理，该进程运行时所消耗及占用的资源（如 CPU、内存）等；是可以被其他宿主机进程或其他容器进程享用的。为了解决这个问题，&lt;code&gt;Linux&lt;/code&gt; 容器设计中引入了 &lt;code&gt;Cgroups&lt;/code&gt; 的概念。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Linux Cgroups 的全称是 Linux Control Group，它的主要作用就是限制一个进程能够使用的资源上限（如 cpu、内存、网络等）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;在 &lt;code&gt;Linux&lt;/code&gt; 中，&lt;code&gt;Cgroups&lt;/code&gt; 给用户暴露出来的操作接口是文件系统，即它以文件和目录的方式组织在操作系统的 &lt;code&gt;/sys/fs/cgroup&lt;/code&gt; 路径下。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/201810/21/1_1540120389_qR8uXvO1YF.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;若你的系统中没有挂载该目录，那你需要自行 google 挂载。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在该目录下，你可以看到很多诸如 &lt;code&gt;cpuset&lt;/code&gt;、&lt;code&gt;cpu&lt;/code&gt;、 &lt;code&gt;memory&lt;/code&gt; 这样的子目录，也叫子系统。每个目录下面又有很多配置文件。如你可能在 &lt;code&gt;cpu&lt;/code&gt; 目录下看到诸如 &lt;code&gt;cfs_period&lt;/code&gt; 和 &lt;code&gt;cfs_quota&lt;/code&gt; 这样的配置文件。而这两个配置文件，是限制 CPU 使用率的关键配置项。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/201810/21/1_1540120417_q22pQKH7pt.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这两个参数需要组合使用，可以用来限制进程在长度为 &lt;code&gt;cfs_period&lt;/code&gt; 的一段时间内，只能被分配到总量为 &lt;code&gt;cfs_quota&lt;/code&gt; 的 &lt;code&gt;CPU&lt;/code&gt; 时间。其中 &lt;code&gt;period&lt;/code&gt; 的默认值为 &lt;code&gt;100 ms&lt;/code&gt;（毫秒），&lt;code&gt;quota&lt;/code&gt; 的默认值为 &lt;code&gt;-1&lt;/code&gt;，即不做限制。当修改 &lt;code&gt;quota&lt;/code&gt; 的值为 &lt;code&gt;20 ms&lt;/code&gt; 时，表示在 &lt;code&gt;100ms&lt;/code&gt; 的时间范围内，&lt;code&gt;cpu&lt;/code&gt; 只能使用 &lt;code&gt;20 ms&lt;/code&gt;。也就是说，&lt;code&gt;cpu&lt;/code&gt; 的使用率最大为 &lt;code&gt;20%&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;上面说到，容器启动后的进程我们需要用 &lt;code&gt;Linux Cgroups&lt;/code&gt; 来限制其资源的访问。接下来我们看看到底是如何进行控制的。&lt;/p&gt;
&lt;p&gt;我们在 &lt;code&gt;/sys/fs/cgroup/cpu&lt;/code&gt; 目录下创建一个文件夹:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/201810/21/1_1540120593_sYLAndCzei.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;p&gt;可以看到，我们创建的文件夹里面已经被系统默认创建了一些配置文件。我们把这样一个目录叫做控制组，接下来修改这个控制组的 &lt;code&gt;cpu&lt;/code&gt; 使用率为 &lt;code&gt;20%&lt;/code&gt; 。&lt;/p&gt;
&lt;p&gt;我们在命令行用一个死循环来测试，也使得该进程能占用全部的 CPU。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/201810/21/1_1540120788_KIPhhC9O7v.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;p&gt;通过 top 参数，我们看到该进程 5451 的 cpu 使用率已经达到 99.3%；由于我们创建该进程时，没有为其指定一个控制组时，他默认将享用全部的 &lt;code&gt;cpu&lt;/code&gt; 及内存配置。接下老我们将该进程通过下面的命令加入到刚刚创建的 &lt;code&gt;container&lt;/code&gt; 组中去。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ echo 5451 &gt; /sys/fs/cgroup/cpu/container/tasks
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;将改进程加入控制组后，改进程的 cpu 使用率一下就从原来的 100% 降到了 20% 左右。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/201810/21/1_1540121479_Ri8UA0z3v8.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在 docker 中，我们可以在启动容器的时候，指定 cpu 参数来控制该容器的资源占用，如下所示，这样启动的 docker 容器，只能使用 20% 的 cpu。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;Docker&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; -it&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; --cpu-period=100000&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; --cpu-quota=20000&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; ubuntu&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; /bin/bash&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;Docker run -it --cpu-period=100000 --cpu-quota=20000 ubuntu /bin/bash&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;当然，通过 &lt;code&gt;Linux Cgroups&lt;/code&gt; 还可以限制其他参数，如网络、挂载点、内存等等。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;今天就学到这儿了，明天继续吧！！！&lt;/p&gt;</content:encoded></item><item><title>推荐一款高颜值的 ssh client 工具</title><link>https://godruoyi.com/posts/recommend-a-high-level-ssh-client-tool/</link><guid isPermaLink="true">https://godruoyi.com/posts/recommend-a-high-level-ssh-client-tool/</guid><description>当我们需要管理的服务器愈来愈多时，用传统的 ssh user@hostname -p port 来连接服务器肯定是不能满足需求的；通常我们会使用下面的 ssh client 来管理服务器账号信息</description><pubDate>Tue, 28 Aug 2018 02:22:48 GMT</pubDate><content:encoded>&lt;p&gt;当我们需要管理的服务器愈来愈多时，用传统的 &lt;code&gt;ssh user@hostname -p port&lt;/code&gt; 来连接服务器肯定是不能满足需求的；通常我们会使用下面的 SSH Client 来管理服务器账号信息：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Xshell&lt;/li&gt;
&lt;li&gt;PuTTY&lt;/li&gt;
&lt;li&gt;SecureCRT&lt;/li&gt;
&lt;li&gt;ZOC Terminal&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;但是不得不说这几款终端，在不经过一番折腾的情况下，都&lt;strong&gt;丑&lt;/strong&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;所以这里我推荐使用 &lt;strong&gt;&lt;a href=&quot;https://www.termius.com/&quot;&gt;Termius&lt;/a&gt;&lt;/strong&gt; 这款终端。跨所有平台支持、基于 &lt;a href=&quot;https://electronjs.org/&quot;&gt;electronjs&lt;/a&gt;（Atom、VS Code 也是采用的该技术），支持数据同步、支持 &lt;code&gt;SFTP&lt;/code&gt;、支持 &lt;code&gt;private key&lt;/code&gt; 登录；默认还支持 &lt;code&gt;Operator&lt;/code&gt; 字体；是目前遇到颜值最高的 &lt;code&gt;ssh client&lt;/code&gt; 了！&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/201911/05/_1572945788_FLSKePt0Gx.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;p&gt;当然，这么好的产品肯定是要收费的，阉割的免费版除不能进行数据同步这点比较难受外，其于的都能还能接受；付费版平均 &lt;code&gt;￥203人民币/年&lt;/code&gt; 也是值得购买的。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/201808/27/1_1535365085_04N0jAvtwV.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;p&gt;你可以在首选项里配置多个 &lt;code&gt;private key&lt;/code&gt; 管理、主题字体选择、快捷键等等。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/201911/05/_1572945902_RatO2rEh92.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;p&gt;支持快捷搜索，默认登录后的背景色为白色，也是可以设置的（遗憾的是不支持自定义）。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/201808/27/1_1535365368_iJTTEAJZmg.gif&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;blockquote&gt;
&lt;p&gt;你还可以在手机端下载使用该应用噢。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;enjoy &lt;del&gt;_&lt;/del&gt;&lt;/p&gt;</content:encoded></item><item><title>为你的 Laravel Faker 指定中文支持</title><link>https://godruoyi.com/posts/specify-chinese-support-for-your-faker-fake-data-generator/</link><guid isPermaLink="true">https://godruoyi.com/posts/specify-chinese-support-for-your-faker-fake-data-generator/</guid><description>在 Laravel 中使用 Faker 构造假数据时，默认的输出为英文格式，你可通过简单的配置，让其支持中文输出</description><pubDate>Fri, 27 Jul 2018 09:13:30 GMT</pubDate><content:encoded>&lt;h2 id=&quot;laravel-faker&quot;&gt;Laravel Faker&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;在 &lt;code&gt;Laravel&lt;/code&gt; 中使用 &lt;code&gt;Faker&lt;/code&gt; 构造假数据时，默认的输出为英文格式，如下&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/201908/31/_1567223760_pdSKfVH2DK.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;p&gt;可通过在 &lt;code&gt;config/app.php&lt;/code&gt; 增加如下配置使其支持中文。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;faker_locale&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;zh_CN&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt; &amp;#x27;zh_CN&amp;#x27;,&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;修改后再次执行效果如下：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/201908/31/_1567223770_zCV5eHOy9L.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;今天在使用 &lt;code&gt;Faker&lt;/code&gt; 构造数据时，纳闷为什么不能是中文，查看 &lt;a href=&quot;https://github.com/fzaninotto/Faker&quot;&gt;官方扩展包&lt;/a&gt; 原来早就支持了啊。（是我孤陋寡闻了呀，赶紧分享给没发现的同学）&lt;/p&gt;
&lt;p&gt;目前 &lt;code&gt;Laravel &gt;= 5.7&lt;/code&gt; 已经在 &lt;code&gt;config\app.php&lt;/code&gt; 中默认添加了 &lt;code&gt;faker_locale&lt;/code&gt; 配置。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;laravel-databaseserviceprovider&quot;&gt;Laravel DatabaseServiceProvider&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Laravel&lt;/code&gt; 源码中是在 &lt;code&gt;DatabaseServiceProvider&lt;/code&gt; 中注册的国际化支持。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// vendor/laravel/framework/src/Illuminate/Database/DatabaseServiceProvider.php&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;protected&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; registerEloquentFactory&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;    $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;app&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;singleton&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;FakerGenerator&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($app) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; FakerFactory&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;create&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($app[&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;config&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;]&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;app.faker_locale&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;en_US&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    });&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;    $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;app&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;singleton&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;EloquentFactory&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($app) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; EloquentFactory&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;construct&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;            $app&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;make&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;FakerGenerator&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;), &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;app&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;databasePath&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;factories&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        );&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    });&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;app-&gt;singleton(FakerGenerator::class, function ($app) {
        return FakerFactory::create($app[&amp;#x27;config&amp;#x27;]-&gt;get(&amp;#x27;app.faker_locale&amp;#x27;, &amp;#x27;en_US&amp;#x27;));
    });

    $this-&gt;app-&gt;singleton(EloquentFactory::class, function ($app) {
        return EloquentFactory::construct(
            $app-&gt;make(FakerGenerator::class), $this-&gt;app-&gt;databasePath(&amp;#x27;factories&amp;#x27;)
        );
    });
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;</content:encoded></item><item><title>RESTful API 设计规范</title><link>https://godruoyi.com/posts/the-resetful-api-design-specification/</link><guid isPermaLink="true">https://godruoyi.com/posts/the-resetful-api-design-specification/</guid><description>RESTful API 设计规范，文章大量参考了目前比较常见的 RESTful API 设计。  为了更好的讨论规范带来的争议及问题，现已把该文档整...</description><pubDate>Sun, 24 Jun 2018 13:41:48 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;本文是为 &lt;a href=&quot;http://cq.qq.com&quot;&gt;大渝网&lt;/a&gt; &lt;code&gt;API&lt;/code&gt; 开发规范拟定的一个 &lt;code&gt;beta&lt;/code&gt; 版，文章大量参考了目前比较常见的 &lt;code&gt;RESTful API&lt;/code&gt; 设计。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;为了更好的讨论规范带来的争议及问题，现已把该文档整理并开源到 &lt;a href=&quot;https://github.com/godruoyi/restful-api-specification&quot;&gt;GitHub&lt;/a&gt;，关于大家补充及提问。&lt;/p&gt;
&lt;h2 id=&quot;关于能愿动词的使用&quot;&gt;关于「能愿动词」的使用&lt;/h2&gt;
&lt;p&gt;为了避免歧义，文档大量使用了「能愿动词」，对应的解释如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;必须 (MUST)&lt;/code&gt;：绝对，严格遵循，请照做，无条件遵守；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;一定不可 (MUST NOT)&lt;/code&gt;：禁令，严令禁止；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;应该 (SHOULD)&lt;/code&gt; ：强烈建议这样做，但是不强求；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;不该 (SHOULD NOT)&lt;/code&gt;：强烈不建议这样做，但是不强求；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;可以 (MAY)&lt;/code&gt; 和 &lt;code&gt;可选 (OPTIONAL)&lt;/code&gt; ：选择性高一点，在这个文档内，此词语使用较少；&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;参见：&lt;a href=&quot;http://www.ietf.org/rfc/rfc2119.txt&quot;&gt;RFC 2119&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;protocol&quot;&gt;Protocol&lt;/h2&gt;
&lt;p&gt;客户端在通过 &lt;code&gt;API&lt;/code&gt; 与后端服务通信的过程中，&lt;code&gt;应该&lt;/code&gt; 使用 &lt;code&gt;HTTPS&lt;/code&gt; 协议。&lt;/p&gt;
&lt;h2 id=&quot;api-root-url&quot;&gt;API Root URL&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;API&lt;/code&gt; 的根入口点应尽可能保持足够简单，这里有两个常见的 &lt;code&gt;URL&lt;/code&gt; 根例子：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;api.example.com/*&lt;/li&gt;
&lt;li&gt;example.com/api/*&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;如果你的应用很庞大或者你预计它将会变的很庞大，那 &lt;code&gt;应该&lt;/code&gt; 将 &lt;code&gt;API&lt;/code&gt; 放到子域下（&lt;code&gt;api.example.com&lt;/code&gt;）。这种做法可以保持某些规模化上的灵活性。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;versioning&quot;&gt;Versioning&lt;/h2&gt;
&lt;p&gt;所有的 &lt;code&gt;API&lt;/code&gt; 必须保持向后兼容，你 &lt;code&gt;必须&lt;/code&gt; 在引入新版本 &lt;code&gt;API&lt;/code&gt; 的同时确保旧版本 &lt;code&gt;API&lt;/code&gt; 仍然可用。所以 &lt;code&gt;应该&lt;/code&gt; 为其提供版本支持。&lt;/p&gt;
&lt;p&gt;目前比较常见的两种版本号形式：&lt;/p&gt;
&lt;h3 id=&quot;在-url-中嵌入版本编号&quot;&gt;在 URL 中嵌入版本编号&lt;/h3&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;api.example.com/v1/*&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;api.example.com/v1/*&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;这种做法是版本号直观、易于调试；另一种做法是，将版本号放在 &lt;code&gt;HTTP Header&lt;/code&gt; 头中：&lt;/p&gt;
&lt;h3 id=&quot;通过媒体类型来指定版本信息&quot;&gt;通过媒体类型来指定版本信息&lt;/h3&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;Accept:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; application/vnd.example.com.v1+json&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;Accept: application/vnd.example.com.v1+json&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;其中 &lt;code&gt;vnd&lt;/code&gt; 表示 &lt;code&gt;Standards Tree&lt;/code&gt; 标准树类型，有三个不同的树: &lt;code&gt;x&lt;/code&gt;，&lt;code&gt;prs&lt;/code&gt; 和 &lt;code&gt;vnd&lt;/code&gt;。你使用的标准树需要取决于你开发的项目&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;未注册的树（&lt;code&gt;x&lt;/code&gt;）主要表示本地和私有环境&lt;/li&gt;
&lt;li&gt;私有树（&lt;code&gt;prs&lt;/code&gt;）主要表示没有商业发布的项目&lt;/li&gt;
&lt;li&gt;供应商树（&lt;code&gt;vnd&lt;/code&gt;）主要表示公开发布的项目&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;后面几个参数依次为应用名称（一般为应用域名）、版本号、期望的返回格式。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;至于具体把版本号放在什么地方，这个问题一直存在很大的争议，但由于我们大多数时间都在使用 &lt;code&gt;Laravel&lt;/code&gt; 开发，&lt;code&gt;应该&lt;/code&gt; 使用 &lt;a href=&quot;https://github.com/dingo/api&quot;&gt;dingo/api&lt;/a&gt; 来快速构建应用，它采用第二种方式来管理 &lt;code&gt;API&lt;/code&gt; 版本，并且已集成了标准的 &lt;code&gt;HTTP Response&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id=&quot;endpoints&quot;&gt;Endpoints&lt;/h2&gt;
&lt;p&gt;端点就是指向特定资源或资源集合的 &lt;code&gt;URL&lt;/code&gt;。在端点的设计中，你 &lt;code&gt;必须&lt;/code&gt; 遵守下列约定：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;URL 的命名 &lt;code&gt;必须&lt;/code&gt; 全部小写&lt;/li&gt;
&lt;li&gt;URL 中资源（&lt;code&gt;resource&lt;/code&gt;）的命名 &lt;code&gt;必须&lt;/code&gt; 是名词，并且 &lt;code&gt;必须&lt;/code&gt; 是复数形式&lt;/li&gt;
&lt;li&gt;&lt;code&gt;必须&lt;/code&gt; 优先使用 &lt;code&gt;Restful&lt;/code&gt; 类型的 URL&lt;/li&gt;
&lt;li&gt;URL &lt;code&gt;必须&lt;/code&gt; 是易读的&lt;/li&gt;
&lt;li&gt;URL &lt;code&gt;一定不可&lt;/code&gt; 暴露服务器架构&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;至于 URL 是否必须使用连字符（&lt;code&gt;-&lt;/code&gt;） 或下划线（&lt;code&gt;_&lt;/code&gt;），不做硬性规定，但 &lt;code&gt;必须&lt;/code&gt; 根据团队情况统一一种风格。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;来看一个反例&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://api.example.com/getUserInfo?userid=1&quot;&gt;https://api.example.com/getUserInfo?userid=1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://api.example.com/getusers&quot;&gt;https://api.example.com/getusers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://api.example.com/sv/u&quot;&gt;https://api.example.com/sv/u&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://api.example.com/cgi-bin/users/get_user.php?userid=1&quot;&gt;https://api.example.com/cgi-bin/users/get_user.php?userid=1&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;再来看一个正列&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://api.example.com/zoos&quot;&gt;https://api.example.com/zoos&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://api.example.com/animals&quot;&gt;https://api.example.com/animals&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://api.example.com/zoos/%7Bzoo%7D/animals&quot;&gt;https://api.example.com/zoos/{zoo}/animals&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://api.example.com/animal_types&quot;&gt;https://api.example.com/animal_types&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://api.example.com/employees&quot;&gt;https://api.example.com/employees&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;http-动词&quot;&gt;HTTP 动词&lt;/h2&gt;
&lt;p&gt;对于资源的具体操作类型，由 &lt;code&gt;HTTP&lt;/code&gt; 动词表示。常用的 &lt;code&gt;HTTP&lt;/code&gt; 动词有下面五个（括号里是对应的 &lt;code&gt;SQL&lt;/code&gt; 命令）。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GET（SELECT）：从服务器取出资源（一项或多项）。&lt;/li&gt;
&lt;li&gt;POST（CREATE）：在服务器新建一个资源。&lt;/li&gt;
&lt;li&gt;PUT（UPDATE）：在服务器更新资源（客户端提供改变后的完整资源）。&lt;/li&gt;
&lt;li&gt;PATCH（UPDATE）：在服务器更新资源（客户端提供改变的属性）。&lt;/li&gt;
&lt;li&gt;DELETE（DELETE）：从服务器删除资源。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;其中&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;删除资源 &lt;code&gt;必须&lt;/code&gt; 用 &lt;code&gt;DELETE&lt;/code&gt; 方法&lt;/li&gt;
&lt;li&gt;创建新的资源 &lt;code&gt;必须&lt;/code&gt; 使用 &lt;code&gt;POST&lt;/code&gt; 方法&lt;/li&gt;
&lt;li&gt;更新资源 &lt;code&gt;应该&lt;/code&gt; 使用 &lt;code&gt;PUT&lt;/code&gt; 方法&lt;/li&gt;
&lt;li&gt;获取资源信息 &lt;code&gt;必须&lt;/code&gt; 使用 &lt;code&gt;GET&lt;/code&gt; 方法&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;针对每一个端点来说，下面列出所有可行的 &lt;code&gt;HTTP&lt;/code&gt; 动词和端点的组合&lt;/p&gt;
















































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;请求方法&lt;/th&gt;&lt;th&gt;URL&lt;/th&gt;&lt;th&gt;描述&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;GET&lt;/td&gt;&lt;td&gt;/zoos&lt;/td&gt;&lt;td&gt;列出所有的动物园(ID和名称，不要太详细)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;POST&lt;/td&gt;&lt;td&gt;/zoos&lt;/td&gt;&lt;td&gt;新增一个新的动物园&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;GET&lt;/td&gt;&lt;td&gt;/zoos/{zoo}&lt;/td&gt;&lt;td&gt;获取指定动物园详情&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;PUT&lt;/td&gt;&lt;td&gt;/zoos/{zoo}&lt;/td&gt;&lt;td&gt;更新指定动物园(整个对象)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;PATCH&lt;/td&gt;&lt;td&gt;/zoos/{zoo}&lt;/td&gt;&lt;td&gt;更新动物园(部分对象)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;DELETE&lt;/td&gt;&lt;td&gt;/zoos/{zoo}&lt;/td&gt;&lt;td&gt;删除指定动物园&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;GET&lt;/td&gt;&lt;td&gt;/animal_types&lt;/td&gt;&lt;td&gt;获取所有动物类型(ID和名称，不要太详细)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;GET&lt;/td&gt;&lt;td&gt;/animal_types/{type}&lt;/td&gt;&lt;td&gt;获取指定的动物类型详情&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;GET&lt;/td&gt;&lt;td&gt;/employees&lt;/td&gt;&lt;td&gt;检索整个雇员列表&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;GET&lt;/td&gt;&lt;td&gt;/employees/{employee}&lt;/td&gt;&lt;td&gt;检索指定特定的员工&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;GET&lt;/td&gt;&lt;td&gt;/zoos/{zoo}/employees&lt;/td&gt;&lt;td&gt;检索在这个动物园工作的雇员的名单(身份证和姓名)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;POST&lt;/td&gt;&lt;td&gt;/employees&lt;/td&gt;&lt;td&gt;新增指定新员工&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;POST&lt;/td&gt;&lt;td&gt;/zoos/{zoo}/employees&lt;/td&gt;&lt;td&gt;在特定的动物园雇佣一名员工&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;DELETE&lt;/td&gt;&lt;td&gt;/zoos/{zoo}/employees/{employee}&lt;/td&gt;&lt;td&gt;从某个动物园解雇一名员工&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;超出 &lt;code&gt;RESTful&lt;/code&gt; 端点的，&lt;code&gt;应该&lt;/code&gt; 模仿上表的方式来定义端点。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;filtering&quot;&gt;Filtering&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;如果记录数量很多，服务器不可能都将它们返回给用户。API &lt;code&gt;应该&lt;/code&gt; 提供参数，过滤返回结果。下面是一些常见的参数。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;?limit=10：指定返回记录的数量&lt;/li&gt;
&lt;li&gt;?offset=10：指定返回记录的开始位置。&lt;/li&gt;
&lt;li&gt;?page=2&amp;#x26;per_page=100：指定第几页，以及每页的记录数。&lt;/li&gt;
&lt;li&gt;?sortby=name&amp;#x26;order=asc：指定返回结果按照哪个属性排序，以及排序顺序。&lt;/li&gt;
&lt;li&gt;?animal_type_id=1：指定筛选条件&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所有 &lt;code&gt;URL&lt;/code&gt; 参数 &lt;code&gt;必须&lt;/code&gt; 是全小写，&lt;code&gt;必须&lt;/code&gt; 使用下划线类型的参数形式。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;分页参数 &lt;code&gt;必须&lt;/code&gt; 固定为 &lt;code&gt;page&lt;/code&gt;、&lt;code&gt;per_page&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;经常使用的、复杂的查询 &lt;code&gt;应该&lt;/code&gt; 标签化，降低维护成本。如&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;GET&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; /trades?status=closed&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;&amp;#x26;sort&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;sortby&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;&amp;#x26;order&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;asc&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;# 可为其定制快捷方式&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;GET&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; /trades/recently_closed&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;GET /trades?status=closed&amp;#x26;sort=sortby=name&amp;#x26;order=asc

# 可为其定制快捷方式
GET /trades/recently_closed&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&quot;authentication&quot;&gt;Authentication&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;应该&lt;/code&gt; 使用 &lt;code&gt;OAuth2.0&lt;/code&gt; 的方式为 API 调用者提供登录认证。&lt;code&gt;必须&lt;/code&gt; 先通过登录接口获取 &lt;code&gt;Access Token&lt;/code&gt; 后再通过该 &lt;code&gt;token&lt;/code&gt; 调用需要身份认证的 &lt;code&gt;API&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;Oauth 的端点设计示列&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;RFC 6749   /token&lt;/li&gt;
&lt;li&gt;Twitter    /oauth2/token&lt;/li&gt;
&lt;li&gt;Fackbook   /oauth/access_token&lt;/li&gt;
&lt;li&gt;Google     /o/oauth2/token&lt;/li&gt;
&lt;li&gt;Github     /login/oauth/access_token&lt;/li&gt;
&lt;li&gt;Instagram  /oauth/authorize&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;客户端在获得 &lt;code&gt;access token&lt;/code&gt; 的同时 &lt;code&gt;必须&lt;/code&gt; 在响应中包含一个名为 &lt;code&gt;expires_in&lt;/code&gt; 的数据，它表示当前获得的 &lt;code&gt;token&lt;/code&gt; 会在多少 &lt;code&gt;秒&lt;/code&gt; 后失效。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;json&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;json&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;access_token&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;token....&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;token_type&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;Bearer&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;expires_in&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;3600&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;{
    &amp;#x22;access_token&amp;#x22;: &amp;#x22;token....&amp;#x22;,
    &amp;#x22;token_type&amp;#x22;: &amp;#x22;Bearer&amp;#x22;,
    &amp;#x22;expires_in&amp;#x22;: 3600
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;客户端在请求需要认证的 &lt;code&gt;API&lt;/code&gt; 时，&lt;code&gt;必须&lt;/code&gt; 在请求头 &lt;code&gt;Authorization&lt;/code&gt; 中带上 &lt;code&gt;access_token&lt;/code&gt;。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;Authorization:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Bearer&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; token...&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;Authorization: Bearer token...&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;当超过指定的秒数后，&lt;code&gt;access token&lt;/code&gt; 就会过期，再次用过期/或无效的 &lt;code&gt;token&lt;/code&gt; 访问时，服务端 &lt;code&gt;应该&lt;/code&gt; 返回 &lt;code&gt;invalid_token&lt;/code&gt; 的错误或 &lt;code&gt;401&lt;/code&gt; 错误码。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;HTTP&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1.1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 401&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Unauthorized&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Content-Type&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; application/json&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Cache-Control&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; no-store&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Pragma&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; no-cache&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;error&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;invalid_token&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;HTTP/1.1 401 Unauthorized
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache

{
    &amp;#x22;error&amp;#x22;: &amp;#x22;invalid_token&amp;#x22;
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;blockquote&gt;
&lt;p&gt;Laravel 开发中，&lt;code&gt;应该&lt;/code&gt; 使用 &lt;a href=&quot;https://github.com/tymondesigns/jwt-auth&quot;&gt;JWT&lt;/a&gt; 来为管理你的 Token，并且 &lt;code&gt;一定不可&lt;/code&gt; 在 &lt;code&gt;api&lt;/code&gt; 中间件中开启请求 &lt;code&gt;session&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;response&quot;&gt;Response&lt;/h2&gt;
&lt;p&gt;所有的 &lt;code&gt;API&lt;/code&gt; 响应，&lt;code&gt;必须&lt;/code&gt; 遵守 &lt;code&gt;HTTP&lt;/code&gt; 设计规范，&lt;code&gt;必须&lt;/code&gt; 选择合适的 &lt;code&gt;HTTP&lt;/code&gt; 状态码。&lt;code&gt;一定不可&lt;/code&gt; 所有接口都返回状态码为 &lt;code&gt;200&lt;/code&gt; 的 &lt;code&gt;HTTP&lt;/code&gt; 响应，如：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;HTTP&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1.1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 200&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; ok&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Content-Type&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; application/json&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Server&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; example.com&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;code&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;msg&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;success&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;data&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;        &quot;username&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;username&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;HTTP/1.1 200 ok
Content-Type: application/json
Server: example.com

{
    &amp;#x22;code&amp;#x22;: 0,
    &amp;#x22;msg&amp;#x22;: &amp;#x22;success&amp;#x22;,
    &amp;#x22;data&amp;#x22;: {
        &amp;#x22;username&amp;#x22;: &amp;#x22;username&amp;#x22;
    }
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;或&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;HTTP&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1.1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 200&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; ok&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Content-Type&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; application/json&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Server&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; example.com&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;code&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;-1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;msg&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;该活动不存在&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;HTTP/1.1 200 ok
Content-Type: application/json
Server: example.com

{
    &amp;#x22;code&amp;#x22;: -1,
    &amp;#x22;msg&amp;#x22;: &amp;#x22;该活动不存在&amp;#x22;,
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;下表列举了常见的 &lt;code&gt;HTTP&lt;/code&gt; 状态码&lt;/p&gt;





























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;状态码&lt;/th&gt;&lt;th&gt;描述&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;1xx&lt;/td&gt;&lt;td&gt;代表请求已被接受，需要继续处理&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2xx&lt;/td&gt;&lt;td&gt;请求已成功，请求所希望的响应头或数据体将随此响应返回&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;3xx&lt;/td&gt;&lt;td&gt;重定向&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;4xx&lt;/td&gt;&lt;td&gt;客户端原因引起的错误&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;5xx&lt;/td&gt;&lt;td&gt;服务端原因引起的错误&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;只有来自客户端的请求被正确的处理后才能返回 &lt;code&gt;2xx&lt;/code&gt; 的响应，所以当 API 返回 &lt;code&gt;2xx&lt;/code&gt; 类型的状态码时，前端 &lt;code&gt;必须&lt;/code&gt; 认定该请求已处理成功。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;必须强调的是，所有 &lt;code&gt;API&lt;/code&gt; &lt;code&gt;一定不可&lt;/code&gt; 返回 &lt;code&gt;1xx&lt;/code&gt; 类型的状态码。当 &lt;code&gt;API&lt;/code&gt; 发生错误时，&lt;code&gt;必须&lt;/code&gt; 返回出错时的详细信息。目前常见返回错误信息的方法有两种：&lt;/p&gt;
&lt;p&gt;1、将错误详细放入 &lt;code&gt;HTTP&lt;/code&gt; 响应首部；&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;X-MYNAME-ERROR-CODE&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; 4001&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;X-MYNAME-ERROR-MESSAGE&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Bad authentication token&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;X-MYNAME-ERROR-INFO&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; http://docs.example.com/api/v1/authentication&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;X-MYNAME-ERROR-CODE: 4001
X-MYNAME-ERROR-MESSAGE: Bad authentication token
X-MYNAME-ERROR-INFO: http://docs.example.com/api/v1/authentication&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;2、直接放入响应实体中；&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;HTTP&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1.1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 401&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Unauthorized&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Server&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; nginx/1.11.9&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Content-Type&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; application/json&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Transfer-Encoding&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; chunked&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Cache-Control&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; no-cache, private&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Date&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Sun, 24 Jun 2018 10:02:59 GMT&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Connection&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; keep-alive&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;&quot;error_code&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;40100&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;Unauthorized&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;HTTP/1.1 401 Unauthorized
Server: nginx/1.11.9
Content-Type: application/json
Transfer-Encoding: chunked
Cache-Control: no-cache, private
Date: Sun, 24 Jun 2018 10:02:59 GMT
Connection: keep-alive

{&amp;#x22;error_code&amp;#x22;:40100,&amp;#x22;message&amp;#x22;:&amp;#x22;Unauthorized&amp;#x22;}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;考虑到易读性和客户端的易处理性，我们 &lt;code&gt;必须&lt;/code&gt; 把错误信息直接放到响应实体中，并且错误格式 &lt;code&gt;应该&lt;/code&gt; 满足如下格式：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;json&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;json&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;message&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;您查找的资源不存在&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;error_code&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;404001&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;{
    &amp;#x22;message&amp;#x22;: &amp;#x22;您查找的资源不存在&amp;#x22;,
    &amp;#x22;error_code&amp;#x22;: 404001
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;其中错误码（&lt;code&gt;error_code&lt;/code&gt;）&lt;code&gt;必须&lt;/code&gt; 和 &lt;code&gt;HTTP&lt;/code&gt; 状态码对应，也方便错误码归类，如：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;HTTP&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1.1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 429&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Too Many Requests&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Server&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; nginx/1.11.9&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Content-Type&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; application/json&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Transfer-Encoding&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; chunked&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Cache-Control&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; no-cache, private&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Date&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Sun, 24 Jun 2018 10:15:52 GMT&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Connection&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; keep-alive&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;&quot;error_code&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;429001&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;你操作太频繁了&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;HTTP/1.1 429 Too Many Requests
Server: nginx/1.11.9
Content-Type: application/json
Transfer-Encoding: chunked
Cache-Control: no-cache, private
Date: Sun, 24 Jun 2018 10:15:52 GMT
Connection: keep-alive

{&amp;#x22;error_code&amp;#x22;:429001,&amp;#x22;message&amp;#x22;:&amp;#x22;你操作太频繁了&amp;#x22;}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;HTTP&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1.1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 403&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Forbidden&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Server&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; nginx/1.11.9&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Content-Type&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; application/json&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Transfer-Encoding&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; chunked&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Cache-Control&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; no-cache, private&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Date&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Sun, 24 Jun 2018 10:19:27 GMT&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Connection&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; keep-alive&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;&quot;error_code&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;403002&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;用户已禁用&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;HTTP/1.1 403 Forbidden
Server: nginx/1.11.9
Content-Type: application/json
Transfer-Encoding: chunked
Cache-Control: no-cache, private
Date: Sun, 24 Jun 2018 10:19:27 GMT
Connection: keep-alive

{&amp;#x22;error_code&amp;#x22;:403002,&amp;#x22;message&amp;#x22;:&amp;#x22;用户已禁用&amp;#x22;}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;应该&lt;/code&gt; 在返回的错误信息中，同时包含面向开发者和面向用户的提示信息，前者可方便开发人员调试，后者可直接展示给终端用户查看如：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;json&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;json&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;message&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;直接展示给终端用户的错误信息&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;error_code&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;业务错误码&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;error&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;供开发者查看的错误信息&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;debug&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;        &quot;错误堆栈，必须开启 debug 才存在&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    ]&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;{
    &amp;#x22;message&amp;#x22;: &amp;#x22;直接展示给终端用户的错误信息&amp;#x22;,
    &amp;#x22;error_code&amp;#x22;: &amp;#x22;业务错误码&amp;#x22;,
    &amp;#x22;error&amp;#x22;: &amp;#x22;供开发者查看的错误信息&amp;#x22;,
    &amp;#x22;debug&amp;#x22;: [
        &amp;#x22;错误堆栈，必须开启 debug 才存在&amp;#x22;
    ]
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;下面详细列举了各种情况 API 的返回说明。&lt;/p&gt;
&lt;h3 id=&quot;200-ok&quot;&gt;200 ok&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;200&lt;/code&gt; 状态码是最常见的 &lt;code&gt;HTTP&lt;/code&gt; 状态码，在所有 &lt;strong&gt;成功&lt;/strong&gt; 的 &lt;code&gt;GET&lt;/code&gt; 请求中，&lt;code&gt;必须&lt;/code&gt; 返回此状态码。&lt;code&gt;HTTP&lt;/code&gt; 响应实体部分 &lt;code&gt;必须&lt;/code&gt; 直接就是数据，不要做多余的包装。&lt;/p&gt;
&lt;p&gt;错误示例：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;HTTP&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1.1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 200&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; ok&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Content-Type&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; application/json&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Server&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; example.com&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;user&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;        &quot;id&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;        &quot;nickname&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;fwest&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;        &quot;username&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;example&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;HTTP/1.1 200 ok
Content-Type: application/json
Server: example.com

{
    &amp;#x22;user&amp;#x22;: {
        &amp;#x22;id&amp;#x22;:1,
        &amp;#x22;nickname&amp;#x22;:&amp;#x22;fwest&amp;#x22;,
        &amp;#x22;username&amp;#x22;: &amp;#x22;example&amp;#x22;
    }
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;正确示例：&lt;/p&gt;
&lt;p&gt;1、获取单个资源详情&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;json&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;json&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;id&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;username&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;godruoyi&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;age&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;88&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;{
    &amp;#x22;id&amp;#x22;: 1,
    &amp;#x22;username&amp;#x22;: &amp;#x22;godruoyi&amp;#x22;,
    &amp;#x22;age&amp;#x22;: 88,
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;2、获取资源集合&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;json&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;json&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;[&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;        &quot;id&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;        &quot;username&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;godruoyi&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;        &quot;age&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;88&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;        &quot;id&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;        &quot;username&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;        &quot;age&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;88&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;[
    {
        &amp;#x22;id&amp;#x22;: 1,
        &amp;#x22;username&amp;#x22;: &amp;#x22;godruoyi&amp;#x22;,
        &amp;#x22;age&amp;#x22;: 88,
    },
    {
        &amp;#x22;id&amp;#x22;: 2,
        &amp;#x22;username&amp;#x22;: &amp;#x22;foo&amp;#x22;,
        &amp;#x22;age&amp;#x22;: 88,
    }
]&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;3、额外的媒体信息&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;json&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;json&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;data&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;            &quot;id&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;            &quot;avatar&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;https://lorempixel.com/640/480/?32556&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;            &quot;nickname&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;fwest&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;            &quot;last_logined_time&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;2018-05-29 04:56:43&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;            &quot;has_registed&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        },&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;            &quot;id&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;            &quot;avatar&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;https://lorempixel.com/640/480/?86144&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;            &quot;nickname&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;zschowalter&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;            &quot;last_logined_time&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;2018-06-16 15:18:34&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;            &quot;has_registed&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    ],&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;meta&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;        &quot;pagination&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;            &quot;total&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;101&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;            &quot;count&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;            &quot;per_page&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;            &quot;current_page&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;            &quot;total_pages&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;51&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;            &quot;links&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;                &quot;next&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;http://api.example.com?page=2&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;{
    &amp;#x22;data&amp;#x22;: [
        {
            &amp;#x22;id&amp;#x22;: 1,
            &amp;#x22;avatar&amp;#x22;: &amp;#x22;https://lorempixel.com/640/480/?32556&amp;#x22;,
            &amp;#x22;nickname&amp;#x22;: &amp;#x22;fwest&amp;#x22;,
            &amp;#x22;last_logined_time&amp;#x22;: &amp;#x22;2018-05-29 04:56:43&amp;#x22;,
            &amp;#x22;has_registed&amp;#x22;: true
        },
        {
            &amp;#x22;id&amp;#x22;: 2,
            &amp;#x22;avatar&amp;#x22;: &amp;#x22;https://lorempixel.com/640/480/?86144&amp;#x22;,
            &amp;#x22;nickname&amp;#x22;: &amp;#x22;zschowalter&amp;#x22;,
            &amp;#x22;last_logined_time&amp;#x22;: &amp;#x22;2018-06-16 15:18:34&amp;#x22;,
            &amp;#x22;has_registed&amp;#x22;: true
        }
    ],
    &amp;#x22;meta&amp;#x22;: {
        &amp;#x22;pagination&amp;#x22;: {
            &amp;#x22;total&amp;#x22;: 101,
            &amp;#x22;count&amp;#x22;: 2,
            &amp;#x22;per_page&amp;#x22;: 2,
            &amp;#x22;current_page&amp;#x22;: 1,
            &amp;#x22;total_pages&amp;#x22;: 51,
            &amp;#x22;links&amp;#x22;: {
                &amp;#x22;next&amp;#x22;: &amp;#x22;http://api.example.com?page=2&amp;#x22;
            }
        }
    }
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;blockquote&gt;
&lt;p&gt;其中，分页和其他额外的媒体信息，必须放到 &lt;code&gt;meta&lt;/code&gt; 字段中。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;201-created&quot;&gt;201 Created&lt;/h3&gt;
&lt;p&gt;当服务器创建数据成功时，&lt;code&gt;应该&lt;/code&gt; 返回此状态码。常见的应用场景是使用 &lt;code&gt;POST&lt;/code&gt; 提交用户信息，如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;添加了新用户&lt;/li&gt;
&lt;li&gt;上传了图片&lt;/li&gt;
&lt;li&gt;创建了新活动&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;等，都可以返回 &lt;code&gt;201&lt;/code&gt; 状态码。需要注意的是，你可以选择在用户创建成功后返回新用户的数据&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;HTTP&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1.1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 201&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Created&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Server&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; nginx/1.11.9&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Content-Type&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; application/json&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Transfer-Encoding&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; chunked&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Date&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Sun, 24 Jun 2018 09:13:40 GMT&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Connection&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; keep-alive&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;id&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;avatar&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;https:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#F47067&quot;&gt;\/\/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;lorempixel.com&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#F47067&quot;&gt;\/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;640&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#F47067&quot;&gt;\/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;480&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#F47067&quot;&gt;\/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;?32556&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;nickname&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;fwest&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;last_logined_time&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;2018-05-29 04:56:43&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;created_at&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;2018-06-16 17:55:55&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;    &quot;updated_at&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;2018-06-16 17:55:55&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;HTTP/1.1 201 Created
Server: nginx/1.11.9
Content-Type: application/json
Transfer-Encoding: chunked
Date: Sun, 24 Jun 2018 09:13:40 GMT
Connection: keep-alive

{
    &amp;#x22;id&amp;#x22;: 1,
    &amp;#x22;avatar&amp;#x22;: &amp;#x22;https:\/\/lorempixel.com\/640\/480\/?32556&amp;#x22;,
    &amp;#x22;nickname&amp;#x22;: &amp;#x22;fwest&amp;#x22;,
    &amp;#x22;last_logined_time&amp;#x22;: &amp;#x22;2018-05-29 04:56:43&amp;#x22;,
    &amp;#x22;created_at&amp;#x22;: &amp;#x22;2018-06-16 17:55:55&amp;#x22;,
    &amp;#x22;updated_at&amp;#x22;: &amp;#x22;2018-06-16 17:55:55&amp;#x22;
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;也可以返回一个响应实体为空的 &lt;code&gt;HTTP Response&lt;/code&gt; 如：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;HTTP&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1.1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 201&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Created&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Server&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; nginx/1.11.9&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Content-Type&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; text/html; charset=UTF-8&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Transfer-Encoding&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; chunked&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Date&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Sun, 24 Jun 2018 09:12:20 GMT&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Connection&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; keep-alive&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;HTTP/1.1 201 Created
Server: nginx/1.11.9
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Date: Sun, 24 Jun 2018 09:12:20 GMT
Connection: keep-alive&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;blockquote&gt;
&lt;p&gt;这里我们 &lt;code&gt;应该&lt;/code&gt; 采用第二种方式，因为大多数情况下，客户端只需要知道该请求操作成功与否，并不需要返回新资源的信息。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;202-accepted&quot;&gt;202 Accepted&lt;/h3&gt;
&lt;p&gt;该状态码表示服务器已经接受到了来自客户端的请求，但还未开始处理。常用短信发送、邮件通知、模板消息推送等这类很耗时需要队列支持的场景中；&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;返回该状态码时，响应实体 &lt;code&gt;必须&lt;/code&gt; 为空。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;html&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;html&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;HTTP/1.1 202 Accepted&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;Server: nginx/1.11.9&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;Content-Type: text/html; charset=UTF-8&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;Transfer-Encoding: chunked&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;Date: Sun, 24 Jun 2018 09:25:15 GMT&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;Connection: keep-alive&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;HTTP/1.1 202 Accepted
Server: nginx/1.11.9
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Date: Sun, 24 Jun 2018 09:25:15 GMT
Connection: keep-alive&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h3 id=&quot;204-no-content&quot;&gt;204 No Content&lt;/h3&gt;
&lt;p&gt;该状态码表示响应实体不包含任何数据，其中：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在使用 &lt;code&gt;DELETE&lt;/code&gt; 方法删除资源 &lt;strong&gt;成功&lt;/strong&gt; 时，&lt;code&gt;必须&lt;/code&gt; 返回该状态码&lt;/li&gt;
&lt;li&gt;使用 &lt;code&gt;PUT&lt;/code&gt;、&lt;code&gt;PATCH&lt;/code&gt; 方法更新数据 &lt;strong&gt;成功&lt;/strong&gt; 时，也 &lt;code&gt;应该&lt;/code&gt; 返回此状态码&lt;/li&gt;
&lt;/ul&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;HTTP&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1.1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 204&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; No Content&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Server&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; nginx/1.11.9&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Date&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Sun, 24 Jun 2018 09:29:12 GMT&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Connection&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; keep-alive&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;HTTP/1.1 204 No Content
Server: nginx/1.11.9
Date: Sun, 24 Jun 2018 09:29:12 GMT
Connection: keep-alive&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h3 id=&quot;3xx-重定向&quot;&gt;3xx 重定向&lt;/h3&gt;
&lt;p&gt;所有 &lt;code&gt;API&lt;/code&gt; &lt;code&gt;不该&lt;/code&gt; 返回 &lt;code&gt;3xx&lt;/code&gt; 类型的状态码。因为 &lt;code&gt;3xx&lt;/code&gt; 类型的响应格式一般为下列格式：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;HTTP/1.1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 302&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Found&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;Server:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; nginx/1.11.9&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;Content-Type:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; text/html&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;; charset&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;UTF-8&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;Transfer-Encoding:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; chunked&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;Cache-Control:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; no-cache,&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; private&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;Date:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Sun,&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 24&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Jun&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 2018&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; 09:41:50&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; GMT&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;Location:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; https://example.com&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;HTTP/1.1 302 Found
Server: nginx/1.11.9
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Cache-Control: no-cache, private
Date: Sun, 24 Jun 2018 09:41:50 GMT
Location: https://example.com&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;所有 &lt;code&gt;API&lt;/code&gt; &lt;code&gt;一定不可&lt;/code&gt; 返回纯 &lt;code&gt;HTML&lt;/code&gt; 结构的响应；若一定要使用重定向功能，&lt;code&gt;可以&lt;/code&gt; 返回一个响应实体为空的 &lt;code&gt;3xx&lt;/code&gt; 响应，并在响应头中加上 &lt;code&gt;Location&lt;/code&gt; 字段:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;HTTP/1.1 302 Found
Server: nginx/1.11.9
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Date: Sun, 24 Jun 2018 09:52:50 GMT
Location: https://godruoyi.com
Connection: keep-alive
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;400-bad-request&quot;&gt;400 Bad Request&lt;/h3&gt;
&lt;p&gt;由于明显的客户端错误（例如，请求语法格式错误、无效的请求、无效的签名等），服务器 &lt;code&gt;应该&lt;/code&gt; 放弃该请求。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;当服务器无法从其他 4xx 类型的状态码中找出合适的来表示错误类型时，都 &lt;code&gt;必须&lt;/code&gt; 返回该状态码。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;HTTP&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1.1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 400&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Bad Request&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Server&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; nginx/1.11.9&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Content-Type&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; application/json&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Transfer-Encoding&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; chunked&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Cache-Control&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; no-cache, private&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Date&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Sun, 24 Jun 2018 13:22:36 GMT&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Connection&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; keep-alive&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;&quot;error_code&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;40000&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;无效的签名&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;HTTP/1.1 400 Bad Request
Server: nginx/1.11.9
Content-Type: application/json
Transfer-Encoding: chunked
Cache-Control: no-cache, private
Date: Sun, 24 Jun 2018 13:22:36 GMT
Connection: keep-alive

{&amp;#x22;error_code&amp;#x22;:40000,&amp;#x22;message&amp;#x22;:&amp;#x22;无效的签名&amp;#x22;}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h3 id=&quot;401-unauthorized&quot;&gt;401 Unauthorized&lt;/h3&gt;
&lt;p&gt;该状态码表示当前请求需要身份认证，以下情况都 &lt;code&gt;必须&lt;/code&gt; 返回该状态码。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;未认证用户访问需要认证的 API&lt;/li&gt;
&lt;li&gt;access_token 无效/过期&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;客户端在收到 &lt;code&gt;401&lt;/code&gt; 响应后，都 &lt;code&gt;应该&lt;/code&gt; 提示用户进行下一步的登录操作。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;HTTP&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1.1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 401&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Unauthorized&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Server&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; nginx/1.11.9&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Content-Type&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; application/json&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Transfer-Encoding&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; chunked&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;WWW-Authenticate&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; JWTAuth&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Cache-Control&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; no-cache, private&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Date&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Sun, 24 Jun 2018 13:17:02 GMT&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Connection&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; keep-alive&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;&quot;message&quot;:&quot;Token Signature could not be verified.&quot;,&quot;error_code&quot;: &quot;40100&quot;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;HTTP/1.1 401 Unauthorized
Server: nginx/1.11.9
Content-Type: application/json
Transfer-Encoding: chunked
WWW-Authenticate: JWTAuth
Cache-Control: no-cache, private
Date: Sun, 24 Jun 2018 13:17:02 GMT
Connection: keep-alive

&amp;#x22;message&amp;#x22;:&amp;#x22;Token Signature could not be verified.&amp;#x22;,&amp;#x22;error_code&amp;#x22;: &amp;#x22;40100&amp;#x22;}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h3 id=&quot;403-forbidden&quot;&gt;403 Forbidden&lt;/h3&gt;
&lt;p&gt;该状态码可以简单的理解为没有权限访问该请求，服务器收到请求但拒绝提供服务。&lt;/p&gt;
&lt;p&gt;如当普通用户请求操作管理员用户时，&lt;code&gt;必须&lt;/code&gt; 返回该状态码。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;HTTP&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1.1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 403&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Forbidden&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Server&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; nginx/1.11.9&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Content-Type&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; application/json&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Transfer-Encoding&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; chunked&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Cache-Control&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; no-cache, private&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Date&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Sun, 24 Jun 2018 13:05:34 GMT&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Connection&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; keep-alive&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;&quot;error_code&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;40301&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;权限不足&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;HTTP/1.1 403 Forbidden
Server: nginx/1.11.9
Content-Type: application/json
Transfer-Encoding: chunked
Cache-Control: no-cache, private
Date: Sun, 24 Jun 2018 13:05:34 GMT
Connection: keep-alive

{&amp;#x22;error_code&amp;#x22;:40301,&amp;#x22;message&amp;#x22;:&amp;#x22;权限不足&amp;#x22;}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h3 id=&quot;404-not-found&quot;&gt;404 Not Found&lt;/h3&gt;
&lt;p&gt;该状态码表示用户请求的资源不存在，如&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;获取不存在的用户信息 （get /users/9999999）&lt;/li&gt;
&lt;li&gt;访问不存在的端点&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;都 &lt;code&gt;必须&lt;/code&gt; 返回该状态码，若该资源已永久不存在，则 &lt;code&gt;应该&lt;/code&gt; 返回 &lt;code&gt;401&lt;/code&gt; 响应。&lt;/p&gt;
&lt;h3 id=&quot;405-method-not-allowd&quot;&gt;405 Method Not Allowd&lt;/h3&gt;
&lt;p&gt;当客户端使用的 &lt;code&gt;HTTP&lt;/code&gt; 请求方法不被服务器允许时，&lt;code&gt;必须&lt;/code&gt; 返回该状态码。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;如客户端调用了 &lt;code&gt;POST&lt;/code&gt; 方法来访问只支持 GET 方法的 API&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;该响应 &lt;code&gt;必须&lt;/code&gt; 返回一个 &lt;code&gt;Allow&lt;/code&gt; 头信息用以表示出当前资源能够接受的请求方法的列表。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;HTTP&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1.1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 405&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Method Not Allowed&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Server&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; nginx/1.11.9&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Content-Type&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; application/json&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Transfer-Encoding&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; chunked&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Allow&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; GET, HEAD&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Cache-Control&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; no-cache, private&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Date&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Sun, 24 Jun 2018 12:30:57 GMT&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Connection&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; keep-alive&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;405 Method Not Allowed&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;&quot;error_code&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;40500&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;HTTP/1.1 405 Method Not Allowed
Server: nginx/1.11.9
Content-Type: application/json
Transfer-Encoding: chunked
Allow: GET, HEAD
Cache-Control: no-cache, private
Date: Sun, 24 Jun 2018 12:30:57 GMT
Connection: keep-alive

{&amp;#x22;message&amp;#x22;:&amp;#x22;405 Method Not Allowed&amp;#x22;,&amp;#x22;error_code&amp;#x22;: 40500}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h3 id=&quot;406-not-acceptable&quot;&gt;406 Not Acceptable&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;API&lt;/code&gt; 在不支持客户端指定的数据格式时，应该返回此状态码。如支持 &lt;code&gt;JSON&lt;/code&gt; 和 &lt;code&gt;XML&lt;/code&gt; 输出的 &lt;code&gt;API&lt;/code&gt; 被指定返回 &lt;code&gt;YAML&lt;/code&gt; 格式的数据时。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Http 协议一般通过请求首部的 Accept 来指定数据格式&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;408-request-timeout&quot;&gt;408 Request Timeout&lt;/h3&gt;
&lt;p&gt;客户端请求超时时 &lt;code&gt;必须&lt;/code&gt; 返回该状态码，需要注意的时，该状态码表示 &lt;strong&gt;客户端请求超时&lt;/strong&gt;，在涉及第三方 &lt;code&gt;API&lt;/code&gt; 调用超时时，&lt;code&gt;一定不可&lt;/code&gt; 返回该状态码。&lt;/p&gt;
&lt;h3 id=&quot;409-gonfilct&quot;&gt;409 Gonfilct&lt;/h3&gt;
&lt;p&gt;该状态码表示因为请求存在冲突无法处理。如通过手机号码提供注册功能的 &lt;code&gt;API&lt;/code&gt;，当用户提交的手机号已存在时，&lt;code&gt;必须&lt;/code&gt; 返回此状态码。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;HTTP&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1.1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 409&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Conflict&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Server&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; nginx/1.11.9&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Content-Type&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; application/json&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Transfer-Encoding&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; chunked&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Cache-Control&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; no-cache, private&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Date&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Sun, 24 Jun 2018 12:19:04 GMT&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Connection&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; keep-alive&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;&quot;error_code&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;40900&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;手机号已存在&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;HTTP/1.1 409 Conflict
Server: nginx/1.11.9
Content-Type: application/json
Transfer-Encoding: chunked
Cache-Control: no-cache, private
Date: Sun, 24 Jun 2018 12:19:04 GMT
Connection: keep-alive

{&amp;#x22;error_code&amp;#x22;:40900,&amp;#x22;message&amp;#x22;:&amp;#x22;手机号已存在&amp;#x22;}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h3 id=&quot;410-gone&quot;&gt;410 Gone&lt;/h3&gt;
&lt;p&gt;和 &lt;code&gt;404&lt;/code&gt; 类似，该状态码也表示请求的资源不存在，只是 &lt;code&gt;410&lt;/code&gt; 状态码进一步表示所请求的资源已不存在，并且未来也不会存在。在收到 &lt;code&gt;410&lt;/code&gt; 状态码后，客户端 &lt;code&gt;应该&lt;/code&gt; 停止再次请求该资源。&lt;/p&gt;
&lt;h3 id=&quot;413-request-entity-too-large&quot;&gt;413 Request Entity Too Large&lt;/h3&gt;
&lt;p&gt;该状态码表示服务器拒绝处理当前请求，因为该请求提交的实体数据大小超过了服务器愿意或者能够处理的范围。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;此种情况下，服务器可以关闭连接以免客户端继续发送此请求。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;如果这个状况是临时的，服务器 &lt;code&gt;应该&lt;/code&gt; 返回一个 &lt;code&gt;Retry-After&lt;/code&gt; 的响应头，以告知客户端可以在多少时间以后重新尝试。&lt;/p&gt;
&lt;h3 id=&quot;414-request-uri-too-long&quot;&gt;414 Request-URI Too Long&lt;/h3&gt;
&lt;p&gt;该状态码表示请求的 &lt;code&gt;URI&lt;/code&gt; 长度超过了服务器能够解释的长度，因此服务器拒绝对该请求提供服务。&lt;/p&gt;
&lt;h3 id=&quot;415-unsupported-media-type&quot;&gt;415 Unsupported Media Type&lt;/h3&gt;
&lt;p&gt;通常表示服务器不支持客户端请求首部 &lt;code&gt;Content-Type&lt;/code&gt; 指定的数据格式。如在只接受 &lt;code&gt;JSON&lt;/code&gt; 格式的 &lt;code&gt;API&lt;/code&gt; 中放入 &lt;code&gt;XML&lt;/code&gt; 类型的数据并向服务器发送，都 &lt;code&gt;应该&lt;/code&gt; 返回该状态码。&lt;/p&gt;
&lt;p&gt;该状态码也可用于如：只允许上传图片格式的文件，但是客户端提交媒体文件非法或不是图片类型，这时 &lt;code&gt;应该&lt;/code&gt; 返回该状态码：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;HTTP&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1.1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 415&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Unsupported Media Type&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Server&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; nginx/1.11.9&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Content-Type&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; application/json&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Transfer-Encoding&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; chunked&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Cache-Control&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; no-cache, private&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Date&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Sun, 24 Jun 2018 12:09:40 GMT&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Connection&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; keep-alive&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;&quot;error_code&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;41500&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;不允许上传的图片格式&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;HTTP/1.1 415 Unsupported Media Type
Server: nginx/1.11.9
Content-Type: application/json
Transfer-Encoding: chunked
Cache-Control: no-cache, private
Date: Sun, 24 Jun 2018 12:09:40 GMT
Connection: keep-alive

{&amp;#x22;error_code&amp;#x22;:41500,&amp;#x22;message&amp;#x22;:&amp;#x22;不允许上传的图片格式&amp;#x22;}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h3 id=&quot;429-too-many-request&quot;&gt;429 Too Many Request&lt;/h3&gt;
&lt;p&gt;该状态码表示用户请求次数超过允许范围。如 &lt;code&gt;API&lt;/code&gt; 设定为 &lt;code&gt;60次/分钟&lt;/code&gt;，当用户在一分钟内请求次数超过 60 次后，都 &lt;code&gt;应该&lt;/code&gt; 返回该状态码。并且也 &lt;code&gt;应该&lt;/code&gt; 在响应首部中加上下列头部：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;X-RateLimit-Limit:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 10&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; 请求速率（由应用设定，其单位一般为小时/分钟等，这里是&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; 10次/5分钟）&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;X-RateLimit-Remaining:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; 当前剩余的请求数量&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;X-RateLimit-Reset:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 1529839462&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; 重置时间&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;Retry-After:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 120&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; 下一次访问应该等待的时间（秒）&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;X-RateLimit-Limit: 10 请求速率（由应用设定，其单位一般为小时/分钟等，这里是 10次/5分钟）
X-RateLimit-Remaining: 0 当前剩余的请求数量
X-RateLimit-Reset: 1529839462 重置时间
Retry-After: 120 下一次访问应该等待的时间（秒）&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;列子&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;HTTP&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1.1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 429&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Too Many Requests&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Server&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; nginx/1.11.9&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Content-Type&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; application/json&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Transfer-Encoding&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; chunked&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;X-RateLimit-Limit&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; 10&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;X-RateLimit-Remaining&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; 0&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;X-RateLimit-Reset&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; 1529839462&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Retry-After&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; 290&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Cache-Control&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; no-cache, private&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Date&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Sun, 24 Jun 2018 11:19:32 GMT&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Connection&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; keep-alive&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;You have exceeded your rate limit.&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;&quot;error_code&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;42900&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;HTTP/1.1 429 Too Many Requests
Server: nginx/1.11.9
Content-Type: application/json
Transfer-Encoding: chunked
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1529839462
Retry-After: 290
Cache-Control: no-cache, private
Date: Sun, 24 Jun 2018 11:19:32 GMT
Connection: keep-alive

{&amp;#x22;message&amp;#x22;:&amp;#x22;You have exceeded your rate limit.&amp;#x22;,&amp;#x22;error_code&amp;#x22;:42900}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;必须&lt;/code&gt; 为所有的 API 设置 Rate Limit 支持。&lt;/p&gt;
&lt;h3 id=&quot;500-internal-server-error&quot;&gt;500 Internal Server Error&lt;/h3&gt;
&lt;p&gt;该状态码 &lt;code&gt;必须&lt;/code&gt; 在服务器出错时抛出，对于所有的 &lt;code&gt;500&lt;/code&gt; 错误，都 &lt;code&gt;应该&lt;/code&gt; 提供完整的错误信息支持，也方便跟踪调试。&lt;/p&gt;
&lt;h3 id=&quot;503-service-unavailable&quot;&gt;503 Service Unavailable&lt;/h3&gt;
&lt;p&gt;该状态码表示服务器暂时处理不可用状态，当服务器需要维护或第三方 &lt;code&gt;API&lt;/code&gt; 请求超时/不可达时，都 &lt;code&gt;应该&lt;/code&gt; 返回该状态码，其中若是主动关闭 API 服务，&lt;code&gt;应该 &lt;/code&gt;在返回的响应首部加上 &lt;code&gt;Retry-After&lt;/code&gt; 头部，表示多少秒后可以再次访问。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;http&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;HTTP&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1.1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 503&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Service Unavailable&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Server&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; nginx/1.11.9&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Content-Type&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; application/json&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Transfer-Encoding&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; chunked&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Cache-Control&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; no-cache, private&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Date&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Sun, 24 Jun 2018 10:56:20 GMT&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Retry-After&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; 60&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#22863A;--shiki-dark:#8DDB8C&quot;&gt;Connection&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; keep-alive&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;&quot;error_code&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;50300&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;服务维护中&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;HTTP/1.1 503 Service Unavailable
Server: nginx/1.11.9
Content-Type: application/json
Transfer-Encoding: chunked
Cache-Control: no-cache, private
Date: Sun, 24 Jun 2018 10:56:20 GMT
Retry-After: 60
Connection: keep-alive

{&amp;#x22;error_code&amp;#x22;:50300,&amp;#x22;message&amp;#x22;:&amp;#x22;服务维护中&amp;#x22;}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;其他 &lt;code&gt;HTTP&lt;/code&gt; 状态码请参考 &lt;a href=&quot;https://zh.wikipedia.org/zh-hans/HTTP%E7%8A%B6%E6%80%81%E7%A0%81&quot;&gt;HTTP 状态码- 维基百科&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&quot;版权声明&quot;&gt;版权声明&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;版权声明：自由转载-非商用-非衍生-保持署名（&lt;a href=&quot;https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh&quot;&gt;创意共享3.0许可证&lt;/a&gt;）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;建议参考&quot;&gt;建议参考&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/aisuhua/restful-api-design-references&quot;&gt;restful-api-design-references&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.cnblogs.com/moonz-wu/p/4211626.html&quot;&gt;Principles of good RESTful API Design（译）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.ruanyifeng.com/blog/2011/09/restful.html&quot;&gt;理解 RESTful 架构 - ruanyifeng&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.ruanyifeng.com/blog/2014/05/restful_api.html&quot;&gt;RESTful API 设计指南 - ruanyifeng&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://zh.wikipedia.org/zh-hans/HTTP%E7%8A%B6%E6%80%81%E7%A0%81&quot;&gt;HTTP 状态码- 维基百科&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Let&apos;s Encrypt 泛域名证书申请及配置</title><link>https://godruoyi.com/posts/let-s-encrypt-generic-domain-name-certificate-application-and-configuration/</link><guid isPermaLink="true">https://godruoyi.com/posts/let-s-encrypt-generic-domain-name-certificate-application-and-configuration/</guid><description>Let&apos;s Encrypt 在今年 3 月份就已经推出泛域名证书支持了，以前我一直是使用的单域名证书，加上站点开启了 `HSTS` 支持，当新增网站应用时不得不为其单独申请证书，十分不便。  目前比较常用的为 `Let&apos;s Encrypt...</description><pubDate>Thu, 21 Jun 2018 15:26:19 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Let’s Encrypt 在今年 &lt;code&gt;3&lt;/code&gt; 月份就已经推出泛域名证书支持了，以前我一直是使用的单域名证书，加上站点开启了 &lt;code&gt;HSTS&lt;/code&gt; 支持，当新增网站应用时不得不为其单独申请证书，十分不便。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;目前比较常用的为 &lt;code&gt;Let&apos;s Encrypt&lt;/code&gt; 生成证书的工具比较多，如&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/diafygi/acme-tiny&quot;&gt;acme-tiny&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/certbot/certbot&quot;&gt;certbot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/Neilpang/acme.sh&quot;&gt;acme.sh&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这里我们将使用 &lt;a href=&quot;https://github.com/Neilpang/acme.sh&quot;&gt;acme.sh&lt;/a&gt; 这个工具来安装 &lt;code&gt;Let&apos;s Encrypt&lt;/code&gt; 证书。&lt;code&gt;acme.sh&lt;/code&gt; 是一个非常优秀的证书生成工具，其 &lt;a href=&quot;https://github.com/Neilpang/acme.sh&quot;&gt;官网&lt;/a&gt; 更是有详细的中文文档支持 。&lt;/p&gt;
&lt;h2 id=&quot;安装&quot;&gt;安装&lt;/h2&gt;
&lt;p&gt;你可以通过下面的脚本来安装 &lt;code&gt;acme.sh&lt;/code&gt;&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;curl&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;  https://get.acme.sh&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; sh&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;curl  https://get.acme.sh | sh&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;blockquote&gt;
&lt;p&gt;该操作需要服务器支持 &lt;code&gt;socat&lt;/code&gt; 及 &lt;code&gt;curl&lt;/code&gt; 模块。（apt install socat curl）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;安装成功后，会在当前文件夹下生成 &lt;code&gt;.acme.sh&lt;/code&gt; 文件夹。&lt;/p&gt;
&lt;h3 id=&quot;生成证书&quot;&gt;生成证书&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;acme.sh&lt;/code&gt; 实现了 &lt;code&gt;acme&lt;/code&gt; 协议支持的所有验证协议，一般有两种方式验证: &lt;code&gt;http&lt;/code&gt; 和 &lt;code&gt;dns&lt;/code&gt; 验证。由于泛域名证书的解析目前仅支持 &lt;code&gt;DNS&lt;/code&gt; 方式验证，下面我们将通过 &lt;code&gt;DNS&lt;/code&gt; 方式来验证你的域名所有权。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;acme.sh&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;  --issue&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;  --dns&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;  -d&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; godruoyi.com&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; -d&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;*.godruoyi.com&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; --yes-I-know-dns-manual-mode-enough-go-ahead-please&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;acme.sh  --issue  --dns  -d godruoyi.com -d &amp;#x27;*.godruoyi.com&amp;#x27; --yes-I-know-dns-manual-mode-enough-go-ahead-please&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;这种方式会将相应的解析记录显示出来，然后你需要在你的域名管理面板中添加这条 &lt;code&gt;txt&lt;/code&gt; 记录。并等待解析完成之后，重新用下面命令生成证书：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;acme.sh&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;  --renew&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;   -d&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; mydomain.com&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;acme.sh  --renew   -d mydomain.com&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;blockquote&gt;
&lt;p&gt;注意第二次这里用的是 &lt;code&gt;--renew&lt;/code&gt;，当然我们并不想这么麻烦，&lt;code&gt;dns&lt;/code&gt; 方式的真正强大之处在于可以使用域名解析商提供的 &lt;code&gt;api&lt;/code&gt; 自动添加 &lt;code&gt;txt&lt;/code&gt; 记录完成验证。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;根据你的域名服务商类型，选择对应的 &lt;a href=&quot;https://github.com/Neilpang/acme.sh/blob/master/dnsapi/README.md&quot;&gt;DNS API&lt;/a&gt;。如&lt;/p&gt;
&lt;p&gt;1、腾讯云&lt;/p&gt;
&lt;p&gt;在 &lt;a href=&quot;https://www.dnspod.cn/console/user/security&quot;&gt;这里申请 API Token&lt;/a&gt;，获取到 &lt;code&gt;ID&lt;/code&gt; 及 &lt;code&gt;Token&lt;/code&gt; 后执行：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/201806/21/1_1529590937_4rCg5hX0qu.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; DP_Id&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; DP_Key&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;token&quot;&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;export DP_Id=&amp;#x22;id&amp;#x22;
export DP_Key=&amp;#x22;token&amp;#x22;&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;2、阿里云&lt;/p&gt;
&lt;p&gt;在 &lt;a href=&quot;https://ak-console.aliyun.com/#/accesskey&quot;&gt;这里申请阿里云 Accesskey&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/201806/21/1_1529591195_ryE5GiaXNP.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;p&gt;获取到 &lt;code&gt;KEY&lt;/code&gt; 及 &lt;code&gt;Secret&lt;/code&gt; 后执行下面命令：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; Ali_Key&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;sdfsdfsdfljlbjkljlkjsdfoiwje&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; Ali_Secret&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;jlsdflanljkljlfdsaklkjflsa&quot;&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;export Ali_Key=&amp;#x22;sdfsdfsdfljlbjkljlkjsdfoiwje&amp;#x22;
export Ali_Secret=&amp;#x22;jlsdflanljkljlfdsaklkjflsa&amp;#x22;&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;3、生成证书&lt;/p&gt;
&lt;p&gt;在配置好上述设置后，就可通过&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;.acme.sh/acme.sh&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; --issue&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; --dns&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; dns_dp&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; -d&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; godruoyi.com&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; -d&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;.godruoyi.com&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;.acme.sh/acme.sh --issue --dns dns_dp -d godruoyi.com -d *.godruoyi.com&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;来生成证书，注意这里第一个域名为顶级域名，后面个为泛域名。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这种方式将自动为你的域名添加一条 &lt;code&gt;txt&lt;/code&gt; 解析，验证成功后，这条解析记录会被删除，所以对你来说是无感的，就是要等 &lt;code&gt;120秒&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;证书生成成功后，默认保存在 &lt;code&gt;.acme.sh/你的顶级域名&lt;/code&gt; 中。&lt;/p&gt;
&lt;p&gt;PS：如果你卡在  Getting domain auth token for each domain 这一步不动了，别担心，升级下你的 acme.sh 就好了。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;.acme.sh/acme.sh&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; --upgrade&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;.acme.sh/acme.sh --upgrade&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&quot;配置-nginx&quot;&gt;配置 Nginx&lt;/h2&gt;
&lt;p&gt;下面我们来为 &lt;code&gt;Nginx&lt;/code&gt; 配置 &lt;code&gt;SSL&lt;/code&gt; 证书支持。&lt;/p&gt;
&lt;p&gt;1、移动下列证书到 &lt;code&gt;/etc/nginx/ssl&lt;/code&gt; 文件夹，若无该文件夹，自行创建。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;cp&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; ~/.acme.sh/godruoyi.com/fullchain.cer&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; /etc/nginx/ssl/fullchain.cer&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;cp&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; ~/.acme.sh/godruoyi.com/godruoyi.com.key&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; /etc/nginx/ssl/godruoyi.key&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;cp ~/.acme.sh/godruoyi.com/fullchain.cer /etc/nginx/ssl/fullchain.cer
cp ~/.acme.sh/godruoyi.com/godruoyi.com.key /etc/nginx/ssl/godruoyi.key&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;2、新建 &lt;code&gt;ssl-params.conf&lt;/code&gt; 并把它放到 Nginx 的 &lt;code&gt;snippets&lt;/code&gt; 目录中。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;下面的这些配置来自 &lt;a href=&quot;https://godruoyi.com/posts/best-nginx-configuration-for-improved-security&quot;&gt;提高安全性的最佳 Nginx 配置&lt;/a&gt;，建议参考。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;vim&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;vim&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;#&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; /etc/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;nginx&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;/snippets/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;ssl&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;-params.conf&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;server_tokens   off;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;ssl_session_cache        shared:SSL:10m;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;ssl_session_timeout      60m;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;ssl_session_tickets      on;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;ssl_stapling             on;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;ssl_stapling_verify      on;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;resolver                 &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;8&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;8&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;4&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;4&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 8&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;8&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;8&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;8&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;  valid&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;300s;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;resolver_timeout         10s;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;ssl_prefer_server_ciphers on;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;#&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; 证书路径 绝对地址&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;ssl_certificate          &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;/etc/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;nginx&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;/ssl/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;fullchain.cer;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;ssl_certificate_key      &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;/etc/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;nginx&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;/ssl/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;godruoyi.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;key&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;ssl_protocols            TLSv1 TLSv1.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; TLSv1.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;ssl_ciphers &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;EECDH+AESGCM:EDH+AESGCM:ECDHE-RSA-AES128-GCM-SHA256:AES256+EECDH:DHE-RSA-AES128-GCM-SHA256:AES256+EDH:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;add_header Strict-Transport-Security &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;max-age=31536000;includeSubDomains;preload&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;add_header  X-Frame-Options  deny;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;add_header  X-Content-&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Type&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;-Options  nosniff;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;add_header x-xss-protection &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;1; mode=block&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;add_header Content-Security-Policy &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;default-src &apos;self&apos;; script-src &apos;self&apos; &apos;unsafe-inline&apos; &apos;unsafe-eval&apos; blob: https:; connect-src &apos;self&apos; https:; img-src &apos;self&apos; data: https: blob:; style-src &apos;unsafe-inline&apos; https:; font-src https:&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;# /etc/nginx/snippets/ssl-params.conf

server_tokens   off;

ssl_session_cache        shared:SSL:10m;
ssl_session_timeout      60m;

ssl_session_tickets      on;

ssl_stapling             on;
ssl_stapling_verify      on;

resolver                 8.8.4.4 8.8.8.8  valid=300s;
resolver_timeout         10s;
ssl_prefer_server_ciphers on;

# 证书路径 绝对地址
ssl_certificate          /etc/nginx/ssl/fullchain.cer;
ssl_certificate_key      /etc/nginx/ssl/godruoyi.key;

ssl_protocols            TLSv1 TLSv1.1 TLSv1.2;

ssl_ciphers &amp;#x22;EECDH+AESGCM:EDH+AESGCM:ECDHE-RSA-AES128-GCM-SHA256:AES256+EECDH:DHE-RSA-AES128-GCM-SHA256:AES256+EDH:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4&amp;#x22;;

add_header Strict-Transport-Security &amp;#x22;max-age=31536000;includeSubDomains;preload&amp;#x22;;
add_header  X-Frame-Options  deny;
add_header  X-Content-Type-Options  nosniff;
add_header x-xss-protection &amp;#x22;1; mode=block&amp;#x22;;
add_header Content-Security-Policy &amp;#x22;default-src &amp;#x27;self&amp;#x27;; script-src &amp;#x27;self&amp;#x27; &amp;#x27;unsafe-inline&amp;#x27; &amp;#x27;unsafe-eval&amp;#x27; blob: https:; connect-src &amp;#x27;self&amp;#x27; https:; img-src &amp;#x27;self&amp;#x27; data: https: blob:; style-src &amp;#x27;unsafe-inline&amp;#x27; https:; font-src https:&amp;#x22;;&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;3、接下来在 Nginx 主配置文件中开启 &lt;code&gt;SSL&lt;/code&gt; 支持&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# /etc/nginx/nginx.conf

http {
    ....
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;完整的 &lt;code&gt;Nginx&lt;/code&gt; 配置文件请参考 &lt;a href=&quot;https://github.com/godruoyi/gblog/blob/master/resources/nginx/&quot;&gt;我的 Nginx 配置&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;4、配置虚拟主机&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;vim&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;vim&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;#&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; /etc/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;nginx&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;/sites-available/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;godruoyi.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;com&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;server {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    listen &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;80&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; default_server;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    listen [::]:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;80&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; default_server;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    server_name godruoyi.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;com&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; www.godruoyi.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;com&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 301&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; https://$server_name$request_uri;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;server {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    #&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; 注意我们设置该站点为默认站点，并移除了 nginx 默认的 default 配置&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    listen &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;443&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; ssl&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; http2 fastopen&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; reuseport default_server;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    listen [::]:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;443&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; ssl&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; http2 fastopen&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; reuseport default_server;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    server_name www.godruoyi.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;com&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; godruoyi.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;com&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    #&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; 引入 SSL 及 PHP 配置&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;    include&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; snippets/fastcgi-php.conf;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;    include&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; snippets/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;ssl&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;-params.conf;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    root &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;/home/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;godruoyi&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;/websites/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;godruoyi.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;com&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;/public;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    access_log &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;/home/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;godruoyi&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;/websites/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;godruoyi.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;com&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;/storage/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;logs/nginx-access.log;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    error_log  &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;/home/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;godruoyi&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;/websites/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;godruoyi.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;com&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;/storage/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;logs/nginx-error.log error;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    index index.php;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    #&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; 当访问域名是不  godruoyi.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;com&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; 强制跳转到 https://godruoyi.com&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($host &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;!=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;godruoyi.com&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        rewrite ^&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;/(.*)$ https://godruoyi.com/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; permanent;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;# /etc/nginx/sites-available/godruoyi.com

server {
    listen 80 default_server;
    listen [::]:80 default_server;

    server_name godruoyi.com www.godruoyi.com;
    return 301 https://$server_name$request_uri;
}

server {
    # 注意我们设置该站点为默认站点，并移除了 nginx 默认的 default 配置
    listen 443 ssl http2 fastopen=3 reuseport default_server;
    listen [::]:443 ssl http2 fastopen=3 reuseport default_server;

    server_name www.godruoyi.com godruoyi.com;

    # 引入 SSL 及 PHP 配置
    include snippets/fastcgi-php.conf;
    include snippets/ssl-params.conf;

    root /home/godruoyi/websites/godruoyi.com/public;

    access_log /home/godruoyi/websites/godruoyi.com/storage/logs/nginx-access.log;
    error_log  /home/godruoyi/websites/godruoyi.com/storage/logs/nginx-error.log error;

    index index.php;

    # 当访问域名是不  godruoyi.com 强制跳转到 https://godruoyi.com
    if ($host != &amp;#x27;godruoyi.com&amp;#x27; ) {
        rewrite ^/(.*)$ https://godruoyi.com/$1 permanent;
    }
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;再来看一个 &lt;code&gt;admin.godruoyi.com&lt;/code&gt; 的配置&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;vim&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;vim&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;#&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; /etc/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;nginx&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;/sites-available/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;admin.godruoyi.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;com&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;server {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    listen &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;80&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    listen [::]:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;80&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    server_name admin.godruoyi.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;com&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 301&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; https://$server_name$request_uri;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;server {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    #&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; 如果多个域名配置在同一主机，这里只需要监听到 &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;433&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; 就可以了，&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    #&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; 不需要再添加 &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;ssl&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; http2 fastopen&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; reuseport default_server 之类的了&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    listen &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;443&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;      &lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    listen [::]:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;443&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    root &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;/home/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;godruoyi&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;/websites/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;admin.godruoyi.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;com&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;/public-admin;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    access_log &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;/home/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;godruoyi&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;/websites/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;admin.godruoyi.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;com&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;/storage/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;logs/nginx-access.log;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    error_log  &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;/home/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;godruoyi&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;/websites/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;admin.godruoyi.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;com&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;/storage/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;logs/nginx-error.log error;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    server_name admin.godruoyi.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;com&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    index index.php;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    client_max_body_size 20M;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;    include&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; snippets/fastcgi-php.conf;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;    include&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; snippets/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;ssl&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;-params.conf;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($host &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;!=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;admin.godruoyi.com&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        rewrite ^&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;/(.*)$ https://admin.godruoyi.com/&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; permanent;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;# /etc/nginx/sites-available/admin.godruoyi.com

server {
    listen 80;
    listen [::]:80;

    server_name admin.godruoyi.com;
    return 301 https://$server_name$request_uri;
}
server {
    # 如果多个域名配置在同一主机，这里只需要监听到 433 就可以了，
    # 不需要再添加 ssl http2 fastopen=3 reuseport default_server 之类的了
    listen 443;      
    listen [::]:443;

    root /home/godruoyi/websites/admin.godruoyi.com/public-admin;

    access_log /home/godruoyi/websites/admin.godruoyi.com/storage/logs/nginx-access.log;
    error_log  /home/godruoyi/websites/admin.godruoyi.com/storage/logs/nginx-error.log error;

    server_name admin.godruoyi.com;

    index index.php;

    client_max_body_size 20M;

    include snippets/fastcgi-php.conf;
    include snippets/ssl-params.conf;

    if ($host != &amp;#x27;admin.godruoyi.com&amp;#x27; ) {
        rewrite ^/(.*)$ https://admin.godruoyi.com/$1 permanent;
    }
}
&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;4、虚拟主机配置完成，接下来为其配置软连接测试成功后就可以重启 Nginx 啦。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;sudo&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; ln&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; -s&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; /etc/nginx/sites-available/godruoyi.com&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; /etc/nginx/sites-enabled/&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;sudo&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; ln&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; -s&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; /etc/nginx/sites-available/admin.godruoyi.com&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; /etc/nginx/sites-enabled/&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;# 测试配置是否成功&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;sudo&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; nginx&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; -t&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;sudo&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; service&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; nginx&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; restart&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;sudo ln -s /etc/nginx/sites-available/godruoyi.com /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/admin.godruoyi.com /etc/nginx/sites-enabled/

# 测试配置是否成功
sudo nginx -t

sudo service nginx restart&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;以上所有配置你都可以在 &lt;a href=&quot;https://github.com/godruoyi/gblog/blob/master/resources/nginx/&quot;&gt;这里&lt;/a&gt; 找到。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;参考链接&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://cloud.tencent.com/developer/article/1064471&quot;&gt;腾讯云DNSPod API申请Let’s Encrypt泛域名证书&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>利用 Intervention Image 合成图片</title><link>https://godruoyi.com/posts/intervention-image-was-used-to-synthesize-images/</link><guid isPermaLink="true">https://godruoyi.com/posts/intervention-image-was-used-to-synthesize-images/</guid><description>在我们实际应用中，经常遇到图片合成的需求（比如合成一整用户信息 + 二维码图片，便于传播分享等），传统的做法是采用 `GD` 或 `Imagick ` 库内置函数来完成。但估计你和我一样，对这些函数都不会很上心。  &gt; 下面...</description><pubDate>Wed, 06 Jun 2018 02:02:11 GMT</pubDate><content:encoded>&lt;p&gt;在我们实际应用中，经常遇到图片合成的需求（比如合成一整用户信息 + 二维码图片，便于传播分享等），传统的做法是采用 &lt;code&gt;GD&lt;/code&gt; 或 &lt;code&gt;Imagick &lt;/code&gt; 库内置函数来完成。但估计你和我一样，对这些函数都不会很上心。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;下面将采用目前功能全面的 &lt;code&gt;intervention/image&lt;/code&gt; 库来完成这一操作。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;使用&quot;&gt;使用&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;http://image.intervention.io/getting_started/installation&quot;&gt;intervention&lt;/a&gt; 的安装这里不再论述，请移至 &lt;a href=&quot;http://image.intervention.io/getting_started/installation&quot;&gt;官网&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&quot;初始化&quot;&gt;初始化&lt;/h2&gt;
&lt;p&gt;你可以直接在原始 &lt;code&gt;PHP&lt;/code&gt; 通过下述方式获取 &lt;code&gt;ImageManager&lt;/code&gt; 管理对象：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;require&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;vendor/autoload.php&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Intervention\Image\ImageManager&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$manager &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; ImageManager&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;array&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;driver&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;imagick&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;));&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt; &amp;#x27;imagick&amp;#x27;));&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;如果是在 &lt;code&gt;Laravel&lt;/code&gt; 中，可直接通过 &lt;code&gt;app(&apos;image&apos;)&lt;/code&gt; 或使用 &lt;code&gt;Intervention\Image\Facades\Image&lt;/code&gt; 门面类。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Intervention\Image\Facades\Image&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;//或&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$manager &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; app&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;image&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;
use Intervention\Image\Facades\Image;

//或
$manager = app(&amp;#x27;image&amp;#x27;);&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&quot;合成&quot;&gt;合成&lt;/h2&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// $image = $manager-&gt;make($bg);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// $image = Image::make($bg);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$image &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; app&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;image&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;make&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($bg);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// 二维码图片&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$qrcodeImage &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; app&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;image&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;make&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($qrcodeurl)&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;resize&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;200&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;200&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// 重置 头像 大小&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$avatarImage  &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; app&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;image&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;make&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($httpClient&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;request&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;GET&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, $avatarurl)&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;getBody&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;())&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;resize&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;200&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;200&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$image&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;text&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;Nickname&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;376&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;320&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($font) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    $font&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;file&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;public_path&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;fonts/SimHei.ttf&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    $font&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;size&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;40&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    $font&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;color&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;#000000&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    $font&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;align&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;center&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    $font&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;valign&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;top&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$image&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;insert&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($qrcodeImage, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;bottom&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;360&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$image&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;insert&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($avatarImage, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;top&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;105&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;make($bg);
// $image = Image::make($bg);

$image = app(&amp;#x27;image&amp;#x27;)-&gt;make($bg);

// 二维码图片
$qrcodeImage = app(&amp;#x27;image&amp;#x27;)-&gt;make($qrcodeurl)-&gt;resize(200, 200);
// 重置 头像 大小
$avatarImage  = app(&amp;#x27;image&amp;#x27;)-&gt;make($httpClient-&gt;request(&amp;#x27;GET&amp;#x27;, $avatarurl)-&gt;getBody())-&gt;resize(200, 200);

$image-&gt;text(&amp;#x27;Nickname&amp;#x27;, 376, 320, function ($font) {
    $font-&gt;file(public_path(&amp;#x27;fonts/SimHei.ttf&amp;#x27;));
    $font-&gt;size(40);
    $font-&gt;color(&amp;#x27;#000000&amp;#x27;);
    $font-&gt;align(&amp;#x27;center&amp;#x27;);
    $font-&gt;valign(&amp;#x27;top&amp;#x27;);
});

$image-&gt;insert($qrcodeImage, &amp;#x27;bottom&amp;#x27;, 0, 360);
$image-&gt;insert($avatarImage, &amp;#x27;top&amp;#x27;, 0, 105);&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;上面的代码逻辑为：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;初始化背景图片，后续的文字图片合成都基于该背景模板&lt;/li&gt;
&lt;li&gt;插入 &lt;code&gt;Nickname&lt;/code&gt; 文本到背景图，文本位置相对于背景图 &lt;code&gt;X轴&lt;/code&gt; 376 偏移量、&lt;code&gt;Y轴&lt;/code&gt; 320 偏移量。并设置字体文件位置、字体大小、字体颜色、字体水平对齐方式（left，center，right 默认 left）、字体垂直对齐方式（top，bottom，middle 默认 bottom）等。&lt;/li&gt;
&lt;li&gt;插入一张二维码图片，并设置图片的插入位置为 &lt;code&gt;bottom&lt;/code&gt; （下方，&lt;a href=&quot;http://image.intervention.io/api/insert&quot;&gt;其他可用位置&lt;/a&gt;），图片位置相对应背景图 &lt;code&gt;X轴&lt;/code&gt; 0 偏移量、&lt;code&gt;Y轴&lt;/code&gt; 105 偏移量。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;需要注意的是，在设置字体大小时，如果不指定字体文件位置，则设置的字体大小恒为默认值 &lt;code&gt;12&lt;/code&gt;。并且中文可能会存在乱码情况，建议采用 &lt;code&gt;Sim&lt;/code&gt; 字体。&lt;a href=&quot;https://github.com/StellarCN/scp_zh/tree/master/fonts&quot;&gt;下载链接&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title>关于这个站点</title><link>https://godruoyi.com/posts/the-about-gblog/</link><guid isPermaLink="true">https://godruoyi.com/posts/the-about-gblog/</guid><description>Blog 是基于 Laravel5.5和 vue2 来搭建的；前台是 vue 的单页应用，后台采用的是传统的模板渲染</description><pubDate>Mon, 07 May 2018 03:07:05 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;在看完 Laravel China 社区的 &lt;a href=&quot;https://laravel-china.org/courses&quot;&gt;两篇教程&lt;/a&gt; 后，我决定基于该教程来写一个 Blog（刚好手头撸了腾讯云的羊毛）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;说明&quot;&gt;说明&lt;/h2&gt;
&lt;p&gt;&lt;del&gt;博客是基于 &lt;code&gt;laravel5.5&lt;/code&gt; 和 &lt;code&gt;vue2.*&lt;/code&gt; 来搭建的；前台是 &lt;code&gt;vue&lt;/code&gt; 的单页应用&lt;/del&gt;，后台采用的是传统的模板渲染（简单快捷）。由于自己样式表写得一塌涂地，前、后所有样式均来自于 &lt;a href=&quot;https://laravel-china.org/courses&quot;&gt;laravel-news&lt;/a&gt; 及 &lt;a href=&quot;https://github.com/the-control-group/voyager&quot;&gt;Voyager&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&quot;更新&quot;&gt;更新&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;2021-08 你目前看到的博客运行在一个 1 核 2 G 的云服务器上；采用 Gin 框架快速搭建，静态资源全部放在 CDN 上&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;前台&quot;&gt;前台&lt;/h3&gt;
&lt;p&gt;前台的单页应用架构主要参考「抄」 &lt;a href=&quot;https://github.com/overtrue&quot;&gt;@overtrue&lt;/a&gt; 和 &lt;a href=&quot;https://pigjian.com/&quot;&gt;@cjjian&lt;/a&gt; 一起写的项目 &lt;a href=&quot;https://github.com/yikeio/yike&quot;&gt;一刻&lt;/a&gt;, 虽然该项目已经停止维护了，但大神的思路还是很值得去学习的。关于 &lt;code&gt;yike&lt;/code&gt; 的更多信息可以去看看  &lt;a href=&quot;https://pigjian.com/&quot;&gt;@cjjian&lt;/a&gt;  的介绍贴 &lt;a href=&quot;https://laravel-china.org/topics/5863/laravel-55-release-pj-blog-upgrade-yike-project-presentation&quot;&gt;PJ Blog 升级 &amp;#x26; Yike 项目介绍&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;贴两张博客截图&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;首页&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202103/25/30k6TLH03yerhMMb0231cXQpkIOtCC0kq1ZpcOZU.png&quot; alt=&quot;二愣的闲谈杂鱼&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;文章详情页&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202103/25/iWMRDUxC0GRFnLEPDH0BHQs8LVXDZZVhVQekH0E3.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;后台&quot;&gt;后台&lt;/h3&gt;
&lt;p&gt;后台完全是基于  &lt;a href=&quot;https://github.com/the-control-group/voyager&quot;&gt;Voyager&lt;/a&gt; 快速搭建的。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202103/25/6y9lzk3huJEzHzzPFaXf7HdSbmSKjX4CiQSfH7E9.png&quot; alt=&quot;二愣的闲谈杂鱼&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/202103/25/iKMijnrjP9Gg7OQlvQpJ7IgoOu9EcArRVA34z6Fq.png&quot; alt=&quot;二愣的闲谈杂鱼&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;最后附上 Blog 地址 &lt;a href=&quot;http://godruoyi.com&quot;&gt;godruoyi.com&lt;/a&gt;。:smile: :smile:&lt;/p&gt;</content:encoded></item><item><title>调整内心，写点东西</title><link>https://godruoyi.com/posts/adjust-your-heart-and-write-something/</link><guid isPermaLink="true">https://godruoyi.com/posts/adjust-your-heart-and-write-something/</guid><description>这个五一，我的老朋友结婚啦。那年都军训完才到学校报告并且还睡我上面的小伙，没想到是我们寝室最早结婚的。当初脑子进水选...</description><pubDate>Thu, 03 May 2018 16:27:06 GMT</pubDate><content:encoded>&lt;p&gt;这个五一，我的老朋友结婚啦。那年都军训完才到学校报告并且还睡我上面的小伙，没想到是我们寝室最早结婚的。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/201805/03/1_1525364306_7ENBwIfTo3.jpg&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;p&gt;当初脑子进水选的计算机专业，现在想起来其实并没有那么差。但要不是你的蛊惑，我也不会踏上这条不归路。&lt;/p&gt;
&lt;p&gt;大学前 &lt;code&gt;3&lt;/code&gt; 年都混过了，最后一年该养猪的养猪，该深造的深造，留下两个来自贵州小山村的我们，你在学习我在撸。终于看不下去的你发了一套马士兵老师的 &lt;code&gt;java&lt;/code&gt; 教学视频，从此开启不一样的人生。&lt;/p&gt;
&lt;p&gt;我是幸运的，一个多月的自学居然也能被录取实习（后面永哥说其实你很菜，只是在你身上能看到 &lt;code&gt;少平&lt;/code&gt; 的影子）。更幸运的是，我在公司里找到了她！！&lt;/p&gt;
&lt;p&gt;一晃你结婚了，再过几年儿子都要喊我叫干爹啦。当初一群大傻们信誓旦旦的说 &lt;code&gt;25&lt;/code&gt; 才结婚。没想到你失言了，更没想到有的大傻可能现在还是处男。&lt;/p&gt;
&lt;h3 id=&quot;朋友&quot;&gt;朋友&lt;/h3&gt;
&lt;p&gt;记得一年多前厦门的聚会感觉都还是 &lt;code&gt;老铁&lt;/code&gt;，一年后涪陵的聚会却感觉多了些什么，那可能是时间吧！刚分开的我们，总是有说不完的梗。而当同样的梗开始说上两三篇后，连超哥都不忍心再让他喝酒了！&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/201805/04/1_1525442740_tRtIRUJPwG.jpg&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;p&gt;目前的微信QQ群，打的火热的总是那么一两个，其实你那有那么忙，每条消息都看，只是不回。等再过一两年，群不再是交流的场所，而是为宝宝点赞、为微商产品打广告的乐园。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;前两天高中的闺蜜难得的找我，但并不是叙旧，只是新开的微商需要流量！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;家人&quot;&gt;家人&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;独在异乡为异客
每逢佳节倍思亲
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;更何况今天还是我的生日。一个人在外地，熟悉的老朋友一个不在，中学时代一起玩坦克大战的伙伴也越来越远。前端时间难得有个亲人过来看我，也被我 &lt;code&gt;冷落&lt;/code&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;其实当时一两句话就能说清楚的事，硬生生被自己给说糊了。谈话真是一门艺术啊。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;愿你我的生活充满阳光，吧！
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/201805/04/1_1525443085_GTttTijZfT.jpg&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;</content:encoded></item><item><title>提高安全性的最佳 Nginx 配置</title><link>https://godruoyi.com/posts/best-nginx-configuration-for-improved-security/</link><guid isPermaLink="true">https://godruoyi.com/posts/best-nginx-configuration-for-improved-security/</guid><description>由于安全问题一直是重中之重，这里整理下 `nginx` 的安全配置。文章大部分参考了 Best nginx configuration for improved security(and performance)</description><pubDate>Sun, 08 Apr 2018 10:16:19 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;由于安全问题一直是重中之重，这里整理下 &lt;code&gt;nginx&lt;/code&gt; 的安全配置。文章大部分参考了 &lt;a href=&quot;https://gist.github.com/plentz/6737338&quot;&gt;Best nginx configuration for improved security(and performance).&lt;/a&gt; 及 &lt;a href=&quot;http://imququ.com&quot;&gt;Jerry Qu&lt;/a&gt;，更多关于 &lt;code&gt;HTTP&lt;/code&gt; 安全及性能可前往 &lt;a href=&quot;http://imququ.com&quot;&gt;Jerry Qu&lt;/a&gt; 查看。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;server_tokens&quot;&gt;server_tokens&lt;/h3&gt;
&lt;p&gt;该响应头用于禁止 &lt;code&gt;nginx&lt;/code&gt; 在响应中报文中包含版本信息。因为具体的版本可能会存在未知 &lt;code&gt;bug&lt;/code&gt;。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;shell&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;shell&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;server_tokens&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; off&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;server_tokens off;&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h3 id=&quot;x-frame-options&quot;&gt;X-Frame-Options&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;该响应头用于是否允许浏览器加载 &lt;code&gt;frame&lt;/code&gt;、 &lt;code&gt;iframe&lt;/code&gt;、 &lt;code&gt;object&lt;/code&gt; 等属性。可以使用该功能来避免 &lt;a href=&quot;https://zh.wikipedia.org/wiki/%E7%82%B9%E5%87%BB%E5%8A%AB%E6%8C%81&quot;&gt;点击劫持&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;shell&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;shell&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;add_header&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; X-Frame-Options&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; SAMEORIGIN&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;add_header X-Frame-Options SAMEORIGIN;&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;该指令用三个可用的配置&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;X-Frame-Options: DENY&lt;/li&gt;
&lt;li&gt;X-Frame-Options: SAMEORIGIN&lt;/li&gt;
&lt;li&gt;X-Frame-Options: ALLOW-FROM &lt;a href=&quot;https://example.com/&quot;&gt;https://example.com/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;当设置为 &lt;code&gt;DENY&lt;/code&gt; 时，站点禁止任何页面被嵌入。&lt;/p&gt;
&lt;p&gt;当设置为 &lt;code&gt;SAMEORIGIN&lt;/code&gt; 时，只允许加载同源的 &lt;code&gt;fram/iframe/object&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;当设置为 &lt;code&gt;ALLOW-FROM&lt;/code&gt; 时，只允许加载指定的源。&lt;/p&gt;
&lt;h3 id=&quot;x-content-type-options&quot;&gt;X-Content-Type-Options&lt;/h3&gt;
&lt;p&gt;在我们通常的请求响应中，浏览器会根据 &lt;code&gt;HTTP&lt;/code&gt; 响应的 &lt;code&gt;Content-Type&lt;/code&gt; 来分辨响应的类型。如 &lt;code&gt;text/html&lt;/code&gt; 代表 &lt;code&gt;html&lt;/code&gt; 文档。 但当响应类型未指定或错误指定时，浏览会尝试启用 &lt;code&gt;MIME-sniffing&lt;/code&gt; 来猜测资源的响应类型。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;如通过精心制作一个图像文件，并在其中嵌入可以被浏览器所展示和执行的 &lt;code&gt;HTML&lt;/code&gt; 和 &lt;code&gt;JavaScript&lt;/code&gt; 代码。由于未关闭资源的类型猜测，浏览器将直接执行嵌入的 &lt;code&gt;JavaScript&lt;/code&gt; 代码，而不是显示图片。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;shell&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;shell&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;add_header&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; X-Content-Type-Options&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; nosniff&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;add_header X-Content-Type-Options nosniff;&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;blockquote&gt;
&lt;p&gt;用来指定浏览器对未指定或错误指定 &lt;code&gt;Content-Type&lt;/code&gt; 资源真正类型的猜测行为，&lt;code&gt;nosniff&lt;/code&gt; 表示不允许任何猜测。（Jerry Qu）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这个响应头的值只能是 &lt;code&gt;nosniff&lt;/code&gt;，可用于 &lt;code&gt;IE8+&lt;/code&gt; 和 &lt;code&gt;Chrome&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id=&quot;x-xss-protection&quot;&gt;X-XSS-Protection&lt;/h3&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;shell&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;shell&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;add_header&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; X-XSS-Protection&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &quot;1; mode=block&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;add_header X-XSS-Protection &amp;#x22;1; mode=block&amp;#x22;;&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;该响应头是用于防范及过滤 &lt;code&gt;XSS&lt;/code&gt; 的。可用的几个指令如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;X-XSS-Protection: 0&lt;/li&gt;
&lt;li&gt;X-XSS-Protection: 1&lt;/li&gt;
&lt;li&gt;X-XSS-Protection: 1; mode=block&lt;/li&gt;
&lt;li&gt;X-XSS-Protection: 1; report=&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;说明&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;0&lt;/code&gt;，禁用 &lt;code&gt;XSS&lt;/code&gt; 过滤&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1&lt;/code&gt;，开启 &lt;code&gt;XSS&lt;/code&gt; 过滤&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1; mode=block&lt;/code&gt;，开启 &lt;code&gt;XSS&lt;/code&gt; 过滤，并且若检查到 &lt;code&gt;XSS&lt;/code&gt; 攻击，停止渲染页面。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;X-XSS-Protection: 1; report=&amp;#x3C;reporting-uri&gt;&lt;/code&gt;，开启 &lt;code&gt;XSS&lt;/code&gt; 过滤，并且若检查到 &lt;code&gt;XSS&lt;/code&gt; 攻击，将使用指导的 url 来发送报告。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;content-security-policy&quot;&gt;Content-Security-Policy&lt;/h3&gt;
&lt;p&gt;该响应头主要用于规定页面可以加载那些资源（&lt;code&gt;css/js/img&lt;/code&gt; 等）。看一个简单的配置&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;shell&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;shell&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;# 定义所有资源文件的默认加载规则为self，表示允许&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;# 相同来源的内容（相同的协议、域名和端口）&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;add_header&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; Content-Security-Policy:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; default-src&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;self&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;# 定义所有资源文件的默认加载规则为self，表示允许
# 相同来源的内容（相同的协议、域名和端口）
add_header Content-Security-Policy: default-src &amp;#x27;self&amp;#x27;;&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;更多 &lt;code&gt;Content-Security-Policy&lt;/code&gt; 的指令及规则及介绍可参考 &lt;a href=&quot;https://imququ.com&quot;&gt;Jerry Qu&lt;/a&gt; 的 &lt;a href=&quot;https://imququ.com/post/content-security-policy-reference.html&quot;&gt;Content Security Policy 介绍&lt;/a&gt;。&lt;/p&gt;
&lt;h3 id=&quot;strict-transport-security&quot;&gt;Strict-Transport-Security&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;http://tools.ietf.org/html/rfc6797&quot;&gt;Strict-Transport-Security&lt;/a&gt;，简称 &lt;code&gt;HSTS&lt;/code&gt;。该响应头用于标识浏览器用 &lt;code&gt;HTTPS&lt;/code&gt; 替代 &lt;code&gt;HTTP&lt;/code&gt; 的方式去访问目标站点。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我们知道 &lt;code&gt;HTTPS&lt;/code&gt; 相对于 &lt;code&gt;HTTP&lt;/code&gt; 有更好的安全性，而很多 &lt;code&gt;HTTPS&lt;/code&gt; 网站，也可以通过 &lt;code&gt;HTTP&lt;/code&gt; 来访问。开发人员的失误或者用户主动输入地址，都有可能导致用户以 &lt;code&gt;HTTP&lt;/code&gt; 访问网站，降低了安全性。一般，我们会通过 &lt;code&gt;Web Server&lt;/code&gt; 发送 &lt;code&gt;301/302&lt;/code&gt; 重定向来解决这个问题。 （Jerry Qu）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我们可以使用下面方式启用 &lt;code&gt;HSTH&lt;/code&gt;。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;shell&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;shell&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;add_header&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; strict-transport-security:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; max-age=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;16070400&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;includeSubDomains&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;add_header strict-transport-security: max-age=16070400; includeSubDomains;&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;当用户第一次访问后，将返回一个包含了 &lt;code&gt;strict-transport-security&lt;/code&gt; 响应头的字段。他将告诉浏览器，在接下来的 &lt;code&gt;16070400&lt;/code&gt; 秒内，当前网站的所有请求都强制使用 &lt;code&gt;HTTPS&lt;/code&gt; 的方式访问。即使用户手动输入 &lt;code&gt;http://&lt;/code&gt;，浏览器也会强制使用 &lt;code&gt;HTTPS&lt;/code&gt; 方式访问。&lt;/p&gt;
&lt;p&gt;参数 &lt;code&gt;includeSubDomains&lt;/code&gt; 是可选的，当指定了该参数，所有子域名将采用同样的 &lt;code&gt;HSTS&lt;/code&gt; 规则。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;可以看到 &lt;code&gt;HSTS&lt;/code&gt; 可以很好的解决 &lt;code&gt;HTTPS&lt;/code&gt; 降级攻击，但是对于 &lt;code&gt;HSTS&lt;/code&gt; 生效前的首次 &lt;code&gt;HTTP&lt;/code&gt; 请求，依然无法避免被劫持。浏览器厂商们为了解决这个问题，提出了 &lt;code&gt;HSTS Preload List&lt;/code&gt; 方案：内置一份可以定期更新的列表，对于列表中的域名，即使用户之前没有访问过，也会使用 &lt;code&gt;HTTPS&lt;/code&gt; 协议。 （Jerry Qu）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;如果你想把自己的域名加入这个列表，可通过 &lt;a href=&quot;https://hstspreload.org/&quot;&gt;hstspreloa.org&lt;/a&gt;  查看是否满足申请条件。更多关于 &lt;code&gt;HSTS&lt;/code&gt; 的配置，可查看 &lt;a href=&quot;https://imququ.com/post/sth-about-switch-to-https.html&quot;&gt;关于启用 HTTPS 的一些经验分享&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;目前 &lt;a href=&quot;https://godruoyi.com&quot;&gt;godruoyi.com&lt;/a&gt; 已成功加入 &lt;code&gt;Preload List&lt;/code&gt;。&lt;/p&gt;</content:encoded></item><item><title>Mysql游标入门</title><link>https://godruoyi.com/posts/get-started-with-mysql-cursor/</link><guid isPermaLink="true">https://godruoyi.com/posts/get-started-with-mysql-cursor/</guid><description>&gt; MySQL检索操作返回一组称为结果集的行。这组返回的行都是与SQL语句相匹配的行（零行或多行）。使用简单的SELECT语句，例如，没有办法得到第一行、下一行或前10行，也不存在每次一行地处理所有行的简单方法（相对于...</description><pubDate>Wed, 04 Apr 2018 07:43:48 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;MySQL检索操作返回一组称为结果集的行。这组返回的行都是与SQL语句相匹配的行（零行或多行）。使用简单的SELECT语句，例如，没有办法得到第一行、下一行或前10行，也不存在每次一行地处理所有行的简单方法（相对于成批地处理它们）。&lt;/p&gt;
&lt;p&gt;有时，需要在检索出来的行中前进或后退一行或多行。这就是使用游标的原因。游标（cursor）是一个存储在MySQL服务器上的数据库查询，它不是一条SELECT语句，而是被该语句检索出来的结果集。在存储了游标之后，应用程序可以根据需要滚动或浏览其中的数据。&lt;/p&gt;
&lt;p&gt;游标主要用于交互式应用，其中用户需要滚动屏幕上的数据，并对数据进行浏览或做出更改。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;创建游标&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;创建一个简单的游标&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;create procedure simplecursor()
begin
	declare youbiaoName cursor
	for
	select name from user;
	
	open youbiaoName;  -- 打开游标
	-- //some code
	close youbiaoName; -- 关闭游标,释放游标使用的所有内部内存和资源
end;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;定义了一个游标，使用declare 游标名 cursor for 来定义游标，该处查询用户表里的用户名， 存储过程处理完成后，游标就消失（因为它局限于存储过程），并没有任何返回和输出。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;create procedure simplecursor2()
begin
	declare tmp int; -- 定义局部变量
	declare youbiao2 cursor for select name from user; -- 定义游标

	open youbiao2;
		fetch youbiao2 into tmp;
	close youbiao2;
end;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;该过程定义一个局部变量和游标， 该游标返回了用户表得所用用户名组成的集合，即如下所示&lt;/p&gt;


























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;name&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;admin&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;admin1&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;admin2&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;admin3&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;然后在游标开启-关闭内， 用fetch遍历每一行， 把得到的数据(即name的值) into  给局部变量tmp。此处只完成了这个流程， 什么都没做。&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;注：&lt;/strong&gt; 该fetch并没有结束标志， 调用时会一直遍历下去， 当遍历完最后一行再继续遍历时，会出现错误，[Err] 1366 - Incorrect integer value:&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;改进如下&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;create procedure simplecursor3()
begin
	declare done boolean default 0; -- 定义一个循环标记默认值为false
	declare tmp int; -- 定义局部变量
	declare youbiao3 cursor for select name from user; -- 定义游标
	-- 当出现02000错误时把局部变量的值设为true
	declare continue handler for sqlstate &apos;02000&apos; set done 1; 
	
	open youbiao3;

	REPEAT
		fetch youbiao3 into tmp;
	until done end REPEAT;	-- 当done为true时结束repeat，
	
	close youbiao3;
end;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;与前一个例子一样，这个例子使用FETCH检索当前name到声明的名为tmp的变量中。但与前一个例子不一样的是，这个例子中的FETCH是在REPEAT内，因此它反复执行直到done为真（由UNTIL done END REPEAT;规定）。为使它起作用，用一个DEFAULT 0（假，不结束）定义变量done。那么，done怎样才能在结束时被设置为真呢？答案是用以下语句：&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;declare continue handler for sqlstate ‘02000’ set done 1;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这条语句定义了一个CONTINUE HANDLER，它是在条件出现时被执行的代码。这里，它指出当SQLSTATE ‘02000’出现时，SET done=1。SQLSTATE ‘02000’是一个未找到条件，当REPEAT由于没有更多的行供循环而不能继续时，出现这个条件。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;复杂一点的游标使用：创建一个新的表， 把用户和该用户的所有订单金额存入该表。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;create procedure myyoubiao ()
BEGIN
	declare done boolean default 0; --循环标记
	declare tmp int; -- 临时存储变量
	declare t DECIMAL(8,2); -- 同上
	
	declare myyoubiao4 CURSOR for select id from user;
	declare continue handler for sqlstate &apos;02000&apos; set done = 1;

	create table if not exists mytable -- 表不存在是创建， 存在时跳过
	(uId int, total decimal(8,2));

	open myyoubiao4;
	
	REPEAT
		fetch myyoubiao4 into tmp;
			call getTotalByUser2(tmp, 1, t); -- 根据用户id获取该用户总订单金额， 含税
			insert into mytable(uId,total) values(tmp,t); --插入新表
	UNTIL done end REPEAT;
	CLOSE myyoubiao4;
END
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;调用该过程时， 会自动创建一张包含用户id和用户订单总额的表(若不存在)， 再把遍历每一个用户， 通过&lt;a href=&quot;http://blog.csdn.net/xu5733127/article/details/49795913&quot;&gt;上一节&lt;/a&gt;创建的过程返回用户的订单总额，插入新创建的表中。&lt;/p&gt;</content:encoded></item><item><title>Laravel 管道流原理</title><link>https://godruoyi.com/posts/laravel-pipeline-flow-principle/</link><guid isPermaLink="true">https://godruoyi.com/posts/laravel-pipeline-flow-principle/</guid><description>&gt; Laravel管道流原理强烈依赖array_reduce函数，我们先来了解下array_reduce函数的使用。  ## array_reduce  &gt; `array_reduce()` 将回调函数 `callback` 迭代地作用到 `array` 数组中的每一个单元中，从而将数组简化...</description><pubDate>Wed, 04 Apr 2018 07:39:04 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Laravel管道流原理强烈依赖array_reduce函数，我们先来了解下array_reduce函数的使用。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;array_reduce&quot;&gt;array_reduce&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;array_reduce()&lt;/code&gt; 将回调函数 &lt;code&gt;callback&lt;/code&gt; 迭代地作用到 &lt;code&gt;array&lt;/code&gt; 数组中的每一个单元中，从而将数组简化为单一的值。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;mixed&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; array_reduce&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ( &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;array&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $array , &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;callable&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $callback [, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;mixed&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $initial &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; NULL&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ] )&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;mixed array_reduce ( array $array , callable $callback [, mixed $initial = NULL ] )&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;ol&gt;
&lt;li&gt;array&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;输入的 array。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;callback&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;mixed callback ( mixed $carry , mixed $item )
&lt;code&gt;$carry&lt;/code&gt;包括上次迭代的值，如果本次迭代是第一次，那么这个值是 &lt;code&gt;initial&lt;/code&gt;，&lt;code&gt;item&lt;/code&gt; 携带了本次迭代的值&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;initial&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;如果指定了可选参数 initial，该参数将在处理开始前使用，或者当处理结束，数组为空时的最后一个结果。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;从文档说明可以看出，&lt;code&gt;array_reduce&lt;/code&gt;函数是把数组的每一项，都通过给定的&lt;code&gt;callback&lt;/code&gt;函数，来&lt;code&gt;简化&lt;/code&gt;的。&lt;/p&gt;
&lt;p&gt;那我们就来看看是怎么简化的。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$arr &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;AAAA&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;BBBB&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;CCCC&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$res &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; array_reduce&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($arr, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($carry, $item){&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $carry &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $item;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;$arr = [&amp;#x27;AAAA&amp;#x27;, &amp;#x27;BBBB&amp;#x27;, &amp;#x27;CCCC&amp;#x27;];

$res = array_reduce($arr, function($carry, $item){
    return $carry . $item;
});&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;给定的数组长度为&lt;strong&gt;3&lt;/strong&gt;，故总迭代三次。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;第一次迭代时     $carry = null       $item = AAAA          返回AAAA&lt;/li&gt;
&lt;li&gt;第一次迭代时     $carry = AAAA    $item = BBBB          返回AAAABBBB&lt;/li&gt;
&lt;li&gt;第一次迭代时     $carry = AAAABBBB  $item = CCCC      返回AAAABBBBCCCC&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;这种方式将数组&lt;strong&gt;简化&lt;/strong&gt;为一串字符串&lt;code&gt;AAAABBBBCCCC&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;带初始值的情况&quot;&gt;带初始值的情况&lt;/h3&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$arr &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;AAAA&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;BBBB&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;CCCC&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$res &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; array_reduce&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($arr, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($carry, $item){&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $carry &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $item;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;INITIAL-&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;$arr = [&amp;#x27;AAAA&amp;#x27;, &amp;#x27;BBBB&amp;#x27;, &amp;#x27;CCCC&amp;#x27;];

$res = array_reduce($arr, function($carry, $item){
    return $carry . $item;
}, &amp;#x27;INITIAL-&amp;#x27;);&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;ol&gt;
&lt;li&gt;第一次迭代时（$carry = INITIAL-），（$item = AAAA） 返回INITIAL-AAAA&lt;/li&gt;
&lt;li&gt;第一次迭代时（$carry = INITIAL-AAAA），（$item = BBBB）， 返回INITIAL-AAAABBBB&lt;/li&gt;
&lt;li&gt;第一次迭代时（$carry = INITIAL-AAAABBBB），（$item = CCCC），返回INITIAL-AAAABBBBCCCC&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;这种方式将数组&lt;strong&gt;简化&lt;/strong&gt;为一串字符串&lt;code&gt;INITIAL-AAAABBBBCCCC&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;闭包&quot;&gt;闭包&lt;/h3&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$arr &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;AAAA&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;BBBB&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;CCCC&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;//没带初始值&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$res &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; array_reduce&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($arr, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($carry, $item){&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($item){&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;//这里只use了item&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; strtolower&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($item) &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;-&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;$arr = [&amp;#x27;AAAA&amp;#x27;, &amp;#x27;BBBB&amp;#x27;, &amp;#x27;CCCC&amp;#x27;];

//没带初始值
$res = array_reduce($arr, function($carry, $item){
    return function() use ($item){//这里只use了item
        return strtolower($item) . &amp;#x27;-&amp;#x27;;
    };
});
&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;ol&gt;
&lt;li&gt;第一次迭代时，$carry：null，$item = AAAA，返回一个use了$item = AAAA的闭包&lt;/li&gt;
&lt;li&gt;第二次迭代时，$carry：use了$item = AAAA的闭包，$item = BBBB，返回一个use了$item = BBBB的闭包&lt;/li&gt;
&lt;li&gt;第一次迭代时，$carry：use了$item = BBBB的闭包，$item = CCCC，返回一个use了$item = CCCC的闭包&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;这种方式将数组&lt;strong&gt;简化&lt;/strong&gt;为一个闭包，即最后返回的&lt;code&gt;闭包&lt;/code&gt;，当我们执行这个闭包时&lt;code&gt;$res()&lt;/code&gt;得到返回值&lt;code&gt;CCCC-&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;上面这种方式只&lt;code&gt;use ($item)&lt;/code&gt;，每次迭代返回的闭包在下次迭代时，我们都没有用起来。只是又重新返回了一个&lt;code&gt;use&lt;/code&gt;了当前&lt;code&gt;item&lt;/code&gt;值的闭包。&lt;/p&gt;
&lt;h3 id=&quot;闭包use闭包&quot;&gt;闭包USE闭包&lt;/h3&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$arr &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;AAAA&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$res &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; array_reduce&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($arr, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($carry, $item){&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($carry, $item) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;is_null&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($carry)) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;            return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;Carry IS NULL&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; .&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $item;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;$arr = [&amp;#x27;AAAA&amp;#x27;];

$res = array_reduce($arr, function($carry, $item){
    return function () use ($carry, $item) {
        if (is_null($carry)) {
            return &amp;#x27;Carry IS NULL&amp;#x27; . $item;
        }
    };
});&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;blockquote&gt;
&lt;p&gt;注意，此时的数组长度为&lt;strong&gt;1&lt;/strong&gt;，并且没有指定初始值&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;由于数组长度为1，故只迭代一次，返回一个闭包 &lt;code&gt;use（$carry = null, $item = &apos;AAAA&apos;）&lt;/code&gt;，当我们执行（&lt;code&gt;$res()&lt;/code&gt;）这个闭包时，得到的结果为&lt;code&gt;Carry IS NULLAAAA&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;接下来我们重新改造下，&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$arr &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;AAAA&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;BBBB&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$res &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; array_reduce&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($arr, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($carry, $item){&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($carry, $item) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;is_null&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($carry)) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;            return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;Carry IS NULL&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; .&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $item;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($carry &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;instanceof&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; \Closure&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;            return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $carry() &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $item;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;$arr = [&amp;#x27;AAAA&amp;#x27;, &amp;#x27;BBBB&amp;#x27;];

$res = array_reduce($arr, function($carry, $item){
    return function () use ($carry, $item) {
        if (is_null($carry)) {
            return &amp;#x27;Carry IS NULL&amp;#x27; . $item;
        }
        if ($carry instanceof \Closure) {
            return $carry() . $item;
        }
    };
});&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;blockquote&gt;
&lt;p&gt;我们新增了一个条件判断，若当前迭代的值是一个闭包，返回该闭包的执行结果。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;第一次迭代时，&lt;code&gt;$carry&lt;/code&gt;的值为&lt;code&gt;null&lt;/code&gt;，&lt;code&gt;$item&lt;/code&gt;的值为AAAA，返回一个闭包，&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;//伪代码&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($carry = null, $item = AAAA) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;is_null&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($carry)) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;Carry IS NULL&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; .&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $item;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($carry &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;instanceof&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; \Closure&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $carry() &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $item;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;//伪代码
function () use ($carry = null, $item = AAAA) {
    if (is_null($carry)) {
        return &amp;#x27;Carry IS NULL&amp;#x27; . $item;
    }
    if ($carry instanceof \Closure) {
        return $carry() . $item;
    }
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;假设我们直接执行该闭包，将会返回&lt;code&gt;Carry IS NULLAAAA&lt;/code&gt;的结果。&lt;/p&gt;
&lt;p&gt;第二次迭代时，&lt;code&gt;$carry&lt;/code&gt;的值为上述返回的闭包（&lt;code&gt;伪代码&lt;/code&gt;），&lt;code&gt;$item&lt;/code&gt;的值为BBBB，返回一个闭包，&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;当我们执行这个闭包时，满足&lt;code&gt;$carry instanceof \Closure&lt;/code&gt;，得到结果&lt;code&gt;Carry IS NULLAAAABBBB&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;laravel中的array_reverse&quot;&gt;Laravel中的array_reverse&lt;/h2&gt;
&lt;p&gt;大致了解了&lt;code&gt;array_reverse&lt;/code&gt;函数的使用后，我们来瞅瞅&lt;code&gt;laravel&lt;/code&gt;管道流里使用&lt;code&gt;array_reverse&lt;/code&gt;的情况。&lt;/p&gt;
&lt;p&gt;我在&lt;a href=&quot;https://laravel-china.org/articles/5180/laravel-middleware-principle&quot;&gt;Laravel中间件原理&lt;/a&gt;中有阐述，强烈建议先去看看&lt;a href=&quot;https://laravel-china.org/articles/5180/laravel-middleware-principle&quot;&gt;Laravel中间件原理&lt;/a&gt;再回过头来接着看。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;php内置方法array_reduce把所有要通过的中间件都通过callback方法并压缩为一个Closure。最后在执行Initial&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;Laravel&lt;/code&gt;中通过全局中间件的核心代码如下：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;//Illuminate\Foundation\Http\Kernel.php&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;protected&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; sendRequestThroughRouter&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($request)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Pipeline&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;app))&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        -&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;send&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($request)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        -&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;through&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;app&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;shouldSkipMiddleware&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;() &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; [] &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;middleware)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        -&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;then&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;dispatchToRouter&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;());&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;protected&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; dispatchToRouter&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($request) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;        $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;app&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;instance&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;request&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, $request);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;router&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;dispatch&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($request);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;app))
        -&gt;send($request)
        -&gt;through($this-&gt;app-&gt;shouldSkipMiddleware() ? [] : $this-&gt;middleware)
        -&gt;then($this-&gt;dispatchToRouter());
}
protected function dispatchToRouter()
{
    return function ($request) {
        $this-&gt;app-&gt;instance(&amp;#x27;request&amp;#x27;, $request);
        return $this-&gt;router-&gt;dispatch($request);
    };
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;正如我前面说的,我们发送一个&lt;code&gt;$request&lt;/code&gt;对象通过&lt;code&gt;middleware&lt;/code&gt;中间件数组，最后在执行&lt;code&gt;dispatchToRouter&lt;/code&gt;方法。&lt;/p&gt;
&lt;p&gt;假设有两个全局中间件，我们来看看这两个中间件是如何通过管道&lt;strong&gt;压缩&lt;/strong&gt;为一个&lt;code&gt;Closure&lt;/code&gt;的。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;App\Http\Middleware\AllowOrigin&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;//自定义中间件&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
App\Http\Middleware\AllowOrigin::class,//自定义中间件&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;blockquote&gt;
&lt;p&gt;Illuminate\Pipeline\Pipeline为laravel的管道流核心类.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在&lt;code&gt;Illuminate\Pipeline\Pipeline&lt;/code&gt;的&lt;code&gt;then&lt;/code&gt;方法中，&lt;code&gt;$destination&lt;/code&gt;为上述的&lt;code&gt;dispatchToRouter&lt;/code&gt;闭包，&lt;code&gt;pipes&lt;/code&gt;为要通过的中间件数组，&lt;code&gt;passable&lt;/code&gt;为&lt;code&gt;Request&lt;/code&gt;对象。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; then&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Closure&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $destination)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    $pipeline &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; array_reduce&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;        array_reverse&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;pipes), &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;carry&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(), &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;prepareDestination&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($destination)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    );&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $pipeline(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;passable);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;pipes), $this-&gt;carry(), $this-&gt;prepareDestination($destination)
    );
    return $pipeline($this-&gt;passable);
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;array_reverse&lt;/code&gt;函数将中间件数组的每一项都通过&lt;code&gt;$this-&gt;carry()&lt;/code&gt;，初始值为上述&lt;code&gt;dispatchToRouter&lt;/code&gt;方法返回的闭包。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;protected&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; prepareDestination&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Closure&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $destination)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($passable) &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($destination) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $destination($passable);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;protected&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; carry&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($stack, $pipe) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($passable) &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($stack, $pipe) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;            if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($pipe &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;instanceof&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Closure&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;                return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $pipe($passable, $stack);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;            } &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;elseif&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; is_object&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($pipe)) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;                //解析中间件参数&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;                list&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($name, $parameters) &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;parsePipeString&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($pipe);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;                $pipe &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;getContainer&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;make&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($name);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;                $parameters &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; array_merge&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;([$passable, $stack], $parameters);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;            } &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;                $parameters &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; [$passable, $stack];&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;            return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $pipe&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;method}(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;...&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$parameters);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        };&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;parsePipeString($pipe);
                $pipe = $this-&gt;getContainer()-&gt;make($name);
                $parameters = array_merge([$passable, $stack], $parameters);
            } else {
                $parameters = [$passable, $stack];
            }
            return $pipe-&gt;{$this-&gt;method}(...$parameters);
        };
    };
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;第一次迭代时，返回一个闭包，&lt;code&gt;use&lt;/code&gt;了&lt;code&gt;$stack&lt;/code&gt;和&lt;code&gt;$pipe&lt;/code&gt;，&lt;code&gt;$stack&lt;/code&gt;的值为初始值闭包，&lt;code&gt;$pipe&lt;/code&gt;为中间件类名，此处是&lt;code&gt;App\Http\Middleware\AllowOrigin::class&lt;/code&gt;(注意&lt;code&gt;array_reverse&lt;/code&gt;函数把传进来的中间件数组倒叙了)。&lt;/p&gt;
&lt;p&gt;假设我们直接运行该闭包，由于此时&lt;code&gt;$pipe&lt;/code&gt;是一个&lt;code&gt;String&lt;/code&gt;类型的中间件类名，只满足&lt;code&gt;! is_object($pipe)&lt;/code&gt;这个条件，我们将直接从容器中&lt;code&gt;make&lt;/code&gt;一个该中间件的实列出来，在执行该中间件实列的&lt;code&gt;handle&lt;/code&gt;方法（默认&lt;code&gt;$this-&gt;method&lt;/code&gt;为&lt;code&gt;handle&lt;/code&gt;）。并且将&lt;code&gt;request&lt;/code&gt;对象和初始值作为参数，传给这个中间件。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; handle&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($request, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Closure&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $next)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    //......&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;public function handle($request, Closure $next)
{
    //......
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;在这个中间件的&lt;code&gt;handle&lt;/code&gt;方法中，当我们直接执行&lt;code&gt;return $next($request)&lt;/code&gt;时，相当于我们开始执行&lt;code&gt;array_reduce&lt;/code&gt;函数的初始值闭包了，即上述的&lt;code&gt;dispatchToRouter&lt;/code&gt;方法返回的闭包。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;protected&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; dispatchToRouter&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($request) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;        $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;app&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;instance&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;request&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, $request);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;router&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;dispatch&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($request);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;app-&gt;instance(&amp;#x27;request&amp;#x27;, $request);
        return $this-&gt;router-&gt;dispatch($request);
    };
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;好，假设结束。在第二次迭代时，也返回一个&lt;code&gt;use&lt;/code&gt;了&lt;code&gt;$stack&lt;/code&gt;和&lt;code&gt;$pipe&lt;/code&gt;，&lt;code&gt;$stack&lt;/code&gt;的值为我们第一次迭代时返回的闭包，&lt;code&gt;$pipe&lt;/code&gt;为中间件类名，此处是&lt;code&gt;Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;两次迭代结束，回到&lt;code&gt;then&lt;/code&gt;方法中，我们手动执行了第二次迭代返回的闭包。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $pipeline(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;passable);&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;passable);&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;当执行第二次迭代返回的闭包时，当前闭包&lt;code&gt;use&lt;/code&gt;的&lt;code&gt;$pipe&lt;/code&gt;为&lt;code&gt;Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class&lt;/code&gt;，同样只满足&lt;code&gt;! is_object($pipe)&lt;/code&gt;这个条件，我们将会从容器中&lt;code&gt;make&lt;/code&gt;出&lt;code&gt;CheckForMaintenanceMode&lt;/code&gt;中间件的实列，在执行该实列的&lt;code&gt;handle&lt;/code&gt;方法，并且把第一次迭代返回的闭包作为参数传到&lt;code&gt;handle&lt;/code&gt;方法中。&lt;/p&gt;
&lt;p&gt;当我们在&lt;code&gt;CheckForMaintenanceMode&lt;/code&gt;中间件的&lt;code&gt;handle&lt;/code&gt;方法中执行&lt;code&gt;return $next($request)&lt;/code&gt;时，此时的&lt;code&gt;$next&lt;/code&gt;为我们第一次迭代返回的闭包，将回到我们刚才假设的流程那样。从容器中&lt;code&gt;make&lt;/code&gt;一个&lt;code&gt;App\Http\Middleware\AllowOrigin&lt;/code&gt;实列，在执行该实列的&lt;code&gt;handle&lt;/code&gt;方法，并把初始值闭包作为参数传到&lt;code&gt;AllowOrigin&lt;/code&gt;中间件的&lt;code&gt;handle方法中&lt;/code&gt;。当我们再在&lt;code&gt;AllowOrigin&lt;/code&gt;中间件中执行&lt;code&gt;return $next($request)&lt;/code&gt;时，代表我们所有中间件都通过完成了，接下来开始执行&lt;code&gt;dispatchToRouter&lt;/code&gt;。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;中间件是区分先后顺序的，从这里你应该能明白为什么要把中间件用&lt;code&gt;array_reverse&lt;/code&gt;倒叙了。&lt;/li&gt;
&lt;li&gt;并不是所有中间件在运行前都已经实例化了的，用到的时候才去想容器取&lt;/li&gt;
&lt;li&gt;中间件不执行$next($request)后续所有中间件无法执行。&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;这篇文章是专们为了上一篇&lt;a href=&quot;https://laravel-china.org/articles/5180/laravel-middleware-principle&quot;&gt;Laravel中间件原理&lt;/a&gt;写的，因为在写Laravel中间件原理时我也不很清楚&lt;code&gt;array_reduce&lt;/code&gt;在&lt;code&gt;laravel&lt;/code&gt;中的运行流程。如果有什么不对的，欢迎指正。&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded></item><item><title>使用预加载优化 Laravel Model 查询</title><link>https://godruoyi.com/posts/optimize-laravel-model-queries-using-preload/</link><guid isPermaLink="true">https://godruoyi.com/posts/optimize-laravel-model-queries-using-preload/</guid><description>原文译自[eloquent-eager-loading](https://laravel-news.com/eloquent-eager-loading)，简化其前面~~构造数据~~部分。  ## 介绍  对象关系映射（`ORM`）使数据库的工作变得非常简单。 在以面向对象的方式定义数据库...</description><pubDate>Wed, 04 Apr 2018 07:34:04 GMT</pubDate><content:encoded>&lt;p&gt;原文译自&lt;a href=&quot;https://laravel-news.com/eloquent-eager-loading&quot;&gt;eloquent-eager-loading&lt;/a&gt;，简化其前面&lt;del&gt;构造数据&lt;/del&gt;部分。&lt;/p&gt;
&lt;h2 id=&quot;介绍&quot;&gt;介绍&lt;/h2&gt;
&lt;p&gt;对象关系映射（&lt;code&gt;ORM&lt;/code&gt;）使数据库的工作变得非常简单。 在以面向对象的方式定义数据库关系时，可以轻松查询相关的模型数据，开发人员可能不会注意底层数据库调用。&lt;/p&gt;
&lt;p&gt;下面将通过一些例子，进一步帮助您了解如何优化查询。&lt;/p&gt;
&lt;p&gt;假设您从数据库收到了100个对象，并且每个记录都有1个关联模型（即belongsTo）。 默认使用ORM将产生101个查询; 如下所示：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;//获取已发布的100条文章&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$posts &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Post&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;limit&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;100&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(); &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;//一次查询&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$authors &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; array_map&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($post) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    // 对作者模型生成查询&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $post&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;author&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;name;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}, $posts);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;get(); //一次查询

$authors = array_map(function($post) {
    // 对作者模型生成查询
    return $post-&gt;author-&gt;name;
}, $posts);
&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;我们在查询时没有告诉&lt;code&gt;Post&lt;/code&gt;模型，我们还需要所有的作者，所以每次从单个&lt;code&gt;Post&lt;/code&gt;模型实例获取作者的名字时，都会发生单独的查询。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;array_maps时发生100次查询，加上先前一次查询，累计产生101次查询。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;预加载&quot;&gt;预加载&lt;/h2&gt;
&lt;p&gt;接下来，如果我们打算使用关联的模型数据，我们可以使用预加载将该&lt;code&gt;101&lt;/code&gt;个查询总数减少到&lt;code&gt;2&lt;/code&gt;个查询。 只需要告诉模型你需要什么来加载。如下：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;//获取已发布的100条文章  - 并预加载文章对应作者&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$posts &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Post&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;with&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;author&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;limit&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;100&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;();&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;//2次查询&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$authors &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; array_map&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($post) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    // 对作者模型生成查询&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $post&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;author&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;name;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;//这里讲不在产生查询&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}, $posts);&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;limit(100)-&gt;get();//2次查询

$authors = array_map(function($post) {
    // 对作者模型生成查询
    return $post-&gt;author-&gt;name;//这里讲不在产生查询
}, $posts);&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;如果你开启了&lt;code&gt;sql&lt;/code&gt;日志，你将看到上述预加载将只会产生两条查询：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;sql&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;sql&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;select&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; `posts`&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;select&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; `authors`&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; where&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; `authors`&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;`id`&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (?, ?, ?, ?, ?) [1,2,3,4,5]&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;select * from &amp;#x60;posts&amp;#x60;
select * from &amp;#x60;authors&amp;#x60; where &amp;#x60;authors&amp;#x60;.&amp;#x60;id&amp;#x60; in (?, ?, ?, ?, ?) [1,2,3,4,5]&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;如果您有多个关联模型，则可以使用数组加载它们：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$posts &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; App\Post&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;with&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;([&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;author&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;comments&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;])&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;();&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;get();&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;接下来我们重新定义如下关系&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Post&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; -&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; belongsTo &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; Author &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;//每个文章只属于一个用户&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Author&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; -&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; hasMany &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; Post   &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;//每个用户拥有多个文章&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Author&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; -&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; hasOne &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; Profile &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;//每个用户只有一个简介&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt; belongsTo -&gt; Author //每个文章只属于一个用户
Author -&gt; hasMany -&gt; Post   //每个用户拥有多个文章
Author -&gt; hasOne -&gt; Profile //每个用户只有一个简介&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;考虑下述情况：获取已发布文章所属作者的个人简介。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;//获取所有文章 - 并预加载文章对应作者&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$posts &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; App\Post&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;with&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;author&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;();&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;//两次查询&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;//根据每个 `作者` 获取其简介&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$posts&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($post) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    //虽然我们直接通过$author = $post-&gt;author不会产生查询，&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    //但当调用$author-&gt;profile时，每次都会产生一个新查询&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $post&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;author&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;profile;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;get();//两次查询

//根据每个 &amp;#x60;作者&amp;#x60; 获取其简介
$posts-&gt;map(function ($post) {
    //虽然我们直接通过$author = $post-&gt;author不会产生查询，
    //但当调用$author-&gt;profile时，每次都会产生一个新查询
    return $post-&gt;author-&gt;profile;
});&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;blockquote&gt;
&lt;p&gt;假设上述&lt;code&gt;App\Post::with(&apos;author&apos;)-&gt;get()&lt;/code&gt;有100条记录，将会产生多少条查询呢？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;通过优化预加载，我们可以避免嵌套关系中的额外查询。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;//获取所有文章 - 并预加载文章对应作者及每个作者对应de profile&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$posts &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; App\Post&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;with&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;author.profile&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;get&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;();&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;//三次查询&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$posts&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($post) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    //不在产生新查询&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $post&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;author&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;profile;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;get();//三次查询

$posts-&gt;map(function ($post) {
    //不在产生新查询
    return $post-&gt;author-&gt;profile;
});&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;你可以打开你的&lt;code&gt;sql&lt;/code&gt;日志看到对应的三条查询。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;sql&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;sql&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;select&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; `posts`&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;  &lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;select&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; `authors`&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; where&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; `authors`&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;`id`&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (?, ?, ?, ?, ?) [.....] &lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;select&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; `profiles`&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; where&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; `profiles`&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;`author_id`&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (?, ?, ?, ?, ?) [.....] &lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;select * from &amp;#x60;posts&amp;#x60;  
select * from &amp;#x60;authors&amp;#x60; where &amp;#x60;authors&amp;#x60;.&amp;#x60;id&amp;#x60; in (?, ?, ?, ?, ?) [.....] 
select * from &amp;#x60;profiles&amp;#x60; where &amp;#x60;profiles&amp;#x60;.&amp;#x60;author_id&amp;#x60; in (?, ?, ?, ?, ?) [.....] &quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&quot;懒惰加载&quot;&gt;懒惰加载&lt;/h2&gt;
&lt;p&gt;有时候您可能只需要根据条件收集相关联的模型。 在这种情况下，您可以懒惰地调用相关数据的其他查询：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$posts &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; App\Post&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;all&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;();&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;//一次查询&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$posts&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;load&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;author.profile&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;//两次查询&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$posts&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($post) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;    //不在产生新查询&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $post&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;author&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;profile;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;load(&amp;#x27;author.profile&amp;#x27;);//两次查询
$posts-&gt;map(function ($post) {
    //不在产生新查询
    return $post-&gt;author-&gt;profile;
});&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;查看您的sql日志，总共看到三个查询，但只有调用&lt;code&gt;$posts-&gt;load()&lt;/code&gt;时才会显示。&lt;/p&gt;
&lt;h2 id=&quot;结论&quot;&gt;结论&lt;/h2&gt;
&lt;p&gt;希望您更加了解有关加载型号的更多信息，并了解其在更深层次上的工作原理。 Laravel相关的文档已经很全面了，希望额外的实践练习可以帮助您更有信心优化关系查询。&lt;/p&gt;</content:encoded></item><item><title>FastCgi 与 PHP-fpm 之间的关系</title><link>https://godruoyi.com/posts/the-relationship-between-fastcgi-and-php-fpm/</link><guid isPermaLink="true">https://godruoyi.com/posts/the-relationship-between-fastcgi-and-php-fpm/</guid><description>原文分享自 segmentfault 尹川的回答，搞不清 FastCgi 与 PHP-fpm 之间是个什么样的关系</description><pubDate>Wed, 04 Apr 2018 07:32:25 GMT</pubDate><content:encoded>&lt;p&gt;原文分享自&lt;a href=&quot;segmentfault.com&quot;&gt;segmentfault&lt;/a&gt; @尹川的回答，&lt;a href=&quot;https://segmentfault.com/q/1010000000256516&quot;&gt;搞不清FastCgi与PHP-fpm之间是个什么样的关系&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我在网上查fastcgi与php-fpm的关系，查了快一周了，基本看了个遍，真是众说纷纭，没一个权威性的定义。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;网上有的说，fastcgi是一个协议，php-fpm实现了这个协议；&lt;/li&gt;
&lt;li&gt;有的说，php-fpm是fastcgi进程的管理器，用来管理fastcgi进程的；&lt;/li&gt;
&lt;li&gt;有的说，php-fpm是php内核的一个补丁;&lt;/li&gt;
&lt;li&gt;有的说，修改了php.ini配置文件后，没办法平滑重启，所以就诞生了php-fpm；&lt;/li&gt;
&lt;li&gt;还有的说PHP-CGI是PHP自带的FastCGI管理器，那这样的话干吗又弄个php-fpm出来？&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;首先，CGI是干嘛的？&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;CGI是为了保证web server传递过来的数据是标准格式的，方便CGI程序的编写者。&lt;/li&gt;
&lt;li&gt;web server（比如说nginx）只是内容的分发者。比如，如果请求/index.html，那么web server会去文件系统中找到这个文件，发送给浏览器，这里分发的是静态数据。好了，如果现在请求的是/index.php，根据配置文件，nginx知道这个不是静态文件，需要去找PHP解析器来处理，那么他会把这个请求简单处理后交给PHP解析器。Nginx会传哪些数据给PHP解析器呢？url要有吧，查询字符串也得有吧，POST数据也要有，HTTP header不能少吧，好的，&lt;code&gt;CGI&lt;/code&gt;就是规定要传哪些数据、以什么样的格式传递给后方处理这个请求的协议。&lt;/li&gt;
&lt;li&gt;当web server收到/index.php这个请求后，会启动对应的CGI程序，这里就是PHP的解析器。接下来PHP解析器会解析php.ini文件，初始化执行环境，然后处理请求，再以规定CGI规定的格式返回处理后的结果，退出进程。web server再把结果返回给浏览器。&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;好了，CGI是个协议，跟进程什么的没关系。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;那fastcgi又是什么呢？&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Fastcgi是用来提高CGI程序性能的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;提高性能，那么CGI程序的性能问题在哪呢？&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“PHP解析器会解析php.ini文件，初始化执行环境”，就是这里了。标准的CGI对每个请求都会执行这些步骤（不闲累啊！启动进程很累的说！），所以处理每个时间的时间会比较长。这明显不合理嘛！那么Fastcgi是怎么做的呢？首先，Fastcgi会先启一个master，解析配置文件，初始化执行环境，然后再启动多个worker。当请求过来时，master会传递给一个worker，然后立即可以接受下一个请求。这样就避免了重复的劳动，效率自然是高。而且当worker不够用时，master可以根据配置预先启动几个worker等着；当然空闲worker太多时，也会停掉一些，这样就提高了性能，也节约了资源。这就是fastcgi的对进程的管理。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;那PHP-FPM又是什么呢？&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;是一个实现了Fastcgi的程序，被PHP官方收了。&lt;/li&gt;
&lt;li&gt;大家都知道，PHP的解释器是php-cgi。php-cgi只是个CGI程序，他自己本身只能解析请求，返回结果，不会进程管理（皇上，臣妾真的做不到啊！）所以就出现了一些能够调度php-cgi进程的程序，比如说由lighthttpd分离出来的spawn-fcgi。好了PHP-FPM也是这么个东东，在长时间的发展后，逐渐得到了大家的认可（要知道，前几年大家可是抱怨PHP-FPM稳定性太差的），也越来越流行。&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;好了，最后来回来上面的问题。&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;网上有的说，fastcgi是一个协议，php-fpm实现了这个协议&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;对。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;有的说，php-fpm是fastcgi进程的管理器，用来管理fastcgi进程的&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;对。php-fpm的管理对象是php-cgi。但不能说php-fpm是fastcgi进程的管理器，因为前面说了fastcgi是个协议，似乎没有这么个进程存在，就算存在php-fpm也管理不了他（至少目前是）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;有的说，php-fpm是php内核的一个补丁&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;以前是对的。因为最开始的时候php-fpm没有包含在PHP内核里面，要使用这个功能，需要找到与源码版本相同的php-fpm对内核打补丁，然后再编译。后来PHP内核集成了PHP-FPM之后就方便多了，使用—enalbe-fpm这个编译参数即可。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;有的说，修改了php.ini配置文件后，没办法平滑重启，所以就诞生了php-fpm&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;是的，修改php.ini之后，php-cgi进程的确是没办法平滑重启的。php-fpm对此的处理机制是新的worker用新的配置，已经存在的worker处理完手上的活就可以歇着了，通过这种机制来平滑过度。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol start=&quot;5&quot;&gt;
&lt;li&gt;还有的说PHP-CGI是PHP自带的FastCGI管理器，那这样的话干吗又弄个php-fpm出来？&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;不对。php-cgi只是解释PHP脚本的程序而已。&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded></item><item><title>我的 Sublime text 插件配置</title><link>https://godruoyi.com/posts/my-sublime-text-plug-in-configuration/</link><guid isPermaLink="true">https://godruoyi.com/posts/my-sublime-text-plug-in-configuration/</guid><description>只从沾上了 sublime 这款编辑器，我对其他所有的都没爱了。</description><pubDate>Wed, 04 Apr 2018 07:29:58 GMT</pubDate><content:encoded>&lt;p&gt;首先，我应该正确安装了 &lt;a href=&quot;https://packagecontrol.io/installation#st3&quot;&gt;Package Control&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&quot;material-theme&quot;&gt;Material Theme&lt;/h2&gt;
&lt;p&gt;外观是最重要的，一个好的主题会让你爱上完美。这里推荐 &lt;a href=&quot;http://equinsuocha.io/material-theme/&quot;&gt;Material Theme&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/201909/24/_1569340531_wzgl3oXMKl.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;operator&quot;&gt;Operator&lt;/h2&gt;
&lt;p&gt;是不是感觉字体不好看，咳不出来又咽不下去，试试这款吧 &lt;a href=&quot;https://www.typography.com/fonts/operator/styles/&quot;&gt;Operator&lt;/a&gt;，号称贵到灵魂出窍的等宽编程字体(@JokerLinly)，一套只需 &lt;code&gt;$599.0&lt;/code&gt;。:smile: 我还是用我的默认字体吧！&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/201909/24/_1569340566_wQolvwi4WT.png&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;alignment&quot;&gt;Alignment&lt;/h2&gt;
&lt;p&gt;定义了一大堆变量，想让他们快速对齐怎么办！（强迫症患者福利）&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;安装完成后进入 &lt;code&gt;preferences-&gt;Package settings-&gt;Alignment-&gt;Key Bindings-User&lt;/code&gt; 设置快捷键（我的是 &lt;code&gt;ctrl+alt+0&lt;/code&gt;）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;json&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;json&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{ &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;&quot;keys&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: [&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;ctrl+alt+0&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;], &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;alignment&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; }&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;{ &amp;#x22;keys&amp;#x22;: [&amp;#x22;ctrl+alt+0&amp;#x22;], &amp;#x22;command&amp;#x22;: &amp;#x22;alignment&amp;#x22; }&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/201909/24/_1569340586_2SOJxMa275.gif&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;advancednewfile&quot;&gt;AdvancedNewFile&lt;/h2&gt;
&lt;p&gt;你还在用鼠标创建和移动文件吗，看这里。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;默认情况下安装成功后快捷键不是很好用且只有一个创建文件的快捷键(&lt;code&gt;ctrl+alt+n&lt;/code&gt;)，建议你覆盖默认快捷键配置。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;json&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;json&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{ &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;&quot;keys&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: [&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;ctrl+n&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;], &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;advanced_new_file_new&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; }, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;//快速新建文件&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{ &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;&quot;keys&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: [&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;alt+m&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;], &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;advanced_new_file_move&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; }, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;//移动文件&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{ &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;&quot;keys&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: [&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;alt+delete&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;], &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;advanced_new_file_delete&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; , &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;&quot;args&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: {&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;&quot;current&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}}, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;//删除当前文件&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{ &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;&quot;keys&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: [&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;alt+.&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;], &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#8DDB8C&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;advanced_new_file_copy&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; }  &lt;/span&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;//复制当前文件的内容到一个新文件&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;{ &amp;#x22;keys&amp;#x22;: [&amp;#x22;ctrl+n&amp;#x22;], &amp;#x22;command&amp;#x22;: &amp;#x22;advanced_new_file_new&amp;#x22; }, //快速新建文件
{ &amp;#x22;keys&amp;#x22;: [&amp;#x22;alt+m&amp;#x22;], &amp;#x22;command&amp;#x22;: &amp;#x22;advanced_new_file_move&amp;#x22; }, //移动文件
{ &amp;#x22;keys&amp;#x22;: [&amp;#x22;alt+delete&amp;#x22;], &amp;#x22;command&amp;#x22;: &amp;#x22;advanced_new_file_delete&amp;#x22; , &amp;#x22;args&amp;#x22;: {&amp;#x22;current&amp;#x22;: true}}, //删除当前文件
{ &amp;#x22;keys&amp;#x22;: [&amp;#x22;alt+.&amp;#x22;], &amp;#x22;command&amp;#x22;: &amp;#x22;advanced_new_file_copy&amp;#x22; }  //复制当前文件的内容到一个新文件&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/201909/24/_1569340614_IbQrHDZsH3.gif&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;editorconfig&quot;&gt;EditorConfig&lt;/h2&gt;
&lt;p&gt;当项目涉及到多人开发时，定义一套 &lt;code&gt;简单&lt;/code&gt; 的编码规范是必须的。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;安装成功后在项目根目录下创建一个 &lt;code&gt;.editorconfig&lt;/code&gt; 的配置文件，我的默认配置如下，&lt;a href=&quot;http://editorconfig.org/&quot;&gt;查看更多配置细节&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;json&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;json&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;root = &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;--shiki-light:#B31D28;--shiki-light-font-style:italic;--shiki-dark:#FF938A;--shiki-dark-font-style:italic&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;indent_style = space&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;end_of_line = lf&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;charset = utf&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;-8&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;trim_trailing_whitespace = &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;insert_final_newline = &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;--shiki-light:#B31D28;--shiki-light-font-style:italic;--shiki-dark:#FF938A;--shiki-dark-font-style:italic&quot;&gt;*.&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;--shiki-light:#B31D28;--shiki-light-font-style:italic;--shiki-dark:#FF938A;--shiki-dark-font-style:italic&quot;&gt;js,py,css,vue&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}]&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;indent_size = &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;2&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;--shiki-light:#B31D28;--shiki-light-font-style:italic;--shiki-dark:#FF938A;--shiki-dark-font-style:italic&quot;&gt;*.md&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;trim_trailing_whitespace = &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;false&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;root = true

[*]
indent_style = space
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.{js,py,css,vue}]
indent_size = 2

[*.md]
trim_trailing_whitespace = false
&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&quot;sublimelinter-php&quot;&gt;SublimeLinter-php&lt;/h2&gt;
&lt;p&gt;该插件是一个自动检查 &lt;code&gt;PHP&lt;/code&gt; 语法错误的插件，安装前先要安装 &lt;a href=&quot;https://packagecontrol.io/packages/SublimeLinter&quot;&gt;SublimeLinter&lt;/a&gt;，默认安装成功后，&lt;code&gt;ctrl+s&lt;/code&gt; 保存时将自动提示语法错误。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;若你没有把 &lt;code&gt;php&lt;/code&gt; 及 &lt;code&gt;python&lt;/code&gt; 加入环境变量，你可能要在 &lt;code&gt;SublimeLinter&lt;/code&gt; 的配置文件中指定 &lt;code&gt;python&lt;/code&gt; 及 &lt;code&gt;php&lt;/code&gt; 的执行路径。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/201909/24/_1569340682_DMJlNwtfia.gif&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;phpcs&quot;&gt;Phpcs&lt;/h2&gt;
&lt;p&gt;关于 PHPCS的介绍及安装请 &lt;a href=&quot;https://learnku.com/articles/5646/php-series-code-sniffer-for-code-specification&quot;&gt;戳这里&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://images.godruoyi.com/posts/201909/24/_1569340734_Q5ZoetnMcZ.gif&quot; alt=&quot;file&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;其他&quot;&gt;其他&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;All Autocomplete&lt;/li&gt;
&lt;li&gt;ConvertToUTF8&lt;/li&gt;
&lt;li&gt;DocBlockr&lt;/li&gt;
&lt;li&gt;Emmet&lt;/li&gt;
&lt;li&gt;Git&lt;/li&gt;
&lt;li&gt;GitGutter&lt;/li&gt;
&lt;li&gt;Laravel 5 Artisan&lt;/li&gt;
&lt;li&gt;SideBarEnhancements&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Laravel 队列优先级的一个坑</title><link>https://godruoyi.com/posts/a-pit-in-the-laravel-queue-priority/</link><guid isPermaLink="true">https://godruoyi.com/posts/a-pit-in-the-laravel-queue-priority/</guid><description>准确来说这不算是坑，但骚不注意就掉进去了，在使用 Laravel 队列时，有时候我们希望为他设定一个优先级，如 bash php artisan queue:work --queue=high,low 这样，当我们的任务需要优先发送时</description><pubDate>Wed, 04 Apr 2018 07:24:36 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;准确来说这不算是坑，但骚不注意就掉进去了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在使用 &lt;code&gt;laravel&lt;/code&gt; 队列时，有时候我们希望为他设定一个优先级，如：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;bash&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt;php&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; artisan&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; queue:work&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; --queue=high,low&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;php artisan queue:work --queue=high,low&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;这样，当我们的任务需要优先发送时，就可以通过指定队列名 &lt;code&gt;high&lt;/code&gt; 来优先发送。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;dispatch&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;new&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Job&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;onQueue&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;high&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;));&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;onQueue(&amp;#x27;high&amp;#x27;));&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;但是当你后续任务没有指定队列名（&lt;code&gt;high&lt;/code&gt;、&lt;code&gt;low&lt;/code&gt;）时，你的队列任务永远也不会执行。（比如我们在发送消息通知时）&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;&amp;#x3C;?&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;php&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;namespace&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; App\Notifications&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Illuminate\Bus\Queueable&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Illuminate\Notifications\Notification&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Illuminate\Contracts\Queue\ShouldQueue&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; YourNotification&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#6CB6FF&quot;&gt; Notification&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; implements&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#6CB6FF&quot;&gt; ShouldQueue&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    use&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Queueable&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&lt;?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;

class YourNotification extends Notification implements ShouldQueue
{
    use Queueable;
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;你发现即使你按照文档说的，&lt;code&gt;implements ShouldQueue&lt;/code&gt; 并且 &lt;code&gt;use Queueable&lt;/code&gt;，该通知还是无法加入队列。&lt;/p&gt;
&lt;p&gt;那是因为 &lt;code&gt;config\queuq.php&lt;/code&gt; 配置中，指定了默认的队列名为 &lt;code&gt;default&lt;/code&gt;，所以所有的队列任务，如果没指定队列名时，默认是 &lt;code&gt;default&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;但是我们在启动队列进程时，只指定了 &lt;code&gt;high&lt;/code&gt; 和 &lt;code&gt;low&lt;/code&gt;。当然不会生效。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;解决办法：
1、修改config\queuq.php 默认队列名为 low 或 high
2、启动队列进程时添加 default（—queue=high,default,low）&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded></item><item><title>Laravel 响应宏原理</title><link>https://godruoyi.com/posts/laravel-response-macro-principle/</link><guid isPermaLink="true">https://godruoyi.com/posts/laravel-response-macro-principle/</guid><description>在使用 Laravel 来写 API 时，经常需要返回一个 JSON 字符串或 JsonResponse，通常的做法可能有两种。  1、在 BaseController 中定义一个返回 JSON 响应的方法，然后继承该 BaseController。</description><pubDate>Wed, 04 Apr 2018 05:09:16 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;我们在使用 &lt;code&gt;Laravel&lt;/code&gt; 来写 API 时，经常需要返回一个 JSON 字符串或 JsonResponse 对象，通常的做法可能有：&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;在 &lt;code&gt;BaseController&lt;/code&gt; 中定义一个返回 JSON 响应的方法，然后其他 class 继承该 &lt;code&gt;BaseController&lt;/code&gt;，如：&lt;/li&gt;
&lt;/ul&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// BaseController.php&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; json&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($data &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; null&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, $status &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 200&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, $headers &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; [], $options &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; JsonResponse&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($data, $status, $headers, $options);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// YourController.php&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; YourController&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#6CB6FF&quot;&gt; BaseController&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; users&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;UserRepository&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $userRepository)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;json&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($userRepository&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;allUser&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;());&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;json($userRepository-&gt;allUser());
    }
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;这种写法确实方便快捷，大部分项目估计也是这样做的；然而当你要在其他地方也需要输出 JSON 响应时（如中间件验证失败），你根本无法复用该方法；针对这种情况：你或许可以直接返回一个错误的 JSON 响应：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; func&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; handle&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($next, $request) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($request&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;user&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;role_id &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;!=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;		    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;				    &quot;code&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; -&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;						&quot;message&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &quot;some error&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;				];&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;		}&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;user()-&gt;role_id != 1) {
		    return [
				    &amp;#x22;code&amp;#x22; =&gt; -1,
						&amp;#x22;message&amp;#x22; =&gt; &amp;#x22;some error&amp;#x22;
				];
		}
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;但这样写当前端小伙伴固定了返回格式或后续需要调整返回格式时，你不得不修改所有有可能要改的地方。&lt;/p&gt;
&lt;h2 id=&quot;response-宏&quot;&gt;Response 宏&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Laravel&lt;/code&gt; 提供了一个非常方便的 &lt;code&gt;响应宏&lt;/code&gt; 来处理这一情况；&lt;/p&gt;
&lt;p&gt;首先，我们需要先注册一个响应宏；在任意一个 &lt;code&gt;ServiceProvider&lt;/code&gt; 的 &lt;code&gt;boot&lt;/code&gt; 方法里，使用 &lt;code&gt;Response Facade&lt;/code&gt; 注册：&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;/**&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt; * Bootstrap any application services.&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt; *&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt; * &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;@return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; void&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt; */&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; boot&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;		Response&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;macro&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;success&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($data &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; [], $message &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;success&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;				return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; JsonResponse&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;([&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;						&apos;code&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;						&apos;data&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $data,&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;						&apos;message&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $message&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;				], &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;200&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;		});&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt; 0,
						&amp;#x27;data&amp;#x27; =&gt; $data,
						&amp;#x27;message&amp;#x27; =&gt; $message
				], 200);
		});
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;接下来， 你可以再&lt;strong&gt;任何地方&lt;/strong&gt;使用它；&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;//UserController.php&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; users&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;UserRepository&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $userRepository)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; response&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;success&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($userRepository&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;all&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(), &lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;success&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;success($userRepository-&gt;all(), &amp;#x27;success&amp;#x27;);
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;h2 id=&quot;原理&quot;&gt;原理&lt;/h2&gt;
&lt;p&gt;在 &lt;code&gt;ServiceProvider&lt;/code&gt; 里，作者是使用 &lt;code&gt;Response Facade&lt;/code&gt; 来注册的 &lt;code&gt;success&lt;/code&gt; 宏，先看看 &lt;code&gt;Response&lt;/code&gt; 这个 &lt;code&gt;Facade&lt;/code&gt; 的正真类是什么。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;// Illuminate\Support\Facades.php&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;protected&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; static&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; getFacadeAccessor&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt; &apos;Illuminate\Contracts\Routing\ResponseFactory&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;// Illuminate\Support\Facades.php

protected static function getFacadeAccessor()
{
    return &amp;#x27;Illuminate\Contracts\Routing\ResponseFactory&amp;#x27;;
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;该&lt;code&gt;Facade&lt;/code&gt;返回了一个&lt;code&gt;ResponseFactory&lt;/code&gt;接口，那该接口的具体实列对象时什么呢。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;//Illuminate\Routing\RoutingServiceProvider.php&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt;/**&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt; * Register the response factory implementation.&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt; *&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt; * &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;@return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; void&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#6A737D;--shiki-dark:#768390&quot;&gt; */&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;protected&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; registerResponseFactory&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;    $this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;app&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;singleton&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;Illuminate\Contracts\Routing\ResponseFactory&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; ($app) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; ResponseFactory&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($app[&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;Illuminate\Contracts\View\Factory&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;], $app[&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&apos;redirect&apos;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    });&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;app-&gt;singleton(&amp;#x27;Illuminate\Contracts\Routing\ResponseFactory&amp;#x27;, function ($app) {
        return new ResponseFactory($app[&amp;#x27;Illuminate\Contracts\View\Factory&amp;#x27;], $app[&amp;#x27;redirect&amp;#x27;]);
    });
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;可以看到，该&lt;code&gt;RoutingServiceProvider&lt;/code&gt;注册了一个&lt;code&gt;Illuminate\Routing\ResponseFactory&lt;/code&gt;的实列给&lt;code&gt;Response Facade&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;我们在&lt;code&gt;Illuminate\Routing\ResponseFactory&lt;/code&gt;的源码中可以看到，它引用了一个&lt;code&gt;Illuminate\Support\Traits\Macroable  trait&lt;/code&gt;。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;namespace&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; Illuminate\Routing&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Illuminate\Support\Traits\Macroable&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; ResponseFactory&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; implements&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#6CB6FF&quot;&gt; FactoryContract&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    use&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Macroable&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;namespace Illuminate\Routing;

use Illuminate\Support\Traits\Macroable;

class ResponseFactory implements FactoryContract
{
    use Macroable;
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;&quot;&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;p&gt;该&lt;code&gt;Trait&lt;/code&gt;源码如下，看完源码就知道为什么调用&lt;code&gt;response()&lt;/code&gt;就能正常访问&lt;code&gt;success&lt;/code&gt;方法了。&lt;/p&gt;
&lt;figure data-rehype-pretty-code-figure=&quot;&quot;&gt;&lt;pre tabindex=&quot;0&quot; data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot;&gt;&lt;code data-language=&quot;php&quot; data-theme=&quot;github-light github-dark-dimmed&quot; style=&quot;display: grid;&quot;&gt;&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;trait&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#F69D50&quot;&gt; Macroable&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    protected&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; static&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $macros &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; [];&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; static&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; macro&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($name, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;callable&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $macro)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        static::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$macros[$name] &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; $macro;&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; static&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt; hasMacro&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($name)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; isset&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;static::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$macros[$name]);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; static&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; __callStatic&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($method, $parameters)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; static::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;hasMacro&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($method)) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;            throw&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; BadMethodCallException&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;Method {&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$method&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;} does not exist.&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;static::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$macros[$method] &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;instanceof&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Closure&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;            return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; call_user_func_array&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;Closure&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;bind&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;static::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$macros[$method], &lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;null&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;static::class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;), $parameters);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; call_user_func_array&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;static::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$macros[$method], $parameters);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt; &lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;    public&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; __call&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($method, $parameters)&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; static::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;hasMacro&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;($method)) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;            throw&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt; new&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; BadMethodCallException&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;&quot;Method {&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$method&lt;/span&gt;&lt;span style=&quot;--shiki-light:#032F62;--shiki-dark:#96D0FF&quot;&gt;} does not exist.&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;static::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$macros[$method] &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;instanceof&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; Closure&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;            return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; call_user_func_array&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;static::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$macros[$method]&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;-&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-light:#6F42C1;--shiki-dark:#DCBDFB&quot;&gt;bindTo&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt;$this&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;static::class&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;), $parameters);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;        return&lt;/span&gt;&lt;span style=&quot;--shiki-light:#005CC5;--shiki-dark:#6CB6FF&quot;&gt; call_user_func_array&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-light:#D73A49;--shiki-dark:#F47067&quot;&gt;static::&lt;/span&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;$macros[$method], $parameters);&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span data-line=&quot;&quot;&gt;&lt;span style=&quot;--shiki-light:#24292E;--shiki-dark:#ADBAC7&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;button type=&quot;button&quot; title=&quot;Copy code&quot; aria-label=&quot;Copy code&quot; data=&quot;&quot;&gt;bindTo($this, static::class), $parameters);
        }
        return call_user_func_array(static::$macros[$method], $parameters);
    }
}&quot; class=&quot;rehype-pretty-copy&quot; onclick=&quot;navigator.clipboard.writeText(this.attributes.data.value);this.classList.add(&amp;#x27;rehype-pretty-copied&amp;#x27;);window.setTimeout(() =&gt; this.classList.remove(&amp;#x27;rehype-pretty-copied&amp;#x27;), 2500);&quot;&gt;&lt;span class=&quot;ready&quot;&gt;&lt;/span&gt;&lt;span class=&quot;success&quot;&gt;&lt;/span&gt;&lt;/button&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;
&lt;blockquote&gt;
&lt;p&gt;其实该 &lt;code&gt;trait Illuminate\Support\Traits\Macroable&lt;/code&gt; 在很多地方都有使用，包括 &lt;code&gt;FileSystem&lt;/code&gt;、&lt;code&gt;Database-Builder&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;http://d.laravel-china.org/docs/5.4/responses#response-macros&quot;&gt;Response-macros文档---Laravel-China&lt;/a&gt;&lt;/p&gt;</content:encoded></item></channel></rss>