<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Bloggings</title>
        <link>https://theoutsidelaine.com/</link>
        <description>Recent content on Bloggings</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>en-us</language>
        <lastBuildDate>Sat, 28 Mar 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://theoutsidelaine.com/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>Project Hail Mary</title>
        <link>https://theoutsidelaine.com/p/project-hail-mary/</link>
        <pubDate>Sat, 28 Mar 2026 00:00:00 +0000</pubDate>
        
        <guid>https://theoutsidelaine.com/p/project-hail-mary/</guid>
        <description>&lt;img src="https://theoutsidelaine.com/p/project-hail-mary/cover.png" alt="Featured image of post Project Hail Mary" /&gt;&lt;iframe width=&#34;560&#34; height=&#34;315&#34; src=&#34;https://www.youtube.com/embed/v-Hb0UknaMg?si=lZxe825dBbiKT8y2&#34; title=&#34;YouTube video player&#34; frameborder=&#34;0&#34; allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; allowfullscreen&gt;&lt;/iframe&gt;
&lt;p&gt;當初聽到號稱可以媲美星際效應的極限返航要上映時，就覺得啊～又有太空電影了嗎！好興奮啊！一方面想知道到底是不是真的有料的星際電影（想當初看完米奇 17 號的幻滅感），一方面自己也算是一個輕度太空科幻迷，對於講述宇宙題材的電影都蠻有興趣的，喜歡那種浩瀚無垠、很慢很廣的故事風格。&lt;/p&gt;
&lt;p&gt;Project Hail Mary 的電影畫面一如其他太空電影，外太空與銀河的呈現非常廣袤而寧靜，但不同的是他的色彩配置又再更絢爛許多，有一些場景是滿版的粉紅色、亮黃色，搭配管弦樂的襯托，宇宙的浩瀚與人類的渺小呈現顯著對比，令人印象深刻。&lt;/p&gt;
&lt;p&gt;而他的故事敘事也很特別，採取倒敘的方式，先告訴我們主角 Ryland Grace 遇到了太空空難，兩名隊友去世僅存他一人，再進一步道出他的個人背景、他為何會出現在這艘太空船上，他的任務目標是什麼。觀眾在電影的進程才逐漸了解主角與世界遇到的問題是什麼：一個鬱鬱不得志的生物學博士，處在太陽逐漸被吞噬的地球危機，被危機處理小組的 Stratt 招募，與數十個地球頂尖的科學家一起解決這個問題。&lt;/p&gt;
&lt;p&gt;Ryland 成功找到 astrophage 繁殖的方法（we made a baby!），也發現 astrophage 可以帶來巨大的能量，提供動能讓地球科學家造訪 Tau Ceti(天倉五) 去找尋這顆星球未被 astrophage 奪去光能的原因。但因爲實驗失誤，原本要參加這次計畫的科學家全被炸死，所以只能讓 Ryland 親自上場。從一開始 Ryland 以小學自然科老師初登場，到他被迫出航、滿心抗拒，就讓人看出這次電影主角與其他部非常不一樣的地方。相較於星際效應、阿波羅 13 號、星空浩劫（禮砲7號）主角們那種義無反顧的英雄氣概，Ryland 可說是相較的比較沒自信、愛說幹話、厭世感滿滿，對於去拯救全人類這件事情也沒有捨我其誰的責任心。他在電影裡有句名言：I put the not in astronaut! I can&amp;rsquo;t even moonwalk, why send me to it!&lt;/p&gt;
&lt;p&gt;可是當時光逐漸流逝，可以發現 Ryland 逐漸地去接受並完成他的任務目標。他送走了兩位同事，還遇到了外星生物 Rocky，在與它交流的過程建築了友誼。和 Rocky 交流這段讓人看到 Ryland 聰明的一面，首先他先尋找一種方法讓他與未知生物 Rocky 可以溝通（aka 數字），再來他用筆電迅速建構一個語言模型（啊，好熟悉！），讓 Rocky 的表達與他熟知的語言可以互通，再轉成語音模式，這樣一來就有一種 Rocky 與 Ryland 在同頻說話的感覺了（選擇語音樣本這段也很好玩）。有個小地方也讓我覺得 Ryland 很聰明，一開始他要費力地爬行在艙外，才可以取得 Rocky 投擲過來的太空信物，後來幾個畫面顯示他調轉了太空船的方向，讓信物被投擲過來時可以對準艙門，這樣東西直直送過來他手上，就不用費力去外面撿回來了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/project-hail-mary/pic.png&#34;
	width=&#34;1200&#34;
	height=&#34;750&#34;
	srcset=&#34;https://theoutsidelaine.com/p/project-hail-mary/pic_huee80da1d1cbad09711d5e923f8dffae8_87926_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/project-hail-mary/pic_huee80da1d1cbad09711d5e923f8dffae8_87926_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;160&#34;
		data-flex-basis=&#34;384px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;電影對於外星生物的設定也很有趣，從 Ryland 需要用語言模型才可以解譯 Rocky、但 Rocky 不需要，可以想像這個外星生物應該比地球人聰明不少，感官也敏銳很多，掌握許多科學機械技能，但在構築溝通模式上卻是讓比較不那麼智能的地球人 Ryland 來主導，而且由於其物種限制，對於相對論等高等物理知識也沒有去探索過。兩個在其星球都是最為聰慧的個體相遇，思考頻率快的契合，很快的就找到天倉五沒有被吞噬的原因：Taumeoba，並合力策劃解決方法，試圖拯救兩人的星球。&lt;/p&gt;
&lt;p&gt;後來 Rocky 與 Ryland 互相拯救的橋段讓人看的熱淚盈匡，也讓人發現 Ryland 變得更 committed、不逃避，勇於去追求自己真正想要做的事情。最後他選擇放棄回去地球的機會，利用剩餘的動能去拯救 Rocky，最後兩人一起回到 Rocky 的星球。在那裡 Rocky 為他打造了一片海洋，填充了適合地球人呼吸的空氣，並讓他回歸最熟悉的生活方式：當一個小學自然科老師，教導 Erid 星球上的外星小朋友們物理知識，是一個很可愛的結局。Ryland 選擇放棄回地球，除了重視友誼之外，也許也因為地球上已再無讓他掛懷的存在。而在這裡他有新的生活方式，探索新的未知領域，相較於以前侷促、被比較的狀態，外星反而是一個更自在的地方。或許 Ryland 不只拯救了地球、拯救了太陽，也拯救了他自己。&lt;/p&gt;
&lt;iframe width=&#34;560&#34; height=&#34;315&#34; src=&#34;https://www.youtube.com/embed/qN4ooNx77u0?si=spA68_gGScRAZJKC&#34; title=&#34;YouTube video player&#34; frameborder=&#34;0&#34; allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; allowfullscreen&gt;&lt;/iframe&gt;
&lt;p&gt;電影裡面有個橋段是 Project Hail Mary leader Stratt 唱卡拉 ok 的情境。當時她上台唱歌時，歌曲語調悠揚、緩慢，意境悠遠，還以為是哪首經典的歐美老歌。沒想到後來去查才發現是 Harry Styles 的 Sign of the Times，著實讓我小小驚訝了一下。&lt;/p&gt;
&lt;p&gt;我很喜歡這首歌的歌詞，與 Ryland 即將遠航、與地球上的同事訣別的心態很能呼應。&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Just stop crying its the sign of the times, we got to get away from here&amp;hellip;. Just stop crying it will be alright, they told me that the end is near, we gotta get away from here&amp;hellip; Just stop your crying have the time of life, breaking through the atmosphere, and things are pretty good from here&amp;hellip; Remember everything will be alright, we can meet again somewhere.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;其實一直不太清楚為何這個計畫的名字叫做 Hail Mary (聖母)，一開始看電影以為是因為他是很神聖而重要的計畫，才以聖母命名。後來去查才知道這算是一個運動用語，指的是美式足球的極長距離長傳，因爲難度太高、容易被攔截，成功率極低，所以被稱為 Hail Mary pass. 起源是達拉斯牛仔隊的 Roger Staubach 在比賽最後傳出逆轉長傳，讓球隊獲得勝利，事後採訪他說當時他傳出去時閉上雙眼、嘴上喊著 Hail Mary 才做到的，自此之後這種球就被稱為 Hail Mary pass。後來沿用於日常生活，指的是一種孤注一擲的行為。中文片名可以翻譯為極限返航，也是很神奇的操作，極限是極限但主角的目標從來不是返航🤔 但如果直接翻譯為萬福瑪莉雅號也是沒有什麼美感。翻譯真是一個難題。&lt;/p&gt;
&lt;p&gt;另外也突然想到，對於外星人外貌的描述原本我覺得會有一點驚喜，不過看到 Rocky 的樣子，讓我覺得蠻意料之內的（跟膽大黨的蝦蛄星人長得有點像？！）。也覺得有點困惑，視覺是多麼重要的能力，但是在電影的描述上反而外星生物的演化上是會捨棄這個能力的。&lt;/p&gt;
&lt;p&gt;總之這是一部美學造詣高，也很與眾不同的太空科幻劇，相較於其他太空電影，比較著重於主角個人的淬煉與轉變，敘事步調平實也有許多幽默自嘲的對話，很可以讓觀眾有所共鳴。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>The Last of Us</title>
        <link>https://theoutsidelaine.com/p/short-thoughts-on-the-last-of-us/</link>
        <pubDate>Thu, 12 Feb 2026 00:00:00 +0000</pubDate>
        
        <guid>https://theoutsidelaine.com/p/short-thoughts-on-the-last-of-us/</guid>
        <description>&lt;img src="https://theoutsidelaine.com/p/short-thoughts-on-the-last-of-us/cover.png" alt="Featured image of post The Last of Us" /&gt;&lt;iframe width=&#34;560&#34; height=&#34;315&#34; src=&#34;https://www.youtube.com/embed/_5I3NLky540?si=oNImGWE0IWotbRqH&#34; title=&#34;YouTube video player&#34; frameborder=&#34;0&#34; allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; allowfullscreen&gt;&lt;/iframe&gt;
&lt;p&gt;最後生還者講述了一個後末日時代的故事。二十年前真菌感染人體引爆全球疫情，病毒將患者快速轉化為失去神智的喪屍，致使人類文明崩塌。二十前後的現在，軍閥割據、解放軍崛起，平民艱難地在夾縫中生存。後疫情時代的人們是複雜的，從上個時代活下來的人，有些早已麻木，有些讓雙手染上了鮮血，有些人為了回到過去仍充滿希望。而新生代的人們出生於新秩序，對於生死帶有習以為之的樂觀，儼然與上個世代的人形成對比。&lt;/p&gt;
&lt;p&gt;故事從 Joel 跟 Tess 帶著身攜免疫抗體的少女 Ellie 尋找 Firefly 製作抗體開始，一路上遇到的阻礙無窮。隨著 Tess 身亡、Frank 與 Bill 自戕，很快的 Joel 與 Ellie 只剩下彼此結伴同行。後來兩人遇到第一次重大事件：在 Kansas city 遭遇 Kansas-based Firefly group 的暗襲，與 Henry, Sam 兩兄弟結伴脫逃，最後卻難逃喪屍病毒的感染，Ellie 被迫一槍解決昨日還在談笑風生的朋友 Sam，以拯救岌岌可危的 Joel。這整個橋段是我第一次體會到故事創作者想要帶給我們的討論，關於道德兩難的情況究竟該是多麼的糾結。一邊是解救人民於軍閥荼毒的 Firefly, 一邊是愛弟心切的平凡的哥哥。為了讓瀕死的弟弟排到醫療資源，Henry 不惜背刺 Firefly 讓領導人被俘虜處死以與政府軍交換資源，結果最後 Firefly 擊敗政府軍，Henry 於是被滿城通緝。要居高臨下的責備 Henry 、或者責備為了找到 Henry 給家人復仇而殘忍逼供的現役 Firefly 領導人，感覺都是一件困難的事情。如果自己遇到類似的事情，可能也會如此這般的惶恐、憤怒、焦急吧。而憤怒在末世的渲染下肯定也會釀成更大的衝突，所以這個橋段的情節感覺很合理，也很讓人感嘆。這是作者第一次討論到復仇這個主題。&lt;/p&gt;
&lt;p&gt;後來 Joel 和 Ellie 繼續行走，終於找到了失散的弟弟 Tommy，也被歡迎加入了一個存在著大量倖存者、資源豐富的社區。不過為了疫苗，Joel 和 Ellie 待了社區一陣子後就馬上繼續出發尋找 Firefly。結果在途中他們不幸遇襲，Joel 身受重傷，Ellie 則被俘虜到一個食人聚落。最後靠著 Joel 神力天降，以及 Ellie 關鍵時刻爆發，兩人才得以逃出。這段是所有橋段中數一數二印象深刻的，牧師 David 從好人反轉為一個 psychopath 真的是非常嚇人。這裡也是作者第一次談到末日邪教的議題，末日下人們對於信仰領導者有極大的依賴，導致他們感受到無上的權力，進而做出令人髮指的事情。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/short-thoughts-on-the-last-of-us/drama.png&#34;
	width=&#34;1186&#34;
	height=&#34;798&#34;
	srcset=&#34;https://theoutsidelaine.com/p/short-thoughts-on-the-last-of-us/drama_hucd534702321fec13083610de861e5a86_1368895_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/short-thoughts-on-the-last-of-us/drama_hucd534702321fec13083610de861e5a86_1368895_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;148&#34;
		data-flex-basis=&#34;356px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;最後，便到了讓我久久無法釋懷的故事橋段。當 Joel 和 Ellie 終於費盡千辛萬苦來到了 Salt Lake City，找到了 Firefly 的精英部隊與醫生，結果領導人 Marlene 卻告訴 Joel, 這個研製疫苗的手術確定會導致 Ellie 喪命，請他為了大局不要干預。結果 Joel 在反抗無效、且情緒高昂的狀況下，做了一件讓當時看到這裡的我極為震驚的事：他一人一槍，把 Firefly 醫院幾乎所有人都殺光，連老戰友 Marlene 也不放過，然後扛起 Ellie 轉身就走。而當醒來的 Ellie 問手術進行得如何，他一邊開車一邊淡淡的回答，還有很多人有抗體可以做疫苗，不用擔心，我們回家吧。我覺得這是故事很高光的地方，他帶出了 Joel 人格的轉變，從一開始麻木的任務導向，後來在不知不覺之間對 Ellie 產生極大的移情，把她當成是自己的女兒，所以做出這樣可怕的事情。&lt;/p&gt;
&lt;p&gt;Joel 的失控就像是一股帶著死亡氣息的蝴蝶效應。幾年之後，醫院事件的遺族 Abby 找到了 Joel 與 Ellie 所在的聚落，並趁暴雪來臨、Joel 放鬆警惕之際襲擊了他，且殘忍的虐殺了他。Ellie 得知後，憤怒的她在夜晚不顧眾人的勸阻，與夥伴 Dina 一起前往 Washington 尋找 Abby 復仇。其實 Ellie 早就知道 Joel 說謊了，但這個謊言也讓她心中產生負罪感、產生憤怒，最後隨著 Joel 的死亡凝固為想念、悲傷，與復仇的動力。Ellie 潛入 WLF 巢穴，結果卻誤殺了 Abby 的兩個夥伴。最後一幕，是 Abby 對她說：we let you live, and you wasted，然後槍殺了 Ellie 視為家人的兩個夥伴。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/short-thoughts-on-the-last-of-us/guitar.png&#34;
	width=&#34;1638&#34;
	height=&#34;812&#34;
	srcset=&#34;https://theoutsidelaine.com/p/short-thoughts-on-the-last-of-us/guitar_hu78c1c370d6812520b03508c843448e18_1126186_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/short-thoughts-on-the-last-of-us/guitar_hu78c1c370d6812520b03508c843448e18_1126186_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;Joel teached Ellie how to play a guitar.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;201&#34;
		data-flex-basis=&#34;484px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;看完 season 1, season 2 後產生了許多複雜、難以言喻的思緒。理智上覺得 Joel 受到了懲罰，但感性上卻為他們感到很悲傷，其實看完後從不覺得 Joel 是個壞人，從觀眾的角度看著 Ellie 與 Joel 一路走來，好不容易可以團聚了，任誰都可以理解 Joel 的行動。但對於 Abby 這一切又何曾公平？這可能就是故事想說的，that someone&amp;rsquo;s hero, and someone&amp;rsquo;s is a villain in someone&amp;rsquo;s story。看到 Abby 對 Joel 的復仇，再看到 Ellie 對 Abby 的復仇，讓人不禁感嘆暴力的循環像風暴一樣把所有人都捲進去。相較於影集僅從 Ellie 的視角看待，遊戲原作者在劇情設計上更為精妙，先讓玩家以 Ellie 的視角玩過一遍，再讓玩家用 Abby 的視角玩過一次，目的是希望玩家隨著遊戲的深入，可以理解 Ellie 也理解 Abby，引導玩家了解仇恨、學習放下。不得不說，在為 Ellie 同理後又要反過來原諒 Abby，對於玩家那肯定是一個十分痛苦的過程。&lt;/p&gt;
&lt;p&gt;過往許多文學、影劇都曾描繪過復仇這個主題，從西方的基督山恩仇記、東方的聊齋誌異，到近代的武俠文學連城訣、電影 John Wick、韓劇黑暗榮耀。復仇是一種私刑正義，是對主角本人與其所愛之人所承受的傷害的報復，在這些作品中，復仇透過暴力、計謀的方式得到實現，可以讓主角宣洩痛苦與恨意。最終惡人有罪、沉冤得雪，觀眾內心也可以得到一絲釋放的快感。但在 The Last of Us 對於復仇感受到更多的不是快感，而是沈重與悲傷。Ellie 和 Abby 在復仇之路上可說非但沒有解脫，犧牲的反而越來越多。所恨的人死了沒錯，但因為對方的恨，自己身旁又有其他人也逝去，於是產生了循環。但時間無法倒退，最原始的傷害是無法復原的，再怎麼傷害對方，心中難免都還是會有缺憾。我覺得這放大到許多地方，比如戰爭的發生，都是相同的道理。反過來說，要求他人要原諒也是不合理的，畢竟每個人心中的傷痛難以放在同一個天平上等價估量。到頭來我們都是遊戲的玩家、劇外的觀眾，只能努力的去體會故事主角走過的路，去感受他們感受到的，並學會更寬容地看待世上很多事情。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Do we think differently in different languages?</title>
        <link>https://theoutsidelaine.com/p/language-learning-reflection/</link>
        <pubDate>Sun, 26 Oct 2025 00:00:00 +0000</pubDate>
        
        <guid>https://theoutsidelaine.com/p/language-learning-reflection/</guid>
        <description>&lt;img src="https://theoutsidelaine.com/p/language-learning-reflection/cover.png" alt="Featured image of post Do we think differently in different languages?" /&gt;&lt;p&gt;最近在學習韓文，發現韓文的動詞型態非常有趣。水平來看，每個動詞都有現在式、過去式、未來式、命令式&amp;hellip;等多種型態；垂直而言，動詞們又被「敬語」態所影響，每個動詞一共有正式敬語、一般敬語、半語等三種樣態。用另外種角度來詮釋的話，很像是在韓文的 context 中，動作被「時間」、「意圖」、「人與人之間的距離感」所劃分與定義著。另外，韓文大量使用了中、美元素，再轉換為自身的文字，給人一種喝了明明是新口味的飲料，但內容物讓人很熟悉的奇特的感覺。&lt;/p&gt;
&lt;p&gt;對韓文的種種發現讓我感到很有趣，於是我開始逐一比較之前學過的語言，對我而言最鮮明的特色：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;首先是最熟悉的英文，每個動詞只被「時間」這個維度所切分，不會有其他因素影響動詞的轉換。所有的 letters 由僅僅 26 個字母組成，代名詞以人稱狀態（性別、單複數）區分。相較於其他語言，英文給人感覺沒有太多的預設，有種簡易的感覺。&lt;/li&gt;
&lt;li&gt;接著是西班牙文，對我來說最特別的莫過於西文世界中，把所有名詞區分為男性/女性的特點，讓人感覺西文的世界觀裡性別很重要，男的有男的的特色、女的有女的的特色，會被重視、區分開來。另外，西文的動詞型態不僅被「時間」所劃分，也會依據「人稱狀態」劃分，與韓文相同，也會依據「意圖」（命令、推測）做劃分。&lt;/li&gt;
&lt;li&gt;至於母語中文，相較於英文/西班牙文/韓文，有 alphabet/character 的概念，中文的每個字都是象形文字，難以用少量的元素拼裝成不同的字，截至 2025 年 9 月 unicode 已經收錄了至少 15 萬個中文字體，因此相較於其他語言，要讀懂中文，必須要擁有一定量的閱讀中文字的能力才行。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果用比喻的話：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;中文的文字核心感覺很小，每個字像是一個小宇宙一樣有自己的意思、書寫方法，但他們必須像樂高一樣，得經由組裝，才可以形成一個句子，形成時間、先後等文意，以及表達你我他她人稱、性別等狀態（他來了、他要來了）&lt;/li&gt;
&lt;li&gt;英文的文字核心稍微擴大一點，每個動詞靠著 ed / ing 表達樣態，每個名詞由26個字母組成，簡單的二元分類性別，擁有很大的靈活度，但是更多的型態也必須靠著組裝擴大文句的意思（he came / he is coming）&lt;/li&gt;
&lt;li&gt;西文的文字核心就比較大一點，每個動詞依據不同的型態，可以有不同時間、狀態的意思（va / vino / esta viniendo），每個名詞也內建了不同性別，有種擬人化、擬真化的童趣感；&lt;/li&gt;
&lt;li&gt;韓文的文字核心也是算大的一種，除了跟西文一樣動詞會依據時間、意圖區分，更會用「人與人的距離感」區分（안녕하십니까/ 안녕하세요/ 안녕），是我覺得很獨特的，某種意義上也代表著他們重視情感距離、上下關係的民族特性。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;the-thinking-for-speaking-hypothesis&#34;&gt;The thinking for speaking hypothesis&lt;/h2&gt;
&lt;p&gt;思考至此，我開始認為，每個語言的設計或許相當於是表達了每種語言自己的一套思考方式，使用該語言的人，經過經年累月的學習將語言烙印在腦海，卻也會被規則所內化，這樣是否代表語言也會影響學習語言的人的思考方式？使用某種語言的人，是否潛意識下會接受某種價值觀，形成某種個性？&lt;/p&gt;
&lt;iframe width=&#34;500&#34; height=&#34;315&#34; src=&#34;https://www.youtube.com/embed/XINQvKbqzq0?si=RynAN9vIqntnh0_3&#34; title=&#34;YouTube video player&#34; frameborder=&#34;0&#34; allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; allowfullscreen&gt;&lt;/iframe&gt;
&lt;p&gt;BBC 在 2020 年有隻影片有談論到這件事情。有位學者提到一個有意思的理論：The thinking for speaking hypothesis。這個假說提到，語言的結構會影響我們的思維，使我們在使用該語言時，特別注意現實中與該語言相關的重要特徵。語言涉及某種形式的「影像模擬」，而這會影響我們對某些事件的感知、理解方式。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/language-learning-reflection/color.png&#34;
	width=&#34;1544&#34;
	height=&#34;506&#34;
	srcset=&#34;https://theoutsidelaine.com/p/language-learning-reflection/color_hub7e49b1dbeb484b4fffae65210d052c5_305637_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/language-learning-reflection/color_hub7e49b1dbeb484b4fffae65210d052c5_305637_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;305&#34;
		data-flex-basis=&#34;732px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;有個實驗是讓受測者在眾多的、光譜漸層排列的色塊中選擇一個，30 秒後，再請他們重新從眾多色塊中，挑出哪一個是剛剛看到的色塊。&lt;/p&gt;
&lt;p&gt;在受測者中，英文使用者大多難以再次把色塊正確挑出來，但 Himba speaker （納米比亞地區的語言）卻可以輕鬆地把那個色塊從相似的其他色塊中挑出來。學者指出，會有這樣表現的差異，是因為「顏色」在 Himba 是關鍵要素，他們的語言系統細緻的 encode 了許多種不同顏色，因此他們的大腦自然的可以輕鬆解譯、分辨不同的顏色。&lt;/p&gt;
&lt;h2 id=&#34;we-are-almost-certainly-given-to-think-in-a-particular-way&#34;&gt;We are almost certainly given to think in a particular way&lt;/h2&gt;
&lt;p&gt;更早以前（2018 年）在 TED 上有隻我很喜歡的影片，由 Lera Boroditsky 講述，介紹語言與文化認知之間的關係。&lt;/p&gt;
&lt;iframe width=&#34;500&#34; height=&#34;315&#34; src=&#34;https://www.youtube.com/embed/RKK7wGAYP6k?si=aJxZkFuM0YPDEM-O&#34; title=&#34;YouTube video player&#34; frameborder=&#34;0&#34; allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; allowfullscreen&gt;&lt;/iframe&gt;
&lt;p&gt;她提到了一個很有趣的實驗：在描述一起意外事件，英文和西班牙文分別會怎麼描述呢？&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/language-learning-reflection/vase.png&#34;
	width=&#34;1280&#34;
	height=&#34;602&#34;
	srcset=&#34;https://theoutsidelaine.com/p/language-learning-reflection/vase_hu1041a5d8bc1057469c7016192f6e78ea_958023_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/language-learning-reflection/vase_hu1041a5d8bc1057469c7016192f6e78ea_958023_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;212&#34;
		data-flex-basis=&#34;510px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;英文使用者會形容 He broke the vase，因為英文結構需要有主詞 + 受詞；西文使用者會形容 El jarrón fue roto，因為這是一起意外事故，通常西文 context 描述意外時，會著重在事件主體，使用不同的語法結構。因此，英文使用者比較會記得是誰發生了這起意外，而西文使用者比較不會記得是誰做的、但是比較會記得這是一件意外、記得事件的意圖。&lt;/p&gt;
&lt;p&gt;不同語言的使用者，會依據語言的規則要求，把注意力放在不同的地方。因此，兩個人看到同一起事件，最後卻會記得該事件不同的細節，這跟語言使用的特性密不可分。Language guides reasoning of events。&lt;/p&gt;
&lt;h2 id=&#34;學習新語言的心態&#34;&gt;學習新語言的心態&lt;/h2&gt;
&lt;p&gt;思考至此，不禁在想，學習一個新的語言，就代表我即將 include 進這個語言的某種特性，這是否是我所想要、預期的？長時間學習英文讓我接觸了自由、兼容、幽默自嘲、理性的美式文化，學習西文讓我體會南歐人隨興、浪漫的文化外，語言上也練習了用更精簡的方式去表達一個動作、一件事情，並對生活上的各種物件注入情緒，賦予他們情感意義；韓文方面，雖然對於敬語這件事情感到尊重而不理解，但韓國產出了很多我所喜愛的文化特色，比如他們的影視作品、某些歌手的創作，如果可以因為學習韓文而更貼近創作者的心境，那感覺也會很不錯的。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>夢想成為律師的律師</title>
        <link>https://theoutsidelaine.com/p/2025-korean-drama/</link>
        <pubDate>Tue, 07 Oct 2025 00:00:00 +0000</pubDate>
        
        <guid>https://theoutsidelaine.com/p/2025-korean-drama/</guid>
        <description>&lt;img src="https://theoutsidelaine.com/p/2025-korean-drama/cover.png" alt="Featured image of post 夢想成為律師的律師" /&gt;&lt;p&gt;最近看了一部好看的韓劇，記錄一下自己的觀後心得。&lt;/p&gt;
&lt;p&gt;&amp;lt;夢想成為律師的律師們&amp;gt; 以女主角姜孝敏的視角展開，講述了她與組員們在訴訟組組長尹錫勳的帶領下，接手了許多不同的案子的故事。每個案子背後都有一個核心議題，藉由律師們的視角，帶領觀眾去體會每個訴訟背後的困難，以及隱藏的情感。&lt;/p&gt;
&lt;p&gt;這部劇將「訴訟」與「愛」關聯在一起，我覺得是一個有意思的觀點。女主角在當加入訴訟組時，被問到加入的原因，她提到一個觀點：「&lt;strong&gt;世上存在許多種愛，但是愛會傷人，當這個傷口開始化膿，人們會想到訴訟。瀕臨那個臨界點時，他們相信最後的手段&amp;ndash;訴訟，是唯一能守護他們幸福與權利的方法。&lt;/strong&gt;」因為愛與訴訟兩者息息相關，所以律師們收集證據、還原現場、形成論述，都需要更深刻地以人性為出發點去思考。不過相較於其他律政劇更為灰暗的走向，&amp;lt;夢想成為律師的律師們&amp;gt; 對於案件背後愛的敘述更為溫柔正面，且在最後，總是會讓案件的主角有一個完好的結局。&lt;/p&gt;
&lt;p&gt;在主角們調查不同的案件，遇到種種的反轉時，身為觀眾也總能在他們成功解決問題後感嘆，律師原來不只是要為當事人辯護，也同時要找到案件背後的真實原因來鞏固自己的權益，更需要在扎身於權謀險惡後，仍然對於人性的美好、對於人與人之間的愛與關懷感到有信心。&lt;/p&gt;
&lt;p&gt;我覺得這部劇的演員們都有把律師嚴肅對待案件的態度演繹得很好，不管是在法庭上的論敘、尋找線索的過程、團隊合作的過程，都有營造出身為律師那種專業、專注的感覺 (kudos to 主演李陣郁、鄭彩娟)。另外，每一個單元的法律解釋也很淺顯易懂，同時呼應人情義理，劇情十分的流暢且扎實。尤其喜歡第二集與生殖醫學中心的辯護戰，金句「打官司重點不是誰贏，而是誰受的傷害比較小」，以及第三集無碰撞的交通事故，在尋找證據的過程揭開原告的家庭問題，進而剖析了家庭教養帶來的孩童心理創傷。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/2025-korean-drama/drama.png&#34;
	width=&#34;1480&#34;
	height=&#34;992&#34;
	srcset=&#34;https://theoutsidelaine.com/p/2025-korean-drama/drama_hu813e039e906aa82969631b8a7dbb86fb_1379927_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/2025-korean-drama/drama_hu813e039e906aa82969631b8a7dbb86fb_1379927_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;劇照&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;149&#34;
		data-flex-basis=&#34;358px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;除了講述案情外，&amp;lt;夢想成為律師的律師們&amp;gt; 也以案件帶出主角們面臨的工作、人生問題。第二集時，原告看見女主角也一起進來聆聽他的案件，表示他不習慣在講述關乎他身體狀況時有異性律師來聆聽，希望僅由男主角處理就好。身為新人的女主角尊重當事人心情，於是順從的離開，結果結束後被男主角提醒：「他把你當作女人而不是他的律師，你這麼容易就退讓了？&lt;strong&gt;連自己都無法辯護，你怎麼為別人辯護呢？&lt;/strong&gt;」這句話點醒了女主角，後來她約了當事人單獨談話，以法學院教授的話告訴他，那些讓人不敢提到的詞，在律所這邊是不帶情緒的，只是法律名詞，讓當事人可以放心討論，並在後面用分析找到當事人隱藏的事實片段，最後靠著謹慎與策略贏得訴訟的勝利。&lt;/p&gt;
&lt;p&gt;最後，隨著角色們逐步解決每個案件，他們的人生難題也隨著時間慢慢地得到答案。我喜歡這部劇的結局安排，典型的喜劇收場，男二女二收穫愛情，女主角在這份淡淡的 mentorship 情誼之中得到了許多成長，男主角則是釋懷了對於婚姻的遺憾。如同真實世界中，我們不斷生活，並有所收穫，對於未來也充滿展望，不需要方方面面都顧及到。平淡、溫和的結局，為這部劇帶來了許多餘韻。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Chiikawa</title>
        <link>https://theoutsidelaine.com/p/chiikawa-afterthoughts/</link>
        <pubDate>Wed, 09 Oct 2024 00:08:00 +0000</pubDate>
        
        <guid>https://theoutsidelaine.com/p/chiikawa-afterthoughts/</guid>
        <description>&lt;img src="https://theoutsidelaine.com/p/chiikawa-afterthoughts/cover.png" alt="Featured image of post Chiikawa" /&gt;&lt;p&gt;想了很久要紀錄最近看得什麼，結果浮現在腦袋的居然是這三隻毛茸茸的小生物～ 那就來寫寫我看吉伊卡哇的小小心得吧&lt;/p&gt;
&lt;p&gt;先上一首暖貓小八的自彈自唱之歌：&lt;/p&gt;
&lt;iframe width=&#34;560&#34; height=&#34;315&#34; src=&#34;https://www.youtube.com/embed/lkelVwG2f_c?si=gtbTCJMH9L5oyxRU&#34; title=&#34;YouTube video player&#34; frameborder=&#34;0&#34; allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; allowfullscreen&gt;&lt;/iframe&gt;
&lt;p&gt;大概是藏壽司之亂之後，過很久的十月才開始看吉伊卡哇的動畫的。之前只有輾轉從 thread 上面看到很多可愛的片段，以為只是配合貼圖出的小短片，結果沒想到出的順序居然還是漫畫 -&amp;gt; 動畫 -&amp;gt; 貼圖，真是令我大開眼界。在脆上覺得最可愛的就是&lt;a class=&#34;link&#34; href=&#34;https://www.threads.net/@_yuk.1n/post/C_V-vIAvnmr?xmt=AQGzkCZqbi9yY6BGJYbHXbH30HX3nzvqZGhdRnQdGDzOuA&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;小可愛跟小八道別之後不約而同回頭的片段&lt;/a&gt;，以及&lt;a class=&#34;link&#34; href=&#34;https://www.threads.net/@camille_8318/post/DAREG_PPFhK?xmt=AQGzzxABCMBmsN99LItAo3jyPLcIHb4Sorp5NPkeqC4Mxw&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;烏薩奇兔子超級奇怪又可愛的「蛤」&lt;/a&gt;，每次聽到都會有種莫名其妙的感覺而笑出聲。可能最近的生活要面對的新事情很多，需要一點療癒感，加上又有人說這其實是大人向動畫激起了我的好奇心，所以就下載了巴哈姆特開始追啦。&lt;/p&gt;
&lt;p&gt;第一集看到烏薩奇用鍋鏟打鬆餅然後呼嚕一口吞掉我還以為只是很一般的可愛番，沒想到很多設定讓人很眼睛一亮，算是用可愛的形式呈現這個社會真實運作的某些部分，甚至還有彈幕說這是熱血少年番（？）&lt;/p&gt;
&lt;h2 id=&#34;多樣的種族設計精細的工作制度&#34;&gt;多樣的種族、設計精細的工作制度&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/chiikawa-afterthoughts/fight.png&#34;
	width=&#34;1000&#34;
	height=&#34;563&#34;
	srcset=&#34;https://theoutsidelaine.com/p/chiikawa-afterthoughts/fight_hue2908abc8808611599fbe6a77cc6d0b1_72599_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/chiikawa-afterthoughts/fight_hue2908abc8808611599fbe6a77cc6d0b1_72599_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;小可愛和小八在練習討伐&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;177&#34;
		data-flex-basis=&#34;426px&#34;
	
&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=&#34;另有隱情的吉伊卡哇世界&#34;&gt;另有隱情的吉伊卡哇世界&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/chiikawa-afterthoughts/armor.png&#34;
	width=&#34;800&#34;
	height=&#34;450&#34;
	srcset=&#34;https://theoutsidelaine.com/p/chiikawa-afterthoughts/armor_hu178afcf0f8dff47eea6f1767cfb4b935_97855_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/chiikawa-afterthoughts/armor_hu178afcf0f8dff47eea6f1767cfb4b935_97855_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;177&#34;
		data-flex-basis=&#34;426px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;從不曉得第幾話的片段開始，吉伊卡哇就給我「umm 這部動畫應該另有隱情」的感覺：首先，「討伐」這個工作本身命名就是挺奇怪的，大可以說是就是去除蟲就好，但討伐這個用詞聽起來就很像他們有個共同的敵人族類。延伸下去，到大概 20 幾話白色松鼠出現後，劇情開始讓我覺得不太對勁，有一隻很像年獸的白色大蟲追趕著白色松鼠，嘴上喊著還給我的字眼，爾後白色松鼠的出現都讓我感到很不適，比如他很喜歡裝可愛想吸引盔甲人的讚美、或者要求兔兔把食物讓給他之類的，一舉一動都讓人很難覺得他也是可愛小生物的一員，非常不討喜。後來去看了網友的推測，才知道其實白色松鼠是與大蟲交換了靈魂。而大蟲想要交換靈魂的原因，細究下去又是一個令人細思極恐的設定：可愛小生物與奇美拉其實是同一個物種的，可愛小生物會因為不知名原因，轉換為醜陋、大隻，且有爪子的奇美拉，受到其他小可愛的討伐。所以大蟲才想要跟白色松鼠交換靈魂，因為交換之後「它也可以被讚美很可愛的」。也因此，盔甲人種族被交代「不要與可愛小生物處得太近」，估計也是因為可愛小生物就是可怕奇美拉，怕因此有感情無法順利驅趕等等。也才有後來，草地上躺著一隻穿不進去睡衣的奇美拉，雖然動畫沒交代清楚，但似乎曾經就是吉伊小可愛的工廠同伴呢。&lt;/p&gt;
&lt;p&gt;了解到這邊之後突然有點小哀傷，可愛小生物們知不知道那些被他們努力討伐、被他們害怕的奇美拉，其實也跟自己同類呢？有一種感覺，這個世界觀撥開來看，會是另外一個讓人心涼的不一樣的世界觀呢，莫名有種可愛版 &amp;lt;進擊的巨人&amp;gt; 的感覺 lol。&lt;/p&gt;
&lt;p&gt;不過相較於進擊的巨人，吉伊卡哇的設定還是溫馨許多啦，比如奇美拉有分成友善型、擬態型兩種。友善型奇美拉就是無害的可愛怪獸，比如可愛的獨眼大叔就是友善型奇美拉，而擬態型就是會傷害小可愛們的怪獸。雖然如此，劇情還是寫實了一波，安排讓擬態型奇美拉有能力偽裝成友善型去欺騙小可愛們。不知道第幾集，吉伊小可愛被小甲蟲擬態型欺騙友情，一開始還讓小甲蟲住進家裡，最後反而差點被小甲蟲變身抓走，看到吉伊傷心落淚真的也讓我覺得有點難過呀。&lt;/p&gt;
&lt;h2 id=&#34;對於三個小生物的想法&#34;&gt;對於三個小生物的想法&lt;/h2&gt;
&lt;p&gt;看了 190 集，來說說對三隻主角小生物的想法～&lt;/p&gt;
&lt;h3 id=&#34;吉伊小可愛&#34;&gt;吉伊小可愛&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/chiikawa-afterthoughts/chiikawa.png&#34;
	width=&#34;594&#34;
	height=&#34;340&#34;
	srcset=&#34;https://theoutsidelaine.com/p/chiikawa-afterthoughts/chiikawa_hu9f0e616b67e20a9569b191e8aababf49_169974_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/chiikawa-afterthoughts/chiikawa_hu9f0e616b67e20a9569b191e8aababf49_169974_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;吉伊小可愛重新準備除草證照&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;174&#34;
		data-flex-basis=&#34;419px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;吉伊小可愛（其實他正式名稱就是小可愛而已，但我比較喜歡叫他吉伊小可愛～）給我一種溫和好朋友的感覺。雖然是個極致 I 人，不太喜歡說話，也很容易害羞，比較膽小愛哭，但是很滿足於生活，對朋友們都很好，比如他會默默在台下幫小八的吉他演奏應援，請兔兔吃東西，他也是串起三隻小生物友情的橋樑。我很喜歡吉伊小可愛雖然感到害怕，但還是會努力去做對的或是有意義的事情，比如去救人、去考試、去跟超人拍照，鼓勵真實世界的我們，雖然感到害怕但還是要努力，因為結果都是會有意義的。&lt;/p&gt;
&lt;p&gt;吉伊小可愛有個很有趣的設定，雖然考試運氣非常不好，連兩次除草證照都沒考過，但是抽獎運氣相當不錯，看到他又是抽到壽喜燒又是抽到房子就覺得，上帝為你關了一道門就會為你開一扇窗是吧（有個彈幕還直接叫吉伊考試用猜的ＸＤ很好笑）&lt;/p&gt;
&lt;h3 id=&#34;小八&#34;&gt;小八&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/chiikawa-afterthoughts/hachiwali.png&#34;
	width=&#34;501&#34;
	height=&#34;507&#34;
	srcset=&#34;https://theoutsidelaine.com/p/chiikawa-afterthoughts/hachiwali_hu3e920441f64a0fe735a6c374c44ac510_156902_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/chiikawa-afterthoughts/hachiwali_hu3e920441f64a0fe735a6c374c44ac510_156902_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;多才多藝的小八貓&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;98&#34;
		data-flex-basis=&#34;237px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;暖貓小八！在劇中小八貓是唯一會講話的可愛小生物（吉伊小可愛其實也會說但不多），而且他真的是一個很溫暖很可靠的好朋友，不僅對不同種族的生物都會很有禮貌，也超級有正義感，會主動去救落難的小生物同胞，但是對於比較無理取鬧的白色小松鼠會適時的不去過多關注。我很喜歡小八看到吉伊有什麼心事，就會主動說「你是不是在煩惱這個呀？別擔心～」，給予膽小的吉伊很多安慰和鼓勵，真的是很溫暖而且共情能力超強。不過小八的優點不僅於此，他會彈吉他、寫歌，甚至還是個學霸，除草證照那是一次過關的。&lt;/p&gt;
&lt;p&gt;不過小八在劇中算是經濟能力比較不好的那隻小生物，沒有錢買房子，只能住在沒有門的山洞，常常得擔心奇美拉侵襲，用破破的碗吃飯，想要相機還得慢慢存錢買ＱＱ 不過相信這麼厲害的小八有朝一日一定可以存錢買到房子的！買不到的話來姊姊這邊，衣櫃讓你隨便住！（入戲太深）&lt;/p&gt;
&lt;h3 id=&#34;烏薩奇個人的最愛&#34;&gt;烏薩奇（個人的最愛）&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/chiikawa-afterthoughts/usagi.png&#34;
	width=&#34;747&#34;
	height=&#34;416&#34;
	srcset=&#34;https://theoutsidelaine.com/p/chiikawa-afterthoughts/usagi_hu7f78540ba8e3b1d6c7a7a7e2c78e5449_463691_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/chiikawa-afterthoughts/usagi_hu7f78540ba8e3b1d6c7a7a7e2c78e5449_463691_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;呀哈～&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;179&#34;
		data-flex-basis=&#34;430px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;人稱社會我兔哥的烏薩奇，當初就是被他的「蛤」所吸引入坑的，結果也如我所預料，他真的是我最喜歡的小生物！&lt;/p&gt;
&lt;p&gt;兔兔烏薩奇一開始給我感覺就是一隻ㄎㄧㄤ兔，不會說話但很愛鬼叫，脆上面甚至有&lt;a class=&#34;link&#34; href=&#34;https://www.threads.net/@gengen._.life/post/DAxyvZXTK3H?xmt=AQGzvZTqStvzuqHmN46P0EDVBxEylKqxr1GvwHm7AApv3Q&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;烏薩奇語言解析&lt;/a&gt;。兔子的每一個狀聲詞都讓我覺得超級好笑，是一種怎麼聲音這麼莫名其妙，然後想著想著就笑出來的感覺。&lt;/p&gt;
&lt;p&gt;有許多烏薩奇的高光片段：印象深刻有一集，小八在講鬼故事，講完之後吉伊小可愛直接入戲太深被嚇哭，結果烏薩奇就是一句「蛤？」ＸＤ 還有一集，白色松鼠想要強迫烏薩奇給他好吃的果醬吐司，烏薩奇用手語表示用飯糰來交換，白色松鼠耍賴不要，結果烏薩奇就對他使出屁屁舞ＸＤ 另外一集，吉伊小可愛和小八和烏薩奇一起去森林，當其他人在擔心森林旅程時，烏薩奇就是很嗨，還把蘑菇直接往嘴裡塞，直接變成眼睛發射強光的兔兔ＸＤ&lt;/p&gt;
&lt;p&gt;我覺得烏薩奇給人一種充滿活力的感覺，不會把很多事情看得太 serious，每天都活得開開心心鬼吼鬼叫。雖然有時候會因為肚子太餓還是太無聊觸發一些小小危機，不過整體而言，烏薩奇給人一種很安心的感覺：當睡衣派對缺人、吉伊小可愛跟小八猶豫要不要當臨時演員時，烏薩奇已經主動把睡衣穿起來並跳起兔版睡衣舞（雖然被小八形容好像跟原版不太像），用行動表示對朋友的支持；當三人跟獨眼大叔被關到監獄，是烏薩奇早早吃完飯開始挖地道計畫越獄；當吉伊還是小八差點被抓走，也是烏薩奇一個飛踢把擬態奇美拉踢走的。是一隻在乎朋友、會主動提供他人有效幫助的兔子呢。我想是以上這些種種原因，讓我最喜歡他的吧。&lt;/p&gt;
&lt;p&gt;除此之外，烏薩奇也給人一種深不可測的感覺：雖然整天在外面浪，但卻有除草證照三級的好成績，而且感覺經濟能力不差，不僅只有他的討伐棒會噴火，還有餘裕購買整身的螢光棒cosplay，真讓人疑惑我們可愛的兔兔到底是何方神聖呢。套一句彈幕的評語：安全的時候，烏薩奇會帶給你危險；危險的時候，烏薩奇會帶給你安全。&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;可惜巴哈姆特中字只出到第 120 集，剩下只能轉往 bilibili 繼續追了。不知道作者之後還會帶給我們什麼驚喜，世界觀最後又會走向什麼發展呢？真讓人期待呢～&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Robot Dreams</title>
        <link>https://theoutsidelaine.com/p/robot-dreams/</link>
        <pubDate>Fri, 12 Jul 2024 00:00:00 +0000</pubDate>
        
        <guid>https://theoutsidelaine.com/p/robot-dreams/</guid>
        <description>&lt;img src="https://theoutsidelaine.com/p/robot-dreams/cover.png" alt="Featured image of post Robot Dreams" /&gt;&lt;p&gt;&amp;lt;再見機器人 Robot Dreams&amp;gt; 一部沒有任何台詞的劇，但是訴說了千言萬語於兩名主角之間的故事。&lt;/p&gt;
&lt;p&gt;Dog 如同城市裡朝九晚五的打工仔，擁有固定的工作、固定的 evening routine，可是有一天他發現自己身邊沒有一個可以分享大小事的人。他感到很孤單。於是他上網訂購了一個陪伴型機器人。Robot 從被組裝好開始就是一個很乖很可愛的機器人，有著無辜的大眼睛和快樂上揚的嘴角，總是輕輕地跟在 Dog 後面、模仿他做著一樣的手勢、和 Dog 一起吃東西一起玩，兩人會聽著&amp;lt; September &amp;gt; 快樂的起舞，這是他們最喜歡的歌。&lt;/p&gt;
&lt;p&gt;可是有一天，Robot 因為浸水動不了了。Dog 承諾會來救他回家，可是他後來才發現海灘因為凜冬來臨，強迫封閉半年才能重新開啟。所以他只能放 Robot 一個人在海灘慢慢等待。Dog 試過很多方法，甚至因此被警察抓起來，在碰了很多個釘子之後，他決定不再嘗試，而是靜靜等待半年後再去救 Robot。這段時間他嘗試交新的朋友，可是都不盡理想，也無疾而終。&lt;/p&gt;
&lt;p&gt;這段期間，Robot 也過得並不好。他被壞心的兔子船夫砍掉一隻腿，只為了取下他的一根金屬腳趾，被冰凍、被沙子掩埋，一個人面對數不盡的黑夜。他沒有怪罪 Dog，只是偶爾會因為夢到 Dog 重新找了一個替代的機器人，而感覺很難過而已。這段時間他也有遇到可愛的鳥兒家族的陪伴，給予一絲溫暖。可是就在海灘重新開放的前一天，很剛好的被戴著一顆金牙的拾荒者給賣去廢棄回收廠，被拆解成一塊一塊的。所以當 Dog 匆匆趕到海灘時，只來得及揀回 Robot 的一條斷腿。&lt;/p&gt;
&lt;p&gt;然而命運就是這麼的奇妙，當 Dog 悲傷於 Robot 的消失，而在一陣子之後重新買了一個與其極為神似的陪伴機器人，同時，Robot 分散的部件被浣熊油漆工撿回家，重新治療、復活變成一個音樂型機器人。在浣熊的細心照顧之下，Robot 不僅擁有了新的腿、也與浣熊留下很多新的回憶，一起看棒球賽、吃燒烤，也有共同喜歡的音樂。&lt;/p&gt;
&lt;p&gt;某一天，Robot 在窗外遠遠地看到 Dog 牽著他的黃色機器人。情不自禁的，他播放起那首兩個人曾經都很喜歡的 &amp;lt; September &amp;gt;，他知道 Dog 靈敏的耳朵一定聽得到他在放歌。果然，Dog 聽到了，兩個人遠遠的在不同地方一起起舞，一起唱歌。當旋律結束時，Dog 望向音樂的來源處，好奇是誰在播著這首歌。Robot 腦袋閃過一絲可能：他衝下樓，用著他機器人的長腿與奔跑速度跑向 Dog，兩人互相注視，然後給予一個大大的擁抱。但是最後，他只是在 Dog 望過來得瞬間下從窗前躲開了。隨後，他走向浣熊，兩個人聽著浣熊與他最喜歡的音樂起舞，牽起了手。遠處，Dog 帶著黃色機器人緩緩回家。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/robot-dreams/image1.png&#34;
	width=&#34;893&#34;
	height=&#34;500&#34;
	srcset=&#34;https://theoutsidelaine.com/p/robot-dreams/image1_hu702191abe6b00ce825dbd2710fa346e1_583771_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/robot-dreams/image1_hu702191abe6b00ce825dbd2710fa346e1_583771_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;Dancing to September&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;178&#34;
		data-flex-basis=&#34;428px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;有時候覺得人與人之間的緣分是一種很難以言喻的東西。明明兩個相互扶持的人，卻會因為一些不可抗力的因素，而沒有辦法走到最後。遇到困境時，又會適時的有這麼一些人出現在生命中，用堆積的溫暖為你建築一道堡壘。很多時候我們都有著這種時刻，懷念過去的美好，想著如果衝下去，叫住那個人，會發生什麼事&amp;hellip; 可是就像 Dog 止住了腳步、Robot 閃避到窗戶之後，即便有再多的回憶那也是過去了，現在身邊的人才是最值得珍惜的。只不過呢，當聽到那首 &amp;lt; September &amp;gt;，還是會想到那一個有陽光的秋天，有一隻孤單小狗和一個快樂機器人成為了很好的朋友。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Inside Out 2</title>
        <link>https://theoutsidelaine.com/p/inside-out-2/</link>
        <pubDate>Sat, 22 Jun 2024 00:00:00 +0000</pubDate>
        
        <guid>https://theoutsidelaine.com/p/inside-out-2/</guid>
        <description>&lt;img src="https://theoutsidelaine.com/p/inside-out-2/cover.png" alt="Featured image of post Inside Out 2" /&gt;&lt;p&gt;Inside Out 終於在九年之後推出了續集，講述 Riley 步入青春期後，情緒們出現了什麼樣的變化。&lt;/p&gt;
&lt;p&gt;故事線很簡單的圍繞在 Riley 參加曲棍球營隊的一個小事件：剛得知無法跟好朋友們上同一間高中後，為了順利加入心儀的高中曲棍球隊、獲得女神隊長學姊的認同，Riley 做出有別於以往的選擇，包含服裝與言行上變得跩酷、遠離以前朋友圈、偷拿老師筆記本等等。雖然最後因為撞傷朋友、陷入自責而差點輸掉比賽，但後來還是順利取得勝利，並與朋友和好。&lt;/p&gt;
&lt;p&gt;Inside Out 2 以「青春期」作為開端，引進了四個新的情緒：Anxiety（焦慮）, Envy（嫉妒）, Ennui（厭世）,Embarassment（害羞），以及偶爾出來露個臉的 Nostalgia（念舊）。很有趣的是，新情緒出場方式是 Riley 腦內主控室突然出現陌生的紅色 &amp;ldquo;Puberty&amp;rdquo; 警鈴大響，伴隨著工人進來大施工翻新一切裝潢，象徵著新情緒的加入將會帶來一場混亂。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/inside-out-2/new-emotions.png&#34;
	width=&#34;1000&#34;
	height=&#34;556&#34;
	srcset=&#34;https://theoutsidelaine.com/p/inside-out-2/new-emotions_hu51ec52bda9f81f795894d1b0fbea8091_839618_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/inside-out-2/new-emotions_hu51ec52bda9f81f795894d1b0fbea8091_839618_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;New emotions come: Embarassment, Anxiety, Envy, Ennui&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;179&#34;
		data-flex-basis=&#34;431px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;事實上也的確如此：雖然大家已經習慣了 Joy 主導 Riley 的行為，不過伴隨著友情與環境的變化，在 Joy 的主導之下 Riley 總是會出糗、似乎會失去進入火鷹隊的機會。所以這時候 Anxiety 與她偉大的計畫們登場救火了。&amp;ldquo;Anxiety always has a plan&amp;rdquo; 不是說假的，在橘色爆炸頭的一頓分析與預測、以及一眾小工人們日以繼夜的趕工所有 &amp;ldquo;各種未來的可能性與防範措施&amp;quot;之下，Riley 執行了各種讓自己表現得更好的行動，包含早起練球、加入姐姐團的話題、甚至是偷看老師的評價筆記本。在 Anxiety 的暴風掌舵下，Riley 的大腦世界常常處於白天，自我意識也從白色透明的 &amp;ldquo;I am a good person&amp;rdquo;, 變成橘色的 &amp;ldquo;I am not good enough&amp;rdquo;。其中有一段描述 Anxiety 為了想要成功讓 Riley 進入火鷹隊，不僅讓 Ennui 發威、自嘲過去很喜歡的樂團，還發動了一場 &amp;ldquo;Brainstorm&amp;rdquo;，大腦主控室後方隨即出現一場龍捲風，不斷吸走所有駐藏不見得是好點子們的玻璃球。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/inside-out-2/belief-system.png&#34;
	width=&#34;981&#34;
	height=&#34;408&#34;
	srcset=&#34;https://theoutsidelaine.com/p/inside-out-2/belief-system_hu33f9e800a25f76af03b33f012e148d15_423126_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/inside-out-2/belief-system_hu33f9e800a25f76af03b33f012e148d15_423126_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;Riley&amp;rsquo;s belief system is formed of good &amp; bad memories.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;240&#34;
		data-flex-basis=&#34;577px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;從 Anxiety 掌舵、Joy等元老成員們努力回到主控室開始的劇情，電影便刻畫了許多很棒的情緒擬人化設計，讓我真心感到讚嘆。比如 Brainstorm 真的是大腦內的一場狂暴龍捲風、嘲諷的峽谷讓原本的語意聽起來變得扭曲、Riley 對於未來的想像是一個穿西裝的大法官巨大人型飛天氣球，飄揚在她的腦後區域。最後有一場戲，Joy 想到一個讓四個元老情緒返回主控室的絕妙點子：引爆炸彈，讓大夥乘坐 Avalanche of Bad Emotions 一鼓作氣的回家。這裡很巧妙的承先啟後：原本這麼一個成堆的壞記憶之山就是 Joy 的點子，讓不好的情緒透過管子「丟到腦後」，這樣 Riley 就可以很快樂順利的度過她的一生；不過最後 Joy 在這一趟旅程也終於理解到，或許讓 Riley 的 belief system 中擁有由壞記憶萌芽的思緒不見得是一件壞事，或許 Riley 的信念便該是多樣的，是由好的、壞的、快樂與悲傷所集結交織而成的小光束。所以在 Anxiety 快失控時，Joy 與成堆的壞記憶「碰！」的返回主控室，釋放了其實也很困擾與自責的 Anxiety，並讓 Riley 恢復冷靜、重振旗鼓。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/inside-out-2/riley-friends.png&#34;
	width=&#34;1428&#34;
	height=&#34;586&#34;
	srcset=&#34;https://theoutsidelaine.com/p/inside-out-2/riley-friends_hufa874dc5a04eee6d713e7614f4f7066e_1140961_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/inside-out-2/riley-friends_hufa874dc5a04eee6d713e7614f4f7066e_1140961_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;Riley and her friends.&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;243&#34;
		data-flex-basis=&#34;584px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;我對其中一個橋段感到印象深刻：Anxiety 在一開始讓 Riley 可以不斷獲得她想要的目標，使得 Joy 看起來很沒用。在 Joy 與元老們被放逐、找不到回去的路時，Joy 陷入沮喪的情緒，並說出這麼一句話：Maybe This Is What Happens When You Grow Up. You Feel Less Joy. 隨著年紀增長，我們時常會需要計劃未來，設想自己目前的狀態是否符合社會期待這個年紀該擁有的一切，所以會感到焦慮，失去快樂，並時常感到不足。對應到 Inside Out 的世界觀的話，就好像是從青少年時期接收期待開始，我們的主控室就轉為由 Anxiety 主導的橘色天地了，信念之樹時常迴響著沒有自信的語調。&lt;/p&gt;
&lt;p&gt;不過最後電影的結局給了我們一個很好的啟示：在 Joy 的重新執舵下，Riley 對於未來充滿期待，專心於當下。Anxiety 偶爾也會發揮作用，便是舒服的躺在按摩椅上計畫下週的西班牙文小考該怎麼準備就好。至於遠大的未來該怎麼因應，至於打開信件、是否真的有錄取火鷹隊&amp;hellip;其實都不怎麼重要了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/inside-out-2/Nostalgia.png&#34;
	width=&#34;692&#34;
	height=&#34;816&#34;
	srcset=&#34;https://theoutsidelaine.com/p/inside-out-2/Nostalgia_hu2632ff28aa7c9f10e04d8711f693a529_723674_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/inside-out-2/Nostalgia_hu2632ff28aa7c9f10e04d8711f693a529_723674_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;New character: Nostalgia the grandma&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;84&#34;
		data-flex-basis=&#34;203px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;看完電影後，我跟朋友討論著自己的主控台住了哪五個 emotions，很有趣的是，我們都覺得其中一個會是 Nostalgia。這個婆婆在電影中的很好玩，每次只要出現便被其他的成員們說「出現得太早啦！」然後被推回去ＸＤ 所有的情緒中，Sadness 還是如同第一季一樣可愛，雖然很溫軟單純，但是不管是爬上管子還是阻止 Anxiety 的過度計劃她都是大功臣，躲藏時被發現後用書本遮臉、以為自己不會被發現的樣子真的好萌啊。&lt;/p&gt;
&lt;p&gt;想想第一次看 Inside Out 居然是九年之前的事，當時還是個大學生，如今都已經步入社會了，不禁令人感嘆時間真的過得好快（Nostalgia 發威中）。很期待 Inside Out 3 會有什麼 Riley 有趣的故事發生，一定會有更多有趣的情緒加入主控台的吧（突然想到散場時，某個觀眾大聲問他的朋友：為什麼沒有色色這個情緒！umm～）。&lt;/p&gt;
&lt;p&gt;Inside Out 第一集的導演是執導&amp;lt;怪獸電力公司&amp;gt;、&amp;lt;天外奇蹟&amp;gt;、&amp;lt;靈魂急轉彎&amp;gt;，大名鼎鼎的 Pete Docter。第二集雖然不是他，但是新導演 Kelsey Mann 在這部動畫表現的相當出色，延續第一季的創意火炬，並且發揮的更上一層樓。很期待看到他之後的更多作品啊！&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Friends</title>
        <link>https://theoutsidelaine.com/p/friends/</link>
        <pubDate>Tue, 04 Jun 2024 00:00:00 +0000</pubDate>
        
        <guid>https://theoutsidelaine.com/p/friends/</guid>
        <description>&lt;img src="https://theoutsidelaine.com/p/friends/cover.png" alt="Featured image of post Friends" /&gt;&lt;p&gt;說來有趣，Friends 是我從國高中就聽過的美劇了，不過遲至 2024 年 1 月才真正開始看它。&lt;/p&gt;
&lt;p&gt;以前很常聽到別人在討論這部劇、讚美他是世紀神劇，或是學英文必看之類的。可能叛逆心發作，覺得越多人喜歡的劇我越要避開，而且也覺得劇中穿插的罐頭笑聲很老套，所以很久以來一直都沒有起心動念去看這部劇。&lt;/p&gt;
&lt;p&gt;後來在 2024年 1 月的某一天，好像是為了犒賞自己終於寫完 side project 吧，所以想說做個不一樣的嘗試，打開 netflix 開始看這部劇。結果不看還好，一看竟發現這部劇不論是劇情、笑點、人物，都是那麼的平凡卻又那麼的細膩而深刻，而且又可愛又好笑，完全打中我的心。所以總共 10 季的長度，花了一週不眠不休的看完了（就是這麼瘋狂），然後直到 2024 年 6 月的今天，已經是我第五回合看這麼劇了（對，總共 10 季 236 集，我用半年的時間重複看了整整四次！而且還沒打算停下來）&lt;/p&gt;
&lt;p&gt;看完第一輪的感想是一種很惆悵的感覺，所以開始加入很多 Facebook 的 Friends Chat，看著影迷們製作的 meme 回溫著內容。那時候恰好經歷了大事：Matthew Perry過世。因為對於 Friends 還沒有足夠深的情感基礎，所以沒有很大的感覺，只是隱隱覺得很遺憾。大概過一個月之後，我想說有點忘記當初 Chandler 和 Joey 是怎麼認識的了，所以便開始看第二輪 Friends。還記得那時候看到原來是 Mr.Heckles 的打亂才得以讓 Joey 加入，心裡覺得一陣好笑又覺得啊原來這就是緣分哈哈。緊接著二三月開始準備正職面試，壓力不小，每當夜晚整理實習筆記、準備刷題或是更新履歷表時，都會把手機開著，播放起第三回合的 Friends。此時罐頭笑聲對我而言已不再老套，而好像是一群也跟我一樣很享受 Friends 的人發出認同一般的笑聲。&lt;/p&gt;
&lt;p&gt;直到四五月找工作的轟炸告一段落後，回頭一看，才驚覺 Friends 已經悄然融入我的生活，手機裡面到處都是 Friends meme，加入了好多個 Friends論壇，追蹤了 Lisa Kudrow 的 ig、關注許多 Friends cast 的動向，也在書店翻閱起 Matthew Perry 的回憶錄。現在才想起之前有個人說過：看完 Friends 這部劇會讓你好像多了六個朋友一樣。原來是這樣的感覺啊。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/friends/guessing-game.png&#34;
	width=&#34;1744&#34;
	height=&#34;1082&#34;
	srcset=&#34;https://theoutsidelaine.com/p/friends/guessing-game_hucae91678f3cfb65edc9e11f04b3d2e39_2887379_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/friends/guessing-game_hucae91678f3cfb65edc9e11f04b3d2e39_2887379_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;Season 4: Trivia Game&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;161&#34;
		data-flex-basis=&#34;386px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;六月的現在是我第五次看這部劇，看到第五季中間，恰好是英國旅行剛回來，Chandler 與 Monica 戀愛中、Phoebe 剛生完 triplets、Ross 剛與 Emily 慘烈的分開、Rachel 整理完對 Ross 的感情之後開始新一輪的戀情、Joey 繼續追逐演藝夢。&lt;/p&gt;
&lt;p&gt;我覺得第四季、第五季算是一個分水嶺：第一～第四季的時候，恰好是六個人的事業都剛剛起步中：Ross 因循興趣擔任博物館館員，對於考古學一直抱有熱忱、Rachel 放棄了家庭金援開始在紐約獨立生活，到 Central Perk 咖啡館當服務生，後來因緣際會到百貨公司 Bloomingdale&amp;rsquo;s 擔任導購員，開啟她的時尚追夢之旅。Monica 在小餐館當廚師手，又當過 caterer，不過她從來沒有放棄成為主廚的夢想。Joey 拍了一些劇、接了一些三流廣告，然後有個 Days of our life 出演 Dr. Drake Ramoray 的小成就，持續嚮往成為電影明星。Phoebe 既是吉他駐唱又是按摩師，出過專輯，過著隨心所欲的生活。然後 Chandler 是數據分析師，擁有一間獨立辦公室，後來還升了小主管。不過他一直以來都覺得這個職業很無趣，只不過剛好他對數字很在行而已。&lt;/p&gt;
&lt;p&gt;不僅是事業，連感情生活也算是起步中：Chandler 分別和 Janice, Kathy 有過短暫的戀情，不過說到底他對於自己終究不是很有自信。在某一集他提到，自己很害怕 committment, 也很容易因為挑惕伴侶的小毛病而無法與她們順利走下去，他也不知道自己為什麼會這樣，甚至害怕自己會終老一生如 Mr.Heckles。而 Monica 雖然遇到了 Richard，但是因為未來規劃上的差異，她不得不忍痛放下這段感情。雖然還有 Pete, Fun Bobby, 但是她都很清楚自己在感情中想要的跟他們具有的差別，而沒有選擇輕易因為想要談戀愛而繼續談戀愛。Joey 跟 Phoebe 兩個在前四季還算是寶寶階段、享受人生對很多事情不會想太多，不過 Joey 在與 Kate 談戀愛時曾經經歷所謂的 &amp;ldquo;The Night of Love&amp;rdquo;，算是他生平第一次談著交心的戀愛。&lt;/p&gt;
&lt;p&gt;至於 Ross 和 Rachel，兩人在前四季發生了太多故事，從曖昧到熱戀到猜疑到分手到復合到分手，我們看到兩個不論是職業、興趣、個性、家庭背景上有極大差異的人，在感情路上不斷磨合的過程，很多地方寫實的很細膩，比如爭論 &amp;ldquo;We were on a break&amp;rdquo; 究竟是不是代表分手？分手之後如果還愛著前男友/前女友，如果對方已經進入一段看似穩定的關係，可不可以再次對他/她表白心意，為自己做一個 closure？看著兩人分手之後仍然為對方跟其他異性出去感到不悅，以及雖然不明說但是偷偷為對方做出很多暖心的事，有時候在想或許他們個性上固執的一面很相似，如果有其中一方願意軟化，搞不好我們就不用看到他們維持 10 季的 on and off relationship 了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/friends/c-and-j.png&#34;
	width=&#34;613&#34;
	height=&#34;613&#34;
	srcset=&#34;https://theoutsidelaine.com/p/friends/c-and-j_hu0ace2df7cff33fdb80fbf0dd70d51568_43628_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/friends/c-and-j_hu0ace2df7cff33fdb80fbf0dd70d51568_43628_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;The famous scene: Chandler and Joey on their favorite sofa chairs&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;100&#34;
		data-flex-basis=&#34;240px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;除了事業、感情，其實我最喜歡看的就是他們六個人的友誼了。前四季算是他們友誼的萌芽、茁壯期，裡面有很多溫馨搞笑的互動，每一次看都讓我覺得很滿足。首先是 Chandler 和 Joey，我覺得他們兩個人真的世紀絕配，為什麼這麼說呢：Chandler 個性有點厭世但蠻聰明、講話很常精準抓住一些小地方說機智的玩笑話，而 Joey 則是有點傻有點少根筋，但很重義氣又樂天。兩人大致上的調性算是很不同、蠻互補的，不過很多時候都可以看得出來他們本質上的一致性：他們都很愛籃球賽、貪玩、喜歡美女、重視朋友記得他們的大小事、願意為了配合對方犧牲一點自己、對於生活品味的喜好相同（foosball, sofa chair, pizza, Bay Watch）。我很喜歡看他們大吵一架，之後又擁抱對方和好的過程，不管是為了小雞小鴨的教養（？）、為了 Janice、為了租新房子、為了金手錶 etc，這代表他們既願意把不開心講出來、也願意試著了解對方不開心的點，做出彌補的事情，最後接受道歉。Chandler 會說之後有買房子會空出一個房間 &amp;ldquo;To Keep a Joey here&amp;rdquo;，然後 Joey 也會想到什麼就先揪 Chandler。總之，看到他們互動都會讓我覺得心裡暖洋洋的。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/friends/the-cute-4.jpg&#34;
	width=&#34;1080&#34;
	height=&#34;1080&#34;
	srcset=&#34;https://theoutsidelaine.com/p/friends/the-cute-4_hu6da945534f1a454ac9b3f6c9fc09b389_91635_480x0_resize_q75_box.jpg 480w, https://theoutsidelaine.com/p/friends/the-cute-4_hu6da945534f1a454ac9b3f6c9fc09b389_91635_1024x0_resize_q75_box.jpg 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;The Cute 4&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;100&#34;
		data-flex-basis=&#34;240px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;至於其他人的互動也都蠻可愛的，比如 Ross 跟 Monica 好勝的 Geller 兄妹，雖然 Ross 享盡父母的寵愛，但是仍然對小妹 Monica 照顧有加，而 Monica 也沒有因為父母的偏愛而走歪，反而獨立自主又很會照顧人（雖然潔癖病到後來越來越嚴重 lol）。Monica 和 Rachel, Phoebe 的姐妹情誼也是一個可愛的點，大多數時候是 Monica 在照顧兩位，不過有時候 Rachel 也會給予很多有用而溫暖的意見，而 Phoebe 也會在 Monica, Rachel 遇到問題、或是吵架時，給予解答或是充當和事佬。至於 Joey 和 Phoebe 則是我最喜歡的異性朋友搭檔，兩個同樣腦迴路異於常人，相處起來竟有種惺惺相惜得感覺，每次看他們與他人頻率不合但跟彼此頻率一致然後開心互視就覺得很快樂。&lt;/p&gt;
&lt;h1 id=&#34;about-phoebe&#34;&gt;About Phoebe&lt;/h1&gt;
&lt;p&gt;把 Phoebe 首先獨立出來是因為在看完整整四回合後，我發現自己最喜歡的角色是她。Phoebe 雖然成長背景異於常人的悲慘（？），但是她卻樂天、很做自己，擁有一套自己有點怪異但是獨特的處事方式和價值觀。雖然很多時候她的離線（？）會讓身旁的朋友不知所措，比如人家在講重點她可能會著重於另外的小細節，但她都可以在關鍵時刻帶給大家正解甚至可以說是真知灼見，有種大智若愚的感覺。&lt;/p&gt;
&lt;p&gt;比如說第四季時，Ross 很焦慮 Emily 跟著 Susan 一起去倫敦會被她掰彎，但 Phoebe 很直白地點出是因為 Ross 在上一段感情被 Carol 傷害得太深，導致他在無論是跟 Rachel 或是 Emily都無法相信愛人，並進而造成無法挽回的錯誤。又或者是當 Monica 原本很開心的接到媽媽給的任務，幫忙親戚的宴會準備食物，卻因為不小心掉了一塊指甲到 pie 裡面，被媽媽小小的嘲笑了一下說 &amp;ldquo;You are pulling a Monica again&amp;rdquo;（意即 Monica 就是搞砸事情的代表）。然而 Phoebe 這時候卻對 Monica說，&amp;ldquo;Let&amp;rsquo;s give pulling a Monica a different meaning. Like if a kid wons a prize, he will be called &amp;lsquo;pulling a Monica&amp;rsquo;. &amp;quot; 鼓勵她再把場子找回來，讓這句玩笑話擁有新的正面意義。&lt;/p&gt;
&lt;p&gt;最重要的是她真的很做自己，不論是彈吉他、決定當代理孕母、按摩、捐款 etc，很少因為他人的眼光就阻止她做一件事情，或是跟一個人談戀愛。有時候天馬行空的想法也讓我覺得很搞笑可愛。Phoebe 前三季的造型是金色長髮，這張回頭燦笑的表情真的是好美啊，令人難以忘記。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/friends/phoebe.jpg&#34;
	width=&#34;564&#34;
	height=&#34;564&#34;
	srcset=&#34;https://theoutsidelaine.com/p/friends/phoebe_hu3adc58f9931156de3e787b6e6a05561c_45305_480x0_resize_q75_box.jpg 480w, https://theoutsidelaine.com/p/friends/phoebe_hu3adc58f9931156de3e787b6e6a05561c_45305_1024x0_resize_q75_box.jpg 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;Phoebe Buffay&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;100&#34;
		data-flex-basis=&#34;240px&#34;
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;about-monica&#34;&gt;About Monica&lt;/h1&gt;
&lt;p&gt;Monica 在劇中一直給我長姐的感覺，不僅是因為她很可靠、又會煮飯、又愛打掃，也是因為她很樂天又天不怕地不怕的個性（&amp;ldquo;I know!&amp;quot;）。我很喜歡劇裡面描述她小時候的時光，雖然很胖，但站在纖細好看的 Rachel 旁邊，她沒有因此很沮喪，反而在參加派對時還是很享受派對的氣氛、食物。長大後的她變瘦變美，卻也不會忌諱別人討論以前胖胖的樣子，或是偶爾被叫 Fatty 小虧一下。&lt;/p&gt;
&lt;p&gt;但我最喜歡 Monica一點的是她很知道自己要什麼。跟每個人交往，她都可以知道自己在這段關係中，這個人帶給她什麼感覺、她不能接受什麼，然後最後如何好聚好散。比如 Richard，她想要孩子但 Richard不想，她不會勉強他，所以忍痛和平分手。比如 Fun Bobby，她想要他戒酒但是又不希望他因為自己而失去快樂，所以後來也分手了。最後就是 Pete，她知道他的夢想是成為超級格鬥家（？），但她無法忍受看著愛人天天被打，所以在得知他確定不會改變夢想後，她也跟他分手了。除此之外，她也一直知道自己想要成為的是一家餐廳的主廚，掌管廚房，做出讓人垂涎欲滴的菜色。所以即使跟 Phoebe 一起 cater很快樂，但她還是忍痛拒絕了 Phoebe。&lt;/p&gt;
&lt;p&gt;總之，幸好她最後遇到了 Chandler，也當上餐廳主廚，算是很棒的結局。此外，我很喜歡 Monica 演員沙沙的聲音，配上黑短髮與藍眼睛，還有經典紅衣服，真的超級有魅力！&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/friends/monica.jpg&#34;
	width=&#34;540&#34;
	height=&#34;602&#34;
	srcset=&#34;https://theoutsidelaine.com/p/friends/monica_hu4a76be85bf35986f0f6e6ac2a88cb632_26904_480x0_resize_q75_box.jpg 480w, https://theoutsidelaine.com/p/friends/monica_hu4a76be85bf35986f0f6e6ac2a88cb632_26904_1024x0_resize_q75_box.jpg 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;Monica Geller&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;89&#34;
		data-flex-basis=&#34;215px&#34;
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;about-rachel&#34;&gt;About Rachel&lt;/h1&gt;
&lt;p&gt;Rachel 是我看下來喜愛程度大反轉的角色，一開始真的覺得很無感，後來第三季開始越來越喜歡她。她就像個小公主一樣，出場時穿著新娘婚紗，在 Central Perk 裡面不知所措地找著Monica。記得她有一幕去倒垃圾，因為不會倒，被管理員罵說是小公主，結果哭著回來跟大家說管理員很可怕，覺得很好笑很可愛哈哈。Rachel 的這種特質不僅是男生，就連女生也會忍不住想要多多照顧她一下。&lt;/p&gt;
&lt;p&gt;但 Rachel 也是成長幅度最大的一個角色。從一開始還會偷刷爸爸信用卡，到後來從咖啡館服務生、百貨公司秘書、導購員，一路成為時尚業的專員，甚至最後有機會到法國巴黎拓展事業，真的覺得她很棒。雖然有點公主嬌氣，比如會把別人送她的禮物換成百貨公司點數，但是朋友有難她都會在旁邊給予陪伴，也不會像一般電影演的 mean girl 對 Phoebe 出現排擠行為，甚至以身作則的告訴妹妹們要獨立自主。&lt;/p&gt;
&lt;p&gt;另外，她的角色最大特色就是與 Ross 的關係，看著她在這段關係中的起起伏伏，也讓我們可以從旁觀察很多伴侶溝通與相處上的模式。&lt;/p&gt;
&lt;p&gt;第四季的 Rachel 剛好是 28歲，跟如今的我一樣大，所以看到第四季的時候特別有感：是不是我也正在第四季的階段呢。&lt;/p&gt;
&lt;p&gt;Rachel 到後面的劇情安排有很多讓人討論的點，比如跟 Joey的感情線是否合理？（關於這點我覺得蠻可以接受的）不過最後跟 Ross 的 endgame 很棒我很喜歡。看著他們一群人走出門外，Chandler問說 Where? 心裡頓時覺得啊人生好像也跟著他們走過很多東西了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/friends/rachel.jpg&#34;
	width=&#34;1004&#34;
	height=&#34;753&#34;
	srcset=&#34;https://theoutsidelaine.com/p/friends/rachel_hu4ac6e79ac60e7d97c9c6eb3a89754872_42208_480x0_resize_q75_box.jpg 480w, https://theoutsidelaine.com/p/friends/rachel_hu4ac6e79ac60e7d97c9c6eb3a89754872_42208_1024x0_resize_q75_box.jpg 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;Rachel Green&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
&gt;&lt;/p&gt;
&lt;h1 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;Friends 的很多笑點放到如今一點都不會過時，反而歷久彌新。很開心自己可以看完它，也期待之後可以去搜尋更多 bloopers、採訪花絮來細細品味。最後就用一個 Ben 的視角來總結這部劇的影評吧：&lt;/p&gt;
&lt;p&gt;&lt;a class=&#34;link&#34; href=&#34;hi-ben.png&#34; &gt;Hi Ben&lt;/a&gt;&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Use Go to develop a price increaser cronjob in a Javascript project</title>
        <link>https://theoutsidelaine.com/p/go-cronjob-price-stealthy-increaser/</link>
        <pubDate>Sun, 02 Jun 2024 07:00:00 +0000</pubDate>
        
        <guid>https://theoutsidelaine.com/p/go-cronjob-price-stealthy-increaser/</guid>
        <description>&lt;img src="https://theoutsidelaine.com/p/go-cronjob-price-stealthy-increaser/cover.png" alt="Featured image of post Use Go to develop a price increaser cronjob in a Javascript project" /&gt;&lt;p&gt;最近開始學 Go，常聽人說最好的學習方法就是直接 build 一個簡單的小 feature，於是乎便想到了在我原本的 js side project 中建構一個簡單的 go cronjob 的方法。以下紀錄了這個小專案的背景、撰寫，與運行方式。&lt;/p&gt;
&lt;h2 id=&#34;1-background&#34;&gt;1. Background&lt;/h2&gt;
&lt;p&gt;根據彭博社報導，全世界近期受到通貨膨脹的衝擊，尤其是美國面臨了嚴峻的物價指數上漲，根據美國勞工統計局的數據分析，牛絞肉、薯片等基本物品如今的平均價格高於 Covid-19 前水準、汽油價格再次上漲，而電費等日常必需品的成本仍然維持高價位。&lt;/p&gt;
&lt;p&gt;有鑑於此，台灣電商 &lt;a class=&#34;link&#34; href=&#34;https://github.com/EHsieh0212/Synoptic&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Synoptic&lt;/a&gt; 為了可以跟上經濟趨勢、降低銷貨成本，因此希望趁著消費者分神時提高商品價格。方式即是透過打造一個 cronjob, 每分鐘偷偷調高各類商品的價格 1 塊錢。&lt;/p&gt;
&lt;h2 id=&#34;2-method&#34;&gt;2. Method&lt;/h2&gt;
&lt;h3 id=&#34;go-module-in-general&#34;&gt;Go module in general&lt;/h3&gt;
&lt;p&gt;Go 的 repo 通常會由一個 module 與多個 packages 組成。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;module 在 Go 1.11 引入，用於管理多個 packages 與其依賴關係，並支援版本控制，存在一個 go.mod 紀錄版本、依賴項、替換規則等等。&lt;/li&gt;
&lt;li&gt;package 則是一組相關聯的代碼，用於代碼組織封裝與重新利用。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;假設有一個 repo 架構如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;myapp/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── go.mod
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── main.go
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── utils/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   └── util.go
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;└── models/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    └── model.go
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;可知：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;go.mod 文件位於 myapp 目錄中，定義了 &amp;ldquo;example.com/myapp&amp;rdquo; 這個 module。&lt;/li&gt;
&lt;li&gt;main.go、utils/util.go 和 models/model.go 都屬於同一個 module &amp;ldquo;example.com/myapp&amp;rdquo;。&lt;/li&gt;
&lt;li&gt;utils 和 models 是 package，但它們不需要各自擁有 go.mod 文件。它們共享位於根目錄的 go.mod 文件。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;file-structure&#34;&gt;File Structure&lt;/h3&gt;
&lt;p&gt;先 top-down 的展示這個小專案的檔案架構：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;└── server/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    ├── cronjob/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    │   └── product-price-stealthy-increaser/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    │       ├── .env
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    │       ├── go.mod
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    │       ├── go.sum
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    │       └── main.go
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    ├── services/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    │   └── prices.js
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    └── routes/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        └── priceRouter.js
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;init-a-go-module&#34;&gt;Init a Go module&lt;/h3&gt;
&lt;p&gt;執行以下指令，init 我們的專案：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;go mod init product-price-stealthy-increaser
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;這時便會產出 &lt;code&gt;go.mod&lt;/code&gt; 檔如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-v&#34; data-lang=&#34;v&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;module&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;product&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;price&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;stealthy&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;nv&#34;&gt;increaser&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;go&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;1.22.3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;install-related-packages&#34;&gt;Install related packages&lt;/h3&gt;
&lt;p&gt;在這個專案中，我們需要額外兩個 packages:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;godotenv&lt;/code&gt;: 用於讀取寫於 &lt;code&gt;.env&lt;/code&gt; 的環境變數&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cron&lt;/code&gt;: 用於執行 cronjob&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;執行安裝指令如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;go get -u github.com/joho/godotenv
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;go get -u github.com/robfig/cron/v3
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;此時安裝內容便會被記錄於 &lt;code&gt;go.sum&lt;/code&gt; 檔案中。每個安裝的 packages 會有兩個 hash value, 分別確保直接下載與紀錄於 module 的依賴是否可以通過校驗。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;-u&lt;/code&gt; flag 代表在安裝時，同時檢查是否有缺漏沒有安裝到的 dependencies，有的話就安裝。但他不會更新既有的 dependencies。&lt;/p&gt;
&lt;h3 id=&#34;maingo&#34;&gt;main.go&lt;/h3&gt;
&lt;p&gt;main.go 算是 module 的主要入口點。主要的檔案內容如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;在 main.go 中，我們需要兩個函數： incrementProductPrices(), 與 main()。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;incrementProductPrices(): 負責與 db 互動，將每項商品的價格調漲一塊。&lt;/li&gt;
&lt;li&gt;main(): 載入環境變數，並設定 incrementProductPrices() 每分鐘執行一次。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;incrementproductprices&#34;&gt;incrementProductPrices()&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;func incrementProductPrices() {
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	nodeServerURL := os.Getenv(&amp;#34;BACKEND_URL&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	resp, err := http.Post(nodeServerURL+&amp;#34;/increasePriceToFightInflation&amp;#34;, &amp;#34;application/json&amp;#34;, nil)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	if err != nil {
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		log.Fatalf(&amp;#34;Failed to increate price product: %v&amp;#34;, err)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	defer resp.Body.Close()
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	body, err := io.ReadAll(resp.Body)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	if err != nil {
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        log.Fatalf(&amp;#34;Failed to read response body: %v&amp;#34;, err)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	if resp.StatusCode != http.StatusOK {
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        log.Fatalf(&amp;#34;Failed to update prices, status code: %d&amp;#34;, resp.StatusCode)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	maxBodyLength := 500
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	bodyToPrint := string(body)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	if len(bodyToPrint) &amp;gt; maxBodyLength {
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        bodyToPrint = bodyToPrint[:maxBodyLength] + &amp;#34;...&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	log.Printf(&amp;#34;Response Body: %v&amp;#34;, bodyToPrint)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	log.Println(&amp;#34;Product prices incremented successfully.&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;這個函數主要有以下幾個重點：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;這個 Go cronjob 會透過打 api 的方式，讓 PriceRepository (written in Javascript) 執行每個商品價格加ㄧ的動作。POST api 裡面連結到 js price service。這麼做有幾個優點：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;讓 Go cronjob 調用 Javascript repo 的資源。&lt;/li&gt;
&lt;li&gt;我們可以不用在 cronjob 中撰寫 db crud 相關指令，讓程式層級切割的更明確。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;defer response.Body.Close()&lt;/code&gt; ：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;這個寫法通常見於 HTTP request / response, 是 Go 語言中的一種用法，用於確保在 HTTP 請求完成後關閉response body，以釋放相關資源。&lt;/li&gt;
&lt;li&gt;&amp;ldquo;defer&amp;rdquo; 確保了函數無論在什麼時候結束、如何結束，資源都可以被釋放。&lt;/li&gt;
&lt;li&gt;如果不寫 defer, 僅僅在最後加上 response.Body.Close(), 可能導致某些 error handling 出口忘記釋放資源，讓資源被洩漏。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;使用 &lt;code&gt;io.ReadAll()&lt;/code&gt; 讓 response 成為一個 list：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用途：有時候 response 長度會太長，這時我們可以指定最長 logging 的長度，再把 response 讀成一個 list, 借此讓 response 可以好看一點。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;使用 &lt;code&gt;log&lt;/code&gt; 而非 &lt;code&gt;fmt&lt;/code&gt;: 讓輸入的狀態擁有時間戳、格式化輸出選擇。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;main&#34;&gt;main()&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;func main() {
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	if err := godotenv.Load(); err != nil {
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		log.Fatalf(&amp;#34;Error loading .env file: %v&amp;#34;, err)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	schedule := cron.New(cron.WithSeconds())
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	_, err := schedule.AddFunc(&amp;#34;0 * * * * *&amp;#34;, incrementProductPrices)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	if err != nil {
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;		log.Fatalf(&amp;#34;Error scheduling cron job: %v&amp;#34;, err)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	schedule.Start()
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;	select {}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;這個函數主要有以下幾個重點：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;載入環境變數，並在無法順利載入時 log.Fatalf()&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在 main() 而非 incrementProductPrices() 調用環境變數，可以確保調用的行為只會發生一次，降低不必要的開銷。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;cron.New(cron.WithSeconds())&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;創建一個 cronjob, 並將精細度由預設的分鐘級調整為秒級&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;schedule.AddFunc(&amp;quot;0 * * * * *&amp;quot;, incrementProductPrices)&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;將 &amp;ldquo;incrementProductPrices&amp;rdquo; 添加至 cronjob中，並設定每小時的第 0 秒（＝每分鐘）執行一次。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;schedule.Start()&lt;/code&gt;: 開始 cronjob&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;select {}&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Go 語言用法，用於創建一個 infinite loop, 讓程式無限執行下去。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;因此，當執行 &lt;code&gt;go run main.go&lt;/code&gt;時，這個 cronjob 就會固定於每分鐘將價格表上的所有商品價格調漲一塊了。&lt;/p&gt;
&lt;h2 id=&#34;3-conclusion&#34;&gt;3. Conclusion&lt;/h2&gt;
&lt;p&gt;很好玩！之後可以看看怎麼結合 Render 的 job 功能，讓 cronjob 的運行腳本可以寫得更簡便、更著重於 service 本身就好。之後 Synoptic 也會因應世界上發生的大小事，再次用 Go 拯救公司營運（Ｘ&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Dune</title>
        <link>https://theoutsidelaine.com/p/dune/</link>
        <pubDate>Thu, 30 May 2024 00:08:00 +0000</pubDate>
        
        <guid>https://theoutsidelaine.com/p/dune/</guid>
        <description>&lt;img src="https://theoutsidelaine.com/p/dune/cover.png" alt="Featured image of post Dune" /&gt;&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/dune/dune2-poster.png&#34;
	width=&#34;1120&#34;
	height=&#34;1660&#34;
	srcset=&#34;https://theoutsidelaine.com/p/dune/dune2-poster_hu6d2b7d771c64f221b8f74eee543ceda0_420450_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/dune/dune2-poster_hu6d2b7d771c64f221b8f74eee543ceda0_420450_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;Dune2 Poster&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;67&#34;
		data-flex-basis=&#34;161px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;久聞這是一部史詩級的科幻巨作，因為耳石脫落症治療期間只能乖乖待在房間，所以借機欣賞完了 Dune 2 的內容。&lt;/p&gt;
&lt;p&gt;相較於第一部花了許多篇幅鋪陳 Paul 一家的背景與能力、各族的利害關係，以及發生在 Arrakis 星球上針對 Atreides 一脈的慘烈屠殺，第二部推進了 Paul 如何融入Fremens 一族，展開對皇室與 House Harkonnen 的復仇過程。&lt;/p&gt;
&lt;p&gt;Dune 2 由導演 Denis Villeneuve 所製，誕生於後疫情時代、美國影視業罷工的時期，延期了半年才終於得以上映，推出後廣受很多影迷的喜愛，在票房上開出亮眼的成績。這樣融合科幻、人性的故事主題算是很久沒有接觸了，因此紀錄了一些看完 Dune &amp;amp; Dune2後的心得感想。&lt;/p&gt;
&lt;h2 id=&#34;有意思的派系設定the-emperor-bene-gesserit-atreides-harkonnens-fremens&#34;&gt;有意思的派系設定：The Emperor, Bene Gesserit, Atreides, Harkonnens, Fremens&lt;/h2&gt;
&lt;p&gt;由皇室主導的宇宙國度中，存在著一正（Atredies）一邪（Harkonnens）勢力，以及在背後透過血脈、預言操弄大眾的 Bene Gesserit。在黃沙遍佈的 Arrakis 星球，擁有著讓人類難以生存的氣候條件，卻也誕生了極為珍貴的宇宙資源：The Spice。從電影中可以看到 The Spice 的功能有點像強效版致幻藥劑，開拓人類心靈的未知境界、擴大感官知覺，從而使人可以預知未來，甚至也可以提升健康。然而 The Spice 具有成癮性，而且只存在於 Arrakis 星球，由星球上的強勢物種 Sand Trout (Sand Worm前身) 所產出。因此，星球 Arrakis 便成為兵家必爭之地。&lt;/p&gt;
&lt;p&gt;這個香料星球原本由正派勢力 Atredies 所統治，然而隨著 Atredies 能力愈加卓越，皇室隱隱感受到威脅，因此便改派 House Harkonnen 接管，並利用兩大氏族長期以來的仇恨，加派兵力給 Harkonnens，讓他們可以順利的在 Atredies 撤退之際殲滅他們。僥倖活下的 Paul Atredies 與其母 Lady Jessica，受到沙漠一族 Fremens 的接濟，在 Paul 與生俱來的血派能力與戰鬥技巧加持下，他贏得了 Fremens 的尊崇，在他的領導下，一步步討伐北方，成為令人微風喪膽的 Muad&amp;rsquo;Dib（沙漠跳鼠）。&lt;/p&gt;
&lt;p&gt;這期間，Lady Jessica 提供了不可或缺的助力：身為 Bene Gesserit 的一員，她不僅具有絕佳的魅音術、抗毒能力，也兼具武力、智力，這些能力讓她提早預判一件重要的事： Paul 的復仇戰爭，不僅需要 North Fremens，更需要靠著 South Fremens 的加持才可以成功。所以她深入虎穴以身試毒（The Sand Worm Poison）、證明她身為聖母的能力，並坐著沙蟲親臨南方，不斷散播著 Paul 是這個星球的 Lisan al Gaib（天選之子）的傳言，成功的俘獲南方 Fundamentalists 信眾們的心。&lt;/p&gt;
&lt;p&gt;於是，大戰一觸即發。當皇室還在慢悠悠的與 Hakonnens 爭論他們討伐 Fremens 的失利、懷疑他們隱藏 Paul Atredies 未死的秘密，Paul 已經率領眾人包圍堡壘四周，隨著硝煙四起，族人騎著無數隻沙蟲攻向內部，Paul 也終於得以將刀子刺入這個殺光他一族、卻也是他的外公的 Vladimir Harkonnens 身中。&lt;/p&gt;
&lt;p&gt;尾聲之際，我們看到 Paul 成功的讓皇室禪位給他，成功的讓自己重新成為 Lord Atredies，站在這個星球的最頂端。然而，由於奔向星球而來的氏族軍隊們拒絕承認他的地位，因此，為了守護這個星球、為了維繫家族，亦或者是為了更多他自己的未知的目的，Paul 決定向氏族宣戰，展開為期好多年的聖戰。&lt;/p&gt;
&lt;h2 id=&#34;lady-jessica-的權謀&#34;&gt;Lady Jessica 的權謀&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/dune/jessica.png&#34;
	width=&#34;2006&#34;
	height=&#34;1348&#34;
	srcset=&#34;https://theoutsidelaine.com/p/dune/jessica_hua03ec13423de348c138e1debeaba6747_3149802_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/dune/jessica_hua03ec13423de348c138e1debeaba6747_3149802_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;Lady Jessica Becomes The Reverend Mother&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;148&#34;
		data-flex-basis=&#34;357px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;這部片最讓我印象深刻的便是 Lady Jessica 出場的片段，尤其是她如何讓自己一步步從一個待被驅逐的異星女子，變成聖母，甚至主導了 Paul 成為眾人領袖的地位。&lt;/p&gt;
&lt;p&gt;在 Paul 在外苦苦的與 Fremens 訓練、贏得他們的認可之際，Lady Jessica並沒有閒著，身為 Bene Gesserit，她深知操弄人心的要訣，在於利用恐懼散播希望。所以有勇有謀的她先是接受提議，讓自己成為北方的聖母、穩固自己的地位，加深北方民眾對於 Paul 的崇敬與期待，接著在 Paul 征戰北方之際，她坐騎沙蟲往南方，在那邊用同樣的方法成功讓南方民眾深信有這麼一位 Lisan al Gaib 會解放星球、讓星球重獲與水與綠植。&lt;/p&gt;
&lt;p&gt;一開始 Paul 很厭惡母親的做法，認為她只是散播未知的可能性，他只是一個願意與 Fremens 一起並肩作戰的人、根本不是可以扛起重責的 Lisan al Gaib。但 Lady Jessica 深知這場戰爭是一條沒有回頭的道路，要贏得戰爭只有取得民心，並且也要 Paul 自己認知到這點的重要性。所以她沒有強求 Paul 一開始就接受她的提議：喝沙蟲毒獲得預示能力，而是讓他慢慢體悟擁有能力、權力的重要性，並幫他默默鋪好道路，隨時等待他加入的那一刻。所以，我們看到最後 Paul 如母親所願，來到了南方喝下沙蟲毒，接受自己的命運與眾人的擁戴，在成為 Arrakis 星球之主的道路上越走越遠。&lt;/p&gt;
&lt;h2 id=&#34;paul-成為領袖的這條路&#34;&gt;Paul 成為領袖的這條路&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/dune/paul.png&#34;
	width=&#34;2372&#34;
	height=&#34;1344&#34;
	srcset=&#34;https://theoutsidelaine.com/p/dune/paul_huf7e7b2055b8d21d6ae47b30c716c84e6_4230599_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/dune/paul_huf7e7b2055b8d21d6ae47b30c716c84e6_4230599_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;Paul Atredies Becomes The Lord&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;176&#34;
		data-flex-basis=&#34;423px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;第一部時我們看到 Fremens 對待 Paul 只是認為他是個具有武力值的異星人，對他充滿懷疑與質疑；但隨著第二部，Paul 與族人們日日相處於沙漠，學習蒸餾水、馴服了大隻沙蟲，成功讓自己在 Fremens 中擁有一個新名字：Paul Muad&amp;rsquo;Dib Usul。他在南方選擇喝下毒藥，證明自己身為領袖的絕佳身體能力。在北方與南方的會談中，他力排眾議拒絕遵循傳統與 Stilgar 進行殊死搏鬥，保護己方最強戰力。最後戰爭中，他隻身對抗 Feyd-Rautha Hakonnens，將其殺死，並透過聯姻的提議取得國王的禪位。這種種的一切讓我們看到 Paul 在成為領袖的這條路上，不僅是具有勇氣、毅力，更願意為了取得民心、贏得戰爭這個他始終刻在心裡、存在於戒指上的目標，做出許許多多的嘗試，也都獲得了成功。&lt;/p&gt;
&lt;p&gt;然而，這些嘗試、這些想要統領 Arrakis 所做出的努力，也逐漸讓 Paul 背離了當初他想要成為的人：僅僅是一個與 Fremens 並肩作戰的普通人，希望可以為星球帶回雨水就好。在最後，Paul 做出了與他厭惡的敵人一樣的決定：「Be a Harkennon」，使用原子彈等暴力的戰爭武器炸毀敵方軍營、發動聖戰把其他氏族 &amp;ldquo;send back to paradise&amp;rdquo;。可以理解他為了保護族人做出的努力，只不過就跟很多戰爭一樣，一旦起頭、造成了創傷，對於彼此的傷害就永遠停不下來，&lt;/p&gt;
&lt;p&gt;Paul 也背離了與 Chani 白頭到老的承諾：與公主的聯姻，讓 Chani 勢必無法光明正大與 Paul 在一起，所以結尾她憤怒而遺憾的離開了堡壘，召喚沙蟲就此離開。而 Paul 除了望向她，也沒有任何的表示，或許千言萬語也無法說明 Paul 遺棄諾言的理由吧。&lt;/p&gt;
&lt;h2 id=&#34;被弱化的反派&#34;&gt;被弱化的反派&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/dune/BeneGesserit.jpg&#34;
	width=&#34;1660&#34;
	height=&#34;1204&#34;
	srcset=&#34;https://theoutsidelaine.com/p/dune/BeneGesserit_hu8527a5f53c47d1c43b7363fec7e02dbb_98824_480x0_resize_q75_box.jpg 480w, https://theoutsidelaine.com/p/dune/BeneGesserit_hu8527a5f53c47d1c43b7363fec7e02dbb_98824_1024x0_resize_q75_box.jpg 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;Bene Gesserit&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;137&#34;
		data-flex-basis=&#34;330px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;Dune 2 相較於 Dune, 反派的氣場明顯的被弱化。Dune 2的反派 Feyd-Rautha Hakonnens 原本的設定是一個高智力的反社會份子，用來取代 Rabban 掌控香料星球，因此讓 Bene Gesserit 懼怕無法掌控他。然而只有在轟炸 Fremens 基地、虐待僕人上面顯示了他的恐怖之外，其餘時間並無法看到他在阻擋 Paul 上做出的貢獻，甚至最後與男主角的比武，也並沒有把對方逼到山窮水盡的地步。在整部 Dune2, 感受到更多的是皇族之前形容 Hakonnens 「無腦」的這個形容詞，連 Paul 未死的秘密也並未探查出來。&lt;/p&gt;
&lt;p&gt;不過後來想想，或許真正的反派從來都不是 Hakonnens，而是隱藏在底下、操弄星球血脈的 Bene Gesserit 吧。因為懼怕身為救世主的男主角，因此 Bene Gesserit 唆使皇室滅族 Atredies。背後的另一個原因也是因為 Lady Jessica 擅自將孩子的性別改成男生，破壞傳統，為了維持宇宙平衡（？）才出此下策把整個 Atredies 滅族。總之，如果把反派定位給這個光明姐妹會，或許就不會覺得反派有被弱化的感覺了，畢竟這部續集中，姐妹會的存在感還蠻高的。&lt;/p&gt;
&lt;h2 id=&#34;hans-zimmer-的又一部電影配樂鉅作&#34;&gt;Hans Zimmer 的又一部電影配樂鉅作&lt;/h2&gt;
&lt;iframe width=&#34;560&#34; height=&#34;315&#34; src=&#34;https://www.youtube.com/embed/53BBRKF-L60?si=3EistYjFBw0Tj1aT&#34; title=&#34;YouTube video player&#34; frameborder=&#34;0&#34; allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; allowfullscreen&gt;&lt;/iframe&gt;
&lt;p&gt;在寫這部影評時重新回溫 Dune 的配樂，一邊聽一邊打字，彷彿又回到沙塵滾滾的那個星球中。&lt;/p&gt;
&lt;p&gt;沙丘的每段配樂，都讓我覺得這些音符的組合極度適合沙漠氛圍，那是一種很寂靜、荒蕪、一觸即發，又深不可測的感覺。在令人緊張不安的配樂上加上低沈的鼓聲、些許刺耳的金屬樂器敲擊，營造出沙漠丘壑上一望無際的危機四伏。&lt;/p&gt;
&lt;p&gt;這不禁讓我想到之前另一部電影 Interstellar，同樣是配樂，不過場景是設定於宇宙航行與時間理論，Hans Zimmer 在那部電影配樂營造的感覺就比較漂浮、超脫，相較起來 Dune 果決、征戰的感覺更多了一點。&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;看完第一部、第二部 Dune，覺得不論是畫面、劇情、演員演技、配樂上，都是很值得一看具有高度美學的作品。很期待之後第三季的上映，Anya Taylor Joy 成為新主角之一會有什麼令人驚喜的劇情，以及帶來的值得期待的演出技巧。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>System Design (IV)</title>
        <link>https://theoutsidelaine.com/p/system-design-4/</link>
        <pubDate>Sat, 18 May 2024 20:00:00 +0000</pubDate>
        
        <guid>https://theoutsidelaine.com/p/system-design-4/</guid>
        <description>&lt;img src="https://theoutsidelaine.com/p/system-design-4/cover.png" alt="Featured image of post System Design (IV)" /&gt;&lt;p&gt;這禮拜繼續讀 &amp;lt;System Design Interview- An insider&amp;rsquo;s guide&amp;gt; 第四章。&lt;/p&gt;
&lt;h1 id=&#34;chapter-4-設計-rate-limiter&#34;&gt;Chapter 4: 設計 Rate Limiter&lt;/h1&gt;
&lt;p&gt;這章開始作者用實戰的方法帶大家了解，每一種不同的系統的特性是什麼、以及要設計這樣的元件可以怎麼去做思考。&lt;/p&gt;
&lt;p&gt;首先第一個設計的元件就是 Rate Limiter (網路限速器、限流器)。如果面試時，被要求設計一個 rate limiter，你可以怎麼做？&lt;/p&gt;
&lt;p&gt;所謂的 rate limiter簡單來說，就是一個可以限制 client 在指定時段內發送 request 數量的元件。比如：user 每秒可以發幾則貼文、user 每天只能領幾次的獎勵。他的優點是可以防止 server overload, 進而優化資源分配、降低server成本、並避免 dos 攻擊。&lt;/p&gt;
&lt;h2 id=&#34;第一步驟瞭解問題並確立設計的範圍&#34;&gt;第一步驟：瞭解問題，並確立設計的範圍&lt;/h2&gt;
&lt;p&gt;可以問的問題有：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;哪一種 rate limiter: client side or server side?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;這個問題屬於明知故問型的提問，因為 client side rate limiter 可控性不高、可靠性也不高。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;是否要根據 ip, user_id etc 去追蹤 api request?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;系統的規模大小？for 大公司 or 小新創?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;系統是否需要運作於分散式環境？&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;這個 rate limiter 需要做成一個單獨的服務、或是實作於應用程式中？&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;是否需要通知受到限制的 user?&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;根據回答，可能可以匯總的系統需求如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;必須要準確限制 request -&amp;gt; (：演算法選擇)&lt;/li&gt;
&lt;li&gt;必須要盡可能使用少一點記憶體 -&amp;gt; (：演算法選擇)&lt;/li&gt;
&lt;li&gt;因為系統運作於分散式環境，所以採用分散式做法：多 server 共用一個 rate limiter -&amp;gt; (：rate limiter 實作位置選擇)&lt;/li&gt;
&lt;li&gt;others: 需要高容錯、異常處理&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;第二步驟提出高階設計並取得認可&#34;&gt;第二步驟：提出高階設計，並取得認可&lt;/h2&gt;
&lt;p&gt;這邊主要要思考兩點：(1) rate limiter 施作位置 (2) rate limiter 演算法選擇&lt;/p&gt;
&lt;h3 id=&#34;1-rate-limiter-施作位置&#34;&gt;1. rate limiter 施作位置&lt;/h3&gt;
&lt;p&gt;這邊主要考量的是：放置於api server, 或是施作於 api gateway（microservice 中的一個中介元件，用途廣泛，功能包含身份驗證、白名單etc）.&lt;/p&gt;
&lt;p&gt;書中列出幾個可以拿出來討論的點：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;目前公司是否已經使用微服務架構、並且此架構已用到 api gateway? 有的話，或許可以考慮直接施作於 api gateway.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;所選用的演算法，是否可以於 api gateway中選用，或是需要自製於 server 端？&lt;/p&gt;
&lt;p&gt;-&amp;gt; 2-1. 目前公司使用的程式語言，是否支援實作 server 端 rate limiter?&lt;/p&gt;
&lt;p&gt;-&amp;gt; 2-2. 目前公司提供的時間、成本，是否支援開發 server 端 rate limiter?&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;2-rate-limiter-演算法選擇&#34;&gt;2. rate limiter 演算法選擇&lt;/h3&gt;
&lt;p&gt;有五個比較常見的演算法如下：&lt;/p&gt;
&lt;h4 id=&#34;1-token-bucket&#34;&gt;(1) Token Bucket&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;步驟：有兩個控制變數，分別是 bucket 容量、token 填入速度。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每當一個 request 進來，系統檢查 bucket
&lt;ul&gt;
&lt;li&gt;如果 token 數量足夠，則消耗一個 token 給該 request，並使 request 通過。&lt;/li&gt;
&lt;li&gt;如果 token 數量不足夠，則不使 request 通過。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;固定一個時間之內 refill bucket&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;一個系統可以設計一或多個 bucket&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;多個 bucket 的狀況：要針對每一個 api 做比較嚴格的 rate limit&lt;/li&gt;
&lt;li&gt;一個 bucket 的狀況：系統很鬆，每秒可以接收極大量的 total requests&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;優點：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;容易實作&lt;/li&gt;
&lt;li&gt;bucket 大小固定、有限，因此有比較好的 memory usage&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可以允許短時間內的流量爆炸&lt;/strong&gt;：它不限定某個時間只能有幾個request, 只要 bucket 裡面還有refilled tokens, 就可以通過 request&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;缺點：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;書中提到說要 tune 這兩個參數不是一件容易的事&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;應用該演算法的公司：Amazon, Stripe&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;2-leaking-bucket&#34;&gt;(2) Leaking Bucket&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;步驟：有兩個控制變數，分別是 queue 容量、request 處理速度。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每當一個 request 進來，系統檢查 queue
&lt;ul&gt;
&lt;li&gt;如果 queue not full, 便把 request 加入 queue&lt;/li&gt;
&lt;li&gt;如果 queue full, 便丟棄 request&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;固定一個 request process rate&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;優點：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;queue 大小固定、有限，因此有比較好的 memory usage&lt;/li&gt;
&lt;li&gt;request process rate 是固定速率，所以適合需要穩定 outflow rate 、不會有突然流量爆炸的狀況。&lt;/li&gt;
&lt;li&gt;(somehow 書中沒有提到這個作法是容易實作的)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;缺點：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果流量爆炸，新 request 處理效率會變很差&lt;/li&gt;
&lt;li&gt;書中提到說要 tune 這兩個參數也不是一件容易的事&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;應用該演算法的公司：Shopify&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;3-fixed-window-counter&#34;&gt;(3) Fixed Window Counter&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;步驟：每個固定的 time slot (eg: 1分鐘、5分鐘) 有固定的 request capacity 與一個 counter&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每當一個 request 進來，系統
&lt;ul&gt;
&lt;li&gt;counter ++&lt;/li&gt;
&lt;li&gt;如果 ++ 之後 capacity 超過，則丟棄 request&lt;/li&gt;
&lt;li&gt;如果 ++ 之後 capacity 未超過，則通過 request&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;重要特性（問題）：
&lt;ul&gt;
&lt;li&gt;兩個 time slot request 如果恰恰好是 capacity 最大值（意即極高需求狀況），則中間切分點之 time slot(滾動視窗) 會允許了 capacity 兩倍的 request進來，導致超乎 loading 的需求被處理了。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;優點：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;容易實作&lt;/li&gt;
&lt;li&gt;request capacity 固定、有限，因此有比較好的 memory usage&lt;/li&gt;
&lt;li&gt;可以被 Redis 支援&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;缺點：流量激增會有超過 loading 的需求被處理&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;4-sliding-window-log&#34;&gt;(4) Sliding Window Log&lt;/h4&gt;
&lt;p&gt;這是一個有趣的演算法。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;步驟：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每當一個 request 進來，系統
&lt;ul&gt;
&lt;li&gt;將這個 request的 time stamp 加入日誌中&lt;/li&gt;
&lt;li&gt;檢查目前日誌中、「新 request 的一個 time slot」以前，是否還存有其他老request的 time stamps
&lt;ul&gt;
&lt;li&gt;有則刪除&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;檢查目前日誌中，time stamp capacity 是否超標
&lt;ul&gt;
&lt;li&gt;無則 approve request&lt;/li&gt;
&lt;li&gt;有則 reject request&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;優點：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;限速效果十分準確：很可以確保滾動的 time slot 也可以穩定的不超標&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;缺點：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;必須要額外花很多的 memory 紀錄每一個 request 的 time stamp，不會因為 request 被丟棄而消失&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;5-sliding-window-counter&#34;&gt;(5) Sliding Window Counter&lt;/h4&gt;
&lt;p&gt;這是一個融合 Fixed Window Counter 和 Sliding Windoe Log 的做法&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;它主要的目的是解決 Fixed Window Counter 遇到的問題，透過施作一個「百分比公式」:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;滾動視窗的 total request = 目前視窗 request + 前一視窗 request * 前一視窗佔滾動視窗的百分比。&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;優點：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不會讓滾動視窗突有超高流量出現&lt;/li&gt;
&lt;li&gt;因此有比較好的 memory usage&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;缺點：限速效果不會到非常精確（不過也不會到非常不精確，根據 Cloudflare實驗結果錯誤率是 0.003% in 4b requests）&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;3-rate-limiter-資料儲存地點&#34;&gt;3. rate limiter 資料儲存地點&lt;/h3&gt;
&lt;p&gt;通常會把資料放在 cache: (1)存取速度快 (2)支援過期策略。&lt;/p&gt;
&lt;p&gt;Redis 是一個常被施用的元件，如果使用 Fixed Window Counter，可以用到他的 INCR(counter ++)指令與 EXPIRE(使 counter 過期)指令。&lt;/p&gt;
&lt;h2 id=&#34;第三步驟深入設計&#34;&gt;第三步驟：深入設計&lt;/h2&gt;
&lt;p&gt;根據所設計的 rate limiter 架構，延伸的問題通常有以下兩者：&lt;/p&gt;
&lt;h3 id=&#34;1-basic-question-如果超出-rate-limit&#34;&gt;1. basic question: 如果超出 rate limit&lt;/h3&gt;
&lt;p&gt;solution: API 回傳&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;429 error response&lt;/li&gt;
&lt;li&gt;X-Ratelimit-After&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;補充: rate limiter 常傳送的 HTTP headers&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;X-Ratelimit-Remaing: window 內剩餘的可用 request數&lt;/li&gt;
&lt;li&gt;X-Ratelimit-Limit: 一個 window 總共可用的 request數&lt;/li&gt;
&lt;li&gt;X-Ratelimit-After: 當獲得 429 error, 多久之後可以再次重新發送 request&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;2-advanced-questions-1-當分散式系統有並行的-request-如何避免-race-condition&#34;&gt;2. advanced questions 1: 當分散式系統有並行的 request, 如何避免 race condition&lt;/h3&gt;
&lt;p&gt;solution:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Lock&lt;/li&gt;
&lt;li&gt;Lua script&lt;/li&gt;
&lt;li&gt;Redis ordered data structure&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;3-advanced-questions-2-當分散式系統有多個-rate-limiters-如何同步每個-rate-limiter-讓所有資料都存在於元件中使-rate-limiting可以正常運作&#34;&gt;3. advanced questions 2: 當分散式系統有多個 rate limiters, 如何同步每個 rate limiter, 讓所有資料都存在於元件中，使 rate limiting可以正常運作？&lt;/h3&gt;
&lt;p&gt;solution:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Utilize Redis as centralized data storage&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;4-advanced-questions-others-效能最佳化&#34;&gt;4. advanced questions others: 效能最佳化&lt;/h3&gt;
&lt;p&gt;solution:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;地理位置上，設置多資料中心&lt;/li&gt;
&lt;li&gt;使用 eventual consistency model 同步資料&lt;/li&gt;
&lt;li&gt;監控 rate limiter表現，隨時思考演算法選擇的恰當性&lt;/li&gt;
&lt;li&gt;rate limiter設置不同策略：硬限制、軟限制&lt;/li&gt;
&lt;li&gt;不僅在 HTTP 第七層實作，也可以往底下去實作 rate limiter. eg: Ip Table rate limiter.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;我覺得這章蠻有趣的，不僅初步認識與理解 rate limiter的實作概念，也知道要怎麼去討論一個 rate limiter 的設計方式。很好奇有哪些公司會選用 Sliding Window Log 這種很精細又高成本的算法的。除了算法的選擇上，如何做好 trade-off 不是一件容易的事情之外，要怎麼針對每個 follow up question 去提出適合的解決方法，比如說如何使用 Redis 解決 race condition 問題又是需要額外 survey的議題。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Toastmaster Judge Experience</title>
        <link>https://theoutsidelaine.com/p/ntu-toastmaster-judge/</link>
        <pubDate>Sat, 11 May 2024 20:00:00 +0000</pubDate>
        
        <guid>https://theoutsidelaine.com/p/ntu-toastmaster-judge/</guid>
        <description>&lt;img src="https://theoutsidelaine.com/p/ntu-toastmaster-judge/cover.png" alt="Featured image of post Toastmaster Judge Experience" /&gt;&lt;p&gt;來小小紀錄一下初次當英文演講比賽評審的心得吧～&lt;/p&gt;
&lt;p&gt;大概是 5/8 禮拜三附近接到社團幹部的邀請，當時其實沒想太多，覺得是一個很新奇很有趣的體驗，所以就接下來了。結果 5/10 禮拜五當天突然覺得一個緊張，想到啊自己從來沒有當英文演講評審的經驗，只有演講的經驗，要怎麼做好評論的工作啊？鄰近傍晚點開 Joshua 在 discord 傳的 youtube 連結，狂看以前社團演講的先人評審們都怎麼評論的，也嘗試自己簡單寫了一點小架構（summary, complements, improvements, conclusion）。後來遇到剛 covid 病好的 Matt，也是臨時受邀來當評審、但是經驗遠多於我的前輩，有認識的人坐在旁邊，然後也受到他的鼓勵之後，就覺得比較有頭緒、不那麼緊張了一點。&lt;/p&gt;
&lt;p&gt;剛開始評論的時候，很擔心自己漏掉演講者內容的細節，所以很努力的在記錄他們講了什麼，也不太能 enjoy 演講者們的內容；但是到了第四個演講者之後，慢慢開始熟悉評論的流程，就比較可以專心聆聽他們在講什麼、而不是很著重於要怎麼給出一個好的評論了。&lt;/p&gt;
&lt;p&gt;我後來覺得評論的工作最基本的是掌握演講者的內容，而這其實聽習慣後都會有個 pattern 可以 follow：一個 moral idea, 有一個困境、如何克服，最後帶出 takeaway。聽到第一段之後，就可以大概知道最後他想要告訴我們什麼，所以根據心理的這樣的預測，就可以判斷演講者的演講內容是否有走在那個標準之上。如果內容過關，之後的評論部分就不是問題了：演講者是否有 voal variety, body language, 是否有與觀眾進行互動等等。&lt;/p&gt;
&lt;p&gt;這屆的新生們真的都表現得很好，記得有一個學弟的演講主題是 &amp;ldquo;How to get yourself girlfriends&amp;rdquo; 還有附帶一個魔術表演，很厲害ＸＤ 有的學弟分享調酒、光舞，有學妹分享食物、去美國的旅遊經驗、或是出國交換的經驗，每個人都很努力的在呈現自己想要傳達的內容，勇敢表達，覺得有這樣的精神很棒，畢竟演講就是首先要有勇氣可以表現自己。第二名、第三名的學妹們不論是在台風還是演講架構上都非常的清晰、穩健、優雅，很值得大家學習。第一名的學妹講述 Ai Generation，那個滑順的演講風格讓我不禁懷疑她是否有在 NTU 兼職授課ＸＤ&lt;/p&gt;
&lt;p&gt;附上幾張照片：&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/ntu-toastmaster-judge/1.png&#34;
	width=&#34;1080&#34;
	height=&#34;1080&#34;
	srcset=&#34;https://theoutsidelaine.com/p/ntu-toastmaster-judge/1_hu868bbc697b45f1f347b0a9f46534ad44_83685_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/ntu-toastmaster-judge/1_hu868bbc697b45f1f347b0a9f46534ad44_83685_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;頒獎&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;100&#34;
		data-flex-basis=&#34;240px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/ntu-toastmaster-judge/2.png&#34;
	width=&#34;1080&#34;
	height=&#34;1080&#34;
	srcset=&#34;https://theoutsidelaine.com/p/ntu-toastmaster-judge/2_hu9b372d89186e60010a5556da6bc85b97_170263_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/ntu-toastmaster-judge/2_hu9b372d89186e60010a5556da6bc85b97_170263_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;大合照&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;100&#34;
		data-flex-basis=&#34;240px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;最後，真的覺得很榮幸可以有機會來當社團的演講比賽評審，算是很難得的體驗，也感謝有這個被邀請的機會！&lt;/p&gt;
</description>
        </item>
        <item>
        <title>System Design (III)</title>
        <link>https://theoutsidelaine.com/p/system-design-3/</link>
        <pubDate>Fri, 10 May 2024 08:00:00 +0000</pubDate>
        
        <guid>https://theoutsidelaine.com/p/system-design-3/</guid>
        <description>&lt;img src="https://theoutsidelaine.com/p/system-design-3/cover.png" alt="Featured image of post System Design (III)" /&gt;&lt;p&gt;這禮拜繼續讀 &amp;lt;System Design Interview- An insider&amp;rsquo;s guide&amp;gt; 第三章。&lt;/p&gt;
&lt;h1 id=&#34;chapter-3-系統設計面試的框架&#34;&gt;Chapter 3: 系統設計面試的框架&lt;/h1&gt;
&lt;p&gt;這章要介紹的是，如何進行一個系統設計面試。這章應該是讀到目前為止讓我最有感的一章了吧。&lt;/p&gt;
&lt;p&gt;首先，他提到了一個重要的問題：&lt;strong&gt;「為什麼公司要考系統設計？」&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;考系統設計的原因，不是因為公司想要找到一個可以設計出 Google 搜尋引擎的完美工程師，或是一個十分精通於設計的人才。相反的，一場成功的系統設計面試可以探測到面試者的很多面向：面試者可否有效的與面試官針對某個問題進行合作，提出一個可行的解決方案？在這其中，可以觀察到面試者的協作能力、溝通能力、承受壓力的能力、以建設性方法解決問題的能力。另外，還可以看看面試者是否有透露什麼危險的訊號：是否過度設計、展現固執的個性、或是狹隘的思維？&lt;/p&gt;
&lt;p&gt;因此，這章旨在提供一個簡單有效的框架，讓我們可以訓練自己做一個有效的問題解決者。以下，作者提出了四個步驟，並假設本次我們面對到的題目是「設計一個 news feed system」。&lt;/p&gt;
&lt;h2 id=&#34;第一步驟瞭解問題並確立設計的範圍&#34;&gt;第一步驟：瞭解問題，並確立設計的範圍&lt;/h2&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;如果是 news feed system, 可以先詢問產品最主要的功能為何，假設回覆是「發文」，接著便可以接著詢問發文的排列順序、是否僅包含文字或允許多媒體內容、read-write ratio、consistency requirement等等。接著再針對系統規模大小詢問，比如一天流量，然後接著可以詢問使用者最多的朋友數量等等。&lt;/p&gt;
&lt;h2 id=&#34;第二步驟提出高階設計並取得認可&#34;&gt;第二步驟：提出高階設計，並取得認可&lt;/h2&gt;
&lt;p&gt;接著，便是利用以上取得的資訊、做出初步設計的階段了。&lt;/p&gt;
&lt;p&gt;首先可以先用一個 box diagram，畫出主要 entity 以及其關聯。主要 entity 可能有：client side, web server, db, cache, CDN, message queue 等等。也可以註記主要的 api 有哪些。&lt;/p&gt;
&lt;p&gt;關於粗略估算的部分，作者有提到需針對提出的假說做一點估算，但讀書會的當周講者認為不太需要。我認為如果僅僅是要考驗面試者針對系統功能的規劃思路，或許估算的部分相較於決定哪些功能的使用與否確實看起來比較 minor 一點。&lt;/p&gt;
&lt;p&gt;最後，就是使用一個具體的功能/範例，示範一下你的系統會如何運作。&lt;/p&gt;
&lt;p&gt;這期間必須將面試官視為是你的 partner, 與他討論每一個環節，盡量取的他的回饋。&lt;/p&gt;
&lt;p&gt;如果是 news feed system, 可以針對「發布個人動態」去畫一個 diagram, 說明從 client side開始，如何經過 load balancer, 抵達 post service, 然後存取 post cache, post database 等等。&lt;/p&gt;
&lt;h2 id=&#34;第三步驟深入設計&#34;&gt;第三步驟：深入設計&lt;/h2&gt;
&lt;p&gt;第三步驟是一個比較 tricky 的環節。當你已經釐清需求、畫好藍圖之後，面試官可以延伸問你很多問題。諸如以下（內容為讀書會講者所整理）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;high level design: 製作 on demand new feed 的 trade-off 為何&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;技術細節：how to get friend list by graph database？&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;潛在效能問題：what if the fan-out service in new feed system cannot process new post fast enough?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;envelop estimation (算數學環節)：how many RAMs are needed for caching all news feed?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;新需求的討論：what will happen if we need to edit page? delete page? how to do fraud detection?&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;第四步驟彙整總結&#34;&gt;第四步驟：彙整總結&lt;/h2&gt;
&lt;p&gt;這裡要特別注意的是，要留給自己 wrap up 你的方案的時間，不要花太多時間在細節、或是被面試官不小心帶走節奏。如果還有餘裕與時間，可以再額外討論的點有：&lt;/p&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;可以觀察哪些衡量指標？&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;系統面試環節既是考驗你對不同技術的理解，也考驗你如何在有限的時間之內，流暢地提出一個問題的解方。最主要的 takeaway 如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;沒有正確的答案、也沒有最好的答案，只有最適合的答案。不要背答案，盡量針對每一個問題去想一個合適的解決方法。&lt;/li&gt;
&lt;li&gt;把面試官當成你的隊友，跟他討論、溝通，切記不要一個人悶頭思考。時常取得面試官的 feedback。&lt;/li&gt;
&lt;li&gt;當卡住的時候，勇於跟面試官要求提示。&lt;/li&gt;
&lt;li&gt;永不放棄。&lt;/li&gt;
&lt;/ol&gt;
</description>
        </item>
        <item>
        <title>System Design (II)</title>
        <link>https://theoutsidelaine.com/p/system-design-2/</link>
        <pubDate>Thu, 02 May 2024 08:00:00 +0000</pubDate>
        
        <guid>https://theoutsidelaine.com/p/system-design-2/</guid>
        <description>&lt;img src="https://theoutsidelaine.com/p/system-design-2/cover.png" alt="Featured image of post System Design (II)" /&gt;&lt;p&gt;這禮拜繼續讀 &amp;lt;System Design Interview- An insider&amp;rsquo;s guide&amp;gt; 第二章。這章很短，不過帶出一個很重要的概念－數字估算。&lt;/p&gt;
&lt;h1 id=&#34;chapter-2-粗略的估算&#34;&gt;Chapter 2: 粗略的估算&lt;/h1&gt;
&lt;p&gt;在了解系統設計的基礎架構思路後，下一步我們便需要了解，依照這個思路所架構的系統，它的&lt;strong&gt;能耐、效能&lt;/strong&gt;表現究竟如何。&lt;/p&gt;
&lt;p&gt;我們必須要先知道有哪些常見的系統效能指標，並且知道該怎麼粗略的去估算它們。在取得這項能力之前，這本書推薦了一些必須熟悉的基礎常識，這有助於我們在估算系統效能指標上可以更駕輕就熟。&lt;/p&gt;
&lt;p&gt;以下我就用 top-down 的方式來 recap 這些概念（會跟原文順序相反），並再補充一點細節。&lt;/p&gt;
&lt;h2 id=&#34;1-常見的系統效能指標&#34;&gt;1. 常見的系統效能指標&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;QPS（Query Per Second）:
&lt;ul&gt;
&lt;li&gt;描述&lt;strong&gt;資料庫&lt;/strong&gt;或是&lt;strong&gt;搜尋引擎&lt;/strong&gt;的每秒鐘查詢數量。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;QPS 峰值:
&lt;ul&gt;
&lt;li&gt;一般情況下，QPS peek 會等於 &lt;strong&gt;QPS * 2&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;儲存空間：
&lt;ul&gt;
&lt;li&gt;儲存各種數據，包含document, media, logging等，的空間。會以 bytes 為單位。&lt;/li&gt;
&lt;li&gt;可能指特定檔案儲存空間，也可能指整體儲存空間。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;伺服器數量：
&lt;ul&gt;
&lt;li&gt;用於不同目的的伺服器總數量，包含Web server, DB server, document server, mail server 等。&lt;/li&gt;
&lt;li&gt;可能指特定類型伺服器數量，也可能指總共伺服器數量。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;2-必須熟悉的基礎知識&#34;&gt;2. 必須熟悉的基礎知識&lt;/h2&gt;
&lt;h3 id=&#34;1-table-of-metrics&#34;&gt;(1) Table of metrics&lt;/h3&gt;
&lt;h4 id=&#34;quantity-related-metrics&#34;&gt;Quantity related metrics&lt;/h4&gt;
&lt;p&gt;在軟體領域，通常我們會用 2 的次方來定義這些單位（因為我們使用 byte 為單位存儲）；在電子通訊或是物理學領域，通常則會使用 10 的次方來定義這些單位。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;kilo: =  2&lt;sup&gt;10&lt;/sup&gt; ~= 10&lt;sup&gt;3&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;mega = 2&lt;sup&gt;20&lt;/sup&gt; ~= 10&lt;sup&gt;6&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;giga = 2&lt;sup&gt;30&lt;/sup&gt; ~= 10&lt;sup&gt;9&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;tera = 2&lt;sup&gt;40&lt;/sup&gt; ~= 10&lt;sup&gt;12&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;peta = 2&lt;sup&gt;50&lt;/sup&gt; ~= 10&lt;sup&gt;15&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;exa = 2&lt;sup&gt;60&lt;/sup&gt; ~= 10&lt;sup&gt;18&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;zetta = 2&lt;sup&gt;70&lt;/sup&gt; ~= 10&lt;sup&gt;21&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;yotta = 2&lt;sup&gt;80&lt;/sup&gt; ~= 10&lt;sup&gt;24&lt;/sup&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&#34;speed-related-metrics&#34;&gt;Speed related metrics&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;milli: = 10&lt;sup&gt;-3&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;micro: = 10&lt;sup&gt;-6&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;nano: = 10&lt;sup&gt;-9&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;pico: = 10&lt;sup&gt;-12&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;femto: = 10&lt;sup&gt;-15&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;atto: = 10&lt;sup&gt;-18&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;zepto: = 10&lt;sup&gt;-21&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;yocto: = 10&lt;sup&gt;-24&lt;/sup&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;在軟體領域中，由於容量通常以 byte 為儲存單位，因此容量單位習慣使用 2 為基底的乘數，方便二進制系統的溝通；而由於速度通常是看每秒多少個bit，因此習慣以 10 為基底的乘數，可以更直接簡單的反應單位時間內的傳輸位元數量。&lt;/p&gt;
&lt;h3 id=&#34;2-幾個延遲相關的數字&#34;&gt;(2) 幾個延遲相關的數字&lt;/h3&gt;
&lt;p&gt;書中介紹了 13 個常用的延遲相關數字，由 Google 的 Jeff Dean 在 2010 年提出：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Time (ns)&lt;/th&gt;
&lt;th&gt;Time (us)&lt;/th&gt;
&lt;th&gt;Time (ms)&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;L1 cache reference&lt;/td&gt;
&lt;td&gt;0.5&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;Branch mispredict&lt;/td&gt;
&lt;td&gt;5&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;L2 cache reference&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;14x L1 cache&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mutex lock/unlock&lt;/td&gt;
&lt;td&gt;25&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;Main memory reference&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;20x L2 cache, 200x L1 cache&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compress 1K bytes with Zippy&lt;/td&gt;
&lt;td&gt;3,000&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Send 1K bytes over 1 Gbps network&lt;/td&gt;
&lt;td&gt;10,000&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read 4K randomly from SSD&lt;/td&gt;
&lt;td&gt;150,000&lt;/td&gt;
&lt;td&gt;150&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;~1GB/sec SSD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read 1 MB sequentially from memory&lt;/td&gt;
&lt;td&gt;250,000&lt;/td&gt;
&lt;td&gt;250&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Round trip within same datacenter&lt;/td&gt;
&lt;td&gt;500,000&lt;/td&gt;
&lt;td&gt;500&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read 1 MB sequentially from SSD&lt;/td&gt;
&lt;td&gt;1,000,000&lt;/td&gt;
&lt;td&gt;1,000&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Disk seek&lt;/td&gt;
&lt;td&gt;10,000,000&lt;/td&gt;
&lt;td&gt;10,000&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;20x datacenter roundtrip&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read 1 MB sequentially from disk&lt;/td&gt;
&lt;td&gt;20,000,000&lt;/td&gt;
&lt;td&gt;20,000&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;80x memory, 20X SSD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Send packet CA-&amp;gt;Netherlands-&amp;gt;CA&lt;/td&gt;
&lt;td&gt;150,000,000&lt;/td&gt;
&lt;td&gt;150,000&lt;/td&gt;
&lt;td&gt;150&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;主要要記的就是這 13 個指標，另外書中有提到一些需要記得的延伸內容：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;記憶體查詢速度快、disk 慢&lt;/li&gt;
&lt;li&gt;簡單壓縮法（zippy）速度很快&lt;/li&gt;
&lt;li&gt;(其他過於基本暫略)&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;3-sla&#34;&gt;(3) SLA&lt;/h3&gt;
&lt;p&gt;SLA (Service Level Agreement, 又稱服務等級協議)，是 service provider（eg: AWS, Google）常使用的術語，定義了 service 提供的服務等級。&lt;/p&gt;
&lt;p&gt;SLA 通常會以 99.9% 為正常基準，小數點後的 9 越多，代表服務等級越好。甚至 9 的數量可以換算成系統停機時間。&lt;/p&gt;
&lt;p&gt;比如書中有提到，正常水準 99.9 %的每天停機時間為 14.4分鐘，99.9999%的每天停機時間為 86.4 毫秒。&lt;/p&gt;
&lt;h2 id=&#34;3-範例估算-twitter-的-qps&#34;&gt;3. 範例：估算 Twitter 的 QPS&lt;/h2&gt;
&lt;p&gt;實際在做系統效能指標估算時，必須謹記三點：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;把你的假設與思路寫下來：你要用哪些數字計算這些指標？記下他們，方便釐清也方便日後參考。&lt;/li&gt;
&lt;li&gt;標記單位：你使用的這些數字是什麼單位？寫下來，才不會搞混。&lt;/li&gt;
&lt;li&gt;善用近似法、四捨五入：盡量用簡單乾淨的數字做計算比較方便，不求精確程度。比如題目給說每秒 query 數是 197，可以近似為 200。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;假設系統 Twitter:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每月有 3 億活躍用戶&lt;/li&gt;
&lt;li&gt;每天有 50% 的用戶使用 Twitter&lt;/li&gt;
&lt;li&gt;用戶平均每天發 2 則推文&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;要估算 QPS:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;QPS = 每日推文總數 / 一天的秒數&lt;/li&gt;
&lt;li&gt;每日推文總數 = 3 * 10&lt;sup&gt;8&lt;/sup&gt; * 50% * 2 = 3 * 10&lt;sup&gt;8&lt;/sup&gt; 則&lt;/li&gt;
&lt;li&gt;推文 QPS = 3 * 10&lt;sup&gt;8&lt;/sup&gt; / (24 * 3600 秒) ~= 3500&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;這邊要注意的是，每月 3 億用戶是假設每天流量都是 3 億，整個月都是如此。所以不用把 3 億除以30。&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;這章實際帶系統效能估算只有帶了 QPS 跟 儲存空間，感覺 server 數量也是一個很重要的指標，可能需要額外思考如何計算。&lt;/p&gt;
&lt;p&gt;不過透過這章的介紹，我也瞭解了必須熟記很多容量與速度單位的重要性，以及哪些效能指標是重要的，在估算時可以謹記三點讓自己估算時可以更清楚。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>System Design (I)</title>
        <link>https://theoutsidelaine.com/p/system-design-1/</link>
        <pubDate>Mon, 22 Apr 2024 08:00:00 +0000</pubDate>
        
        <guid>https://theoutsidelaine.com/p/system-design-1/</guid>
        <description>&lt;img src="https://theoutsidelaine.com/p/system-design-1/cover.png" alt="Featured image of post System Design (I)" /&gt;&lt;p&gt;從二月準備正職面試以來，遇到了不只一家公司的面試需要考 system design，有鑒於自己在這方面的知識趨近於 0，剛好朋友分享一個開源社群每週三會定期舉辦一本 system design 書籍的讀書會，便想說藉此機會來讀這個領域有關的書，增加自己的知識量。&lt;/p&gt;
&lt;p&gt;這次要讀的書叫做 &lt;strong&gt;&amp;lt;內行人才知道的系統設計面試指南 System Design Interview- An insider&amp;rsquo;s guide&amp;gt;&lt;/strong&gt;，由 Alex Xu 撰寫，總共有 16 個 chapters，從一開始簡要的介紹 system 由小到大的擴展過程，一步步介紹系統內部元件，最後講解大型系統如 Youtube, Google 的設計思路。&lt;/p&gt;
&lt;p&gt;在這一系列文章中，我會逐章整理每一個章節的內容、並分享一點自己的想法。&lt;/p&gt;
&lt;h1 id=&#34;chapter-1-使用者人數----從零到百萬規模&#34;&gt;Chapter 1: 使用者人數 &amp;ndash; 從零到百萬規模&lt;/h1&gt;
&lt;p&gt;這本書的第一章節是 &amp;lt;使用者人數 &amp;ndash; 從零到百萬規模&amp;gt;，使用循序漸進的方法，帶我們了解系統是怎麼一步步擴大的，包含起點是什麼、怎麼疊加元件，進而長成一個可以承載高流量的系統。以下我把系統擴大的過程拆分為七個階段。&lt;/p&gt;
&lt;h2 id=&#34;phase-1-init-a-system-by-web-server--db-server&#34;&gt;Phase 1: Init a system by Web server &amp;amp; DB server&lt;/h2&gt;
&lt;p&gt;想要打造一個系統，要從哪裡開始？最簡單也是最重要的就是先架一個 Web server。這個 Web server 會與 Web browser user 或是 App user進行互動。&lt;/p&gt;
&lt;p&gt;接著，當使用者稍微開始增加後，我們想要將流量做簡單的分流管理，就會把 server 進一步拆成兩個server: (1) Web server, (2) Database server，如下圖。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/system-design-1/1.png&#34;
	width=&#34;914&#34;
	height=&#34;455&#34;
	srcset=&#34;https://theoutsidelaine.com/p/system-design-1/1_hu29593f92e61ae743c7e9a4e2fae03161_25902_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/system-design-1/1_hu29593f92e61ae743c7e9a4e2fae03161_25902_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;200&#34;
		data-flex-basis=&#34;482px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;從此開始，我們以這兩種 server 為基礎，逐步擴大系統的元件。&lt;/p&gt;
&lt;h2 id=&#34;phase-2-horizontal-scaling&#34;&gt;Phase 2: Horizontal scaling&lt;/h2&gt;
&lt;p&gt;接著，當使用者再逐漸開始增加，我們想要擴展系統，該怎麼做？首先，我們要選擇一種高彈性、低局限性的擴展策略：水平擴展（Horizontal Scaling）。垂直擴展的意思是升級 server內元件規格，這種方法有硬體設計上的限制；水平擴展的則是指使用多台 server來平均分擔流量的方法。&lt;/p&gt;
&lt;p&gt;在 Web server方面的水平擴展，我們可以使用增加多台server搭配負載平衡器（Load Balancer）的方法來達成目標，Load Balancer 會計算出最優的系統流量分配方法。而在 Database server上，我們可以透過使用資料庫複寫機制，意即使用一台 write-only master DB 搭配多台 read-only slave DB來達成目標。如果 master DB 掛了，會有其中一台 slave DB取代他。由於多數系統的 read 永遠大於 write，這麼做可以讓系統以平行的方式處理更多 query。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/system-design-1/2.png&#34;
	width=&#34;899&#34;
	height=&#34;890&#34;
	srcset=&#34;https://theoutsidelaine.com/p/system-design-1/2_huf6d604aaf6bad90c246b3a9be9e5b8fc_51031_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/system-design-1/2_huf6d604aaf6bad90c246b3a9be9e5b8fc_51031_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;101&#34;
		data-flex-basis=&#34;242px&#34;
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;horizontal-scaling-known-as-sharding&#34;&gt;Horizontal scaling known as &amp;ldquo;Sharding&amp;rdquo;&lt;/h3&gt;
&lt;p&gt;書中有提到，水平擴展其實有個更廣為人知的名稱：Sharding（分片）。所謂的 Sharding, 意即做出許多個相同 schema 的資料庫，然後平均的把 data 透過 hashing 等方式分配給不同的 sharding DB。在 sharding DB 的使用架構中，最重要的是 hashing 方法中的 &lt;strong&gt;sharding key&lt;/strong&gt; 是如何被選擇的？如果制定的不好，容易讓 data 通通集中於同一個 sharding DB，那就不好了。&lt;/p&gt;
&lt;p&gt;書中提到了幾個有意思的 sharding 要考量到的議題：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;如何制定具有一致性的 hashing method:
&lt;ul&gt;
&lt;li&gt;目的是讓 data 不會過度集中於一個 shard，或是解決當某個 shard 容量滿了之後如何重新搬動 data的問題。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;celebrity / hotspot problem:
&lt;ul&gt;
&lt;li&gt;某個 shard 因為太熱門，被過度的 read action 所淹沒。&lt;/li&gt;
&lt;li&gt;可能的解決方法有：(1) 為每個 celebrity 制定一個 shard, (2) 每個 shard 可能要進一步 partition。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;難以取得所有資料：
&lt;ul&gt;
&lt;li&gt;因為資料都放在不同的 sharding DB，難以做 json join。&lt;/li&gt;
&lt;li&gt;可能的解決方法是將 schema 做 de-normalization。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;sharding-vs-partition&#34;&gt;Sharding vs Partition&lt;/h3&gt;
&lt;p&gt;這邊額外整理一個書中沒提到、但是常常看到的比較：sharding 跟 partition 的差異是什麼？簡而言之:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;sharding 是將 data 依據 hashing method 等方式分配儲存於不同的 databases, 分隔成實體的單位（實際意義上，這些資料位於不同地方）。&lt;/li&gt;
&lt;li&gt;partition 是將同一個 database 中的 data, 依據比如 country, department等單位再次做分割，分隔成虛擬的單位（實際意義上，這些資料位於同個地方，只是以邏輯意義分成不同組別）。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;phase-3-cache--cdn&#34;&gt;Phase 3: Cache &amp;amp; CDN&lt;/h2&gt;
&lt;p&gt;接著，當使用者再逐漸開始增加，service response time 開始變低，我們想要改善系統效能，該怎麼做？這時候我們以資料性質分成兩種處理方式：使用 cache 改善多數資料的存取速度、使用 CDN 改善靜態資料的存取速度。&lt;/p&gt;
&lt;p&gt;Cache 是一個存取速度很快的臨時資料儲存層，介於 Web server 與 client 之間，根據不同的資料類型、存取模式等等，有許多種 request-cache-server 的互動方式（eg: read-through&amp;hellip;）。要讓 Cache 既可以提升速度、又要維持正確性的方法，是仔細的思考 Cache system 的建構策略：什麼時候該使用 cache 而非query db、多久讓 cache中的資料過期、如何讓 cache 與 db資料保持同步、如果 cache 壞掉怎辦、cache 如果滿了怎辦&amp;hellip;&lt;/p&gt;
&lt;p&gt;CDN 是一個第三方供應商提供、為靜態內容提供快取的server，通常分散於地理位置各處。良好的、可以提升靜態內容存取速度的 CDN 很大關鍵取決於 user 與 CDN 之間的地理距離。如同 cache，要讓 CDN 可以良好正確的提升系統效能的方法是思考 CDN 的使用策略，包含過期時間設定、CDN壞掉時的備案措施。另外，CDN 使用成本也是建構系統需要考慮的一點，因此存放於 CDN 的資料必須是高使用率的。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/system-design-1/3.png&#34;
	width=&#34;924&#34;
	height=&#34;913&#34;
	srcset=&#34;https://theoutsidelaine.com/p/system-design-1/3_hu48c34fa339754230f5dbd39bc65d1e12_44583_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/system-design-1/3_hu48c34fa339754230f5dbd39bc65d1e12_44583_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;101&#34;
		data-flex-basis=&#34;242px&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;phase-4-stateless-web-server--auto-scaling&#34;&gt;Phase 4: Stateless web server &amp;amp; Auto scaling&lt;/h2&gt;
&lt;p&gt;接著，當使用者再逐漸開始增加，我們想要進一步優化 service response time，該怎麼做？這時候我們針對 Web 層做進一步的優化，方法就是讓所有的 state 資料（eg: session）從儲存於 Web server 改成儲存於 DB server，使 Web server 成為 Stateless Web server。當 user request 發送到 Web server，相較於一台一台 server 去尋找該 user state data 的存放之處，在 Stateless web server structure 下，Web server 會統一尋找共用的 DB，取得該 user 的 state 資料。&lt;/p&gt;
&lt;p&gt;另外，當變成 Stateless structure 後，就可以引入 auto scaling 機制，讓系統根據流量大小自動添加或移除 Web server。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/system-design-1/4.png&#34;
	width=&#34;974&#34;
	height=&#34;879&#34;
	srcset=&#34;https://theoutsidelaine.com/p/system-design-1/4_hu382f6b037b20b49579d7f58d0f50b879_52523_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/system-design-1/4_hu382f6b037b20b49579d7f58d0f50b879_52523_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;110&#34;
		data-flex-basis=&#34;265px&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;phase-5-data-center&#34;&gt;Phase 5: Data center&lt;/h2&gt;
&lt;p&gt;接著，當使用者再逐漸開始增加，開始有了國際化的使用者族群，我們想要進一步優化 service response time 與可用性，該怎麼做？&lt;/p&gt;
&lt;p&gt;一個常見的方法，就是在不同的地理位置建構資料中心。user request 根據事先定義的流量分配比例，透過 geoDNS 被傳送到最近的資料中心的 servers。建構不同地理位置的資料中心也有其核心議題：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;資料同步：通常不同地理位置的資料都會略有不同，如果一者故障、流量轉移至另一個資料中心時，該怎麼設計資料同步策略，才可以讓不同地區的 request 來到這處的資料中心也可以被 process。&lt;/li&gt;
&lt;li&gt;服務一致性：要怎麼設計一套用於不同地區的測試、部署方法，讓不同地區都可以提供一致性或是相對應的服務？&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/system-design-1/5.png&#34;
	width=&#34;964&#34;
	height=&#34;903&#34;
	srcset=&#34;https://theoutsidelaine.com/p/system-design-1/5_hudb0ab1ec2ef81e3d12c8f8732ef3c18e_52180_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/system-design-1/5_hudb0ab1ec2ef81e3d12c8f8732ef3c18e_52180_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;106&#34;
		data-flex-basis=&#34;256px&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;phase-6-message-queue&#34;&gt;Phase 6: Message Queue&lt;/h2&gt;
&lt;p&gt;接著，當使用者再逐漸開始增加，而且我們的服務開始變得複雜：有些 services 需要很長的 process time。我們想要讓所有的 request 都可以順利被處理、同時優化 response time 與可用性，該怎麼做？&lt;/p&gt;
&lt;p&gt;一個常見的的作法，是打造訊息佇列（Message Queue），來支援這種非同步的請求情狀，並把 Web server進一步拆分為 Web server 與 Worker server。&lt;/p&gt;
&lt;p&gt;包含 Message Queue 的系統架構中，我們將發送 request 的 server 稱為 &amp;ldquo;Producer&amp;rdquo;，他會是一般的 Web server 。而接受 request 進行任務處理的 server 稱為 &amp;ldquo;Consumer&amp;rdquo;，他會是一個 Worker server。而 Message Queue 通常可以透過第三方服務 RabbitMQ 或是 AWS 的 SQS 來實現。&lt;/p&gt;
&lt;p&gt;首先，Producer (eg: 接收 user request 的 Web server)會將一個複雜任務請求傳送到 Message Queue中。接著，Consumer (eg: 負責執行複雜任務的 Worker process/cronjob/service) 會從 queue 中提領出任務並解決。整個過程是非同步的，意即當 Producer暫時故障沒有發送 request，Consumer 還是可以繼續從 queue 提領任務；當 Consumer 還在處理上一份複雜工作未完成時，Producer也還是可以繼續接單，並傳送至 queue中。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/system-design-1/6.png&#34;
	width=&#34;1044&#34;
	height=&#34;266&#34;
	srcset=&#34;https://theoutsidelaine.com/p/system-design-1/6_huf29ad6f0cfe43133391a6d76d5ac9496_21003_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/system-design-1/6_huf29ad6f0cfe43133391a6d76d5ac9496_21003_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;392&#34;
		data-flex-basis=&#34;941px&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;phase-7-logging--performance-metrics--automation&#34;&gt;Phase 7: Logging &amp;amp; Performance Metrics &amp;amp; Automation&lt;/h2&gt;
&lt;p&gt;最後，我們需要一個機制，讓系統內部發生的事情被紀錄，另外也需要一個元件來監控系統效能表現。透過設定 logging、建構 Performance metrics 並對其進行可視化，我們可以有效地掌握系統狀態並思考如何做得更好。另外，當系統越來越龐大，我們也需要制定 automation strategy (eg: cicd, testing)，以提高生產力。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/system-design-1/7.png&#34;
	width=&#34;885&#34;
	height=&#34;1121&#34;
	srcset=&#34;https://theoutsidelaine.com/p/system-design-1/7_hu986689f4271fbf6887258c0ded2e50b2_57745_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/system-design-1/7_hu986689f4271fbf6887258c0ded2e50b2_57745_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;78&#34;
		data-flex-basis=&#34;189px&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;這章算是用很清楚且易懂的方式建構一個系統擴展的架構與思路出來，我覺得對我而言算是一個很好的思考基礎，之後再碰到關於系統設計從零開始設計的議題時，便可以開始想：web server 與 db server 可以分別怎麼擴展、遇到什麼樣的使用情境可以做什麼樣的優化。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Nodejs APP Monitoring with Prometheus &amp; Grafana (&amp; a little Locust)</title>
        <link>https://theoutsidelaine.com/p/nodejs-prometheus-grafana/</link>
        <pubDate>Sat, 20 Apr 2024 08:00:00 +0000</pubDate>
        
        <guid>https://theoutsidelaine.com/p/nodejs-prometheus-grafana/</guid>
        <description>&lt;img src="https://theoutsidelaine.com/p/nodejs-prometheus-grafana/cover.png" alt="Featured image of post Nodejs APP Monitoring with Prometheus &amp; Grafana (&amp; a little Locust)" /&gt;&lt;p&gt;不論是個人專案或是工作項目，當我們完成功能、想要進一步優化系統效能，我們首先要做的事情，便是知道目前系統的表現狀況為何。表現狀況通常可以簡單分成基本與進階，基本的像是 query 頁面的時間、功能是否都健康，而進階的比如說像是當系統遇到 high traffic 時每個 service表現的狀況、系統會在哪邊出現 bottleneck etc。而一旦知道目前系統的表現，我們也可以開始進一步思考有哪一些部分可以再做得更好，比如說讓查詢效率提升、錯誤率下降etc，可以透過微調哪一些元件來達成我們的目標。&lt;/p&gt;
&lt;p&gt;有很多衡量系統效能的工具，比如 Kibana, Datadog, 以及我們今天要介紹的工具：Prometheus + Grafana。&lt;/p&gt;
&lt;h2 id=&#34;why-using-prometheus--grafana&#34;&gt;Why using Prometheus &amp;amp; Grafana&lt;/h2&gt;
&lt;p&gt;一句話來說的話：免費、開源、社群支持度強。&lt;/p&gt;
&lt;p&gt;Prometheus 最早於 2012 年由 SoundCloud 開發並開源，主要用來收集、儲存與系統有關的數據，以便監控其效能。許多的 Prometheus components 皆以 Go 撰寫，便於 static binaries 的建構以及部署。他主要有六個特點：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;multi-dimensional time-series data model: Prometheus 使用一個多維度的時間序列數據模型。每個時間序列由指標名稱和一組 labels 作為唯一標識，這使得在進行查詢和分析時，可以非常靈活地做資料過濾和聚合。&lt;/li&gt;
&lt;li&gt;多種數據取得方法：Prometheus 除了可以從 server的 metrics endpoint 自 HTTP 收集數據之外，還支援 Node Exporter, Blackboc Exporter等方法。另外，Prometheus 有一個名為 Pushgateway的元件，可以透過 HTTP 去 cache 收集到的數據，然後定期的讓 Prometheus從中取得資料進行監控與存儲。&lt;/li&gt;
&lt;li&gt;PromQL(Prometheus Query Language): 一種自定義的查詢語言，提供豐富的功能，可以執行各種數據分析與操作，像是 data selection或 aggregation。&lt;/li&gt;
&lt;li&gt;警報機制：透過設定閥值，當條件被觸發，Prometheus內建的 AlertManager 就會透過比如 email, slack 等發送通知。&lt;/li&gt;
&lt;li&gt;簡單可視化metrics的 web ui: Prometheus 提供一個簡單的 web ui, 可以可視化 PromQL expressions, 並透過 table或是 graph做數據呈現。&lt;/li&gt;
&lt;li&gt;活躍的開源社群：社群會維護許多第三方 &lt;a class=&#34;link&#34; href=&#34;https://prometheus.io/docs/instrumenting/exporters/#exporters-and-integrations&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;exporters, integrators&lt;/a&gt;, 以利於 metrics 的收集。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Prometheus architecture diagram.
&lt;img src=&#34;https://theoutsidelaine.com/p/nodejs-prometheus-grafana/prom.png&#34;
	width=&#34;1351&#34;
	height=&#34;811&#34;
	srcset=&#34;https://theoutsidelaine.com/p/nodejs-prometheus-grafana/prom_hud5ed5663cc35a882862e3f8b09e662f5_96834_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/nodejs-prometheus-grafana/prom_hud5ed5663cc35a882862e3f8b09e662f5_96834_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;Prometheus architecture diagram&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;166&#34;
		data-flex-basis=&#34;399px&#34;
	
&gt;
Credit: &lt;a class=&#34;link&#34; href=&#34;https://prometheus.io/docs/introduction/overview/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Prometheus.io&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Grafana 最早則於 2014 年由瑞典公司 Grafana Labs 開發與開源。Grafana 會從多種數據來源（datasources）取得資料，並提供豐富的圖表、面板、儀表板，讓這些被處理後的數據可以展示於上。數據來源可以有很多種，而 Prometheus是其中一種常見的來源。原因大概是因為 Prometheus量測的指標比如 cpu, memory, heap, event loop 都是 time series data, 而 Grafana 儀表板中的 panels 在接收格式與呈現方式上很適合這種資料；Grafana query area支援 PromQL，所以會 PromQL 的話也可以輕鬆在 query area製作 Grafana圖表。以下是 Grafana的幾個特點：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;支援多種數據來源：除了 Prometheus之外，也支援時間序列型資料庫（如 Prometheus、InfluxDB、Graphite）、日誌數據（如 Elasticsearch、Loki）、關聯式資料庫（如 MySQL、PostgreSQL）等。&lt;/li&gt;
&lt;li&gt;豐富的圖表、面版：包含折線圖、柱狀圖、餅狀圖、地圖、表格等，並支援多種自定義的配置，以滿足不同的可視化需求。&lt;/li&gt;
&lt;li&gt;豐富的查詢語言：除了 PromQL 之外，也提供像是 InfluxQL等查詢語言。另外，Grafana 也有智能提示、syntax highlighting 等功能。&lt;/li&gt;
&lt;li&gt;警報機制：Grafana 內建警報功能，可以根據數據設定相關閥值，一旦條件被觸發，即可發送警報通知。Grafana 支援多種通知方式，比如 email、Slack、PagerDuty 等。&lt;/li&gt;
&lt;li&gt;活躍的開源社群：一樣，社群會維護許多第三方plugins, 便於 Grafana平台不斷的擴展。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;file-structure--docker-compose&#34;&gt;File structure &amp;amp; docker-compose&lt;/h2&gt;
&lt;p&gt;以下先用 top-down 方式簡單呈現一個 Node.js 專案中如何放入 Prometheus 與 Grafana 相關的檔案，之後再說明各個部分的細節。&lt;/p&gt;
&lt;p&gt;folder structure:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── client/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;├── server/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   ├── grafana/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   │   ├── datasources/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   │   │   └── datasources.yml
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   │   └── dashboards/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   │       ├── dashboard.yml
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   │       └── dashboard-basic.json
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   │       └── dashboard-advanced.json
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   ├── prometheus/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   │   └── prometheus.yml
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   ├── prom-middleware/
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   │   └── index.js
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   ├── index.js
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;│   └── Dockerfile
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;└── docker-compose.yml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;在 local 分別起 server, prometheus, grafana 三個主要的 containers.&lt;/p&gt;
&lt;p&gt;docker-compose.yml:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;40
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;41
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;42
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;43
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;44
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;45
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;46
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;47
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;48
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;49
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;50
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;51
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;52
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;53
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;version: &amp;#39;3.8&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;services:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  server:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    build:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      context: ./server
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      dockerfile: Dockerfile
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    ports:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - &amp;#34;127.0.0.1:4000:4000&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    volumes:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - ./server:/usr/app
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - /usr/app/node_modules
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  ...
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  prometheus:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    image: prom/prometheus:v2.20.1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    container_name: prometheus
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    volumes:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - ./server/prometheus:/etc/prometheus
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - prometheus_data:/prometheus
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    ports:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - &amp;#34;9090:9090&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    expose:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - 9090
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    networks:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - monitoring
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  grafana:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    image: grafana/grafana:7.1.5
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    container_name: grafana
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    volumes:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - ./server/grafana/provisioning:/etc/grafana/provisioning
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - grafana_data:/var/lib/grafana
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    environment:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - GF_AUTH_DISABLE_LOGIN_FORM=true
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - GF_AUTH_ANONYMOUS_ENABLED=true
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    ports:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - &amp;#34;3000:3000&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    expose:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - 3000
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    networks:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - monitoring
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;networks:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  monitoring:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    driver: bridge
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;volumes:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  db_data:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  prometheus_data: {}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  grafana_data: {}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;prometheus-configuration&#34;&gt;Prometheus configuration&lt;/h2&gt;
&lt;h3 id=&#34;1-prometheusyml&#34;&gt;1. &lt;code&gt;prometheus.yml&lt;/code&gt;&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;global:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  scrape_interval: 5s
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;scrape_configs:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  - job_name: &amp;#34;synoptic-app&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    static_configs:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      - targets: [&amp;#34;docker.for.mac.localhost:4000&amp;#34;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Prometheus 會根據你設定的 scrape_interval 以固定的時間間隔擷取資料。scrape_configs 用於設定不同 job 的任務目標，此處意指從 target 的 [address:port number] 處擷取資料。&lt;/p&gt;
&lt;p&gt;值得注意的是：targets 的bracket中，由於我們使用docker，且運行於 mac上，所以冒號前者的 value 會是 &lt;code&gt;docker.for.mac.localhost&lt;/code&gt;。我在一開始把位置誤寫成 server container name, 導致 prometheus 遲遲無法取得送到 localhost:4000的資料。&lt;/p&gt;
&lt;h3 id=&#34;2-prom-client&#34;&gt;2. &lt;code&gt;prom-client&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;接著便要開始利用 Prometheus 自 APP server 擷取資料。我們需要將 APP server與&lt;a class=&#34;link&#34; href=&#34;https://prometheus.io/docs/instrumenting/exporters/#exporters-and-integrations&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt; 許多metrics exporter &lt;/a&gt;連接，並透過這些 exporters 傳送資料至 Prometheus server 讓其存儲與監控 APP 的表現資料。&lt;/p&gt;
&lt;p&gt;Express APP 會透過 &lt;code&gt;prom-client&lt;/code&gt; 這個 nodejs library 將 metrics export出去。安裝方法：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;npm install prom-client
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;完成後，在 &lt;code&gt;index.js&lt;/code&gt; 我們可以開始使用 prom-client 去協助我們取得 APP 中的各種數據。在 Prometheus 中開發者重視的數據基本上可以分成三種：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;defaultMetrics&lt;/strong&gt;: Prometheus 官方推薦了一系列值得觀測的指標，在 prom-client中，這些指標被匯總成為「defaultMetrics」。例子： node_process_cpu_system_seconds, nodejs_eventloop_lag_max_seconds, node_process_heap_bytes 等。這些指標會自帶像是 nodejs, node 等 prefix。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;http_request_duration_seconds&lt;/strong&gt;: 這其實是一個主要功能是計時器的 histogramtimer 指標而已，然後我們把它命名為 http_request_duration_seconds。具體來說，時間對於 routing 來說是最基本的表現指標，因此，這個 metric 最主要的目的是協助我們知道每一個 route 的 http request time performances，透過設定這個指標加上撰寫相關的 PromQL，我們可以知道每一個 route的 response time等。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;custom metrics&lt;/strong&gt;: Prometheus 提供開發者自定義四大類 custom metrics: Histogram, Summary, Counter, Gauge. 簡單說明一下每一個指標的定義與用途：
&lt;ul&gt;
&lt;li&gt;Histogram: 直方圖，用於監控某個指標的分布狀況。Eg: Request time的時間分佈。&lt;/li&gt;
&lt;li&gt;Summary: 摘要，用於監控某個指標的分布狀況，特別是其詳細的統計分佈區間，比如 max, min, ?% quantile。Eg: Request time的時間分佈摘要。&lt;/li&gt;
&lt;li&gt;Counter: 計數器，用於監控一個 service 或者 event 的發生次數。Eg: 某 request 的 發生次數。&lt;/li&gt;
&lt;li&gt;Gauge: 計量器，用於監控一個隨時間而變化的數值。Eg: Memory usage。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;這邊我們主要針對 defaultMetrics 與 http_request_duration_seconds 去監控。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;const client = require(&amp;#39;prom-client&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;const register = new client.Registry();
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;client.collectDefaultMetrics({
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  app: &amp;#39;synoptic-monitoring-app&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  prefix: &amp;#39;node_&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  timeout: 10000,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  gcDurationBuckets: [0.001, 0.01, 0.05, 0.1, 0.5, 1, 2, 5], // These are the default buckets.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  register,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;「Registry」是 Prometheus 數據存處所在之處，我們會 init 一個 registry instance，然後將收集到的 defaultMetrics 數據傳入 registry 中。&lt;code&gt;collectDefaultMetrics()&lt;/code&gt; 提供其他客製化的選項，像是 &lt;code&gt;prefix&lt;/code&gt; 可以在每個 default metrics前面加上前綴、&lt;code&gt;timeout&lt;/code&gt; 可以客製化 timeout 時長、&lt;code&gt;gcDurationBuckets&lt;/code&gt; 則是設定 Garbage Collection Histogram(垃圾回收行為監控)的 bucket 寬度。最後再將 register instance 也放入 options 中完成註冊。&lt;/p&gt;
&lt;h3 id=&#34;3-prom-middleware&#34;&gt;3. prom-middleware&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;const express = require(&amp;#39;express&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;const app = express();
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;app.use(httpRequestHistogramMiddleware(register));
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;當 defaultMetrics 已經註冊於 register，我們需要將另一個重要指標 http_request_duration_seconds 也放到 register 中。這邊我使用的方法是簡單寫一個 middleware，讓每一個 route 都可以被其經過，並被紀錄 request 打入後 response time 與 response status code。&lt;/p&gt;
&lt;p&gt;middleware 如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;const promClient = require(&amp;#39;prom-client&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;const onFinished = require(&amp;#39;on-finished&amp;#39;);
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;const httpRequestHistogramMiddleware = (register) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  const httpRequestDurationMicroseconds = new promClient.Histogram({
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    name: &amp;#39;http_request_duration_seconds&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    help: &amp;#39;Duration of HTTP requests in seconds&amp;#39;,
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    labelNames: [&amp;#39;method&amp;#39;, &amp;#39;route&amp;#39;, &amp;#39;code&amp;#39;],
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    buckets: [0.01, 0.05, 0.1, 0.3, 0.5, 0.7, 1, 5, 7, 10],
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    registers: [register],
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  });
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  return (req, res, next) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    const end = httpRequestDurationMicroseconds.startTimer();
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    onFinished(res, () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;      end({ method: req.method, route: req.route.path, code: res.statusCode });
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    next();
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;module.exports = httpRequestHistogramMiddleware;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;從這兩段程式碼我們可以發現，基本上我們做的事情如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;(1) 創建一個 middleware，其中 new 一個 histogram，把它命名為 http_request_duration_seconds，並將其註冊於 register。&lt;/li&gt;
&lt;li&gt;(2) 在我們的 main app 中，使用 app.use() 讓之後出現的 routes 都可以經過它。值得注意的是，如果你的 express app 有使用 session，必須確保 Prometheus configuration 發生於 session 使用之前、routes註冊之前。&lt;/li&gt;
&lt;li&gt;(3) 當 route 被打時，計時器被啟動，將數據記錄於 histogram，當 route結束時，紀錄 method, route, code。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;app.get(&amp;#39;/metrics&amp;#39;, async (req, res) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  try {
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    const metrics = await register.metrics();
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    res.setHeader(&amp;#39;Content-Type&amp;#39;, register.contentType);
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    res.end(metrics);
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  } catch (error) {
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    console.error(&amp;#39;Error generating metrics:&amp;#39;, error);
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    res.status(500).json({ error: &amp;#39;Internal server error from Prometheus&amp;#39; });
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;app.listen(port, () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;console.log(`Hello server ${port} port.`);
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;最後，創建一個 &lt;code&gt;/metrics&lt;/code&gt; endpoint，當 app 被使用，app會將register收集到的 metrics() 返還。由於 metrics() 會返回一個 Promise, 所以必須使用 async-await 來取得數據結果。&lt;/p&gt;
&lt;h2 id=&#34;grafana-configuration&#34;&gt;Grafana configuration&lt;/h2&gt;
&lt;h3 id=&#34;1-datasourcesyml&#34;&gt;1. &lt;code&gt;datasources.yml&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;如同前面介紹所說，grafana 從 datasources取得數據呈現於儀表板上，所以這個 yaml 主要用於 configure datasource 來源。以下為 yaml檔內容：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;apiVersion: 1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;datasources:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  - name: Prometheus
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    type: prometheus
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    access: proxy
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    orgId: 1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    url: http://prometheus:9090
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    basicAuth: false
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    isDefault: true
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    editable: true
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;configure後，grafana container 會自動註冊 http://prometheus:9090 於 Settings, 不需額外登入 grafana 手動註冊。&lt;/p&gt;
&lt;h3 id=&#34;2-dashboards&#34;&gt;2. dashboards&lt;/h3&gt;
&lt;p&gt;dashboards 為定義 grafana 儀表板的內容，基本上你可以在 grafana container 起起來之後手動拉儀表板，也可以使用 predefined dashboards，使 container 起起來之後快速的看到儀表板。&lt;/p&gt;
&lt;p&gt;這邊借用 &lt;a class=&#34;link&#34; href=&#34;https://github.com/StackAbuse/node-prometheus-grafana/tree/master/grafana/provisioning/dashboards&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Stack Abuse 網站創建的超完整儀表板&lt;/a&gt;。基本上他建立了四種不同的儀表板，當 Prometheus 數據進來之後，可以依此監控各種目標。四個儀表板分別是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Node Application Dashboard: 監控如 Process CPU Usage, Process Memory Usage等基本 metrics.&lt;/li&gt;
&lt;li&gt;Node Service Level Metrics Dashboard: 以 route/service 為單位，監控每隻 route的 Number of request, error rate, request處理時間等。&lt;/li&gt;
&lt;li&gt;Node Request Flow Dashboard: 以 request 為基礎，監控 request的各種面向，如時長 (duration)，並用各種圖表可視化。&lt;/li&gt;
&lt;li&gt;High Level Application Metrics: 監控整體app的 throughput, response time等。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;另外，dashboard 也需要有 yaml 做 configuration. 如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;apiVersion: 1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;providers:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;- name: &amp;#39;Prometheus&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  orgId: 1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  folder: &amp;#39;&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  type: file
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  disableDeletion: false
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  editable: true
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  options:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    path: /etc/grafana/provisioning/dashboards
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;showcase-each-part&#34;&gt;Showcase Each Part&lt;/h2&gt;
&lt;p&gt;當我們成功 configure Prometheus, Grafana，我們便可以看到以下幾個畫面:&lt;/p&gt;
&lt;h3 id=&#34;1-on-localhostportmetrics&#34;&gt;1. on &lt;code&gt;localhost:{port}/metrics&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;main app metrics endpoint 可以看到許多筆監控數據。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/nodejs-prometheus-grafana/metrics.png&#34;
	width=&#34;1224&#34;
	height=&#34;664&#34;
	srcset=&#34;https://theoutsidelaine.com/p/nodejs-prometheus-grafana/metrics_hud8081b730284b09239c539a7e012321b_152701_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/nodejs-prometheus-grafana/metrics_hud8081b730284b09239c539a7e012321b_152701_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;app metrics endpoint exposes service monitoring data&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;184&#34;
		data-flex-basis=&#34;442px&#34;
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;2-on-localhost9090&#34;&gt;2. on &lt;code&gt;localhost:9090&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Prometheus 走 9090 port，有一個簡易 web UI, 讓我們用簡單的 table / graph, 看到這些數據隨時間變化的樣子。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/nodejs-prometheus-grafana/prometheus.png&#34;
	width=&#34;1692&#34;
	height=&#34;883&#34;
	srcset=&#34;https://theoutsidelaine.com/p/nodejs-prometheus-grafana/prometheus_hu1a2cc50b53e456f684ce571b3b596f4b_107549_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/nodejs-prometheus-grafana/prometheus_hu1a2cc50b53e456f684ce571b3b596f4b_107549_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;Prometheus web UI&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;191&#34;
		data-flex-basis=&#34;459px&#34;
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;3-on-localhost3000&#34;&gt;3. on &lt;code&gt;localhost:3000&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Grafana 走 3000 port，透過 dashboard folder中定義的 dashboard.json，直接 config 出儀表板。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/nodejs-prometheus-grafana/grafana.png&#34;
	width=&#34;1701&#34;
	height=&#34;985&#34;
	srcset=&#34;https://theoutsidelaine.com/p/nodejs-prometheus-grafana/grafana_hu833af1013c844be5794df5931d274c43_281920_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/nodejs-prometheus-grafana/grafana_hu833af1013c844be5794df5931d274c43_281920_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;Grafana Dashboard&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;172&#34;
		data-flex-basis=&#34;414px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/nodejs-prometheus-grafana/grafana2.png&#34;
	width=&#34;1704&#34;
	height=&#34;994&#34;
	srcset=&#34;https://theoutsidelaine.com/p/nodejs-prometheus-grafana/grafana2_hu7d6e6064c60fd70be7c941f19cc8cf2c_224286_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/nodejs-prometheus-grafana/grafana2_hu7d6e6064c60fd70be7c941f19cc8cf2c_224286_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;Grafana Dashboard2&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;171&#34;
		data-flex-basis=&#34;411px&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;test-the-monitoring-results&#34;&gt;Test the monitoring results&lt;/h2&gt;
&lt;p&gt;基本指標如 cpu, memory, event loop只要 app在動都可以自動進行監測，不過像是 service routes 沒有打的話我們基本上不會從 Prometheus 與 Grafana上知道他們的 response time。因此，這邊便需要借助壓力測試工具 &lt;code&gt;locust&lt;/code&gt; 來協助我們完成這個目標。&lt;/p&gt;
&lt;p&gt;Locust 是一個以 Python 編寫的開源壓力測試工具，可以執行 app defined actions，比如發起 http request, 查詢資料庫並操作回傳資料等。目的是評估 web app 在高流量下的效能。&lt;/p&gt;
&lt;p&gt;以下說明使用 Locust 的步驟：&lt;/p&gt;
&lt;h3 id=&#34;1-build-up-virtual-environment&#34;&gt;1. build up virtual environment&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;python3 -m venv locust_venv
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;source locust_venv/bin/activate 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;2-install-locust&#34;&gt;2. install locust&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;pip install -r requirements.txt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;3-write-indexpy-and-run-it&#34;&gt;3. write &lt;code&gt;index.py&lt;/code&gt; and run it&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;from locust import HttpUser, task, between
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;class BrowsingUser(HttpUser):
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    wait_time = between(1, 2)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    host = &amp;#34;http://localhost:4000&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    @task
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    def get_products(self):
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        self.client.get(&amp;#34;/api/v1/products/women/?paging=1&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;class CheckoutUser(HttpUser):
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    wait_time = between(1, 3)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    host = &amp;#34;http://localhost:4000&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    @task
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    def browse_product_details(self):
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        self.client.get(&amp;#34;/api/v1/products/details?id=64&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;這邊假設有兩種 user action: BrowsingUser, 瀏覽主頁面的 /products/women/?paging=1。CheckoutUser, 在結帳之前會點擊某個商品頁面 /products/details?id=64。然後我們使用以下執行執行這兩個用戶行為模擬：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;locust -f index.py
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;接著便可以在 http://0.0.0.0:8089/ 看到 Locust Dashboard了。可以指定使用者數量、每秒生成新用戶的速率（ramp up）。由於我們有兩個 user, 所以這邊設定使用者數量=2。
&lt;img src=&#34;https://theoutsidelaine.com/p/nodejs-prometheus-grafana/locust1.png&#34;
	width=&#34;1695&#34;
	height=&#34;582&#34;
	srcset=&#34;https://theoutsidelaine.com/p/nodejs-prometheus-grafana/locust1_hu7a6d3b7699e02772fbf01f34a11940d2_69619_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/nodejs-prometheus-grafana/locust1_hu7a6d3b7699e02772fbf01f34a11940d2_69619_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;Locust Dashboard&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;291&#34;
		data-flex-basis=&#34;698px&#34;
	
&gt;&lt;/p&gt;
&lt;h3 id=&#34;4-observations&#34;&gt;4. observations&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/nodejs-prometheus-grafana/locust-result1.png&#34;
	width=&#34;1613&#34;
	height=&#34;555&#34;
	srcset=&#34;https://theoutsidelaine.com/p/nodejs-prometheus-grafana/locust-result1_huf0b2ef071943cb4d9579a9ebc59ad6ce_82487_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/nodejs-prometheus-grafana/locust-result1_huf0b2ef071943cb4d9579a9ebc59ad6ce_82487_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;Locust Result: in table view&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;290&#34;
		data-flex-basis=&#34;697px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/nodejs-prometheus-grafana/locust-result2.png&#34;
	width=&#34;1608&#34;
	height=&#34;1033&#34;
	srcset=&#34;https://theoutsidelaine.com/p/nodejs-prometheus-grafana/locust-result2_huc60787304d7be5fef06b8a113640391f_133773_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/nodejs-prometheus-grafana/locust-result2_huc60787304d7be5fef06b8a113640391f_133773_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;Locust Result: in graph view&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;155&#34;
		data-flex-basis=&#34;373px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;當同時啟動 server / prometheus / grafana container, 還可以把這些打 api的結果實時的傳送給 grafana dashboards。&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;透過這個簡單的練習，我們知道如何將一個 Node.js App 串接 Prometheus 與 Grafana，以及使用簡單的 locust load test觀察兩隻api的表現。日後可以思考的點是：要怎麼觀察出「異常」與「正常」之差異，並且根據異常狀態設計方法，真正達成系統效能優化的目標。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>base64</title>
        <link>https://theoutsidelaine.com/p/base64-encode-decode/</link>
        <pubDate>Mon, 25 Mar 2024 22:00:00 +0000</pubDate>
        
        <guid>https://theoutsidelaine.com/p/base64-encode-decode/</guid>
        <description>&lt;img src="https://theoutsidelaine.com/p/base64-encode-decode/cover.png" alt="Featured image of post base64" /&gt;&lt;p&gt;沒想到第一篇技術分享文章會先從這個小主題開始。主要原因是之前在面試一家非常喜歡的公司，其中一個面試關卡因為沒有考慮到問題條件限制之一是 input 資料為以 base64 編譯後的資料，需要先用程式 decode 為 binary 再做後續處理，最後很可惜沒有進入下一關。後來回想一下，其實我在實習的時候就有碰過這個東西了，只不過當時做 base64 encode 我都直接把 service 密碼明文丟到 google 上的 &amp;ldquo;online base64 encoder&amp;rdquo; 直接做好 encode 後的 string🫠，算是根本沒懂過這個概念。所以決定針對他好好來研究一下，並記錄自己的研究內容～&lt;/p&gt;
&lt;h2 id=&#34;base64-的基本概念&#34;&gt;base64 的基本概念&lt;/h2&gt;
&lt;p&gt;base64 是一種數據格式轉換方法，基本上 base64 encoding 的輸入資料會是一組二進制（binary）編碼，之後依照某種轉換規則，將一組組的 binary 數據對應到特定的 base64 character，最後再輸出一個 text form 的 output。&lt;/p&gt;
&lt;h2 id=&#34;為什麼要做-base64-轉換&#34;&gt;為什麼要做 base64 轉換?&lt;/h2&gt;
&lt;p&gt;主要原因大概如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;提升安全性&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;有時候一些人類很直觀能理解的密碼明文，比如 「&lt;code&gt;elaine_hsieh&lt;/code&gt;」，如果直接撰寫在 service configuration 上，會讓取得密碼的過程太過容易。如果將這樣的資訊包上一層 base64 encoding、變成「&lt;code&gt;ZWxhaW5lX2hzaWVo&lt;/code&gt;」，之後有人想要取得 service 密碼將需要多一道解譯的手續。因此，透過 base64 encoding 可以進而提升一點資訊安全性。&lt;/p&gt;
&lt;p&gt;以 base64 編碼還有個額外好處：便於不同編碼方式之間進行數據轉換後的數據傳輸。&lt;/p&gt;
&lt;p&gt;通常某個 service 密碼明文會使用 &lt;strong&gt;ASCII&lt;/strong&gt; 或是 &lt;strong&gt;UTF-8&lt;/strong&gt; 的編碼方式。使用 ASCII 的明文包含範圍較小，通常僅包含 256 個基本的字元，例如英文字母、數字、標點符號（此處指的是extended ASCII）；使用 UTF-8 的明文包含範圍較大，例如英文字母、數字、標點符號、拉丁字母、中日韓等亞洲文字、表情符號與其他符號。由於 base64 character set包含了部分 ASCII character set，所以不論原始數據是以 ASCII做編碼（例：&lt;code&gt;titan&lt;/code&gt; in ASCII code）、或是以 UTF-8做編碼（例：&lt;code&gt;titan&lt;/code&gt; in UTF-8 code），都可以透過 base64 轉換規則轉換為一組 base64 encoding（例：都會被 base64 轉譯為 &lt;code&gt;dGl0YW4=&lt;/code&gt;）。所以可以說，base64 可用於不同編碼方式之間進行數據轉換後的數據傳輸。&lt;/p&gt;
&lt;p&gt;如果要寬鬆一點說，base64 會有點類似於加密，只不過 base64 可以被輕易的反編碼、讀取到原文，並且此資訊公開給所有人，沒有公私鑰授權特定用戶的功能，所以嚴格來說 base64 不能算是一種加密 (encryption)方法，只能算是一種編譯（encoding）方法。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;便於圖片、音樂、多媒體文件等資訊在網路世界中傳遞&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;在 HTTP、SMTP 等網絡通訊協議中，想要傳送圖片等多媒體資訊其實會有很多限制。HTTP、SMTP、POP3、FTP 等網絡協議只能傳送 text form data，但是圖片、音樂、多媒體文件等資訊通常都會使用 binary code 來儲存資訊：比如像素、顏色深度、每幀畫面、音頻等，通常會用 16 或 32 bit 資訊做紀錄。&lt;/p&gt;
&lt;p&gt;所以當我們想要用電子郵件傳送圖片時，我們會需要先將「&lt;code&gt;00101010101000001&lt;/code&gt;」的圖片 binary data，用 base64 轉換為「&lt;code&gt;MDAxMDEwMTAxMDEwMDAwMDE=&lt;/code&gt;」，電子郵件的 html 形式會類似於 &amp;lt;img&amp;gt; &lt;code&gt;MDAxMDEwMTAxMDEwMDAwMDE=&lt;/code&gt; &amp;lt;/img&amp;gt;。之後傳送到目的方時，再使用 base64 decode 那串 text，就可以看得到&amp;lt;img&amp;gt; &amp;lt;/img&amp;gt; 裡的內容了。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;base64-轉換規則&#34;&gt;base64 轉換規則&lt;/h2&gt;
&lt;p&gt;以string input為例，大致上來說 base64 有四個步驟：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;將 string 依照編碼（eg: ASCII） 轉換為 binary。我們知道 ASCII 編碼一共有 256 種可能，所以轉換後每個字母會對應到 0~255 ASCII number，與8 個bits。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;將這些 binary 以每 6 bits 為單位進行分組。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;將每組 bits 轉換為對應的 base64 character:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;(1) base64 有一個自己的 character set, 只有 64 個 character，分別是：大小寫字母（A-Z、a-z）、數字（0-9）和兩個特殊字符（+ 和 /）。這也是 base64 得到這個名字的原因。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;(2) 每組 binary 有6 bits, 相當於一共有 64 種可能性，正好可以對應到 base64 character set。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果原始的 string長度不是3的倍數，base64 會自動補上一個 &amp;ldquo;=&amp;quot;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;(1) 這麼做的理由相當於宣告最後一組bits「後面有補0，才得以是 6個bits」&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;(2) &amp;ldquo;=&amp;rdquo; 相當於宣告這組 bits的特殊性&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;最後將轉換後的 base64 characters回傳，即大功告成。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;值得注意的是，base64 編碼後的數據通常比原始數據更長。這是因為 base64 編碼會將原本以 8 bits 為單位的 string，重新打散單位、轉換為 6 bits 的 base64 characters，這導致編碼後的數據會比原始數據增加約 33% 的大小。&lt;/p&gt;
&lt;h2 id=&#34;舉例說明&#34;&gt;舉例說明&lt;/h2&gt;
&lt;p&gt;假設我們有一串 string名為「Hello」，其長度=5，不是3的倍數。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;依照 ASCII 編碼，「Hello」每一個 character 會對應到一個 8 bits 組合（＝對應到 0~ 255 之間的一個數字）。組合起來即為「01001000 01100101 01101100 01101100 01101111」，一共 40 bits。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;將以上 40 bits 每 6 個分一組，一共可以分成 6 組有完整 6 bits + 1組只有4 bits：「010010」、「000110」、「010101」、「101100」、「011011」、「000110」、「1111」&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;最後那一組補 0，讓他還是擁有 6 bits, 只不過後面放入一個 base64 character: &amp;ldquo;=&amp;quot;，標註他的特殊性。所以就變成以下：「010010」、「000110」、「010101」、「101100」、「011011」、「000110」、「11110&lt;strong&gt;0=&lt;/strong&gt;」&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;每一組對應到 base64 character set中的數字（對應表可以參考本篇文章的首頁圖）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;最後的 output即為 「SGVsbG8=」&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;python-library-實作&#34;&gt;python library 實作&lt;/h2&gt;
&lt;p&gt;在 python中，有很便利的 library協助我們做到 base64 encoding. 可以參考以下的 code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;import base64
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;original_text = &amp;#34;Hello, world!&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;original_bytes = original_text.encode(&amp;#39;utf-8&amp;#39;)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;encoded_bytes = base64.b64encode(original_bytes)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;encoded_text = encoded_bytes.decode(&amp;#39;utf-8&amp;#39;)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;print(&amp;#34;Base64 編碼結果:&amp;#34;, encoded_text)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;decoded_bytes = base64.b64decode(encoded_bytes)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;decoded_text = decoded_bytes.decode(&amp;#39;utf-8&amp;#39;)
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;print(&amp;#34;Base64 解碼結果:&amp;#34;, decoded_text)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;結果如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Base64 編碼結果: SGVsbG8sIHdvcmxkIQ==
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Base64 解碼結果: Hello, world!
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
        </item>
        <item>
        <title>低谷醫生</title>
        <link>https://theoutsidelaine.com/p/doctor-slump/</link>
        <pubDate>Wed, 28 Feb 2024 00:00:00 +0000</pubDate>
        
        <guid>https://theoutsidelaine.com/p/doctor-slump/</guid>
        <description>&lt;img src="https://theoutsidelaine.com/p/doctor-slump/cover.png" alt="Featured image of post 低谷醫生" /&gt;&lt;p&gt;在我個人的看劇歷史中，相較於美劇、韓劇其實算是追得不多的，而在看過的韓劇中，通常我喜歡的類型主要都是溫馨感人的生活喜劇。我最喜歡的兩部 &lt;a class=&#34;link&#34; href=&#34;https://www.youtube.com/watch?v=Y31A-n2Sr_I&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&amp;lt;穿破屋頂的 High Kick&amp;gt;&lt;/a&gt; 和 &lt;a class=&#34;link&#34; href=&#34;https://www.youtube.com/watch?v=OXOAsAW-17s&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&amp;lt; Dodososolalaso &amp;gt;&lt;/a&gt; 就是都屬於這個類型：前者講述由一對姐妹串起兩個家庭生活圈的故事，後者則是由落魄鋼琴女孩和逃學少年共譜的生活故事。他們的共同點是演員們都很擅長搞笑，角色性格又大多很可愛討喜，而且劇情都很溫暖可愛，雖然偶有摩擦但是大家最後還是都走到一起，愛情drama或是狗血片段的安排也都還在我的接受範圍之內。&lt;/p&gt;
&lt;p&gt;一直以來我都會把自己看過最喜歡的劇記錄起來，因為我知道被我非常喜歡的劇通常不管過多久我都還是會繼續很喜歡，而且很有趣的是，不同時間點重看這些劇，得到的感想、接受到的感覺，或是可以呼應的生活經驗也都不一樣。&lt;/p&gt;
&lt;p&gt;比如說 Dodososolalaso 在最一開始看的時候是單純的沈浸在一邊看劇一邊聽鋼琴曲的開心心情中（身為一個會彈鋼琴的人，真的很愛這種很像交響情人夢的劇）；考研究所的時候重看一次，那時候最印象深刻的是，女主角在她的大學畢業鋼琴發表會上把曲子從華麗的演奏曲臨時換成小星星變奏曲，其他人看到直呼她是瘋子，但是只有她知道，這首曲子是她小時候表演時因為緊張所以搞砸了的曲子，當時沈默的觀眾席上只有她爸爸站起來為她鼓掌喝采，所以此刻的她就想彈奏這首曲子，並把它獻給一直都很支持她的爸爸，作為她這條鋼琴路上的總結。不知為何就讓我想到有一陣子一邊工作一邊考研究所唸書有點辛苦，但也是有很支持我的家人朋友，所以心裡覺得暖暖的。&lt;/p&gt;
&lt;h2 id=&#34;所以為什麼喜歡低谷醫生呢&#34;&gt;所以為什麼喜歡低谷醫生呢？&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/doctor-slump/image1.png&#34;
	width=&#34;1080&#34;
	height=&#34;1350&#34;
	srcset=&#34;https://theoutsidelaine.com/p/doctor-slump/image1_huf0d1a81b50750350fd6ee0a8518d0700_439257_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/doctor-slump/image1_huf0d1a81b50750350fd6ee0a8518d0700_439257_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;正宇和荷娜在櫻花樹下的散步，兩個人第一次成為朋友&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;80&#34;
		data-flex-basis=&#34;192px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;這部 &lt;a class=&#34;link&#34; href=&#34;https://www.youtube.com/watch?v=3lVZd3GYGZo&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;&amp;lt;低谷醫生&amp;gt;&lt;/a&gt; 也屬於這樣類型的劇，不過它更著重在兩個跌倒的人互相扶持的故事上。余正宇（朴炯植飾演）和南荷娜（朴信惠飾演）是一對高中時代互相較量成績的勁敵，長大後兩人分別走向不一樣的人生，不過卻恰好的在同一個時間點分別受到重挫：荷娜長期在麻醉科受到不公平的職場待遇而罹患憂鬱症，因為與教授抗爭權益無果，憤而從醫院辭職；正宇雖然一開始生活一帆風順，不過卻意外捲入澳門繼承人手術事故風波，一夕之間身敗名裂、眾叛親離，只能蝸居在一間屋塔房中準備打官司，而這家屋塔房剛好是荷娜媽媽出租的房子，命運就這樣把他們兩個人重新連結在一起。&lt;/p&gt;
&lt;p&gt;我想我會喜歡這部劇不外乎幾個原因：&lt;strong&gt;角色塑造&lt;/strong&gt;、&lt;strong&gt;台詞設計&lt;/strong&gt;、&lt;strong&gt;演員演技&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;角色塑造&#34;&gt;角色塑造&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/doctor-slump/image2.png&#34;
	width=&#34;2252&#34;
	height=&#34;1006&#34;
	srcset=&#34;https://theoutsidelaine.com/p/doctor-slump/image2_hucc585497dbf5c87f585abaa3b248c693_1914976_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/doctor-slump/image2_hucc585497dbf5c87f585abaa3b248c693_1914976_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;高中時期的正宇和荷娜在台上解數學題目&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;223&#34;
		data-flex-basis=&#34;537px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;低谷醫生&amp;gt; 的兩大主角雖然都是學霸，卻有著截然不同的個性：正宇算是那種天生聰明、有點自命不凡的小屁孩，長相不錯、家境優渥，又有一群擁戴他的跟班們，雖然家人比較忙碌與冷漠一點，但他的生活一直都過得很無憂無慮。荷娜則是相反，生長於釜山漁村、算是全村的希望的她，雖然也很聰明，不過考得好成績好像是她識別自己的主要方式，所以她從小除了唸書之外並沒有別的生活目標，搬來首爾之後為了維持跟以前一樣的好成績，更把自己一頭栽入讀書之中。&lt;/p&gt;
&lt;p&gt;兩個人在競爭全校第一名的過程中逐漸變成一種 frenemy 的關係：正宇會搶跟荷娜比賽誰是第一個去圖書館、正宇學荷娜生吃咖啡粉節省尿尿時間用來唸書、發考卷當天發現自己考第二名輸給荷娜正宇當場被氣暈 etc（發現好像都是正宇單方面發動幼稚攻擊的哈哈）；不過偶爾也會有溫馨的場面，比如說正宇幫助右手受傷的荷娜做一個月的筆記、荷娜寫了關心紙條給沒有家人來探病的正宇 etc。&lt;/p&gt;
&lt;p&gt;我自己尤其喜歡荷娜這個角色的塑造，覺得她算是一部分台灣學生的縮影：從小家人對於考試成績極為要求，所以為了讀書而讀書，為了考高分而考高分。畢業後進入的職場環境不佳，受到了上級醫生的壓榨，但因為從小就是習慣迎合他人期待的個性，所以不懂得說不，內耗久了身體逐漸變差。第一集劇情用一個簡單的轉折劇情，帶到荷娜發現自己可能不是身體、而是心靈上出現問題的方式，讓我很喜歡：&lt;em&gt;因為胃痛倒在大馬路上差點被車撞，當時的荷娜只想到的是，如果被車撞了是不是就可以不用那麼累了，因為發現自己居然有這種想法的荷娜感到很驚訝，才決定去看心理醫生尋找解答&lt;/em&gt;。雖然荷娜因為個性關係導致自己過勞、壓力過大，但是編劇給予了她一個溫暖有趣的家庭，所以在遇到低谷的時候，身為觀眾的我們看到了荷娜因為擁有一個要她放輕鬆不再給予過高期望的媽媽、一個用自己的嘴砲與快樂生活感染姐姐的學渣弟弟、一個用食物溫暖她的舅舅，而逐漸有餘裕可以自我療傷。&lt;/p&gt;
&lt;p&gt;正宇這個角色則是給我一種可愛活寶的感覺，我很喜歡編劇讓這個享盡一切優勢的角色，仍然保有幼稚、搞笑，與善良的一面。比如說會因為比荷娜快五秒鐘解答出數學問題，而感覺自己是世界上最厲害的人，洋洋得意了一整天；但是當荷娜右手受傷，問他說為什麼要免費幫她做筆記，不把握機會趁機超車？正宇則傲嬌但又真誠的回答，希望看到最大的對手趕快恢復，與他公平競爭，這樣他才會贏的比較開心。老實說，正宇這個角色讓我唯一覺得不合理的地方，就是他在冷漠的家庭環境下長大，卻仍然保有善良正向的一面。當正宇因為手術意外跌落谷底時，媽媽在電話中用極為冷漠的聲音，要他好好處理不要影響到爸爸的海外事業，我想任何人聽了大概都會變得相當厭世吧。更不用說他身旁也沒有一直支持他的好哥們，所以我很好奇正宇的正能量是打哪裡培養來的。可能編劇重點是想要呈現荷娜的改變，正宇的背景沒有放太多細節處理，但 Anyway 我還是很喜歡這個角色帶來的可愛氛圍。&lt;/p&gt;
&lt;p&gt;而荷娜和正宇兩個人之間的互動是我最喜歡的劇情設計了，從兩個人在櫻花樹下談心開始，成為朋友，一起喝酒，一起看日出，幫忙找論文資料，跑到花本一起旅行，一起唱卡拉OK，一起打遊戲。我很喜歡這些互動很自然的感覺，你幫助了我，下次我帶你出去玩，彼此可以感覺到對方難過的點，再用輕鬆的方式幫忙化解。其實看劇的時候我並不是很在乎他們感情線的發展，我只是看到兩個在生活遇到問題的人互相支援對方的過程，每次都可以因為他們的對話而感到很溫暖，有時候也覺得很多互動很好笑，看完一小段心情都會覺得很輕鬆，這大概就是被療癒的感覺了吧。&lt;/p&gt;
&lt;h3 id=&#34;台詞設計&#34;&gt;台詞設計&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/doctor-slump/image3.png&#34;
	width=&#34;1948&#34;
	height=&#34;1294&#34;
	srcset=&#34;https://theoutsidelaine.com/p/doctor-slump/image3_hueb1cd3dd6f83f6a1300caec76f1a6e6e_4327276_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/doctor-slump/image3_hueb1cd3dd6f83f6a1300caec76f1a6e6e_4327276_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;朴信惠、朴炯植讀劇本的照片&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;150&#34;
		data-flex-basis=&#34;361px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;這部劇的台詞不是走 &amp;lt;鬼怪&amp;gt; 那種華麗的編排，但我喜歡荷娜、正宇兩個人為對方打氣的對話，很恰到好處不會太多太cheesy。除此之外，我也喜歡有一段講述荷娜和弟弟博多的名字由來，覺得有很多可以延伸思考的地方：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;因為出生那天天空蔚藍晴朗，所以荷娜（하늘）取名音似天空，博多（바다）則為了呼應取名音似大海。可能因為如此，荷娜從小成績就比天還高，博多的成績則是比地平線還要低，但是比天還要高的姊姊會因為考一百分不是全因實力而難過，弟弟則會因為姊姊考一百分媽媽會做年糕吃而感到開心無比。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;明朗的天空都是為人嚮往的，不過就像電影 &amp;lt;陽光普照&amp;gt; 所說，有時候陽光太熾熱了，總是有人找不到陰影可以躲藏。太過可靠的人就像永遠晴朗的天空，我們期待這樣的溫暖一直持久，所以就不會有人過問會不會有雨天。荷娜在大學考試失利、職場失意時走向了人生淺淺的陰影，當時身旁的家人朋友沒有告訴她「沒關係，你還是很讓人感到驕傲」，而是告訴她要繼續努力，繼續優秀，期待她繼續明亮著。在 &amp;lt;陽光普照&amp;gt; 中，阿豪沒有考上醫學系，處在人生不夠優秀的時候，父親沒有告訴他「沒關係」，而是送給他一本寫著勵志標語的筆記本，提醒他必須再次回到優秀的軌道。&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/doctor-slump/sunshine.png&#34;
	width=&#34;1584&#34;
	height=&#34;878&#34;
	srcset=&#34;https://theoutsidelaine.com/p/doctor-slump/sunshine_hue56768b939a78be818ca38d161d7d1c9_1810106_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/doctor-slump/sunshine_hue56768b939a78be818ca38d161d7d1c9_1810106_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;電影 &amp;lt;陽光普照&amp;gt;&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;180&#34;
		data-flex-basis=&#34;432px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;在這部劇中，荷娜很幸運的取得媽媽的那句「媽媽不想要優秀的女兒，只想要健康的女兒」。但是在 &amp;lt;陽光普照&amp;gt; 中，阿豪是父親告誡弟弟要努力的目標、是大家的榜樣，沒有人過問阿豪會不會很辛苦，因為他總是明亮照人，可是他累了，卻發現這個世界上沒有可以供他休憩的陰翳之地，所以他選擇以另外一種方式永久的離開。&lt;/p&gt;
&lt;p&gt;生長在亞洲社會很常會面臨這種過大的家庭期待，彷彿人生沒有追求卓越以外的事情，如果達不到期待，家人展現的失望感（輕微的肢體暗示或是明顯的語言暴力），都會讓他們覺得自己是不是不值得被愛。這可以用很多理論來試圖解釋：中產階級的焦慮、家庭系統的分化&amp;hellip;，也有&lt;a class=&#34;link&#34; href=&#34;https://www.twreporter.org/a/high-academic-achievement-students-psychological-distress-backlight&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;很多社論&lt;/a&gt;嘗試分析這件事情。 但老實說，直到今日我還是覺得這種社會氛圍很難以突破，事實上直到大學畢業我也才稍微得以從這種期待的牢籠中跳脫出來。我想很多時候還是得在某一個剎那，很幸運的從某些事情、某個人身上，你了解自己存在的意義在這些表層的期望之上，從心底真正覺得自己有價值值得被喜歡，才有辦法走出來吧。就像荷娜得到了媽媽的那句話，還有正宇如荒漠中清泉一般的友情，帶著她看到了除了卓越以外，還有很多有趣的事情值得追求。&lt;/p&gt;
&lt;h3 id=&#34;演員演技&#34;&gt;演員演技&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/doctor-slump/image4.png&#34;
	width=&#34;1080&#34;
	height=&#34;810&#34;
	srcset=&#34;https://theoutsidelaine.com/p/doctor-slump/image4_hu407074a9c897dd2843980fbd71569e90_152902_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/doctor-slump/image4_hu407074a9c897dd2843980fbd71569e90_152902_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
		alt=&#34;朴信惠、朴炯植和劇組的合照，取自朴信惠IG&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;133&#34;
		data-flex-basis=&#34;320px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;很久以前看 &amp;lt;繼承者們&amp;gt; 就很喜歡朴信惠，不僅是因為她長得有點像柴犬很可愛討喜，也是因為她的演技很細膩，每一種情緒都演繹得很到位，比如因為生氣而哭、因為感動而哭、因為開心而哭，都會讓人覺得是不一樣的，所以很喜歡看她演戲。這次她回歸演 &amp;lt;低谷醫生&amp;gt;，飾演高中生、大學生、社會人士都不違和，真的很期待可以看到她的更多作品！&lt;/p&gt;
&lt;p&gt;至於朴炯植老實說我沒有看過他的任何一部作品，也是後來才知道當初 &amp;lt;繼承者們&amp;gt; 他也有飾演一個社團學長的小角色。但是這部劇讓我對他的印象大好，覺得他很適合演這種幼稚可愛、大寶感十足的角色，也很期待之後他會有什麼讓人驚艷的演出！&lt;/p&gt;
&lt;h2 id=&#34;記錄低谷醫生中自己喜愛的那些片段&#34;&gt;記錄低谷醫生中自己喜愛的那些片段&lt;/h2&gt;
&lt;p&gt;以下紀錄一些 1 ~7 集中，自己很受感動或是印象深刻的一些片段或句子～&lt;/p&gt;
&lt;h3 id=&#34;櫻花樹下的相擁而泣&#34;&gt;櫻花樹下的相擁而泣&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/doctor-slump/sentence1.png&#34;
	width=&#34;980&#34;
	height=&#34;653&#34;
	srcset=&#34;https://theoutsidelaine.com/p/doctor-slump/sentence1_hud97d0b252e037c37183f71bcd03c346d_74887_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/doctor-slump/sentence1_hud97d0b252e037c37183f71bcd03c346d_74887_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;150&#34;
		data-flex-basis=&#34;360px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;「不管你怎麼樣，我都愛你。」&lt;/p&gt;
&lt;p&gt;「你怎麼哭了？」「沒事&amp;hellip;我就只是突然就流淚了。」&lt;/p&gt;
&lt;h3 id=&#34;既然倒下來那就休息一下吧&#34;&gt;既然倒下來，那就休息一下吧&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/doctor-slump/sentence4.png&#34;
	width=&#34;1620&#34;
	height=&#34;1080&#34;
	srcset=&#34;https://theoutsidelaine.com/p/doctor-slump/sentence4_hudf566df3c6820401098eae3585b7bedd_1895949_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/doctor-slump/sentence4_hudf566df3c6820401098eae3585b7bedd_1895949_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;150&#34;
		data-flex-basis=&#34;360px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;「我是無所謂的全力以赴，所以倒下了。」&lt;/p&gt;
&lt;p&gt;「既然你已經倒下了&amp;hellip;」
「怎麼，要跟我說加油嗎？」&lt;/p&gt;
&lt;p&gt;「不是，別加油，就躺著吧。既然我們倒下了，就休息一下吧。」&lt;/p&gt;
&lt;h3 id=&#34;憂鬱症派對&#34;&gt;憂鬱症派對&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/doctor-slump/sentence2.png&#34;
	width=&#34;2344&#34;
	height=&#34;990&#34;
	srcset=&#34;https://theoutsidelaine.com/p/doctor-slump/sentence2_hu24d258a162625c16dc4c1dd929a0b4e2_3014316_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/doctor-slump/sentence2_hu24d258a162625c16dc4c1dd929a0b4e2_3014316_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;236&#34;
		data-flex-basis=&#34;568px&#34;
	
&gt;
「我看最近大家悲傷的時候都會辦派對：分手派對、離婚派對&amp;hellip;所以我就發明了這個『憂鬱症派對』！」&lt;/p&gt;
&lt;p&gt;「荷娜啊，我們都會在背後支持你，所以我們一起來克服！來吃吧！」&lt;/p&gt;
&lt;p&gt;「這是能緩解憂鬱症的紅肉魚、我聽說高麗菜對憂鬱症有好的療效。」&lt;/p&gt;
&lt;p&gt;「你是為了接受愛而來到這個世界～」&lt;/p&gt;
&lt;h3 id=&#34;一起看日出吧&#34;&gt;一起看日出吧&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/doctor-slump/sentence3.png&#34;
	width=&#34;1080&#34;
	height=&#34;728&#34;
	srcset=&#34;https://theoutsidelaine.com/p/doctor-slump/sentence3_hu8dba310b67d2078985df6240ee37360b_125229_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/doctor-slump/sentence3_hu8dba310b67d2078985df6240ee37360b_125229_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;148&#34;
		data-flex-basis=&#34;356px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;「我們去東海吧，如何？」
「好啊。」
「我們別當同學，當朋友吧。」
「好啊。」&lt;/p&gt;
&lt;p&gt;「雖然今天太陽沒有出來，但是明天還是會出來的。」&lt;/p&gt;
&lt;h3 id=&#34;自尊心低落的時候連這種話也會被刺傷&#34;&gt;自尊心低落的時候連這種話也會被刺傷&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/doctor-slump/sentence5.png&#34;
	width=&#34;2030&#34;
	height=&#34;1354&#34;
	srcset=&#34;https://theoutsidelaine.com/p/doctor-slump/sentence5_hu0b2cd7773aa35f47bcdaea47a2af8739_3596381_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/doctor-slump/sentence5_hu0b2cd7773aa35f47bcdaea47a2af8739_3596381_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;149&#34;
		data-flex-basis=&#34;359px&#34;
	
&gt;
「這只是妳喝醉不小心投的職位，你不要太著急了。」&lt;/p&gt;
&lt;p&gt;「我也不想著急，只是我卻老是不禁著急起來。其實，在聯誼時我想得不是有沒有喜歡對方，而是覺得自己插不上工作有關的話題，很淒涼。其實我總是羨慕著還在工作的朋友。」&lt;/p&gt;
&lt;p&gt;（對，算你厲害，所以你才會被醫院給放生嗎？）「我的自尊心低落到，會被那種話給深深的刺傷。」&lt;/p&gt;
&lt;h3 id=&#34;你不曾活錯&#34;&gt;你不曾活錯&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/doctor-slump/sentence8.png&#34;
	width=&#34;2018&#34;
	height=&#34;1352&#34;
	srcset=&#34;https://theoutsidelaine.com/p/doctor-slump/sentence8_hu04fa8cf89cc13218d7554a28a4c79b04_3924180_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/doctor-slump/sentence8_hu04fa8cf89cc13218d7554a28a4c79b04_3924180_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;149&#34;
		data-flex-basis=&#34;358px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;「欸余正宇，我面試失敗了，我覺得，自己一直以來好像都活錯了。」&lt;/p&gt;
&lt;p&gt;「你怎麼會來這邊？」&lt;/p&gt;
&lt;p&gt;「你不曾活錯，我來也是要跟你說這句話。」&lt;/p&gt;
&lt;h3 id=&#34;今天的你要好好的才能幫助明天的自己&#34;&gt;今天的你要好好的，才能幫助明天的自己&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/doctor-slump/sentence6.png&#34;
	width=&#34;1822&#34;
	height=&#34;1374&#34;
	srcset=&#34;https://theoutsidelaine.com/p/doctor-slump/sentence6_hu3ec2fb172b492eeabdf042abafc9dd79_3485226_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/doctor-slump/sentence6_hu3ec2fb172b492eeabdf042abafc9dd79_3485226_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;132&#34;
		data-flex-basis=&#34;318px&#34;
	
&gt;&lt;/p&gt;
&lt;p&gt;「你不是因為不知道要怎麼跟家人說面試失敗，所以才跑來花本不回去的嗎？」&lt;/p&gt;
&lt;p&gt;「你怎麼知道的？」&lt;/p&gt;
&lt;p&gt;「我現在終於比較了解你了。你好像對自己太嚴苛了，不要在意別人的眼光，先照顧好自己吧。今天的你要好好的，才能幫助明天的自己。」&lt;/p&gt;
&lt;h3 id=&#34;人在向他人尋求意見的時候其實都已經做好決定了你不管去到哪都會做得很好的&#34;&gt;人在向他人尋求意見的時候，其實都已經做好決定了，你不管去到哪，都會做得很好的！&lt;/h3&gt;
&lt;p&gt;&lt;img src=&#34;https://theoutsidelaine.com/p/doctor-slump/sentence7.png&#34;
	width=&#34;2264&#34;
	height=&#34;958&#34;
	srcset=&#34;https://theoutsidelaine.com/p/doctor-slump/sentence7_hu722d43b21e4c9f31f575187c2face39e_2894018_480x0_resize_box_3.png 480w, https://theoutsidelaine.com/p/doctor-slump/sentence7_hu722d43b21e4c9f31f575187c2face39e_2894018_1024x0_resize_box_3.png 1024w&#34;
	loading=&#34;lazy&#34;
	
	
		class=&#34;gallery-image&#34; 
		data-flex-grow=&#34;236&#34;
		data-flex-basis=&#34;567px&#34;
	
&gt;&lt;/p&gt;
&lt;h2 id=&#34;對於劇情之後的期待&#34;&gt;對於劇情之後的期待&lt;/h2&gt;
&lt;p&gt;目前看到第八集，覺得第七集兩人確認彼此心意，再加上正宇的案子終於抓到真兇後，好像就有點猜不到後續劇情的走向了。希望之後劇情可以給荷娜一個好的結局，想知道她最後會以什麼樣的面貌重新回到職場、找回自信，也希望劇情可以完整的交代正宇如何重新面對那些在低谷時期拋棄他的關係。最後就是希望可以多看一點弟弟博多，覺得這個有點皮、有點可愛的角色很值得更多螢幕時間啊～&lt;/p&gt;
</description>
        </item>
        <item>
        <title>進擊的巨人</title>
        <link>https://theoutsidelaine.com/p/attack-on-titan/</link>
        <pubDate>Mon, 12 Feb 2024 00:00:00 +0000</pubDate>
        
        <guid>https://theoutsidelaine.com/p/attack-on-titan/</guid>
        <description>&lt;img src="https://theoutsidelaine.com/p/attack-on-titan/cover.png" alt="Featured image of post 進擊的巨人" /&gt;&lt;p&gt;在 2024 年的二月終於將進擊的巨人最終章一共七集的內容看完了。距離上一次也是第一次知道並瞭解進擊的巨人的 2021 年五月，不知不覺也過了快三年。&lt;/p&gt;
&lt;p&gt;要在今天一次把對於這部作品的感受一次描繪完成，老實說我好像沒辦法做到，只能透過聽著第五季的片尾曲 &lt;a class=&#34;link&#34; href=&#34;https://www.youtube.com/watch?v=UAuwzw_5JU4&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;
    &gt;Shogeki&lt;/a&gt; 依稀的撿起零碎的感受。大概都是類似這樣一塊塊的短句，比如說，一開始看到城牆與巨人覺得是超自然科幻片的設定，後來才驚覺巨人原來是人而感到震撼與不解，後來經歷了萊納三人組的背叛，在世界觀經歷劇烈轉折後看到 Eren 一個人看海流淚並訴說對自由的渴望。在後來鏡頭轉向馬雷國，被九大巨人的設定所深深吸引（尤其是戰鎚巨人），了解了馬雷國種種可憎可怕的歷史，對於初代王與尤米爾故事感到噁心、對瑪雷國中控制 Eldia 人的手段感到唾棄，最後是每一派人馬對在講述他們希望怎麼達成世界和平，對吉克和平的選擇感到心痛、對於瑪雷和平的選擇感到憎恨、對於弗列茲王和平的選擇感到可悲、對於 Eren 和平的選擇感到無奈。很多時候人物之間的簡單互動會不經意地觸動我的內心，最讓我感到印象深刻的還是 &amp;lt;虛偽的人&amp;gt; 這集的最後，被莎夏救下的女孩與賈碧在森林中的一段話，賈碧問女孩為什麼要幫他們？女孩說，因為之前有個姊姊也是奮不顧身的就過她，所以她也想變成像那個姊姊一樣的人。不知道為什麼，當時在看到這邊時，眼淚克制不住地一直往下流。&lt;/p&gt;
&lt;p&gt;今天看完最後一章，不知為何讓我想到之前很常感受到的一句話：&lt;strong&gt;Ockham’s Razor&lt;/strong&gt;。這個理論的意思是，如果關於一個問題有許多的理論，每一種都能做出差不多準確的預言，那麼我們應該挑選其中假設最少的，因為這可以使我們更快獲取知識，所以是更好的選擇。乍看之下好像有點莫名其妙，但其實我想表達的是，很多複雜的事情背後好像最好、最正確的解決方法會是最簡單的那種。小則是關於演算法的決定、思考一個人做出複雜決定背後的理由，大則是關於世界和平的成因。&lt;/p&gt;
&lt;p&gt;關於最後一章大抵上有三個感受最深刻的部分：第一，是了解了 Eren 的和平方針。第二，是感受到戰爭與仇恨的原因。第三，是感受到生活與存在重要的理由。&lt;/p&gt;
&lt;p&gt;首先是 &lt;strong&gt;Eren 的和平方針&lt;/strong&gt;，乍看之下他的和平方針是將島外異己人士全部屠殺殆盡以達成保護自己人的單方面和平，但其實他想要達成的反而不是這個（不然就不會不限制阿爾敏等人的行動自由了），而是將自己塑造成仇恨的中心，讓所有的仇恨聚集到自己身上，再由以前的朋友們親自解決自己，讓他們成為戰爭英雄，讓其餘眾人透過團結打敗自己而獲取友誼與向心力。這樣既達成了保護他們的目的、也成就了和平，透過自我了斷也讓巨人之力消失於世界上，使針對力量的畏懼與爭奪消失。如果說吉克的絕種計畫是小面積的捨己救人，那 Eren 的地鳴計畫則是用80%的人類與自己的生命換取和平。吉克的絕種計畫總給我一種對 Eldia 人很不公平、很為他們感到心痛的感覺，但是是多方面算計之下傷亡最少且最和緩到感覺不出來的手段。而 Eren 的地鳴計畫則給我一種很殘酷暴力、但是對於 Eren 個人感到很憐惜而且心痛的感覺。前者的心痛是覺得所有的痛都是 Eldia 人在承受：被迫居到島上也是他們、被隱瞞真相無知的受到巨人迫害是他們、要被絕育也是他們。而針對 Eren 的心痛則是覺得這麼大的責任和力量和仇恨都承擔在一個人的身上未免也太過於沈重了，何況他還只是一個青少年。&lt;/p&gt;
&lt;p&gt;在看到這段時我曾經思考假設我是 Eldian，我會作何感想？是悲傷、憤怒、加入抗爭，還是會有別的感覺。我覺得如果是我，吉克的計畫會讓我覺得我必須接受，因為我就是生來是Eldian，我無法控制，但我可以用最好的方法為世界帶來和平，那我似乎就應該這樣選擇，我願意接受這個無奈的現實，但我應該會過著很厭世而隱隱感到怨懟的生活吧。而 Eren 的計畫對我而言，會覺得更複雜，雖然這樣的選擇要殺死很多無辜的陌生人，但是心裡某一塊小角落會想，世界上居然有個人願意為了我們這些少數族群發起殘酷的戰爭，不願意讓我們在單方面承受更多痛苦，是會打從心裡的被觸動。不知道 Eren 是怎麼想到這些的，或者作者怎麼安排他這樣想的，我只是覺得到頭來每種選擇好像都會或快或慢抵達同一個終點，只是擁有越多力量必須承擔越多選擇的責任，而做重大選擇自始至終都是好困難的一件事情。最後阿爾敏和 Eren 說，等到一切結束，我願意和你到地獄一起承受殺人的責任，覺得可以說出這樣的話讓人很感動，因為他理解 Eren 所承擔的責任一部分也來自於自己，並願意這麼表示，讓 Eren 可以感受到被支持而不是只有怨懟。&lt;/p&gt;
&lt;p&gt;第二是感受到戰爭與仇恨的原因，在於&lt;strong&gt;尊重與理解的困難&lt;/strong&gt;。Put yourself in other people’s shoe 從來就是一件知易行難的事情：馬雷人總是單方面的將 Eldian 仇化為「島上的惡魔」，因為害怕巨人的力量而不接受任何溝通。葉卡派的艾爾迪雅人將馬雷所有人視為要殺他們的壞人，無視其中也被馬雷政府迫害的 Eldia 民眾。因為資訊不對稱、恐懼、刻板印象，所以拒絕接收另一方的說法，也不願意聆聽以及給予解釋的機會，所以兩方人馬的鴻溝越來越深。最後當兩方人馬一起對抗 Eren 時，在那一段時間彼此都經歷了對方曾經經歷的，所以才理解才可以有溝通的機會。比如柯尼曾說，因為戰爭殺害了曾經的 Eldia 居民，才知道萊納當時選擇破壞城牆的不易。萊納也在當下理解 Eren 所做的選擇有多麼困難。在飛船基地的馬雷軍官因為一同對抗 Eren, 才會關心亞尼爸爸的傷勢。沒有身處在同一個情境之下，或是共情的心，好像人與人真的很難互相了解。我想這也是為什麼始祖尤米爾不會跟著理性的吉克走，因為只有 Eren 可以共情她的個人苦難，表示對他的同情與憤慨，並選擇站在他的同一邊。所以我想為什麼我在看 &amp;lt;虛偽的人&amp;gt;那集時會特別被觸動，因為女孩的爸爸曾經對殺死女兒的賈碧說到「讓我們一起走出森林」，因為他知道在這個戰爭的時代每個人都是帶有原因的鑄下錯誤，因為理解背後的原因所以選擇原諒。&lt;/p&gt;
&lt;p&gt;最後，我感受到最深刻的就是生活與存在的重要理由，不是很若大的榮耀或者是財富或是權勢，而是&lt;strong&gt;快樂&lt;/strong&gt;。最後一章阿爾敏跟吉克的談心時，他講到一段話：那一天我跟 Eren 跟米卡莎在比賽跑步。那一天的風非常暖和，光是跑著就非常舒服。那時我不知為什麼冒出一種想法：覺得自己彷彿是為了和他們兩個一起奔跑才誕生在這個世界上的。和大家一起逛市集時我也會這麼想。總而言之，這些平凡無奇的時刻對我來說非常重要。吉克也說，小時候的丟球，只不過是丟球、接球、再丟出去，一樣重複的動作，當中沒有任何意義，不過的確&amp;hellip;.我只要能一直玩傳接球就非常開心了。其實這兩個人的這段對話發生在大戰之間，所以是一個很突兀的場景，不過我覺得這大概是進巨想要說的另一個重點吧，很多時候生活與存在就是為了這些簡單的原因，反過來說，如果剝奪了平凡的幸福，人的一生就會非常不快樂，就好像始祖尤米爾簡單的愛情無法被實現甚至被糟蹋，而且千年以來不被任何人所理解。所以米卡莎的理解得以讓他放下並且消失。雖然尤米爾最後的離開並不是很能說服我，為什麼米卡莎說了跟艾倫一樣的答案（支持他、理解他不被王所愛的痛苦），但是她做出兩種不同的行動（支持 Eren 屠殺以及離開 Eren）。有可能是因為最後 Eren 已經被米卡沙所殺，所以也只能離開，不過這些行動比較缺乏一個完整的解釋。我傾向的說法是一個簡單的理由，始祖就是簡單的渴望被共情，被理解。誰做得到他就跟隨誰罷了，一個最簡單的理由。總而言之，快樂與擁有選擇的自由即是生活與存在的重要理由。&lt;/p&gt;
&lt;p&gt;關於最後的一幕看到米卡莎在 Eren 的墓碑前，被一隻小鳥繫上圍巾後淺淺的笑了，我覺得這個簡單而空虛的結局似乎也挺適合這樣一部史詩一般的巨作的。這條追求真相與自由的漫長道路，逐漸將 Eren 這個熱血有目標的少年，以重大的責任與殘酷無法兩全的真相消磨殆盡，不論怎麼選擇，好像都無法走向他真正想要的那個光明之地，好像都無法真正的用成熟的姿態做出對每個人而言最正確的決定，好像都還是會做錯事吧。我想很多人會覺得他這樣追求自由，最後還是宿命一般走向自己無數次看過的結局，也就是自己的死亡，以及身旁很多人的逝去，可能很可悲。不過我倒是覺得 Eren 在走向生命盡頭的那一刻是自由的，因為他擁有意志上的選擇自由。他選擇地鳴、選擇保護想保護的，然後感受到沈重，接受屬於他的責任。如同法國哲學家沙特曾經說的，其實自由與責任是一體的，自由並非五彩繽紛而是沈重如岩石一般。但是當你勇於面對與接受的那刻，你就是自由的。我想我會應該這樣去看待 Eren 是否是自由的吧。&lt;/p&gt;
&lt;p&gt;很多人會覺得最後關於和平的解法沒有定論，甚至定位在米卡莎與 Eren 的愛情，格局總是小了一點。但是我覺得也許最微小的事情就是一切混亂的救贖嗎，比如說要怎麼解決混亂，就是為每個人找到對於自身而言感到幸福快樂的歸宿，然後盡可能的學會尊重、理解。或許就如同 Historia 最後所說戰爭仍未結束，我們要為了自己的選擇再次努力、維繫和平，追求很多虛無的事物比如國與國的溝通、策略與軍武，但是可以在混亂的時代找到生活與存在的重要理由，感受到被愛與獲得快樂，也許就是一個人在一段時間所可以達成的最高成就了吧。&lt;/p&gt;
</description>
        </item>
        <item>
        <title>Archives</title>
        <link>https://theoutsidelaine.com/archives/</link>
        <pubDate>Sun, 06 Mar 2022 00:00:00 +0000</pubDate>
        
        <guid>https://theoutsidelaine.com/archives/</guid>
        <description></description>
        </item>
        <item>
        <title>Search</title>
        <link>https://theoutsidelaine.com/search/</link>
        <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
        
        <guid>https://theoutsidelaine.com/search/</guid>
        <description></description>
        </item>
        
    </channel>
</rss>
