[{"content":"","date":"May 1, 2026","externalUrl":null,"permalink":"/zh-tw/tags/edge-computing.html","section":"Tags","summary":"","title":"Edge Computing","type":"tags"},{"content":"","date":"May 1, 2026","externalUrl":null,"permalink":"/zh-tw/tags/gemma-4.html","section":"Tags","summary":"","title":"Gemma 4","type":"tags"},{"content":"","date":"May 1, 2026","externalUrl":null,"permalink":"/zh-tw/tags/next.js.html","section":"Tags","summary":"","title":"Next.js","type":"tags"},{"content":"","date":"May 1, 2026","externalUrl":null,"permalink":"/zh-tw/tags/openrouter.html","section":"Tags","summary":"","title":"OpenRouter","type":"tags"},{"content":"AI 驅動的漸進式網頁應用（PWA），專為處理非結構化視覺資料並即時生成食譜而設計。為展示安全低延遲的邊緣運算能力，本應用透過影像辨識自動解析食材庫存，針對特殊飲食需求生成結構化輸出，並將視覺任務派送至 Gemma 4 模型。建構了即時 Server-Sent Events（SSE）串流管線，並利用 Redis 實現滑動視窗 IP 速率限制。\n","date":"May 1, 2026","externalUrl":"https://pantry-lens-one.vercel.app/","permalink":"/zh-tw/projects/pantry-lens.html","section":"Projects","summary":"AI 驅動的漸進式網頁應用（PWA），專為處理非結構化視覺資料並即時生成食譜而設計。為展示安全低延遲的邊緣運算能力，本應用透過影像辨識自動解析食材庫存，針對特殊飲食需求生成結構化輸出，並將視覺任務派送至 Gemma 4 模型。建構了即時 Server-Sent Events（SSE）串流管線，並利用 Redis 實現滑動視窗 IP 速率限制。\n","title":"PantryLens | 邊緣路由 AI 視覺 PWA","type":"projects"},{"content":"","date":"May 1, 2026","externalUrl":null,"permalink":"/zh-tw/projects/index.html","section":"Projects","summary":"","title":"Projects","type":"projects"},{"content":"","date":"May 1, 2026","externalUrl":null,"permalink":"/zh-tw/tags/redis.html","section":"Tags","summary":"","title":"Redis","type":"tags"},{"content":"","date":"May 1, 2026","externalUrl":null,"permalink":"/zh-tw/tags/server-sent-events-sse.html","section":"Tags","summary":"","title":"Server-Sent Events (SSE)","type":"tags"},{"content":"","date":"May 1, 2026","externalUrl":null,"permalink":"/zh-tw/tags/system-architecture..html","section":"Tags","summary":"","title":"System Architecture.","type":"tags"},{"content":"","date":"May 1, 2026","externalUrl":null,"permalink":"/zh-tw/tags/index.html","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":"","date":"May 1, 2026","externalUrl":null,"permalink":"/zh-tw/tags/vercel.html","section":"Tags","summary":"","title":"Vercel","type":"tags"},{"content":"","date":"April 1, 2026","externalUrl":null,"permalink":"/zh-tw/tags/api-integration.html","section":"Tags","summary":"","title":"API Integration","type":"tags"},{"content":"","date":"April 1, 2026","externalUrl":null,"permalink":"/zh-tw/tags/api-rate-limiting.html","section":"Tags","summary":"","title":"API Rate Limiting","type":"tags"},{"content":"現代全端應用，整合即時 NOAA 太空天氣資料與 Google Gemini，為極光觀賞行程生成旅遊路線，並透過 Vercel 部署至全球邊緣節點。展示安全 AI 整合架構——採用雙層身份驗證體系（Auth0 M2M），將終端使用者認證與 Gemini AI 推論代理隔離，透過 Upstash Redis 強制執行嚴格的 API 配額與遙測資料收集，並在面向消費者的產品中提供可擴展的邊緣路由能力。\n","date":"April 1, 2026","externalUrl":"https://aurora-path.vercel.app/","permalink":"/zh-tw/projects/aurora-path.html","section":"Projects","summary":"現代全端應用，整合即時 NOAA 太空天氣資料與 Google Gemini，為極光觀賞行程生成旅遊路線，並透過 Vercel 部署至全球邊緣節點。展示安全 AI 整合架構——採用雙層身份驗證體系（Auth0 M2M），將終端使用者認證與 Gemini AI 推論代理隔離，透過 Upstash Redis 強制執行嚴格的 API 配額與遙測資料收集，並在面向消費者的產品中提供可擴展的邊緣路由能力。\n","title":"AuroraPath | 安全 AI 路由網頁應用程式","type":"projects"},{"content":"","date":"April 1, 2026","externalUrl":null,"permalink":"/zh-tw/tags/auth0.html","section":"Tags","summary":"","title":"Auth0","type":"tags"},{"content":"","date":"April 1, 2026","externalUrl":null,"permalink":"/zh-tw/tags/autonomous-agents.html","section":"Tags","summary":"","title":"Autonomous Agents","type":"tags"},{"content":"","date":"April 1, 2026","externalUrl":null,"permalink":"/zh-tw/tags/groq.html","section":"Tags","summary":"","title":"Groq","type":"tags"},{"content":"一個自主 AI 代理，能動態探索、評估並結構化科技活動資料。專為 Notion MCP Hackathon 打造，展示從傳統線性 API 及多重 Model Context Protocol（MCP）整合，邁向自主 AI 代理開發的技術演進。\n","date":"April 1, 2026","externalUrl":"https://github.com/klee1611/HackathonSniper","permalink":"/zh-tw/projects/hackathon-sniper.html","section":"Projects","summary":"一個自主 AI 代理，能動態探索、評估並結構化科技活動資料。專為 Notion MCP Hackathon 打造，展示從傳統線性 API 及多重 Model Context Protocol（MCP）整合，邁向自主 AI 代理開發的技術演進。\n","title":"Hackathon Sniper | 自主探索 AI 代理","type":"projects"},{"content":"","date":"April 1, 2026","externalUrl":null,"permalink":"/zh-tw/tags/llm.html","section":"Tags","summary":"","title":"LLM","type":"tags"},{"content":"","date":"April 1, 2026","externalUrl":null,"permalink":"/zh-tw/tags/model-context-protocol-mcp.html","section":"Tags","summary":"","title":"Model Context Protocol (MCP)","type":"tags"},{"content":"","date":"April 1, 2026","externalUrl":null,"permalink":"/zh-tw/tags/system-architecture.html","section":"Tags","summary":"","title":"System Architecture","type":"tags"},{"content":"","date":"April 1, 2026","externalUrl":null,"permalink":"/zh-tw/tags/typescript.html","section":"Tags","summary":"","title":"Typescript","type":"tags"},{"content":"","date":"March 20, 2026","externalUrl":null,"permalink":"/zh-tw/tags/ai.html","section":"Tags","summary":"","title":"AI","type":"tags"},{"content":"","date":"March 20, 2026","externalUrl":null,"permalink":"/zh-tw/categories/architecture.html","section":"Categories","summary":"","title":"Architecture","type":"categories"},{"content":"","date":"March 20, 2026","externalUrl":null,"permalink":"/zh-tw/tags/architecture.html","section":"Tags","summary":"","title":"Architecture","type":"tags"},{"content":"","date":"March 20, 2026","externalUrl":null,"permalink":"/zh-tw/categories/index.html","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":"","date":"March 20, 2026","externalUrl":null,"permalink":"/zh-tw/tags/embedding-model.html","section":"Tags","summary":"","title":"Embedding Model","type":"tags"},{"content":"","date":"March 20, 2026","externalUrl":null,"permalink":"/zh-tw/tags/gcp.html","section":"Tags","summary":"","title":"GCP","type":"tags"},{"content":"","date":"March 20, 2026","externalUrl":null,"permalink":"/zh-tw/tags/rag.html","section":"Tags","summary":"","title":"RAG","type":"tags"},{"content":"","date":"March 20, 2026","externalUrl":null,"permalink":"/zh-tw/tags/telegram-bot.html","section":"Tags","summary":"","title":"Telegram Bot","type":"tags"},{"content":"RAG(Retrieval Augmented Generation) 是一套 AI framework，能夠在不需要重新訓練 LLM 的前提下，讓開發者得以新增其他的外部資訊，以這些新增的資訊來改善 LLM 回答的精準度。在 2026 的今天已經是一項廣為人知的技術了。\n整套概念大概是這樣的：先將要新增的外部資訊（希望 LLM 能夠回答的資料）用 embedding model 向量化後儲存，在使用者輸入 prompt 之後，透過 embedding model 將使用者輸入的 prompt 向量化，拿來跟之前已經存好的向量化資訊做比對，取出最相近的數個資料，再透過 LLM 整合拿回來的資訊與回答，再回覆給使用者。透過這個方式可以讓 LLM 回答出各種開發者整合進去的知識而不需要重新訓練。\n我最近利用 RAG 開發了一個能用《權力遊戲：冰與火之歌》的台詞來回答我今天要不要翹掉重訓的 AI 機器人，這個專案叫 \u0026ldquo;Iron Counsel\u0026rdquo; ，專案目標是：\n前端介面使用 Telegram Bot 方便我隨時玩這個小專案 RAG 系統架設到雲端，我不打算用自己的電腦開機開整天，還要處理麻煩的網路連線以及安全問題 既 2.，我打算設個白名單，自用或者讓少數的親朋好友玩，避免流量被太大我的帳單會爆炸 雙語言，中英支援 低成本 這個專案的程式碼可以在我的 GitHub 找到，這篇主要是做一些概念的解釋，以及從架構的角度來解釋整個專案的技術選型，運作方式以及實作機制。\n概念解釋 # 向量化搜尋 (Vector Search) # 和傳統的關鍵字搜尋不同的是，向量化搜尋並非尋找和搜尋條件完全一致的資料。而是透過將每一筆資料都分別轉換成多維度的向量，在搜尋時透過運算來比對向量和搜尋條件之間的相似度，找到最接近的數個回答。\nEmbedding Model # 將資料向量化轉換成多維度向量的模型，每種 embedding model 對各種不同的資料類型會有不同的支援度，有些支援多語言有些只支援單語言，有些支援圖片有些只支援文字\u0026hellip;。不同的 embedding model 生成的向量維度也可能不同。 在選擇 embedding model 時必須根據需求選擇，而且在做前期資料處理以及後續要向量化使用者 prompt 的時候必須要用同一個 embedding model 來做轉換，否則向量化搜尋在做比對的時候會產生偏差。\n低成本雲端 RAG 架構 \u0026amp; 技術選型 # 第一階段：資料煉金術 (The Ingestion Pipeline) # Ingestion Pipeline 的主要目的是將資料相量化之後儲存，以便之後可以拿來比對使用者輸入的 prompt 取得最相近的內容資訊。\n在資料進入雲端之前，我選擇在自己的電腦完成最重的體力活。MacBook M1 以上的 CPU 透過較為輕量的 embedding model 來將資料向量化那叫一個輕輕鬆鬆XD\n本地向量化 (Local Embedding)： 我開發了一個 ingest.py 腳本。不透過調用昂貴的雲端 API，而是直接利用 MacBook 的 CPU，透過 FastEmbed (ONNX) 將 2,000 多條 Game of Thrones 劇本台詞轉換為向量。 脫鉤上傳： 生成 vectors.json 後，再批次上傳到 Firestore vector database。 Q\u0026amp;A： 為什麼不在雲端做？因為本地運算不花錢！這叫「離線預處理」，能確保雲端只負責最核心的儲存與查詢。 Embedding Model 選擇 # 為什麼選用 paraphrase-multilingual-MiniLM-L12-v2？\n多語言支援 (Multilingual)： 我想要一個中英文都通的機器人，而這個模型能處理中英雙語，語義對齊上表現得很棒。 專案使用者數量不多，我不需要去選用第三方以 token 計費的 embedding model API 來平衡負載量，那需要花更多預算。 輕量化 (Tiny but Mighty)： 它的維度只有 384 維。 維度越小，Firestore 的儲存成本越低，查詢速度越快。 相比於 OpenAI 的 1536 維模型，它在 CPU 上的運算速度極快，非常適合在沒有 GPU 的 Cloud Run 容器裡執行。 ONNX 兼容性： 透過 FastEmbed，這個 model 能以 ONNX 格式執行。就不需要安裝笨重的 PyTorch ，從而做到讓容器體積更小，啟動速度（Cold Start）更快。 Firestore 向量與歷史資料庫 # 向量檢索的實踐 (Vector Store)：Firestore 支援 KNN 向量搜尋功能。實作上我在 local 透過 embedding model 將台詞轉換為 384 維度的向量，再將這些向量和台詞上傳到 Firestore。在接收到使用者發出的 prompt 時，透過在 Firestore 執行 KNN 向量比對，就能抓出與使用者的 prompt 最相關的數個台詞。例如說，即使你沒提到「龍」，只要你的語義跟「強大的武力」相關，Firestore 也許就能幫你抓出丹妮莉絲的台詞。 對話記憶的持久化 (Chat History)： 除了向量台詞，專案還實作了利用 Firestore 儲存每一位白名單使用者的對話紀錄。透過子集合（Sub-collections）的設計，能以極低的延遲提取最近 10 次的對話內容，並將其注入到 LLM 的 Prompt。這個專案能夠以這個方式具備長期記憶，它會記得你兩分鐘前才剛抱怨過老闆。 為什麼不選專門的向量資料庫（如 Pinecone）？因為在這種規模下，Firestore 的「一站式服務」能讓我們在同一個 ACID 交易空間內完成向量檢索與紀錄寫入。減少了一次網路跳轉（Network Hop）。這個選擇的考量主要出於延遲的高低與較維護的代價。 第二階段：執行時期 (The Runtime) # 智商與速度 — LLM (Groq) # 在選 LLM 的時候我考慮過也測試過好幾個不同的選項，最後選擇使用 Groq 搭配 Llama 3.3 70B。\n對 RAG 專案來說，LLM 不需要\u0026quot;夠大\u0026quot;，因為真正核心的資料是開發者向量化後儲存以提供比對的資料，而不是 LLM。LLM 在這個情境中只要回答得夠快就可以了。Groq 的 LPU (Language Processing Unit) 架構提供了飛快的推論速度。當一般的 GPU 還在載入模型權重時，Groq 已經噴出了上百個 Token。 模型選型：Llama 3.3 70B： 我需要一個能支援中英雙語語境，還能執行指定人設的模型。Llama 3.3 70B 的推理能力與 GPT-4 很接近，而且它的反應速度在 Groq 的加持下真的非常快。 成本與效能的 Pareto 效率： Groq 目前對開發者非常！！非常！！非常的慷慨！！能讓我在不支付太多（甚至量少的話幾乎沒有） API 費用的情況下，享受到當今開源模型界高度推崇的強大的邏輯推理能力。 LangChain # LangChain 在整個專案中負責的是協調使用者、embedding model 和 LLM 之間的對話。 我用了 LangChain 的 Embeddings 介面封裝的 FastEmbed。當使用者問：「我該喝這杯酒嗎？」，整套流程會像這樣：\nLangChain 呼叫 FastEmbed 將這句話轉化為一個 [0.12, -0.05, \u0026hellip;] 的 384 維陣列。 LangChain 接著把這個陣列交給 Firestore 進行向量比對。 LLM 透過 LangChain 拿到的回應就會是與使用者問題真正相關的台詞。 GCP Cloud Run # 自動縮減至零 (Scale-to-Zero)： 我選擇 Google Cloud Run。因為這機器人一天可能只會被呼叫十幾次，Cloud Run 只有在請求進來時才會計費，平常沒人用時，帳單上的數字就是 $0。如果有大量的使用者那用 Cloud Run 就不適合了，cost 過高。 使用者數量少，不需要使用 GKE 常駐，那會需要維護複雜的 cluster。 GCP 強大的生態系：GCP 包含了 Firestore, Cloud Run, 和 Artifact 這些一站式的服務，整套架在上面當我需要看 log、找資訊的時候不需要轉來轉去，而且自動化從 Artifact 取得最新的 image 來跑很方便。 CI/CD: Terraform \u0026amp; GitHub Actions # Infrastructure as Code (IaC)： 用 Terraform 來定義所有的雲端資源。從 Firestore 的索引配置、Secret Manager 的金鑰儲存到 Cloud Run 的權限設定，全部寫成 HCL 檔案。這是為了「可重複性」以及降低維運的複雜度。如果有天我想把這個機器人搬到另一個專案，我只需要 terraform apply，五分鐘內一切就能原地重建。或者我想要一次修改 Cloud Run 的數量上限以及其他各種設定，我只要把檔案改完再 apply 一次就能將設定全部更新，也能避免手動去點介面的時候可能會出現的人為失誤。 CI/CD 與環境變數管理： 透過 GitHub Actions 來實現全自動化的部署流程。當 push 程式碼時，Action 會自動構建 Docker 映像檔並推送到 Artifact Registry。 環境變數的安全性： 將 Telegram Token 和 Groq API Key 儲存在 GCP Secret Manager 中，並透過 Terraform 授權給 Cloud Run 的 Service Account。在 GitHub Actions 的部署腳本中，操控這些環境變數以確保敏感資訊永遠不會出現在 Log, source code 或任何可能被其他人看到的地方。這種「最小權限原則 (Principle of Least Privilege)」的實踐非常重要。 Telegram Bot Webhook # 低傳輸量：Telegram bot 和後端系統的溝通有兩種方式：一種是 Long-polling，行為上類似於每隔一秒就派個信使去門口問一次：「有信嗎？有信嗎？」，效率極低而且不斷佔用網路頻寬。第二種就是 Webhook ，類似於一種被動觸發的機制，當收到訊息才會來後端尋求回應。 Scale to Zero: 當有人在 Telegram 裡向 bot 傳訊，Telegram 會主動發送一個 HTTPS POST 請求到我們的 Cloud Run 的 FastAPI 後端系統。完全符合我們「自動縮減至零」的要求：只有當有人向 bot 傳訊時，Cloud Run 伺服器才會醒來工作，其餘時間完全不消耗任何資源。 Token 驗證：為了防止「異鬼」入侵（未經授權的腳本惡意呼叫端點），我實作了 Secret Token 驗證機制，透過校驗 X-Telegram-Bot-Api-Secret-Token 這個 header，來確保每一則訊息都來自 Telegram。 存取控制 (The Gatekeeper)： 我在程式碼裡實作了 Telegram 使用者 ID 白名單。只有我確認過的使用者才能使用，有效防止路人耗盡我的預算。 全系統架構圖 (Architecture Blueprint) # graph TD subgraph \"Phase 1: Local Ingestion (資料煉金術)\" CSV[GoT 劇本 CSV] --\u003e|本地讀取| ING[scripts/ingest.py] ING --\u003e|LangChain + FastEmbed| FE_L[MiniLM-L12 模型] FE_L --\u003e|生成 384維向量| VEC[vectors.json] VEC --\u003e|批次上傳| FS_V[(Firestore: 向量台詞庫)] end subgraph \"Phase 2: Live Runtime (雲端執行階段)\" User[白名單使用者] --\u003e|Telegram 訊息| App[Cloud Run: FastAPI] subgraph \"Google Cloud Citadel\" App --\u003e|即時向量化| FE_C[FastEmbed in Container] App --\u003e|向量檢索| FS_V[(Firestore: 向量台詞庫)] App --\u003e|記憶提取| FS_H[(Firestore: 對話歷史)] FS_V --\u003e|相關台詞| App FS_H --\u003e|對話脈絡| App end App --\u003e|注入提示詞| Groq[Groq: Llama 3.3 70B] Groq --\u003e|快速生成回應| App App --\u003e|回傳結果| User end 結語 # 在這個專案成功地打造了一個極低成本的 RAG 應用，同時包含了幾個特性：\n開發與生產解耦： 匯入管線在本地，執行管線在雲端。 穩健性：GCP 整個當掉或者被攻擊到不能用的機率非常低。 安全性： 透過白名單機制、Telegram header 以及 Secret Manager 確保。 合理的效能與低成本： 利用 Groq 的高速度與慷慨的額度、 Cloud Run 的彈性，在指定的使用者規模下，實現了合理的反應速度以及低成本（近乎零成本）的實作。 這個專案的設計原則：我要用最少的資源，設計出最合理且安全、穩定的架構。\n","date":"March 20, 2026","externalUrl":null,"permalink":"/zh-tw/posts/build-rag-system-firestore-vector-search-iron-counsel.html","section":"文章","summary":"RAG(Retrieval Augmented Generation) 是一套 AI framework，能夠在不需要重新訓練 LLM 的前提下，讓開發者得以新增其他的外部資訊，以這些新增的資訊來改善 LLM 回答的精準度。在 2026 的今天已經是一項廣為人知的技術了。\n","title":"絕境長城之外：以 Firestore vector search 打造低成本、高效的雲端 RAG 應用","type":"posts"},{"content":"","date":"November 14, 2025","externalUrl":null,"permalink":"/zh-tw/tags/developer-productivity.html","section":"Tags","summary":"","title":"Developer Productivity","type":"tags"},{"content":"","date":"November 14, 2025","externalUrl":null,"permalink":"/zh-tw/categories/developer-tools.html","section":"Categories","summary":"","title":"Developer Tools","type":"categories"},{"content":"","date":"November 14, 2025","externalUrl":null,"permalink":"/zh-tw/tags/devtools.html","section":"Tags","summary":"","title":"DevTools","type":"tags"},{"content":"","date":"November 14, 2025","externalUrl":null,"permalink":"/zh-tw/tags/gitcoin.html","section":"Tags","summary":"","title":"Gitcoin","type":"tags"},{"content":"","date":"November 14, 2025","externalUrl":null,"permalink":"/zh-tw/tags/github.html","section":"Tags","summary":"","title":"GitHub","type":"tags"},{"content":"","date":"November 14, 2025","externalUrl":null,"permalink":"/zh-tw/tags/github-cli.html","section":"Tags","summary":"","title":"GitHub CLI","type":"tags"},{"content":"","date":"November 14, 2025","externalUrl":null,"permalink":"/zh-tw/tags/notifications.html","section":"Tags","summary":"","title":"Notifications","type":"tags"},{"content":"","date":"November 14, 2025","externalUrl":null,"permalink":"/zh-tw/tags/phantom-notification.html","section":"Tags","summary":"","title":"Phantom Notification","type":"tags"},{"content":"","date":"November 14, 2025","externalUrl":null,"permalink":"/zh-tw/tags/scam.html","section":"Tags","summary":"","title":"Scam","type":"tags"},{"content":"","date":"November 14, 2025","externalUrl":null,"permalink":"/zh-tw/tags/security.html","section":"Tags","summary":"","title":"Security","type":"tags"},{"content":"","date":"November 14, 2025","externalUrl":null,"permalink":"/zh-tw/tags/troubleshooting.html","section":"Tags","summary":"","title":"Troubleshooting","type":"tags"},{"content":"GitHub notification 對開發者來說是一向很方便的工具，可以用來追蹤 issue、pull request 和提及 (mention)。但某天我的通知標記忽然就這麼卡住了，即使已讀了所有內容也他還是非常頑固的卡在那裡，在又拖了幾個月之後我才終於花了點時間研究發生了什麼事。\n這幾個月，我的 GitHub 收件夾一直有一個「1」的通知標記，即使每個收件匣都沒有未讀訊息 ，它也沒有消失。沒有封存的項目，沒有訂閱，也沒有任何隱藏的內容。但那個通知就是在那，當其他 notification 出現的時候那個數字就開始往上加。\n我查了一下，發現許多開發者早在 2025 年 9 月就開始回報同樣的問題。這是由一場冒充 Gitcoin 的垃圾訊息攻擊引起的，在 GitHub 的後端留下了沒有被自動清理掉的通知紀錄。\n以下是整個事件的始末，以及如何修復這個幽靈通知。\n這個「幽靈通知」怎麼出現的？ # 一波大規模的詐騙冒充 Gitcoin，創建了假的儲存庫，例如：\nGitcoin-Developer/gitcoin.com Gitcoin-Developers/gitcoin.com 攻擊者將隨機使用者加入 issue 和 PR 討論串中，因此這些倒霉蛋不出意外的收到了 GitHub 通知。當這些儲存庫後來被刪除或封鎖時，通知的條目仍然保留在 GitHub 的後端，卻沒有對應的 UI 元素，導致這些人面臨了一個尷尬的狀況：\n🔴 一個一直存在的未讀標記\n❌ UI 中沒有對應可見的通知\n🧠 一個無法正常清除的資料庫討論串\n所以，如果有這樣的情況的使用者，在這幾個月來一直忽略這個標記，現在想清理它，就會發現透過網頁介面根本清不掉。\n詐騙是如何運作的 # 詐騙流程如下：\n攻擊者創建一個假的儲存庫或 issue 大量提及 (mass-mention) 開發者以產生真實的 GitHub 通知 引導使用者前往一個要求他們連接加密貨幣錢包的網站 要求批准或「驗證」交易 一旦獲得權限，便盜取資產 雖然這篇文章的重點不是詐騙本身，但它解釋了為什麼這麼多開發者會遇到同樣的幽靈通知問題。\n一旦詐騙儲存庫或 issue 被刪除：\nGitHub 的 UI 沒有任何東西可以打開 → 但仍然將其標記為未讀 → 然後它就永遠卡在那裡囉～～～\n詐騙手法 解釋 假的 Gitcoin 儲存庫 透過相似的組織名稱設計，讓他看起來值得信任 自動加入使用者 在未經同意的情況下標記使用者，以引發他們對通知的好奇心 加密貨幣獎勵誘餌 討論串中提及賞金或空投 釣魚連結 將使用者重新導向至假的錢包連接頁面 即使你從未點擊任何東西，這些通知也會默默存在。\n如何移除卡住的 GitHub 通知 # 解決方案來自這個社群討論。\n✔ 最佳解法：透過 GitHub CLI 清除後端的通知紀錄\n步驟 1 — 列出所有活動中的通知討論串 # gh api notifications | jq \u0026#39;.[] | { id, title: .subject.title, repo: .repository.full_name }\u0026#39; 這會顯示所有仍然存在後端的討論串，不管它們是不是能在介面上看不看得到，並附帶它們的討論串 ID。\n步驟 2 — 刪除目標討論串 # 將 $THREAD_ID 替換成你找到的 ID：\ngh api --method DELETE notifications/threads/$THREAD_ID 重新整理 GitHub 頁面——標記就會消失囉！！！\n如果你沒有安裝 jq # gh api notifications gh api --method DELETE notifications/threads/THREAD_ID 結論 # 這個介於存在與不存在之間的通知標記不僅僅是一個介面上的小問題，它是一場大規模詐騙攻擊後，GitHub 未能完全清理的殘留紀錄。許多開發者已經忍受這個問題好幾個月，而 UI 上的清除方法之所以無效，是因為在大家看得到的收件匣中根本沒有任何東西。\n目前來說，透過 GitHub CLI 把通知刪除是最有效的解法。\n參考資料 # GitHub 社群討論\n","date":"November 14, 2025","externalUrl":null,"permalink":"/zh-tw/posts/remove-ghost-notification-github-gitcoin-spam.html","section":"文章","summary":"GitHub notification 對開發者來說是一向很方便的工具，可以用來追蹤 issue、pull request 和提及 (mention)。但某天我的通知標記忽然就這麼卡住了，即使已讀了所有內容也他還是非常頑固的卡在那裡，在又拖了幾個月之後我才終於花了點時間研究發生了什麼事。\n","title":"使用 GitHub CLI 徹底移除 GitHub 上頑固的「幽靈通知」","type":"posts"},{"content":"","date":"October 25, 2025","externalUrl":null,"permalink":"/zh-tw/tags/asyncio.html","section":"Tags","summary":"","title":"Asyncio","type":"tags"},{"content":"","date":"October 25, 2025","externalUrl":null,"permalink":"/zh-tw/tags/concurrency.html","section":"Tags","summary":"","title":"Concurrency","type":"tags"},{"content":"","date":"October 25, 2025","externalUrl":null,"permalink":"/zh-tw/categories/concurrency-programming.html","section":"Categories","summary":"","title":"Concurrency Programming","type":"categories"},{"content":"","date":"October 25, 2025","externalUrl":null,"permalink":"/zh-tw/tags/coroutine.html","section":"Tags","summary":"","title":"Coroutine","type":"tags"},{"content":"","date":"October 25, 2025","externalUrl":null,"permalink":"/zh-tw/tags/gil.html","section":"Tags","summary":"","title":"GIL","type":"tags"},{"content":"","date":"October 25, 2025","externalUrl":null,"permalink":"/zh-tw/tags/multiprocessing.html","section":"Tags","summary":"","title":"Multiprocessing","type":"tags"},{"content":"Python 的效能瓶頸在幾年前一直為人詬病，\n但在開發者的努力之下，Python 3.4 開始出現了 Asyncio 可以在特定情境下提升效能，\n到了 Python 3.13 更出現了可選擇性關閉 GIL 的 Free-threaded (PEP-703) 設計，\n結合過去的 Multiprocessing 和 Multithreading，\n我整理了一下這三項技術適合的原理、差異和使用情境做了幾篇紀錄。\n這一篇先簡單介紹三者的基本概念和適用情境。\nMultiprocessing # 一支程式可以同時執行多個獨立的行程 (Process)。\n每個行程都有自己獨立的記憶體空間，\n也因此可以完全避開 Python GIL (Global Interpreter Lock) 的限制。\n這意味著不管是哪個版本，它們都可以真正地在多核心 CPU 上獨立的平行執行，\n互不干擾。\n適用情境：\nCPU 密集型任務 (CPU-bound)，\n例如大量的數學運算、資料處理、影像辨識等。\n可以有效利用多核心 CPU 的運算能力。\n同時因為具有隔離性，\n如果單一 process 崩潰也不會影響到同時正在運行的其他 process 或主程式。\nimport multiprocessing import time def cpu_bound_task(n): count = 0 for i in range(n): count += i print(f\u0026#34;Finished task with {n}\u0026#34;) if __name__ == \u0026#39;__main__\u0026#39;: start_time = time.time() processes = [] for i in range(4): p = multiprocessing.Process(target=cpu_bound_task, args=(10**7,)) processes.append(p) p.start() for p in processes: p.join() end_time = time.time() print(f\u0026#34;Multiprocessing took {end_time - start_time:.2f} seconds.\u0026#34;) 優點： 能夠利用 multi-core CPU 實現真正的平行運算。 不受 GIL 限制。 行程間記憶體獨立，穩定性高，不太會產生 Race Condition。 缺點： 獨立 Process 建立所需的資源 (CPU, memory) 開銷較大。 行程間通訊 (IPC) 較為複雜，需要透過 Queue、Pipe 或 share memory 等機制實現，且延遲較高。 Multithreading # 在單一 Process 裡建立多個執行緒 (Thread)。\n這些 thread 共享同一個 Process 使用的記憶體空間 (Heap)，\n因此可以輕鬆地進行資料共享與交換。\n在 Python3.13 之前的版本，\nPython 和其他程式語言如 C/C++ 等不同的是，\nPython 的 multithreading 會受到 Python GIL (Global Interpreter Lock) 的限制，\n即使是跑在多核心的 CPU 上，\nPython 的 multithreading 實際上也做不到真正的平行運算。\nGIL (Global Interpreter Lock): GIL 是 CPython (官方的 Python 實現) 中的一個機制， 為了保護 Python object （像是 dict, list 等）不會被損壞，\n這個機制確保同一個時間點只有一個執行緒能執行 Python 的 bytecode。 這會導致在 CPU 密集型任務中， 即使在多核心 CPU 上， Python 的多執行緒也只能在一顆 core 上交替執行， 無法實現真正的平行運算達到加速的效果。\n在過去仍有 GIL 的機制下，當 Thread 遇到 I/O 操作 (如讀寫檔案、網路請求) 時會釋放 GIL，\n讓其他 Thread 有機會執行。\n因此傳統上 Multithreading 主要被用於處理 I/O 密集型任務。\n隨著 Python 的使用者越來越多，\n產生了 PEP-703 這樣的需求，\n直到 Python 3.13 之後開始實驗性的包含了可以選擇性關閉 GIL 的功能 (free-threading mode)，\nPython 3.14 開始出現了無 GIL 的 Free-threaded 的版本。\n在無 GIL 的版本下，\nPython 的 Multithreading 終於可以突破限制，\n在多個核心同時平行處理 CPU-bound task，\n避免 Multiprocessing 的 IPC 開銷。\n適用情境：\nPython 3.13 之前的版本：I/O 密集型任務 (I/O-bound)，例如網路爬蟲、檔案下載、API 請求等。 Python 3.13+ 啟用 Free-threaded mode 之後百無禁忌。 import threading import requests import time def io_bound_task(url): try: response = requests.get(url) print(f\u0026#34;Downloaded {url} with status {response.status_code}\u0026#34;) except Exception as e: print(f\u0026#34;Error downloading {url}: {e}\u0026#34;) if __name__ == \u0026#39;__main__\u0026#39;: urls = [\u0026#34;https://www.google.com\u0026#34;] * 5 start_time = time.time() threads = [] for url in urls: t = threading.Thread(target=io_bound_task, args=(url,)) threads.append(t) t.start() for t in threads: t.join() end_time = time.time() print(f\u0026#34;Multithreading took {end_time - start_time:.2f} seconds.\u0026#34;) 優點： 建立 thread 的開銷比 process 小。 共享記憶體，資料交換方便。 缺點： Python 3.13 前的版本受 GIL 限制，無法利用多核心 CPU 處理 CPU 密集型任務。 需要處理執行緒同步問題，如使用 Lock 來避免 Race condition。 Asyncio I/O (Asyncio) 與 Coroutine # 非同步是 Python 3.4 之後引入的標準函式庫，\n概念上是利用 Event Loop 和 Coroutine 來實現 single thread 下的並行(Concurrency)。\nCoroutine 可以看作是一種輕量級的 thread，\n可以被控制執行到某個需要等待 IO 的地方(await)時暫停，\n等待需要被執行的任務完成，\n在等待的同時將控制權交還給 event loop 去執行其他 coroutine。\n當暫停的條件完成後 (例如等待的 I/O 操作完成)，\nevent loop 會再回來繼續執行該 coroutine。\n除了可以在暫停時間執行其他 coroutine 之外，\n也可以節省作業系統層級的執行緒切換 (context switch)，\n可以提升不少效能。\n適用情境：\n高度並行的 I/O 密集型任務，\n特別是需要同時處理大量網路連線的場景 (如 Web 伺服器、聊天應用、巨量 API requests)。\nimport asyncio import aiohttp import time async def async_io_bound_task(session, url): try: async with session.get(url) as response: print(f\u0026#34;Downloaded {url} with status {response.status}\u0026#34;) except Exception as e: print(f\u0026#34;Error downloading {url}: {e}\u0026#34;) async def main(): urls = [\u0026#34;https://www.google.com\u0026#34;] * 5 start_time = time.time() async with aiohttp.ClientSession() as session: tasks = [async_io_bound_task(session, url) for url in urls] await asyncio.gather(*tasks) end_time = time.time() print(f\u0026#34;Asyncio took {end_time - start_time:.2f} seconds.\u0026#34;) if __name__ == \u0026#39;__main__\u0026#39;: asyncio.run(main()) 優點： Context switch 的成本極低，能夠以極高的效率處理大量 I/O 操作。 在 single thread 下運作，沒有 OS 層級的 race condition 的問題。（但在應用層 race condition 還是有可能被使用者自己寫出來的，自己要注意） 缺點： 不適用於 CPU 密集型任務。CPU-bound task 一個就可以卡住整個 Event loop。 需要使用 async/await 語法，且需要有對應的非同步函式庫支援 (如 aiohttp, asyncpg)。 比較總結 # 特性 多程序 (Multiprocessing) 多執行緒 (Multithreading) 非同步 (Asyncio) 基本單位 行程 (Process) 執行緒 (Thread) 協程 (Coroutine) 記憶體空間 獨立 共享 共享 (單執行緒) GIL 影響 無，直接繞過 舊版受限制，3.13+ 可以避開 無 (單執行緒) 平行/並行 平行 (Parallelism) 舊版並行 (Concurrency)，3.13+ 平行 (Parallelism) 並行 (Concurrency) 適用情境 CPU 密集型、高容錯隔離 一般 I/O 密集型 海量/高度並行的 I/O 密集型 優點 可利用多核心、穩定性高 共享記憶體、開銷低 極高 I/O 吞吐量、低開銷 缺點 資源開銷大、IPC 複雜 舊版(3.13 前)受 GIL 限制、有競爭條件需要處理複雜的 Lock 不適用 CPU 密集型任務 (Event Loop 會卡死) 如果是 CPU-bound 的任務需求，需要大量 CPU 運算，那麼不論新舊版的 Python multiprocessing 都能處理，它能充分利用多核心 CPU。Python 3.13+ 的 multithreading 也可以，還能降低 context switch 的開銷及 IPC 的複雜度。 如果是 IO-bound 的任務需求，且邏輯相對簡單、連線數不大，multithreading 是一個寫法簡單且輕量的選擇。 如果是 IO-bound 且需要處理大量的並行連線 (例如開發 Web 伺服器或 API、微服務)，那麼 asyncio 是效能與吞吐量最高的。 ","date":"October 25, 2025","externalUrl":null,"permalink":"/zh-tw/posts/python-concurrency-parallelism-multiprocessing-multithreading-asyncio.html","section":"文章","summary":"Python 的效能瓶頸在幾年前一直為人詬病，\n但在開發者的努力之下，Python 3.4 開始出現了 Asyncio 可以在特定情境下提升效能，\n到了 Python 3.13 更出現了可選擇性關閉 GIL 的 Free-threaded (PEP-703) 設計，\n結合過去的 Multiprocessing 和 Multithreading，\n我整理了一下這三項技術適合的原理、差異和使用情境做了幾篇紀錄。\n這一篇先簡單介紹三者的基本概念和適用情境。\n","title":"Multiprocessing, Multithreading and Asyncio in Python Part 1 - Basic Concept","type":"posts"},{"content":"","date":"October 25, 2025","externalUrl":null,"permalink":"/zh-tw/tags/multithreading.html","section":"Tags","summary":"","title":"Multithreading","type":"tags"},{"content":"","date":"October 25, 2025","externalUrl":null,"permalink":"/zh-tw/tags/parallelism.html","section":"Tags","summary":"","title":"Parallelism","type":"tags"},{"content":"","date":"October 25, 2025","externalUrl":null,"permalink":"/zh-tw/tags/performance.html","section":"Tags","summary":"","title":"Performance","type":"tags"},{"content":"","date":"October 25, 2025","externalUrl":null,"permalink":"/zh-tw/tags/python.html","section":"Tags","summary":"","title":"Python","type":"tags"},{"content":"","date":"October 4, 2025","externalUrl":null,"permalink":"/zh-tw/tags/debugging.html","section":"Tags","summary":"","title":"Debugging","type":"tags"},{"content":"","date":"October 4, 2025","externalUrl":null,"permalink":"/zh-tw/categories/develop-environment.html","section":"Categories","summary":"","title":"Develop Environment","type":"categories"},{"content":"","date":"October 4, 2025","externalUrl":null,"permalink":"/zh-tw/tags/macos.html","section":"Tags","summary":"","title":"Macos","type":"tags"},{"content":"幾個月前用 rsync 從 Macbook 備份資料到 NAS 上的時候出現了點問題，\nrsync 會看似正常的在螢幕上跑一陣子然後卡死不動，\n從螢幕輸出會看到他原本一邊同步一邊在吐正在同步中的檔案，\n然後吐著吐著就（習慣了？不是）停了，\n沒有任何錯誤訊息、rsync 也沒有直接結束；\n原本以為可能是在傳送大檔案或者可能網路不穩，\n但後來發現 kill 掉之後再下一次 rsync 指令就會從那個卡住的檔案開始順順的傳，\n連著好幾次都一樣，\n這看起來就不是網路問題或是檔案問題可以解釋的了！！\n於是我就打開了 Mac 內建的 Activity Monitor 看看 rsync 到底發生了什麼事，\n不看不知道，一看嚇一跳～～\nrsync 的狀態已經是 terminated 了，\nCPU 0% 是因為已經停了，\n但是還吃了一堆 Virtual memory (34 GB!!!!!) 在那裡卡著不動，\n他的路徑在 /usr/bin 下所以顯然是系統內建的，\n我研究了一下，\n馬上發現有人在某條 Reddit 的討論串 提到 Mac 內建的 rsync 有問題，\n於是我就照著他的建議棄用 Mac 內建的舊版 rsync 改成了用 homebrew 版本的新版 rsync，\n2x GB 的檔案們就順順利利的同步完啦！！！\n到現在我都還是挺疑惑的，\n看起來那條 Reddit 的評論已經是三年前了，\n這個問題也挺明顯的一旦檔案數量一多就會發生，\n為什麽到當時都沒處理掉那個過時的舊版本呢？\n不知道是不是為了 GPL 強迫開源的法律考量才拒絕更新？\n","date":"October 4, 2025","externalUrl":null,"permalink":"/zh-tw/posts/resolve-macos-rsync-hangs.html","section":"文章","summary":"幾個月前用 rsync 從 Macbook 備份資料到 NAS 上的時候出現了點問題，\nrsync 會看似正常的在螢幕上跑一陣子然後卡死不動，\n從螢幕輸出會看到他原本一邊同步一邊在吐正在同步中的檔案，\n","title":"Macos Legacy Rsync Hangs","type":"posts"},{"content":"","date":"October 4, 2025","externalUrl":null,"permalink":"/zh-tw/tags/productivity.html","section":"Tags","summary":"","title":"Productivity","type":"tags"},{"content":"","date":"October 4, 2025","externalUrl":null,"permalink":"/zh-tw/tags/tools.html","section":"Tags","summary":"","title":"Tools","type":"tags"},{"content":"","date":"January 1, 2025","externalUrl":null,"permalink":"/zh-tw/tags/ci/cd.html","section":"Tags","summary":"","title":"CI/CD","type":"tags"},{"content":"","date":"January 1, 2025","externalUrl":null,"permalink":"/zh-tw/tags/fastapi.html","section":"Tags","summary":"","title":"FastAPI","type":"tags"},{"content":"以 RAG 技術為核心的對話式 AI 應用，在複雜資料集上實現精準的 LLM 推論。技術架構：基於 GCP 的無伺服器 FastAPI 後端（透過 Terraform 建置）、本地 ONNX 嵌入模型、Firestore 向量搜尋，以及 LangChain 編排框架。\n","date":"January 1, 2025","externalUrl":"https://github.com/klee1611/iron-counsel","permalink":"/zh-tw/projects/iron-counsel.html","section":"Projects","summary":"以 RAG 技術為核心的對話式 AI 應用，在複雜資料集上實現精準的 LLM 推論。技術架構：基於 GCP 的無伺服器 FastAPI 後端（透過 Terraform 建置）、本地 ONNX 嵌入模型、Firestore 向量搜尋，以及 LangChain 編排框架。\n","title":"Iron Counsel：企業級 RAG 架構與對話式 AI","type":"projects"},{"content":"","date":"January 1, 2025","externalUrl":null,"permalink":"/zh-tw/tags/langchain.html","section":"Tags","summary":"","title":"LangChain","type":"tags"},{"content":"","date":"January 1, 2025","externalUrl":null,"permalink":"/zh-tw/tags/terraform.html","section":"Tags","summary":"","title":"Terraform","type":"tags"},{"content":"","date":"January 1, 2025","externalUrl":null,"permalink":"/zh-tw/tags/vector-database.html","section":"Tags","summary":"","title":"Vector Database","type":"tags"},{"content":"","date":"December 25, 2024","externalUrl":null,"permalink":"/zh-tw/tags/joplin.html","section":"Tags","summary":"","title":"Joplin","type":"tags"},{"content":"","date":"December 25, 2024","externalUrl":null,"permalink":"/zh-tw/tags/obsidian.html","section":"Tags","summary":"","title":"Obsidian","type":"tags"},{"content":"原先我使用的筆記軟體是 Notion，\n功能豐富且介面美觀，\n但幾年前 Notion 出現了隱私權爭議，\n被指控偷看某公司放在 Notion 的內容，\n甚至進一步提出合作；\n就改用了一陣子 Joplin，\n但最後還是轉到了擁有大量外掛及社群支援，\n而且可以高度客製化的 Obsidian。\nJoplin 原先就支援 WebDAV 同步，\n轉到 Obsidian 後我找到了 Remotely save 這個也支援 WebDAV 的 plugin，\n方便我將筆記儲存在自己的 NAS 上，\n同時可以在不同裝置之間保持同步。\nWebDAV 和 HTTPS Certificate 設定 - 穿透 Router 連線內網 NAS 上的 WebDAV server # 設備 # Router: Synology RT2600AC NAS: Synology 918+ 環境建立 # Synology NAS 上安裝 WebDAV Server 套件後，\n基於安全性考量在 WebDAV 的設定關閉 HTTP 連線，\n只開啟 HTTPS 存取權限\n（雖然不是很確定，\n不過手機版的 Joplin 似乎因為安全性限制也只能用 HTTPS），\n然後在 router 上的網路設定將對應的 Port forwarding 做設定如圖，\n由於之前也經設定過 router 的 DDNS，\n所以原先就可以透過網址連線到 router；\n於是我就直接進行了 WebDAV 的連線測試，\n我在 Joplin 的連線設定選了 WebDAV，\n把網址跟 port 號還有要儲存筆記的資料夾放在 WebDAV 的網址列\n（如： https://.....:5xxx/homes/xxx/xxx/xxx），\n點了測試連線後就發現 certificate 出現問題，\ngoogle 了一陣子之後發現是因為連線時網址的 domain name 是對應到 router，\nrouter 再把連線導到 NAS，\n但在 NAS 上卻沒有找到該 domain name 的 certificate，\n導致了 Joplin 連線的時候 certificate 跟網址對不上，\n（可以用 https://www.geocerts.com/ssl-checker 檢查）；\n於是我從 router 把 certificate export 出來以後放到 NAS 上，\n然後在 NAS 上找到設定 certificate 的地方，\n把 WebDAV 的 certificate 換成從 router import 進來的，\n設定如下圖，\n然後去 https://www.geocerts.com/ssl-checker 又做了一次測試，\n這次確認了 HTTPS 連線到的 port 的 certificate 是正確的，\n再回到 Joplin 的連線設定測試一遍確認連線成功後就可以順利同步了。\n補充資訊 - Notion 隱私權爭議 # 2020 年一篇在Notion.Taiwan 台灣官方社群的貼文 中有人提到，\nNotion 會偷看用戶資料甚至提出合作：\n最後 Notion 官方聲明 移除了引發爭議的 Business Development and Strategic Partnerships 條款\n","date":"December 25, 2024","externalUrl":null,"permalink":"/zh-tw/posts/sync-obsidian-joplin-data-across-multiple-device-synology-webdav.html","section":"文章","summary":"原先我使用的筆記軟體是 Notion，\n功能豐富且介面美觀，\n但幾年前 Notion 出現了隱私權爭議，\n被指控偷看某公司放在 Notion 的內容，\n甚至進一步提出合作；\n就改用了一陣子 Joplin，\n但最後還是轉到了擁有大量外掛及社群支援，\n而且可以高度客製化的 Obsidian。\n","title":"Sync Obsidian / Joplin Data Across Multiple Device with Synology WebDAV","type":"posts"},{"content":"","date":"December 25, 2024","externalUrl":null,"permalink":"/zh-tw/tags/synology.html","section":"Tags","summary":"","title":"Synology","type":"tags"},{"content":"","date":"December 25, 2024","externalUrl":null,"permalink":"/zh-tw/categories/tools.html","section":"Categories","summary":"","title":"Tools","type":"categories"},{"content":"","date":"December 25, 2024","externalUrl":null,"permalink":"/zh-tw/tags/webdav.html","section":"Tags","summary":"","title":"WebDAV","type":"tags"},{"content":"","date":"March 1, 2022","externalUrl":null,"permalink":"/zh-tw/tags/docker.html","section":"Tags","summary":"","title":"Docker","type":"tags"},{"content":"","date":"March 1, 2022","externalUrl":null,"permalink":"/zh-tw/tags/microservices.html","section":"Tags","summary":"","title":"Microservices","type":"tags"},{"content":"","date":"March 1, 2022","externalUrl":null,"permalink":"/zh-tw/tags/mongodb.html","section":"Tags","summary":"","title":"MongoDB","type":"tags"},{"content":"","date":"March 1, 2022","externalUrl":null,"permalink":"/zh-tw/tags/software-standardization.html","section":"Tags","summary":"","title":"Software Standardization","type":"tags"},{"content":"生產就緒的非同步 Python 微服務樣板，專為標準化企業後端開發、消除傳統 I/O 瓶頸而設計。透過整合 Docker 並強制要求 95% 測試覆蓋率基準，在組織內建立嚴格的工程文化。\n","date":"March 1, 2022","externalUrl":"https://github.com/klee1611/cookiecutter-fastapi-mongo","permalink":"/zh-tw/projects/cookiecutter-fastapi-mongo.html","section":"Projects","summary":"生產就緒的非同步 Python 微服務樣板，專為標準化企業後端開發、消除傳統 I/O 瓶頸而設計。透過整合 Docker 並強制要求 95% 測試覆蓋率基準，在組織內建立嚴格的工程文化。\n","title":"企業標準：非同步 FastAPI 架構","type":"projects"},{"content":"","date":"November 6, 2021","externalUrl":null,"permalink":"/zh-tw/tags/global-packages.html","section":"Tags","summary":"","title":"Global Packages","type":"tags"},{"content":"今天遇到一個問題，\n安裝過 nvm 後安裝 global package 的路徑就被改變了，\n導致想要移除之前安裝過的 global package 時沒辦法直接用 npm uninstall -g 移除。\n怎麼發現這件事的呢？\n很久以前我在 global 裝過一個 package 可以直接在 terminal 呼叫 command 執行，\n但因為年代久遠，\n要升級那個 package 的時候發現他不在 npm list -g 的範圍，\n只好先用 which 看一下他在的位置，\n接著發現是一個 link 然後就用 ls -al 看那個 link 連到哪裡，\n發現是在 /usr/lib/node_modules 底下，\n很明顯是用 npm -g 安裝的，\n於是再仔細的看了一下 npm list -g 的結果，\n發現其他的 global package 都列在 /Users/\u0026lt;USER_NAME\u0026gt;/.nvm/versions/node/v16.5.0/lib 底下，\n在一陣 google 之後找到了一個方式 nvm use system \u0026amp;\u0026amp; npm ls -g --depth=0 來列出原本安裝過的 global package 有哪些，\n悲劇的是他跳出了\nSystem version of node not found. 看來我已經移除了系統裡的 node \u0026hellip;\n於是我又找到了一個 command nvm deactivate 暫時 disable nvm ，\n然後用 brew 再安裝一次 node ，\n接著再 npm list -g 一次，\n終於讓我看到那個在安裝 nvm 之前安裝過的 package 了！！！\n可喜可賀！！\n終於可以順利的移除 / 升級之前安裝的 global package 了。\n搞定之後要重新把 nvm 叫回來只要用 source ~/.zshrc 之類的重啟 shell 就可以了。\n","date":"November 6, 2021","externalUrl":null,"permalink":"/zh-tw/posts/managing-pre-exist-global-npm-packages-after-installing-nvm.html","section":"文章","summary":"今天遇到一個問題，\n安裝過 nvm 後安裝 global package 的路徑就被改變了，\n導致想要移除之前安裝過的 global package 時沒辦法直接用 npm uninstall -g 移除。\n怎麼發現這件事的呢？\n很久以前我在 global 裝過一個 package 可以直接在 terminal 呼叫 command 執行，\n","title":"Managing Pre-existing Global NPM Packages After Installing NVM","type":"posts"},{"content":"","date":"November 6, 2021","externalUrl":null,"permalink":"/zh-tw/tags/node.js.html","section":"Tags","summary":"","title":"Node.js","type":"tags"},{"content":"","date":"November 6, 2021","externalUrl":null,"permalink":"/zh-tw/tags/npm.html","section":"Tags","summary":"","title":"NPM","type":"tags"},{"content":"","date":"November 6, 2021","externalUrl":null,"permalink":"/zh-tw/tags/nvm.html","section":"Tags","summary":"","title":"NVM","type":"tags"},{"content":"","date":"November 1, 2021","externalUrl":null,"permalink":"/zh-tw/tags/programming.html","section":"Tags","summary":"","title":"Programming","type":"tags"},{"content":"","date":"November 1, 2021","externalUrl":null,"permalink":"/zh-tw/tags/pyenv.html","section":"Tags","summary":"","title":"Pyenv","type":"tags"},{"content":" pyenv 的功能和使用的原因 # pyenv 是用來在系統裡安裝各種不同版本的 python，\n並能夠方便的切換 python 版本的工具。\n當同時有不同 python 版本的專案需要開發或維護時，\n就會需要使用 pyenv 來協助切換 python 的版本。\npython 的新版本通常都會有一些語法上的更新或是新增一些功能，\n例如 python 的 async / await 就是 python 3.5 以上才出現的功能，\n用 python 3.5 以下的版本來開發的專案就無法使用；\n又或者例如同時有 python 2 和 python 3 的專案，\n而且因為 python 2 和 python 3 語法不相容，\n勢必要在系統裡安裝 python 2 和 python 3；\n諸如次類的情況就可以使用 pyenv 方便的切換 python 的版本。\n安裝和初始化 # 安裝\nbrew install pyenv 安裝完畢後執行初始化\npyenv init 之後按照指示將顯示的 code 貼到 ~/.zshrc 或 ~/.bash_profile\n常用指令 # 列出可以安裝的 python 版本 pyenv install --list 會出現\nAvailable versions: 2.1.3 2.2.3 2.3.7 2.4.0 ... 3.9.6 3.9.7 3.10.0 3.10-dev 3.11.0a1 ... 安裝指定版本的 python pyenv install 3.10.0 觀察已經安裝過哪些版本的 python pyenv versions 會出現\n* system (set by ......./.pyenv/version) 3.10.0 代表目前有系統預設的版本跟剛剛安裝過的 3.10.0，\n但目前使用 python 的是系統預設版本\n切換系統使用版本 pyenv global 3.10.0 到系統任何地方下 pyenv versions 都會看到目前使用的 python 版本是 3.10.0\n只切換當前目錄下的 python 版本 pyenv local 3.7.12 在當前目錄下 pyenv versions 會看到正在使用的是 3.7.12 的版本；\n但是到其他目錄下，\n假如之前有用 pyenv global 設定過版本 (假設是 3.10.0)，\n則 pyenv versions 會看到的是之前用 pyenv global 設定的版本 (3.10.0)；\n假如沒有跑過 pyenv global 設定版本則 pyenv versions 會出現系統預設的版本。\n解除安裝指定的 python 版本 pyenv uninstall 3.7.12 Reference # pyenv Github\n","date":"November 1, 2021","externalUrl":null,"permalink":"/zh-tw/posts/pyenv-notes.html","section":"文章","summary":"pyenv 的功能和使用的原因 # pyenv 是用來在系統裡安裝各種不同版本的 python，\n並能夠方便的切換 python 版本的工具。\n","title":"Pyenv Notes","type":"posts"},{"content":"","date":"October 27, 2021","externalUrl":null,"permalink":"/zh-tw/tags/concurrent-processing.html","section":"Tags","summary":"","title":"Concurrent Processing","type":"tags"},{"content":"在出現 asyncio 前，\n當一隻 Python 程式有很多需要並行執行的 task，\n想要提升程式效能，\n只能選用 multiprocessing 或 threading；\nPython 3.4 之後又多出了 asyncio 的選擇。\nasyncio 可以用來撰寫 coroutines，\n並使用 event loop 並行執行 coroutines，\n減少程式不必要的等待時間以提升效能。\nCoroutines # Coroutine 定義 # 在 Python 官方文件 定義裡，\nPython coroutines 是\nCoroutines are a more generalized form of subroutines. Subroutines are entered at one point and exited at another point. Coroutines can be entered, exited, and resumed at many different points. They can be implemented with the async def statement. See also PEP 492.\n意指 Python 的 coroutine 和 subroutines 相當類似，\n不同的地方在於 subroutine 是開始之後直接一次執行到底，\n執行完後結束；\n而 coroutine 則可以執行到某處暫停，\n之後再繼續恢復執行。\n用 async, await 和 asyncio.run 定義並執行 coroutine # async 可以用來定義一個 coroutine，\n只要在定義 function 的 def 前加上 async 就可以用 async def 定義一個 coroutine。\nawait 用來定義一個 coroutine 的暫停處，\n執行到 await 時，\ncoroutine 就可以暫停，\n之後再恢復執行。\nawait 後面只能接 awaitable object，\nawaitable object 包含 coroutine 或 event loop 的 task 等。\nimport asyncio async def ten_sec_sleep(): await asyncio.sleep(10) print(\u0026#39;10 sec sleep finish\u0026#39;) if __name__ == \u0026#39;__main__\u0026#39;: asyncio.run(ten_sec_sleep()) Event Loop # What is event loop # 在 Python 官方文件 裡，\n介紹的 Event loop\nThe event loop is the core of every asyncio application. Event loops run asynchronous tasks and callbacks, perform network IO operations, and run subprocesses.\n簡單來說就是用來跑異步執行的 task 的。\nEvent loop 每次只會執行一個 task，\n使用 event loop 跑 coroutines 時，\n當 task 執行到 programmer 定義的暫停處，\nevent loop 會將該 task 暫停並排程，\n接著切換執行其他的工作（可能是其他的 task 或 callback 等），\n這也使得 event loop 和 coroutine 的組合特別適合用來處理 IO bound task；\n將 coroutine I/O 運作的部分定為暫停處，\n並使用 event loop 來跑這些 coroutines 時就能夠將等待 I/O 的時間切換做其他的工作。\n以 Event loop 執行 coroutines # 執行單一一個 coroutine：\nimport asyncio async def ten_sec_sleep(count): await asyncio.sleep(10) print(f\u0026#39;10 sec sleep finish, count: {count}\u0026#39;) if __name__ == \u0026#39;__main__\u0026#39;: loop = asyncio.get_event_loop() task = loop.create_task(ten_sec_sleep(0)) loop.run_until_complete(task) 執行時間用 time command 看是 10.09 秒\n10 sec sleep finish, count: 0 10.09 real 0.06 user 0.01 sys 多個 coroutine 並行執行：\nimport asyncio async def ten_sec_sleep(count): await asyncio.sleep(10) print(f\u0026#39;10 sec sleep finish, count: {count}\u0026#39;) if __name__ == \u0026#39;__main__\u0026#39;: loop = asyncio.get_event_loop() tasks = [] for i in range(10): tasks.append(loop.create_task(ten_sec_sleep(i))) loop.run_until_complete(asyncio.wait(tasks)) 每當執行到 sleep(10) 的時候 event loop 就可以切換到其他 coroutine 去執行，\n並行執行 coroutine 的時間用 time command 看是 10.09 秒\n10 sec sleep finish, count: 0 10 sec sleep finish, count: 1 10 sec sleep finish, count: 2 10 sec sleep finish, count: 3 10 sec sleep finish, count: 4 10 sec sleep finish, count: 5 10 sec sleep finish, count: 6 10 sec sleep finish, count: 7 10 sec sleep finish, count: 8 10 sec sleep finish, count: 9 10.09 real 0.07 user 0.01 sys 效能測量 # 一支連續發 10 個 requests 到 google 的程式， 在不使用 coroutine 的情況下：\nimport requests def issue_req(count): resp = requests.get(\u0026#39;http://www.google.com.tw\u0026#39;) print(f\u0026#39;count: {count}, resp status: {resp.status_code}\u0026#39;) if __name__ == \u0026#39;__main__\u0026#39;: for i in range(10): issue_req(i) 用 time command 看需要的時間是 0.83 秒\ncount: 0, resp status: 200 count: 1, resp status: 200 count: 2, resp status: 200 count: 3, resp status: 200 count: 4, resp status: 200 count: 5, resp status: 200 count: 6, resp status: 200 count: 7, resp status: 200 count: 8, resp status: 200 count: 9, resp status: 200 0.83 real 0.16 user 0.05 sys 使用 coroutine 來並行發出 requests：\nimport requests import asyncio async def issue_req(count): loop = asyncio.get_event_loop() resp = await loop.run_in_executor( None, requests.get, \u0026#39;http://www.google.com.tw\u0026#39; ) print(f\u0026#39;count: {count}, resp status: {resp.status_code}\u0026#39;) if __name__ == \u0026#39;__main__\u0026#39;: loop = asyncio.get_event_loop() tasks = [] for i in range(10): tasks.append(loop.create_task(issue_req(i))) loop.run_until_complete(asyncio.wait(tasks)) 用 time command 看需要的時間是 0.31 秒\ncount: 0, resp status: 200 count: 2, resp status: 200 count: 3, resp status: 200 count: 7, resp status: 200 count: 5, resp status: 200 count: 1, resp status: 200 count: 9, resp status: 200 count: 6, resp status: 200 count: 8, resp status: 200 count: 4, resp status: 200 0.31 real 0.18 user 0.05 sys 以這 10 個 requests 來看，\n從 0.83 秒到 0.31 秒，\n效能提升了 (0.83 - 0.31)/0.83 * 100% = 62.65% ，\n相當顯著；\n對有相當多 I/O bound task 的程式來說，\n使用 coroutine 是個不錯的選擇。\n","date":"October 27, 2021","externalUrl":null,"permalink":"/zh-tw/posts/python-coroutine-asyncio.html","section":"文章","summary":"在出現 asyncio 前，\n當一隻 Python 程式有很多需要並行執行的 task，\n想要提升程式效能，\n只能選用 multiprocessing 或 threading；\nPython 3.4 之後又多出了 asyncio 的選擇。\nasyncio 可以用來撰寫 coroutines，\n並使用 event loop 並行執行 coroutines，\n減少程式不必要的等待時間以提升效能。\n","title":"Python Coroutine Asyncio","type":"posts"},{"content":"","date":"September 26, 2021","externalUrl":null,"permalink":"/zh-tw/tags/pipenv.html","section":"Tags","summary":"","title":"Pipenv","type":"tags"},{"content":"Why Pipenv # 當有很多 Python project 要維護，\n不同的 project 有可能使用相同的 python libraries 的不同版本，\n不使用 virtual environment 而將所有的 python modules 都裝在自己的機器上就會造成版本衝突。\n過去使用 virtualenv + requirement.txt 的機制可以在不同的 project 使用同一個套件的不同版本，\n也能夠讓新加入的開發者或 production 環境可以快速安裝 project 需要的套件，\n但當套件需要更新時相當麻煩，\n需要手動再去倒出一份新的 requirement.txt，\n而且當 project 有不同環境的需求的時候(例如 development 環境和 production 環境)還要維護requirement-prod.txt 和 requirement-dev.txt 兩份套件設定，\n如果不搭配 pyenv 也無法切換不同的 python 版本。\n後來發現了 Python 官方推薦的 pipenv 解決了這些問題，\n可以方便的只用 command 做到：\n建立獨立的 python 版本和套件虛擬環境 安裝並記錄套件版本到自動生成的 Pipfile 和 Pipfile.lock，同時透過套件的 Hash 值檢查套件安全性 紀錄套件使用環境 (分開 development 和 production 環境) 讀取 .env 檔案設定虛擬環境的環境變數 自動切換系統中的 python 版本（或 pyenv 有安裝的 python 版本） 操作 Pipenv # Install Pipenv # pip3 install pipenv Pipenv Commands # 建立特定 python 版本的獨立虛擬環境:\n到 project 所在的目錄下\npipenv --python 3.8 注意該版本的 python 系統要有，不然就要用 pyenv 裝一下 安裝套件\npipenv install flask 安裝 development 套件\n加上 --dev 的套件在自動生成的 Pipfile 中會被放到 [dev-packages] 底下\npipenv install pytest --dev 移除套件\npipenv uninstall flask 在建立的虛擬環境中執行 script\npipenv run python server.py 也可以執行其他指令如 pytest\npipenv run pytest 進入虛擬環境\npipenv shell 要離開虛擬環境時在輸入 exit 即可\n用取得的 Pipfile 和 Pipfile.lock 建立虛擬環境\npipenv install 如果要連 development 環境的套件一起安裝\npipenv install --dev 從 requirement.txt 建立 Pipfile 和 Pipfile.lock\npipenv install 輸出 requirement.txt\n實際上不需要做這件事，\n但在某些特殊情況下（例如特殊平台的需求）還是可以這樣做\npipenv lock --requirements \u0026gt; requirements.txt 升級虛擬環境中的套件\npipenv update 刪除目前的虛擬環境\npipenv --rm Upgrade Pipenv # pip3 install --upgrade pipenv","date":"September 26, 2021","externalUrl":null,"permalink":"/zh-tw/posts/pipenv-notes.html","section":"文章","summary":"Why Pipenv # 當有很多 Python project 要維護，\n不同的 project 有可能使用相同的 python libraries 的不同版本，\n不使用 virtual environment 而將所有的 python modules 都裝在自己的機器上就會造成版本衝突。\n過去使用 virtualenv + requirement.txt 的機制可以在不同的 project 使用同一個套件的不同版本，\n也能夠讓新加入的開發者或 production 環境可以快速安裝 project 需要的套件，\n","title":"Pipenv Notes","type":"posts"},{"content":"","date":"June 28, 2021","externalUrl":null,"permalink":"/zh-tw/tags/cookie.html","section":"Tags","summary":"","title":"Cookie","type":"tags"},{"content":"","date":"June 28, 2021","externalUrl":null,"permalink":"/zh-tw/tags/session.html","section":"Tags","summary":"","title":"Session","type":"tags"},{"content":" Stateless HTTP # HTTP 是一種 stateless 的 protocol，\n也就是說每一次的 request / response 都是獨立的，\n和之前或之後的 request / response 無關。\n相同的 request 就會回應相同的 response，\n不會因為之前的 request / response 內容而有不同。\n這樣一來 server 因為不需要儲存使用者資訊可以省去大量的資料庫、伺服器儲存空間，\n也因為不需要讓 client 每次都必須連線到相同的 socket 而能夠加快 response time 和省去不少 network bandwidth，\n但在網站需要做連續動作（例如需要確認使用者身份認證時）就會需要一些機制來協助，\n這時候大部分的網站就會利用 session 或 cookie。\nSession # Session 是一段具有狀態 (stateful) 的時間。\nHTTP request / response 是 stateless 的，\n但如果透過 stateless 的 request / response 夾帶 state 資訊的話，\nclient 和 server 就可以透過 request / response 夾帶的 state 資訊製造出 stateful 的運作。\n例如說當某個動作必須要使用者登入並且選定了選項 A 後才能夠操作，\n就希望能有一段具有狀態的時期 (session) 是“使用者已登入且選定了 A 選項”的狀態，\n做出這個狀態的方式有許多種，\n例如在這個期間的 request 透過攜帶加密過後的使用者 ID 和選項 A 來告訴 server 現在使用者已經登入、使用者的身份和選定的選項等。\n做出 session 的做法可以有很多種，\n最常見的是 cookie，\n但 cookie 只是方法之一，\n並不是說 session 一定只能透過 cookie 來實作，\n透過別的方式也能做出 session，\n例如利用 query string 來記錄前幾次的互動等。\nCookie # Cookie 是實作出 session 的一種機制，\nserver 可以用 Set-Cookie 這個 Header 請 browser 設定 cookie 並指定 cookie 的內容，\n之後 browser 在發送 request 到相同的 domain 和 path 時會將 cookie 帶入一併發送，\n這樣一來在需要記憶某些 state 時，\nserver 只要讓 browser 去設定需要的 cookie，\n之後當發送 request 時 server 看到 cookie 的內容就能知道現在的 state 了。\n由於 cookie 的內容可以在使用者端被自行修改，\n所以為了安全性考量，\n在使用 cookie 的時候有比較常見的兩種做法(也能兩者一起使用)：\nCookie-based session # 將 cookie 內容加密，\n傳到 server 後由 server 解密才知道 cookie 存的內容。\n接續上面的例子就是將使用者 ID 和選項 A 加密後放進 cookie。\n使用上要注意：\n因為 Cookie 的大小有限制，所以加密後 cookie 的大小要特別注意不能太大 加密的 key 必須妥善保存 Session ID # 使用一個 ID (Session Identifier, Session ID) 來記錄使用者身份，\n其餘的資料 (Session Data) 都儲存在 server。\n接續上面的例子就是將使用者選定的選項 A 放在 server，\ncookie 裡放置使用者的 ID。\n使用上要注意：\nSession ID 要設計的不好猜，一旦被猜中使用者的身份就會被偷走 如果網站不夠安全，一旦 Session ID 在某個頁面被其他惡意網站或駭客盜走，使用者的身份就會被偷走 ","date":"June 28, 2021","externalUrl":null,"permalink":"/zh-tw/posts/stateless-http-stateful-session-and-cookies.html","section":"文章","summary":"Stateless HTTP # HTTP 是一種 stateless 的 protocol，\n也就是說每一次的 request / response 都是獨立的，\n和之前或之後的 request / response 無關。\n相同的 request 就會回應相同的 response，\n不會因為之前的 request / response 內容而有不同。\n","title":"Stateless HTTP, Stateful Session and Cookies","type":"posts"},{"content":"","date":"June 28, 2021","externalUrl":null,"permalink":"/zh-tw/categories/web.html","section":"Categories","summary":"","title":"Web","type":"categories"},{"content":"","date":"June 28, 2021","externalUrl":null,"permalink":"/zh-tw/tags/web.html","section":"Tags","summary":"","title":"Web","type":"tags"},{"content":"","date":"April 11, 2021","externalUrl":null,"permalink":"/zh-tw/tags/ubuntu.html","section":"Tags","summary":"","title":"Ubuntu","type":"tags"},{"content":"","date":"April 11, 2021","externalUrl":null,"permalink":"/zh-tw/tags/windows-terminal.html","section":"Tags","summary":"","title":"Windows Terminal","type":"tags"},{"content":"","date":"April 11, 2021","externalUrl":null,"permalink":"/zh-tw/tags/wsl.html","section":"Tags","summary":"","title":"WSL","type":"tags"},{"content":" 把在 Linux 和 Mac 上 terminal 的設定也搬到 Windows 上，\n方便操作。\nWindows Terminal 功能 # 用 Windows terminal 可以\n啟用多個分頁 (在多個 Linux CLI、Windows CLI、PowerShell等之間快速切換) 自訂按鍵 (開啟或關閉分頁、複製+貼上等快速鍵) 使用搜尋功能 自訂佈景主題 這些功能比原生 WSL 能支援的多的多，\n也可以設定的和我在 Linux 或 Mac 的開發環境比較相似，\n於是就決定選用 windows terminal 了。\nWindows terminal 設定 # 到 Microsoft store 搜尋 Windows terminal 並安裝完成後，\n就可以開始設定 Windows terminal。\n將 WSL 設定為 Windows terminal 預設開啟環境 # 在 windows terminal 的 [V] 箭頭選單選擇\u0026quot;設定(settings)\u0026quot;，\n會出現一個 JSON 檔可以修改，\n從 profiles 的 list 找到想要做為預設的 Linux distribution，\n例如:\n{ \u0026#34;guid\u0026#34;: \u0026#34;{xxxxxxxxxxxxxxx}\u0026#34;, \u0026#34;hidden\u0026#34;: false, \u0026#34;name\u0026#34;: \u0026#34;Ubuntu-18.04\u0026#34;, \u0026#34;commandline\u0026#34;: \u0026#34;wsl.exe\u0026#34;, \u0026#34;source\u0026#34;: \u0026#34;Windows.Terminal.Wsl\u0026#34; } 把 guid 後面那串被大括號括起來的 ID 複製起來，\n用那個 ID 取代原本預設開啟的 profile 的 ID:\n\u0026#34;defaultProfile\u0026#34;: \u0026#34;{yyyyyy}\u0026#34; (把 yyyyyy 的地方換成 Linux distribution 的 GUID)\n設定 Windows terminal 預設開啟目錄 # 把 JSON 設定檔的 Linux distribution profile 中的\n\u0026#34;commandline\u0026#34;: \u0026#34;wsl.exe\u0026#34;, 後面補上預設要開啟的目錄 (~ 即為使用者 linux 的 home direcotry)\n\u0026#34;commandline\u0026#34;: \u0026#34;wsl.exe ~\u0026#34;, 設定 Windows terminal Scheme # 在 JSON 設定檔的 Linux distribution profile 裡加上這行:\n\u0026#34;colorScheme\u0026#34;: \u0026#34;One Half Dark\u0026#34;, One Half Dark 是 Windows 提供的其中一種色彩配置，\n還有其他種可以在 Microsoft Doc: Windows 終端機中的色彩配置 中找尋。\n設定 Windows terminal 字型 # 在 JSON 設定檔的 Linux distribution profile 裡加上\n\u0026#34;fontFace\u0026#34;: \u0026#34;xxxxx\u0026#34;, xxxxx 的地方是字型的名稱。\n如果有需要使用 Powerline 的需求的話可以先安裝好 Powerline fonts，\n再把想要的字型名稱填進去。\nReference # Microsoft Doc: 安裝和設定 Windows 終端機\n設定 Windows Terminal 作為 WSL 操作介面\nMicrosoft Doc: Windows 終端機中的色彩配置\n","date":"April 11, 2021","externalUrl":null,"permalink":"/zh-tw/posts/wsl-2-on-windows-part-2.html","section":"文章","summary":" 把在 Linux 和 Mac 上 terminal 的設定也搬到 Windows 上，\n方便操作。\nWindows Terminal 功能 # 用 Windows terminal 可以\n啟用多個分頁 (在多個 Linux CLI、Windows CLI、PowerShell等之間快速切換) 自訂按鍵 (開啟或關閉分頁、複製+貼上等快速鍵) 使用搜尋功能 自訂佈景主題 這些功能比原生 WSL 能支援的多的多，\n也可以設定的和我在 Linux 或 Mac 的開發環境比較相似，\n於是就決定選用 windows terminal 了。\n","title":"WSL 2 on Windows Part 2 - Terminal 介面設定","type":"posts"},{"content":"工作的時候習慣用 Linux 或 mac 的 terminal，\n找了個時間在家裡的 PC 上把 WSL 的環境也設定一下方便切換工作環境。 WSL 2 和 WSL 1 的差異 # WSL 2 是基於 Hyper-V 在 virtual machine 中跑完整的 Linux kernal，\nWSL 1 則是在 Windows 系統上對 Linux 功能的模擬，\n因此 WSL 2 比 WSL 1 支援更多 Linux 原生的功能和 system call。\n如果需要用到 Linux 底層的應用，\nWSL 2 支援的能力比 WSL 1 更好。\n一般情況下 WSL 2 啟動 process 的效能也更好，\n但需要讀取 host 系統的檔案時除外。\n但因為 WSL 2 是在 VM 上跑 Linux kernal，\n因此和做為 host 的 Windows 的整合相對較 WSL 1 差。\n在 WSL 2 裡的 process 無法在 Windwos 的工作管理員控管，\nWindows 和 WSL 2 的網路連線方式也多了一層。\n因為 WSL 2 用了 Hyper-V 的關係，\n所以有聽過跟 VMWare 不相容的災情。\n我沒有用 VMWare 所以不知道是不是真的有這個 issue，\n但用 Docker 的時候沒有產生問題。\nMicrosoft Doc 有列舉出 WSL 1 和 WSL 2 詳細的比較。\nRequirement # Windwos 版本要是 Windows 10，低於這些版本的話請善用 Windows 更新:\n若為 X64 系統：版本 1903 或更高版本，含 組建 18362 或更高組建。 若為 ARM64 系統：版本 2004 或更高版本，含 組建 19041 或更高組建。 機器要開啟虛擬化功能，\n通常在主機板的 BIOS 設定裡可以找到，\n找找跟 CPU 設定相關的地方應該會有一些 Intel Virtualization 有關的設定，\n把它開起來。\n安裝啟用 WSL 2 # 用 admin 權限開啟 PowerShell，\n啟用Windows 子系統 Linux 版: 執行 dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart 啟用虛擬機器平台選用功能: 執行 dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart 重新啟動電腦 下載安裝 WSL 2 Linux 核心更新套件 將 WSL 2 設定為預設版本 wsl --set-default-version 2 去 Microsoft Store 找想安裝的 Linux distribution，\n並安裝設定帳號密碼 可以在 PowerShell 中檢查安裝的 Linux distribution 的 WSL 版本: wsl -l -v 也可以更改 Linux distribution 的 WSL 版本:\nwsl --set-version \u0026lt;distribution name\u0026gt; \u0026lt;versionNumber\u0026gt; Reference # Microsoft WSL 2 安裝指南\nMicrosoft DOC: 比較 WSL 1 和 WSL 2\n","date":"April 10, 2021","externalUrl":null,"permalink":"/zh-tw/posts/wsl-2-on-windows-part-1.html","section":"文章","summary":"工作的時候習慣用 Linux 或 mac 的 terminal，\n找了個時間在家裡的 PC 上把 WSL 的環境也設定一下方便切換工作環境。 WSL 2 和 WSL 1 的差異 # WSL 2 是基於 Hyper-V 在 virtual machine 中跑完整的 Linux kernal，\nWSL 1 則是在 Windows 系統上對 Linux 功能的模擬，\n因此 WSL 2 比 WSL 1 支援更多 Linux 原生的功能和 system call。\n","title":"WSL 2 on Windows Part 1 - 安裝啟用","type":"posts"},{"content":" Shallow Copy # 複製越少越好，\nShallow copy 出來的新的 structure 擁有跟舊的 structure 相同的結構，\n並一起共享 elements 的記憶體位置。\n舉個 Java 的例子，\nint[] arr1 = {1, 2, 3}; int[] arr2 = arr1; arr2 就是一個 arr1 的 shallow copy。\n一旦其中一個 structure 更動了 element 另外一個也會受到影響。 Deep Copy # 全部複製，\nDeep copy 出來的 structure 不但有跟舊的 structure 相同的結構，\n還把舊的 structure 的 elements 全部複製了一份到新的記憶體空間。\nint[] arr1 = {1, 2, 3}; int[] arr2 = new int[arr1.length]; for (int i = 0; i \u0026lt; arr1.length; ++i) { arr2[i] = arr1[i]; } arr2 是 arr1 的 deep copy。\n占用的記憶體空間比較多。 ","date":"January 21, 2020","externalUrl":null,"permalink":"/zh-tw/posts/deep-copy-shallow-copy.html","section":"文章","summary":"Shallow Copy # 複製越少越好，\nShallow copy 出來的新的 structure 擁有跟舊的 structure 相同的結構，\n並一起共享 elements 的記憶體位置。\n","title":"Deep Copy and Shallow Copy","type":"posts"},{"content":"","date":"January 21, 2020","externalUrl":null,"permalink":"/zh-tw/categories/programming.html","section":"Categories","summary":"","title":"Programming","type":"categories"},{"content":"","date":"January 12, 2020","externalUrl":null,"permalink":"/zh-tw/categories/c++.html","section":"Categories","summary":"","title":"C++","type":"categories"},{"content":"","date":"January 12, 2020","externalUrl":null,"permalink":"/zh-tw/tags/c++.html","section":"Tags","summary":"","title":"C++","type":"tags"},{"content":" Array # 固定大小的連續記憶體空間所構成\n優缺點 # 不能像其他 C++ container 那樣動態改變儲存空間的大小 random access 很有效率 (O(1)) Vector # 可以動態改變儲存空間大小的 array\n底層實作方式 # 動態的 allocate array，\n當目前的 capacity 不夠大的時候就重新 reallocate 一個新的 array 然後把舊的 element 搬過去 實際上的 capacity 會比目前塞進 vector 裡面的 element 數量大 優缺點 # random access 很有效率 (O(1)) 在尾端加入或刪除 element 相對有效率 在不是尾端的地方加入或刪除 element 比較慢 Deque # Double-ended queue，\n一樣可以動態的改變 container 大小。\n不同的 library 實作 deque 的方式可能會不一樣。\n優缺點 # 可以當作是在 container 的頭和尾做 insert 和 delete 都一樣很有效率的 vector，\n但不保證 elements 都被存在一塊連續記憶體空間 Reallocate 的時候比 vector 有效率 在不是頭或尾端的地方加入或刪除 element 比較慢 random access 很有效率 (O(1)) List # 底層實作方式 # Doubly-linked list\n優缺點 # 在任何一個地方 insert, move 或 erase element 都很快 (O(1)) sorting 的時候用起來很方便 往前或往後 iterate 也很快 random access 慢 (O(n)) 占用額外的 memory space 來存 doubly-linked 的資訊 Forward_list # 底層實作方式 # Singly-linked list\n優缺點 # 在任何一個地方 insert, move 或 erase element 都很快 (O(1)) sorting 的時候用起來很方便 只能往後 iterate random access 慢 (O(n)) 占用額外的 memory space 來存 singly-linked 的資訊 儲存空間相對 list 來說較少 只能往一個方向 iterate Reference # http://www.cplusplus.com/reference/array/array/\nhttp://www.cplusplus.com/reference/vector/vector/\nhttp://www.cplusplus.com/reference/deque/deque/\nhttp://www.cplusplus.com/reference/list/list/\nhttp://www.cplusplus.com/reference/forward_list/forward_list/\n","date":"January 12, 2020","externalUrl":null,"permalink":"/zh-tw/posts/c-stl-container-compare-array-vector-dequeue-list-forward_list.html","section":"文章","summary":"Array # 固定大小的連續記憶體空間所構成\n優缺點 # 不能像其他 C++ container 那樣動態改變儲存空間的大小 random access 很有效率 (O(1)) Vector # 可以動態改變儲存空間大小的 array\n","title":"C++ STL Containers 比較 - array, vector, deque, list, forward_list","type":"posts"},{"content":"","date":"January 12, 2020","externalUrl":null,"permalink":"/zh-tw/tags/container.html","section":"Tags","summary":"","title":"Container","type":"tags"},{"content":"","date":"January 12, 2020","externalUrl":null,"permalink":"/zh-tw/tags/stl.html","section":"Tags","summary":"","title":"STL","type":"tags"},{"content":"","date":"January 9, 2020","externalUrl":null,"permalink":"/zh-tw/categories/docker.html","section":"Categories","summary":"","title":"Docker","type":"categories"},{"content":"接上一篇 Docker 操作紀錄 (一)\nDocker 基本使用 # 刪除 Container # 記得先用 stop 停止 container 才能刪。\ndocker rm CONTAINER_NAME 或\ndocker rm CONTAINER_ID 刪完之後可以用\ndocker ps -a 確認一下是不是 container 就消失了。\n用之前 Export 過的 Container 建立 image # 之前 export 過一個 container 出來叫 c_test.tar，\n可以用它來建立一個新的 image:\ncat c_test.tar | docker import - ubuntu_test_repo:1.0 後面那個 ubuntu_test_repo 是 repository 的名字，\n1.0 是 tag，\n可以用\ndocker images 列出來看一下。\n有了 image 就可以建立新的 container 了。\n刪除 Image # 如果我用\ndocker images 列出的 image 有這些:\nREPOSITORY TAG IMAGE ID CREATED SIZE aaa 2.0 b30c39fffb75 4 seconds ago 64.2MB aaa 1.0 6b8046192d83 8 seconds ago 64.2MB ubuntu_test_repo 1.0 864c36a752c3 5 hours ago 64.2MB ubuntu latest 549b9b86cb8d 2 weeks ago 64.2MB hello-world latest fce289e99eb9 12 months ago 1.84kB 要刪除 repository 名稱為 aaa，\ntag 為 1.0 的 image:\ndocker rmi aaa:1.0 就可以了。\n所有用這個 image 的 container 要先被 rm 掉。\nDockerfile # 是一個檔案，\n可以讓使用者用更簡單的方式來建立 image。\n分成四個部分:\nImage Maintainer (誰要對這個 dockerfile 負責) 操作 command Container 啟動時的 command 舉一個 nginx 的例子:\n# 這是 dockerfile 的註解方式 # Image FROM ubuntu # Maintainer MAINTAINER user user@example.com # 操作 command RUN apt-get update \\ \u0026amp;\u0026amp; apt-get upgrade -y \\ \u0026amp;\u0026amp; apt-get install -y nginx # Container 啟動時的 command CMD [\u0026#34;nginx\u0026#34;, \u0026#34;-g\u0026#34;, \u0026#34;daemon off;\u0026#34;] 建立 Image # 可以用 docker build 來建立 image。\n把剛剛那個 nginx 的 Dockerfile 放在 /tmp/d_file 下，\n名稱叫 test_d_file 要 build 成 image，\n並給那個 image 一個 test-nginx-img/1.0 的 tag:\ndocker build -t test-nginx-img/1.0 -f /tmp/d_file/test_d_file . build 完之後用 docker images 看一下:\nREPOSITORY TAG IMAGE ID CREATED SIZE test-nginx-img/1.0 latest 7293588d00a9 27 seconds ago 152MB Reference # Docker docs\n","date":"January 9, 2020","externalUrl":null,"permalink":"/zh-tw/posts/docker-operating-2.html","section":"文章","summary":"接上一篇 Docker 操作紀錄 (一)\nDocker 基本使用 # 刪除 Container # 記得先用 stop 停止 container 才能刪。\n","title":"Docker 操作紀錄(二)","type":"posts"},{"content":"","date":"January 9, 2020","externalUrl":null,"permalink":"/zh-tw/tags/virtual-environment.html","section":"Tags","summary":"","title":"Virtual Environment","type":"tags"},{"content":"","date":"January 9, 2020","externalUrl":null,"permalink":"/zh-tw/tags/cluster.html","section":"Tags","summary":"","title":"Cluster","type":"tags"},{"content":"設計比較大流量的系統時早晚要遇上 cluster 的問題。\nCluster # 一台以上的機器(node)組成的集合，\n有三種不同的目的:\nLoad Balancing # 讓多台機器一起盡可能的平均分擔任務，\n加速應用程式執行。\nHigh Availability (HA) # 為了高可用性和備援，\n如果其中一台機器突然掛了其他的機器可以接替。\nHigh Performance Computing # 高效能/平行運算系統，\n簡稱 HPC cluster，\n結合多台機器的硬體來增加運算能力，\n用來解決單一一台機器不能解決的任務。\nHA 運作模式 # 有很多種例如 N+1, N+M, \u0026hellip;\n但最常見的是 two-node cluster，\ntwo-node cluster 有兩種運作方式:\nActive-Passive Active-Acitve Active-Passive (AP) # Master-slave 的設計，\n正常狀況下只有 master (Active) 在做 service，\n當 master (Active) 出現問題時 slave (Passive) 才接手，\n等到 master (Active) 恢復正常狀態再換回來由 master (Active) 繼續處理 service\n優點:\nFail-over 的速度快 設計跟設定都相對簡單 缺點:\n沒辦法同時做 load balance 會浪費一些硬體 Active-Active (AA) # 兩台機器都同時有自己獨立執行的 service (都同時是 Active)，\n同時也互相備援 (當對方的 Passive)，\n當其中一台出現問題時另一台接手他的 service。\n優點:\n正常運作的時候兩台機器都沒有閒置，\n運作效益高 缺點:\nFail-over 之後機器的負擔變大，\n速度變慢 設計設定相對複雜 Application Design # 需要有相對簡單的方法來 start, stop, force-stop service 和檢查 service 目前的狀態。\n=\u0026gt; 設計 application 的時候要有 command line interface 或 script 能夠做到這點\n=\u0026gt; 兩台機器上的 service 要互相可以知道對方狀態跟發生意外的時候要能啟動 or 停止 需要有 shared storage，\n而且 Application 要能將自己的狀態盡量仔細的紀錄到 shared storage。\n=\u0026gt; 兩台機器切換的時候才不會少東西 要能 restart 另一個 node 並恢復到 failure 發生前的狀態\n=\u0026gt; 恢復 failure 前的狀態可以用存到 shared storage 的狀態來做 當 application crash 的時候不能毀損存到 shared storage 上的資料\n=\u0026gt; 另外一邊要用 Remark # Application upgrade 時會發生的狀況要考慮進去 有些 SQL 或 noSQL 本身有支援這類的設定可以採用可以減少不少麻煩 ","date":"January 9, 2020","externalUrl":null,"permalink":"/zh-tw/posts/ha-cluster-app-architecture.html","section":"文章","summary":"設計比較大流量的系統時早晚要遇上 cluster 的問題。\nCluster # 一台以上的機器(node)組成的集合，\n有三種不同的目的:\n","title":"HA cluster 筆記和 Application 設計","type":"posts"},{"content":"","date":"January 9, 2020","externalUrl":null,"permalink":"/zh-tw/tags/high-availability.html","section":"Tags","summary":"","title":"High Availability","type":"tags"},{"content":"","date":"January 9, 2020","externalUrl":null,"permalink":"/zh-tw/categories/web-hosting.html","section":"Categories","summary":"","title":"Web Hosting","type":"categories"},{"content":"","date":"January 8, 2020","externalUrl":null,"permalink":"/zh-tw/tags/dns.html","section":"Tags","summary":"","title":"DNS","type":"tags"},{"content":"DNS server 中每個 DNS zone 都有一個 zone file，\nDNS zone 通常會是一個 single domain (有時候不是)，\nzone file 是由很多個 dns resource record (RR) 組成，\nRR 有很多種不同的類型，\n紀錄一下常用幾種。\nA record # 將 hostname 對應到 IPv4。 (32-bit)\nhostname IN A xxx.xxx.xxx.xxx AAAA record # 將 hostname 對應到 IPv6。 (128-bit)\nhostname IN AAAA xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx CNAME record # 為 hostname 設立的別名。\nalias IN CNAME hostname 注意 alias 不能再有其他 A record 或 MX record。\nMX record # Mail exchanger record。\nEmail server 的 domain name, priority 和 hostname。\n注意是 hostname 而不是直接是 IP，\n也不能是 cname 的 alias，\n所以 IP 還要另外多設一個 RR。(A 或 AAAA record)\nPriority 數字越低代表優先權越高 (先傳送到優先權高的)。\nmail_domain_name IN MX priority hostname 例子，\nexample.com. IN MX 10 mailserver.example.com","date":"January 8, 2020","externalUrl":null,"permalink":"/zh-tw/posts/common-dns-resource-record.html","section":"文章","summary":"DNS server 中每個 DNS zone 都有一個 zone file，\nDNS zone 通常會是一個 single domain (有時候不是)，\nzone file 是由很多個 dns resource record (RR) 組成，\nRR 有很多種不同的類型，\n紀錄一下常用幾種。\nA record # 將 hostname 對應到 IPv4。 (32-bit)\nhostname IN A xxx.xxx.xxx.xxx","title":"常用 DNS Resource Record 紀錄","type":"posts"},{"content":"","date":"January 6, 2020","externalUrl":null,"permalink":"/zh-tw/categories/database.html","section":"Categories","summary":"","title":"Database","type":"categories"},{"content":"","date":"January 6, 2020","externalUrl":null,"permalink":"/zh-tw/tags/database.html","section":"Tags","summary":"","title":"Database","type":"tags"},{"content":"","date":"January 6, 2020","externalUrl":null,"permalink":"/zh-tw/tags/nosql.html","section":"Tags","summary":"","title":"NoSQL","type":"tags"},{"content":"","date":"January 6, 2020","externalUrl":null,"permalink":"/zh-tw/tags/rdbms.html","section":"Tags","summary":"","title":"RDBMS","type":"tags"},{"content":"RDBMS # Relational Database Management System\n在資料之前有很強的 Relation (關聯性) 的時候使用: 設計不太會去變動的 schema 將 table 互相關聯，\n就可以去透過 SQL 取得想要的資料 資料的正確性很重要的時候使用 通常會提供ACID 要變動 schema 是一件很浩大的工程: 要把的 table 的 schema 更新還要 migrate 資料 所有用到要被更換 schema 的 table 的程式都要修改 Vertical scaling 效果比較明顯 (提升機器的性能) ACID # 通常 RDBMS 會保證交易(transaction)有四種特性:\nAtomicity\n只有 全部做完(Commit) 跟 全部沒做(Abort) 兩種可能，\n沒有做了一半這種狀態\n如果執行中有 error 的話就是 Rollback 成全部沒做的狀態\nConsistency\n交易前後 database 都會保持合法的狀態。\nIsolation\n多筆 transaction 都要執行時每筆 transaction 都是分開的不會互相干擾。\nA 和 B 做交易不影響 B 和 C 做交易。\nDurability\nTransaction 做完了就是永久有效的不會失效，\n就算系統突然壞了也一樣。\nNoSQL # Not only SQL\n比較不在意資料之間的 Relation 存取資料時不需要固定的 schema 每筆資料單獨存在沒有誰關聯誰的問題 比較在意資料的內容 是否需要更新、新增、刪除等等 資料可以有不同的格式 比較適合分散式系統 通常提供 CAP 其中兩種 Horizontal scaling 效果比較明顯 (多找幾台機器) CAP # 對一個分散式系統來說，\nCAP 三種特性不可能都能保證存在(但有可能同時存在，網路很順的時候)，\n頂多同時能保證存在兩種。\nConsistency\n每次 read 如果不是得到 error 都會讀到最近一次寫入的結果。\n=\u0026gt; 每個 node 的 data 都是一樣的\nAvailability\n每個 request 都會得到一個不是 error 的 response，\n不管這個 response 回傳的資料是不是最新的。\n=\u0026gt; 怎麼樣都保證資料會被回傳，不過資料可能是舊的\nPartition tolerance\n就算有些在 node 之間傳送的訊息被 delay 或是掉了系統也會繼續運作。\n=\u0026gt; 網路有狀況的時候，正常連線的那一部份 node 可以正常運行。\nReference # Wiki ACID\nWiki CAP\n","date":"January 6, 2020","externalUrl":null,"permalink":"/zh-tw/posts/rdbms-acid-nosql-cap.html","section":"文章","summary":"RDBMS # Relational Database Management System\n在資料之前有很強的 Relation (關聯性) 的時候使用: 設計不太會去變動的 schema 將 table 互相關聯，\n就可以去透過 SQL 取得想要的資料 資料的正確性很重要的時候使用 通常會提供ACID 要變動 schema 是一件很浩大的工程: 要把的 table 的 schema 更新還要 migrate 資料 所有用到要被更換 schema 的 table 的程式都要修改 ","title":"RDBMS 與 NoSQL 差異筆記","type":"posts"},{"content":"","date":"January 2, 2020","externalUrl":null,"permalink":"/zh-tw/tags/linux.html","section":"Tags","summary":"","title":"Linux","type":"tags"},{"content":"","date":"January 2, 2020","externalUrl":null,"permalink":"/zh-tw/categories/operating-system.html","section":"Categories","summary":"","title":"Operating System","type":"categories"},{"content":"","date":"January 2, 2020","externalUrl":null,"permalink":"/zh-tw/tags/os.html","section":"Tags","summary":"","title":"OS","type":"tags"},{"content":"為了弄清楚某個 daemon 的東西，\n決定把 daemon 是什麼弄清楚，\n網路上找到的資料實在又多又不清楚，\n乾脆直接從 The Linux Programming Interface 把相關資料找出來做筆記。\nProcess group 是一堆 related processes 組成的集合 Session 是一堆 related process groups 組成的集合 Process group 和 Session 的定義是為了方便做 job control。\nProcess Group # 一堆 related processes 共享相同的 process group identifier (PGID)。\n這群 process 裡面會有一個 process group leader 是建立這個 process group 的 process，\n這個 process group leader 的 PID 就會是這個 process group 的 PGID。\n任何一個新的 process 被建立出來的時候，\n他的 PGID 就會是他的 parent 的 PGID。\nProcess group 的 lifetime 是從 process group leader 建立這個 process group 開始算，\n一直到所有在這個 process group 的 process 都離開這個 process group。\n(有可能是 process 做完結束了，\n或是 process 變換到了別的 process group)\nSession # 一群 related process groups 共享相同的 session identifier (SID)。\n這群 process group 裡面有一個 session leader 是建立這個 session 的 process，\nsession leader 的 PID 就會是這個 session 的 SID。\n任何一個新的 process 被建立出來的時候，\n他的 SID 會是他的 parent 的 SID。\n所有在同一個 session 裡的 process 都會共享一個controlling terminal，\n一個 controlling terminal 會在 session leader 第一次開啟一個 terminal device 的時候建立，\n而一個 terminal 只能當一個 session 的 controlling terminal。\n=\u0026gt; session 和 controlling terminal 是一對一的關係\n在任一時間點會有：\nforeground process group: 在 session 中的一組 porcess group，\n只有這個 process group 裡面的 process 才能讀取從 controlling terminal 來的 input。 background process groups: 其他不是 foreground process group 的所有 process group， Terminal 運作 # 流程大概是：\n開啟一個 terminal 的時候會有一個 session leader 會是 controlling process，\n會有一組 foreground process group 等著接從 terminal 讀取的東西，\n有可能是 user input 或是 user 給的 signal，\n同時也會有一些 background process groups 存在。\n當 terminal 結束的時候 kernel 會送 SIGHUP 給 session leader 通知他這個 terminal 結束了。\nShell Job Control # Process groups 和 session 主要是用來做 shell job control，\n舉一個登入的例子：\n使用者 login 用的那個 terminal 就是 controlling terminal，\n而 login shell 就是 session leader 同時也是這個 terminal 的 controlling process，\n從這個 shell 開始的所有 command 會建立出一個以上的 processes，\n這些 processes 會變成新的 process groups，\n從這些 process 再建立的新的 process 就會是那個建立他的 process 的 process group 的一份子。\n所有這些 process 都從這個 shell 建立，\n所以都屬於這個 login session。\nDaemon # Daemon 具有的特性：\nlong-lived: 通常是在系統啟動的時候跟著啟動開始運作，\n一直到系統關機才結束。 跑在 background 而且不具有 controlling terminal: 確保 kernel 不會自動產生 job control 或 terminal 相關的 signal 去影響到 daemon。 通常 daemon 會用 \u0026rsquo;d\u0026rsquo; 結尾。\n幾個常見的 daemon:\ncron sshd httpd ","date":"January 2, 2020","externalUrl":null,"permalink":"/zh-tw/posts/linux-process-group-sessions-daemon.html","section":"文章","summary":"為了弄清楚某個 daemon 的東西，\n決定把 daemon 是什麼弄清楚，\n網路上找到的資料實在又多又不清楚，\n乾脆直接從 The Linux Programming Interface 把相關資料找出來做筆記。\n","title":"Process Groups, Sessions and Daemon Overview","type":"posts"},{"content":"Docker 基本概念 # 一言以蔽之，\n簡化版的 VM。\n因為 docker 不會把整個 operating system 都裝起來，\n所以大小比 VM 小很多速度也比 VM 快很多。\nImage # 跑在 container 上面的東西，\n裡面包了一個輕量級的 runtime environment，\n包含一些 library 跟 executable。\n可以想像成 VM 上的 .iso，\n只能被讀。\n要改的話就是產生一個新的 image。\nContainer # 真的把 image 拿來跑起來的東西，\n就是把 image 拿來真的啟動放到 memory 去執行，\n跟 VM 一樣和 host environment 是完全隔離的，\n除非經過特別設定否則對 container 做什麼跟 host environment 都沒有什麼關係，\n例如說可以在 container 上把 port 開起來也不會影響到 host，\n但有需要的話也可以設定。\nRepository # 放 Image 的地方。\n可以拿 Git 的 Repository 做比喻，\nGit 會有很多 repository，\n每個 Repository 是拿來放一個專案的 code 的集合，\nDocker 所謂的 repository 也是一樣，\nRepository 是拿來放 image 的地方，\n通常在同一個 repository 的 image 會有一樣的名字但不一樣的 tag。\n所以也會有很多不同的 repository。\nRegistry # 也是放 image 的地方。\n和 repository 不同的是，\nregistry 是一種服務，\n可以讓大家把 image 放上去或拉回自己的機器，\n用 Git 來比喻就是像 Github。\n最有名的就是 Docker Hub。\nDocker 基本使用 # Install # 在 Ubuntu 很簡單，\nsudo apt-get install docker.io Pull Image # 官方 Docker Hub，\n有很多 Image 可以抓，\n例如我想要一個乾淨無汙染的 Ubuntu 環境，\n就可以用 command 來抓一個 Ubuntu 的 image 回自己的機器 (host machine):\ndocker pull ubuntu 或者要指定某一個 tag:\ndocker pull ubuntu:14.04 Run the Image # 可以用剛剛 pull 下來的 Ubuntu echo 一個 Hello World:\ndocker run ubuntu /bin/echo \u0026#39;Hello world\u0026#39; 應該會跳出一行 Hello world。\n這裡只是測試一下 Image，\ndocker run 建立了一個暫時的 Container，\n跑完 hello world 以後 container 就結束了。\n列出 Local 有哪些 Images # docker images 應該會出現剛剛裝過的那個 Ubuntu。\nCreate 一個 Container # Container 被開出來就是可以被改變的東西了!!\n用 image 建一個 container 就好像用 .iso 裝到 VM 裡。\n用剛剛有的 ubuntu 那個 image 來建一個 container:\ndocker create -it ubuntu 也可以建立一個有名字的 Container:\ndocker create -it --name CONTAINER_NAME ubuntu i 是指 input (讓 container 的 stdin 打開)，\nt 是指 tty (有個 terminal 可以用)。\n或者如果要讓 Container 被 create 而且直接被啟動開始跑:\ndocker run -itd ubuntu 或\ndocker run -itd --name CONTAINER_NAME ubuntu d 是指 detach (讓 container 跑在 background)。\n列出有哪些 Container # docker ps -a 可以列出所有現在 host 上有的 container，\n應該會出現前面用 Ubuntu image 建立起來的 Container。\n可以看到 docker create 和 docker run 建立的 Container 的 status 會不一樣，\ndocker create 只有建立了這個 container 還沒有啟動他，\n所以 status 會是 created；\n而 docker run 已經直接讓 container 被建立也被啟動了，\n所以 status 會是 Up。\n最前面有個 container id，\n要去 run 這個 container 的時候可能會用到。\n啟動並進入 Container # 如果是用 docker create 建立的 container 要先被啟動才能進入:\n可以用 container id 來啟動 container:\ndocker start \u0026#34;CONTAINER_ID\u0026#34; 或者如果建立 Container 有指定名字，\n也可以用 container 的名字 來啟動 container:\ndocker start \u0026#34;CONTAINER_NAME\u0026#34; 當 container 的 status 是 exit 時就需要先被 start 啟動才能對 container 做後續的改動。\n可以先用\ndocker ps -a 看一下 status。\n已經被 docker run 啟動過先 run 在 background 的 container，\n或是以敬備 docker start 啟動的 container 都可以用 docker exec 進入:\ndocker exec -it \u0026#34;CONTAINER_ID\u0026#34; bash bash 是要執行的 command，\n也可以用 docker exec 執行其他的 command 如 echo 等。\n如果 container 有名字也能直接用 container 的名字進入 container\ndocker exec -it \u0026#34;CONTAINER_NAME\u0026#34; bash 應該會發現已經進入到 Container 了，\n使用者會變成 Container 的 root，\n可以開始在這個 Container 上面做設定或裝東西。\n要離開 Container 環境的時候打個\nexit 就好。\n離開以後 Container 依然會在 background 跑。\n停止 Container # 就跟把 VM 關機差不多意思，\n讓 container 變成未啟動的狀態，\n讓 container 的 status 變成 exit。\n也是要用到 container id 或名字，\ndocker stop \u0026#34;CONTAINER_ID\u0026#34; 或\ndocker stop \u0026#34;CONTAINER_NAME\u0026#34; 如果這個時候再用\ndocker ps -a 看一下，\n可以發現 status 會變成 Exited。\nExport Container # 就可以把 Container export 出來變成 .tar檔搬到別的主機上，\n也是要用到 container id 或 container name。\n假設要把 container export 出來變成 exported.tar:\ndocker export \u0026#34;CONTAINER_ID\u0026#34; \u0026gt; exported.tar 或\ndocker export \u0026#34;CONTAINER_NAME\u0026#34; \u0026gt; exported.tar 就能夠壓出一個 .tar 檔把 container 搬去別的主機。\nReference # Docker docs\n","date":"January 1, 2020","externalUrl":null,"permalink":"/zh-tw/posts/docker-operating-1.html","section":"文章","summary":"Docker 基本概念 # 一言以蔽之，\n簡化版的 VM。\n因為 docker 不會把整個 operating system 都裝起來，\n所以大小比 VM 小很多速度也比 VM 快很多。\nImage # 跑在 container 上面的東西，\n裡面包了一個輕量級的 runtime environment，\n包含一些 library 跟 executable。\n","title":"Docker Notes 1 - Beginner","type":"posts"},{"content":"","date":"December 30, 2019","externalUrl":null,"permalink":"/zh-tw/categories/blog.html","section":"Categories","summary":"","title":"Blog","type":"categories"},{"content":"","date":"December 30, 2019","externalUrl":null,"permalink":"/zh-tw/tags/github-pages.html","section":"Tags","summary":"","title":"Github Pages","type":"tags"},{"content":"更新 # 已從 Jekyll migrate 到 Hugo，\n這篇方法僅適用 Jekyll。\nSitemap # sitemap 基本上就一個 .xml 檔案，\n裡面包含了網站有哪些頁面連結，\n讓搜尋引擎去爬，\n搜尋引擎爬完以後就可以建立 index，\n之後有人在搜尋引擎打關鍵字才搜尋的到。\nJekyll-sitemap # Jekyll 有一個 plugin 叫做 jekyll-stiemap，\n可以在每次 build 網站之後自動產生 sitemap。\n如果是自己的機器架站的話其實是個不錯的選擇，\n一開始我在 Github Page 用 Jekyll 建 Blog 也是用這個，\n但後來發現不曉得究竟是版本問題還是因為 Github Pages 在 build 網站的時候用的是不同的方法或參數，\nsitemap 是生出來了沒有錯，\n但是網址前段的部分因為莫名其妙的原因被吃掉了。\n自己生 sitemap # 最後只好 Google 一下看看有沒有人是自己生 sitemap 的，\n就找到了這個，\n似乎可以 work，\n於是直接抓來改，\n然後放到 repository 的 sitemap.xml，\nbuild 完之後檢查一下沒有問題就成功了。\n","date":"December 30, 2019","externalUrl":null,"permalink":"/zh-tw/posts/jekyll-sitemap-github-pages.html","section":"文章","summary":"更新 # 已從 Jekyll migrate 到 Hugo，\n這篇方法僅適用 Jekyll。\nSitemap # sitemap 基本上就一個 .xml 檔案，\n裡面包含了網站有哪些頁面連結，\n讓搜尋引擎去爬，\n搜尋引擎爬完以後就可以建立 index，\n之後有人在搜尋引擎打關鍵字才搜尋的到。\nJekyll-sitemap # Jekyll 有一個 plugin 叫做 jekyll-stiemap，\n可以在每次 build 網站之後自動產生 sitemap。\n","title":"Github Pages and Jekyll - sitemap","type":"posts"},{"content":"","date":"December 30, 2019","externalUrl":null,"permalink":"/zh-tw/tags/jekyll.html","section":"Tags","summary":"","title":"Jekyll","type":"tags"},{"content":"","date":"December 30, 2019","externalUrl":null,"permalink":"/zh-tw/tags/seo.html","section":"Tags","summary":"","title":"SEO","type":"tags"},{"content":"","date":"December 30, 2019","externalUrl":null,"permalink":"/zh-tw/tags/c.html","section":"Tags","summary":"","title":"C","type":"tags"},{"content":"const 和一般變數 # 有兩種寫法\nconst TYPE NAME = VALUE; // more common TYPE const NAME = VAULE; 意思都一樣，\n就是這個變數不能再被指定別的值。\n舉個例：\n#include \u0026lt;iostream\u0026gt; using namespace std; int main(void) { const int i = 1; int const j = 1; i = 2; // error j = 2; // error cout \u0026lt;\u0026lt; \u0026#34;i = \u0026#34; \u0026lt;\u0026lt; i \u0026lt;\u0026lt; endl; cout \u0026lt;\u0026lt; \u0026#34;j = \u0026#34; \u0026lt;\u0026lt; j \u0026lt;\u0026lt; endl; return 0; } i和j兩個噴的 error 一模一樣:\nconst.cpp:9:4: error: cannot assign to variable \u0026#39;i\u0026#39; with const-qualified type \u0026#39;const int\u0026#39; i = 2; ~ ^ const.cpp:7:12: note: variable \u0026#39;i\u0026#39; declared const here const int i = 1; ~~~~~~~~~~^~~~~ const.cpp:10:4: error: cannot assign to variable \u0026#39;j\u0026#39; with const-qualified type \u0026#39;const int\u0026#39; j = 2; ~ ^ const.cpp:8:12: note: variable \u0026#39;j\u0026#39; declared const here int const j = 1; ~~~~~~~~~~^~~~~ 2 errors generated. const 和 reference # 跟一般變數一樣有兩種寫法:\nconst TYPE \u0026amp;NAME = VALUE; // more common TYPE const \u0026amp;NAME = VAULE; 意思也一樣，\n有兩個限制:\nReference 不能再拿去指定別的變數 被 reference 指到的變數不能用 reference 去指定別的值。\n不過他可以在不透過 reference 的情況下自己改變他的值。 例子:\n#include \u0026lt;iostream\u0026gt; using namespace std; int main(void) { int i = 1, j = 2; int const \u0026amp;r1 = i; const int \u0026amp;r2 = i; // change value with reference r1 = 3; // error r2 = 3; // error // change value i = 4; // change reference object r1 = j; // error r2 = j; // error return 0; } constant reference 唯一能做的就是拿來讀，\n要改值的話只能是他 reference 到的變數不透過 reference 自己改自己。\nconst 和 pointer # 這就複雜了，\n可以用 const 的位置來記 const 是用來修飾誰:\nTYPE* const pNAME; // 1 TYPE const *pNAME; // 2 const TYPE *pNAME; // 3 const TYPE* const pNAME; // 4 1 的情況下 const 修飾的是 pNAME，\n也就是 pNAME 不能再被改變 (不能 pNAME = ...)；\n2 的情況 const 修飾的是 *pNAME，\n是說 *pNAME 不能再被改變 (不能 *pNAME = ...)；\n3 的情況 const 修飾的是 TYPE *pNAME，\n跟 2 一樣是說 *pNAME 不能再被改變 (不能 *pNAME = ...)；\n4 的情況 const 修飾的是 pNAME 和 TYPE*，\n所以 pNAME 和 TYPE* 都不能改變 (pNAME = ... 和 *pNAME = ... 都不行)。\n#include \u0026lt;iostream\u0026gt; using namespace std; int main(void) { int i = 1, j = 2; int* const p1 = \u0026amp;i; int const *p2 = \u0026amp;i; const int *p3 = \u0026amp;i; const int* const p4 = \u0026amp;i; // change value with pointer *p1 = 2; *p2 = 2; // error *p3 = 2; // error *p4 = 2; // error // change value i = 3; // change pointer position p1 = \u0026amp;j; // error p2 = \u0026amp;j; p3 = \u0026amp;j; p4 = \u0026amp;j; // error return 0; }","date":"December 30, 2019","externalUrl":null,"permalink":"/zh-tw/posts/const-pointer-reference.html","section":"文章","summary":"const 和一般變數 # 有兩種寫法\nconst TYPE NAME = VALUE; // more common TYPE const NAME = VAULE; 意思都一樣，\n就是這個變數不能再被指定別的值。\n舉個例：\n","title":"C/C++ - const 加上 pointer 和 reference 的用法整理","type":"posts"},{"content":"更新 # 已從 Jekyll migrate 到 Hugo，\n這篇方法原理不變但 code 放的位置和內容要修正。\nLikeCoin # 前陣子對 LikeCoin 發生了一點興趣，\nLikeCoin 是一種虛擬貨幣，\n創立的初衷是想做為一種獎勵創作者的機制，\n創作者在文章裡放入 Like Button，\n讓大家對他的按鈕按讚鼓掌，\n他就能收到相對的 LikeCoin。\n至於作者能獲得多少 LikeCoin 就要看按讚讀者的帳戶種類，\n如果讀者註冊的是免費帳戶，\n那點下的讚就由 LikeCoin 的基金會按比例支付;\n如果讀者註冊的是付費帳戶，\n那就看該讀者當月按下多少讚按比例分配。\n詳情可以參考 LikeCoin 的 Medium。\n在 Jekyll Theme 加入 Like Rewords Button # 因為看起來很有趣的樣子所以我就註冊了一個帳號，\n然後默默的發現 Like Button 的 Widget 有支援 Medium, WordPress, Oice, Matters, \u0026hellip;等等等等，\n但是因為 Jekyll theme 要怎麼設計完全是看個人，\n所以只能自己想辦法加。\n於是我只好去 Medium 隨便找了個有加 Like Button 的文章，\n把 Firefox 的 Developer Console 打開觀察了一下:\n原來是用 iframe，\nsrc 的網址看起來直接把黑筆塗掉的地方替換成自己的 liker ID 跟文章網址應該就能 Work 了。\n所以要把 Like Button 加到 blog post 裡有兩種方法:\n把 iframe 加到 blog post 把 iframe 加到產生 blog post 的 template html 顯然第二種方法比較好，\n只要加一次之後所有的 blog post 在產生的時候就可以自己生出 Like Button。\n我用的 jekyll theme 產生 blog post 的 template html 放在 _layouts/post.html，\n所以我就在放 content 的地方的最後面加上了：\n\u0026lt;div align=\u0026#34;center\u0026#34;\u0026gt; \u0026lt;iframe scrolling=\u0026#34;no\u0026#34; src=\u0026#34;https://button.like.co/in/embed/\u0026lt;MY_LIKER_ID\u0026gt;/button/?referrer={{ site.url }}{{ page.url }}\u0026#34; frameborder=\u0026#34;0\u0026#34;\u0026gt;\u0026lt;/iframe\u0026gt; \u0026lt;/div\u0026gt; Liker ID 要換成自己的 Liker ID，\nsite.url 和 page.url 是 Liquid 的語法，\n代表的是當下的那篇 blog post 的網址，\n加完之後之後每篇 blog post 都會自己產生一個 Like button 了。\n","date":"December 27, 2019","externalUrl":null,"permalink":"/zh-tw/posts/likecoin-button-jekyll.html","section":"文章","summary":"更新 # 已從 Jekyll migrate 到 Hugo，\n這篇方法原理不變但 code 放的位置和內容要修正。\nLikeCoin # 前陣子對 LikeCoin 發生了一點興趣，\nLikeCoin 是一種虛擬貨幣，\n創立的初衷是想做為一種獎勵創作者的機制，\n創作者在文章裡放入 Like Button，\n讓大家對他的按鈕按讚鼓掌，\n他就能收到相對的 LikeCoin。\n至於作者能獲得多少 LikeCoin 就要看按讚讀者的帳戶種類，\n如果讀者註冊的是免費帳戶，\n那點下的讚就由 LikeCoin 的基金會按比例支付;\n如果讀者註冊的是付費帳戶，\n那就看該讀者當月按下多少讚按比例分配。\n詳情可以參考 LikeCoin 的 Medium。\n","title":"Add LikeWidget to Jekyll theme","type":"posts"},{"content":"","date":"December 27, 2019","externalUrl":null,"permalink":"/zh-tw/tags/frontend.html","section":"Tags","summary":"","title":"Frontend","type":"tags"},{"content":"","date":"December 27, 2019","externalUrl":null,"permalink":"/zh-tw/tags/likecoin.html","section":"Tags","summary":"","title":"Likecoin","type":"tags"},{"content":"","date":"December 25, 2019","externalUrl":null,"permalink":"/zh-tw/categories/git.html","section":"Categories","summary":"","title":"Git","type":"categories"},{"content":"","date":"December 25, 2019","externalUrl":null,"permalink":"/zh-tw/tags/git.html","section":"Tags","summary":"","title":"Git","type":"tags"},{"content":"","date":"December 25, 2019","externalUrl":null,"permalink":"/zh-tw/tags/gitignore.html","section":"Tags","summary":"","title":"Gitignore","type":"tags"},{"content":"","date":"December 25, 2019","externalUrl":null,"permalink":"/zh-tw/tags/tool.html","section":"Tags","summary":"","title":"Tool","type":"tags"},{"content":"每當建立一個新的資料夾並在裡面放了一些檔案，\nMacOS 就會在該資料夾下自動產生一個 .DS_Store 檔案，\n也就導致了 .DS_Store 在 macOS 裡面散的到處都是。\n而每次有新的 Git repository 就一定要在 .gitignore 裡加 .DS_Store 和 **/.DS_Store 實在是很煩人，\n所以就研究了一下找了個辦法一次設定好之後就能夠一勞永逸。\ngit config 指令 # git config 這個指令可以拿來對 git 的各種值做設定，\n最出名的就是 git config --global user.name 和 git config --global user.email這種，\n對 user.name 和 user.email 做 global 的設定，\n之後每個 Repository 就可以沿用設定好的 user.name 和 user.email；\n這個指令也可以針對個別的 git repository 做單獨設定，\n如果想要針對個別 Repository 設定不同的 user.name 和 user.email，\n只要在那個 Repository 下用git config --local user.name 和 git config --local user.email 去單獨做local 的設定就好。\ngit config 有一個叫 core.excludesfile 的設定，\n可以用來指定一個檔案，\n裡面可以放希望被 ignore 的檔案，\n把這個東西設定到 global，\n之後所有的 repository 就都會忽略那個檔案裡面包含的東西。\n所以如果要讓所有的 Repository 都忽略 .DS_Store，\n就只要把 .DS_Store 和 **/.DS_Store 寫到一個檔案，\n再把 core.excludesfile 的值設定成那個檔案就可以了。\n可以用下面的指令做到這件事:\necho \u0026#34;.DS_Store\u0026#34; \u0026gt;\u0026gt; ~/.gitignore_global echo \u0026#34;**/.DS_Store\u0026#34; \u0026gt;\u0026gt; ~/.gitignore_global git config --global core.excludesfile ~/.gitignore_global","date":"December 25, 2019","externalUrl":null,"permalink":"/zh-tw/posts/remove-ds_store-from-all-git-repo.html","section":"文章","summary":"每當建立一個新的資料夾並在裡面放了一些檔案，\nMacOS 就會在該資料夾下自動產生一個 .DS_Store 檔案，\n也就導致了 .DS_Store 在 macOS 裡面散的到處都是。\n而每次有新的 Git repository 就一定要在 .gitignore 裡加 .DS_Store 和 **/.DS_Store 實在是很煩人，\n所以就研究了一下找了個辦法一次設定好之後就能夠一勞永逸。\n","title":"從所有的 git repository 移除 .DS_Store 追蹤","type":"posts"},{"content":"","date":"December 25, 2019","externalUrl":null,"permalink":"/zh-tw/categories/concurrency.html","section":"Categories","summary":"","title":"Concurrency","type":"categories"},{"content":"Concurrent Processing 和 Parallel Processing 指的都是 CPU 在 一段時間內執行多個 process，\n但兩者在概念上有些差異；\n根據 The Art of Concurrency ，\nConcurrent 指的是\ntwo or more processes are in progress at the same time\n而 Parallel 指的是\ntwo or more processes executing simultaneously\n看起來很相似但實際上執行的方式不同。\nConcurrent 指的是兩個或多個 process 都正在執行中，\n而 parallel 指的是兩個或多個 process 同時執行。\n舉例來說，\n執行兩個 Process，\nProcess A 和 Process B。\nParallel Processing # Parallel processing 的執行方式可能長這樣：\nProcess A 和 Process B 同時都在執行。\nConcurrent Processing # 但對 Concurrent processing 來說，\n執行的狀況可以像上面的 Parallel processing，\n也可以長這樣：\nProcess A 和 Process B 都在執行中，\n但是兩者是交互執行的而非同時執行。\nNotice # 要特別注意的一點是，\nparallel processing 只是 concurrent processing 的一種。\n只要是有兩個以上的 process 同時在執行中就是 Concurrent processing，\n達成這樣的目標有很多種做法，\n而 parallel processing 只是其中之一。\n","date":"December 25, 2019","externalUrl":null,"permalink":"/zh-tw/posts/concurrent-process-parallel-process.html","section":"文章","summary":"Concurrent Processing 和 Parallel Processing 指的都是 CPU 在 一段時間內執行多個 process，\n但兩者在概念上有些差異；\n根據 The Art of Concurrency ，\nConcurrent 指的是\n","title":"Difference between Concurrent Processing and Parallel Processing","type":"posts"},{"content":"","date":"December 25, 2019","externalUrl":null,"permalink":"/zh-tw/tags/parallel-processing.html","section":"Tags","summary":"","title":"Parallel Processing","type":"tags"},{"content":" 關於我 # 具備豐富後端分散式系統開發經驗的資深軟體開發工程師，擁有超過七年的業界經驗，專注於建構與擴展運算密集型服務及現代 AI 應用所需的高效能分散式系統。核心專長深植於穩健的後端架構設計與應用型 AI 開發 (Applied AI development)，同時具有全端與前端開發能力，能夠將複雜的智慧邏輯層與使用者介面進行無縫整合。\n具有優良的開發、架構設計與解決複雜問題的能力，建立在系統的結構完整性、可預期的擴展性以及俐落的抽象化設計之上。擅長識別並解決系統性的效能瓶頸——例如解耦單體式資料結構以消除 $O(N)$ 的運算限制——並致力於建置必要的非同步基礎設施，確保在不犧牲企業級穩定性的前提下，為智慧型產品提供強大的技術後盾。\n具備架構與開發自主型 AI 代理 (Autonomous AI Agents)、複雜的檢索增強生成 (RAG) 處理管線，以及整合模型上下文協定 (Model Context Protocols, MCP)，致力於打造高回應速度、高吞吐量的現代化應用程式經驗與能力。\n核心成就：\nAI 應用與代理開發 (AI Application \u0026amp; Agent Development)：在建置具容錯能力的 AI 整合後端管線方面擁有豐富經驗。曾獨立研發打造自主型探索代理（結合 Groq LLM 與 Brave Search MCP 及 Notion MCP 所開發的「Hackathon Sniper」）、獨立開發結合向量資料庫 (Vector DB) 與 LangChain 技術的 RAG 應用程式，建構了穩健的推論管線。 具專利價值的研發 (Patentable R\u0026amp;D)： 擁有兩項美國專利 (US-20250363378-A1, US-20250240262-A1)。曾親自架構用於優化空間運算 (Spatial Computing) 的 zone-based 生產級別演算法，成功將分散式網路的訊息傳輸量降低高達 80%；並獨立設計了另一套概念性的強化學習 (Reinforcement Learning) 框架。 企業級規模與交付 (Enterprise Scale \u0026amp; Delivery)： 於 HTC Viverse 全球上線專案中擔任資深工程師 (Senior Engineer)，構思了動態邊緣路由網路的系統架構 (AWS Lambda@Edge)，建立能無縫擴展至百萬級月活躍用戶 (MAU) 的架構基礎。 系統架構演進 (Systemic Evolution)： 從零開始獨立設計、開發並開源了完整的非同步微服務架構 (FastAPI/Python)。同時建置零信任 (Zero-trust) 資料傳輸管線，藉由提供穩健的底層架構，實際推動企業級的技術執行力，並確保程式化服務的整合安全無虞。 主要技能： Python · Go · Node.js · C++ · Elixir · GCP · AWS · Docker · MongoDB · PostgreSQL\n完整經歷、專案與背景 →\n文章 # 我也在 dev.to → 發表文章——以黑客松和程式挑戰為主的短篇技術文章。 絕境長城之外：以 Firestore vector search 打造低成本、高效的雲端 RAG 應用 March 20, 2026\u0026middot;3774 字\u0026middot;8 分鐘 RAG(Retrieval Augmented Generation) 是一套 AI framework，能夠在不需要重新訓練 LLM 的前提下，讓開發者得以新增其他的外部資訊，以這些新增的資訊來改善 LLM 回答的精準度。在 2026 的今天已經是一項廣為人知的技術了。 使用 GitHub CLI 徹底移除 GitHub 上頑固的「幽靈通知」 November 14, 2025\u0026middot;1206 字\u0026middot;3 分鐘 GitHub notification 對開發者來說是一向很方便的工具，可以用來追蹤 issue、pull request 和提及 (mention)。但某天我的通知標記忽然就這麼卡住了，即使已讀了所有內容也他還是非常頑固的卡在那裡，在又拖了幾個月之後我才終於花了點時間研究發生了什麼事。 Multiprocessing, Multithreading and Asyncio in Python Part 1 - Basic Concept October 25, 2025\u0026middot;上次編輯: June 9, 2026\u0026middot;2120 字\u0026middot;5 分鐘 Python 的效能瓶頸在幾年前一直為人詬病， 但在開發者的努力之下，Python 3.4 開始出現了 Asyncio 可以在特定情境下提升效能， 到了 Python 3.13 更出現了可選擇性關閉 GIL 的 Free-threaded (PEP-703) 設計， 結合過去的 Multiprocessing 和 Multithreading， 我整理了一下這三項技術適合的原理、差異和使用情境做了幾篇紀錄。 這一篇先簡單介紹三者的基本概念和適用情境。 Macos Legacy Rsync Hangs October 4, 2025\u0026middot;481 字\u0026middot;1 分鐘 幾個月前用 rsync 從 Macbook 備份資料到 NAS 上的時候出現了點問題， rsync 會看似正常的在螢幕上跑一陣子然後卡死不動， 從螢幕輸出會看到他原本一邊同步一邊在吐正在同步中的檔案， Sync Obsidian / Joplin Data Across Multiple Device with Synology WebDAV December 25, 2024\u0026middot;上次編輯: June 9, 2026\u0026middot;749 字\u0026middot;2 分鐘 原先我使用的筆記軟體是 Notion， 功能豐富且介面美觀， 但幾年前 Notion 出現了隱私權爭議， 被指控偷看某公司放在 Notion 的內容， 甚至進一步提出合作； 就改用了一陣子 Joplin， 但最後還是轉到了擁有大量外掛及社群支援， 而且可以高度客製化的 Obsidian。 Managing Pre-existing Global NPM Packages After Installing NVM November 6, 2021\u0026middot;上次編輯: June 9, 2026\u0026middot;383 字\u0026middot;1 分鐘 今天遇到一個問題， 安裝過 nvm 後安裝 global package 的路徑就被改變了， 導致想要移除之前安裝過的 global package 時沒辦法直接用 npm uninstall -g 移除。 怎麼發現這件事的呢？ 很久以前我在 global 裝過一個 package 可以直接在 terminal 呼叫 command 執行， 查看所有文章 →\n合作洽談 — 歡迎諮詢顧問服務、研究合作、演講邀約及專案外包。合作洽談 → ","externalUrl":null,"permalink":"/zh-tw/index.html","section":"Kourtney's Space","summary":"關於我 # 具備豐富後端分散式系統開發經驗的資深軟體開發工程師，擁有超過七年的業界經驗，專注於建構與擴展運算密集型服務及現代 AI 應用所需的高效能分散式系統。核心專長深植於穩健的後端架構設計與應用型 AI 開發 (Applied AI development)，同時具有全端與前端開發能力，能夠將複雜的智慧邏輯層與使用者介面進行無縫整合。\n","title":"Kourtney's Space","type":"page"},{"content":" 我也在 dev.to → 發表文章——以黑客松和程式挑戰為主的短篇技術文章。 ","externalUrl":null,"permalink":"/zh-tw/posts/index.html","section":"文章","summary":" 我也在 dev.to → 發表文章——以黑客松和程式挑戰為主的短篇技術文章。 ","title":"文章","type":"posts"},{"content":" 軟體與系統開發 # 提供網站建置、全端系統、自動化流程、後端系統及 AI 應用的顧問與外包服務。\n合作形式： 技術顧問、外包開發、程式碼審查、架構設計。\n研究合作 # 歡迎在分散式系統、應用 AI 及雲端運算等領域進行學術或產業研究合作，包含共同撰寫論文。\n合作形式： 共同著作、研究顧問、資料集或系統設計貢獻。\n聯絡方式 # 有合作想法嗎？歡迎直接聯絡。\nkourtneylee1611@gmail.com\n","externalUrl":null,"permalink":"/zh-tw/work-with-me.html","section":"Kourtney's Space","summary":"軟體與系統開發 # 提供網站建置、全端系統、自動化流程、後端系統及 AI 應用的顧問與外包服務。\n","title":"合作洽談","type":"page"},{"content":" 摘要 # 具備豐富後端分散式系統開發經驗的資深軟體開發工程師，擁有超過七年的業界經驗，專注於建構與擴展運算密集型服務及現代 AI 應用所需的高效能分散式系統。核心專長深植於穩健的後端架構設計與應用型 AI 開發 (Applied AI development)，同時具有全端與前端開發能力，能夠將複雜的智慧邏輯層與使用者介面進行無縫整合。\n具有優良的開發、架構設計與解決複雜問題的能力，建立在系統的結構完整性、可預期的擴展性以及俐落的抽象化設計之上。擅長識別並解決系統性的效能瓶頸——例如解耦單體式資料結構以消除 O(N) 的運算限制——並致力於建置必要的非同步基礎設施，確保在不犧牲企業級穩定性的前提下，為智慧型產品提供強大的技術後盾。\n具備架構與開發自主型 AI 代理 (Autonomous AI Agents)、複雜的檢索增強生成 (RAG) 處理管線，以及整合模型上下文協定 (Model Context Protocols, MCP)，致力於打造高回應速度、高吞吐量的現代化應用程式經驗與能力。\n核心成就：\nAI 應用與代理開發 (AI Application \u0026amp; Agent Development)：在建置具容錯能力的 AI 整合後端管線方面擁有豐富經驗。曾獨立研發打造自主型探索代理（結合 Groq LLM 與 Brave Search MCP 及 Notion MCP 所開發的「Hackathon Sniper」）、獨立開發結合向量資料庫 (Vector DB) 與 LangChain 技術的 RAG 應用程式，建構了穩健的推論管線。 具專利價值的研發 (Patentable R\u0026amp;D)： 擁有兩項美國專利 (US-20250363378-A1, US-20250240262-A1)。曾親自架構用於優化空間運算 (Spatial Computing) 的 zone-based 生產級別演算法，成功將分散式網路的訊息傳輸量降低高達 80%；並獨立設計了另一套概念性的強化學習 (Reinforcement Learning) 框架。 企業級規模與交付 (Enterprise Scale \u0026amp; Delivery)： 於 HTC Viverse 全球上線專案中擔任資深工程師 (Senior Engineer)，構思了動態邊緣路由網路的系統架構 (AWS Lambda@Edge)，建立能無縫擴展至百萬級月活躍用戶 (MAU) 的架構基礎。 系統架構演進 (Systemic Evolution)： 從零開始獨立設計、開發並開源了完整的非同步微服務架構 (FastAPI/Python)。同時建置零信任 (Zero-trust) 資料傳輸管線，藉由提供穩健的底層架構，實際推動企業級的技術執行力，並確保程式化服務的整合安全無虞。 工作經歷 # 資深軟體工程師 · HTC Viverse Research Team 2022年5月 – 2025年7月 Go · Elixir · Python · Node.js · PostgreSQL · AWS · WebRTC · mediasoup · Docker 支援大規模同步請求的高並發元宇宙平台。\nViverse Worlds 全球上線規模： 設計核心後端架構及無伺服器邊緣路由基礎設施（AWS Lambda@Edge），驅動企業級平台支援全球百萬月活躍用戶。 技術領導： 帶領跨職能工程團隊從系統設計到實際執行，在嚴格維持向下相容性的前提下，將核心平台從孤立的單場景架構演進為互聯的 多場景 3D 生態系統。 資料庫高並發優化： 透過重新設計 PostgreSQL Schema，根除在高並發空間分配期間出現的關鍵 O(N) 資料庫鎖定瓶頸。以細粒度的 O(1) 資料列級鎖定（Row-level locking）取代高度競爭的資料表級鎖定（Table-level locks），從而將吞吐量提升 10 至 100 倍，並將 p99 延遲穩定控制在 2 毫秒至 10 毫秒。 即時 AI 管線： 建構事件驅動的媒體橋接器（mediasoup），將低延遲 WebRTC/RTP 串流連接至持久化雲端儲存，並設計安全的非同步資料管線供第三方 AI 審核使用，直接帶動 33,000 美元的營收增長。 雙重專利發明人（US-20250240262-A1、US-20250363378-A1）： 獨創並開發了基於區域切分的空間演算法（結合視錐剔除、航位推測），及機器學習（強化學習）遮蔽評估框架的專利，將分散式網路頻寬使用量削減高達 80%。 運算擴展性： 主導 WebRTC 核心的多 Worker 架構轉型，突破單核 CPU 上限，將每場活動的並發串流容量從 300 擴展至 4,500+。 負載模擬與可靠性： 開發自訂 WebRTC 負載測試 CLI 工具，模擬大規模並發流量，在上線前主動隔離記憶體洩漏並優化硬體負載平衡。 資深後端工程師 · HTC Vive 2020年9月 – 2022年5月 Python (FastAPI) · Golang · MongoDB · MySQL · Docker · EKS · AWS · React 企業級 VR 平台（Vive Business Training），支援數百家機構客戶的高密度並發作業環境，並管理複雜的 3D 資產生成生態系統。\n架構標準化： 架構非同步 FastAPI 標準，將 API 延遲降低 85%，達成 95% 測試覆蓋率，並透過 Docker 與 AWS EKS 建立容器化自動擴展管線，維持關鍵多平台服務 99.99% 的可用性。 非同步運算管線： 利用 Celery 與 AWS SQS 開發高容錯的微服務生態系統，解耦繁重的 3D 模型及媒體處理任務，保障高可用性與非阻塞 API 執行。 並發與狀態管理： 架構並開發管理 VR 相關物件生命週期的核心後端服務，設計嚴格的事務邏輯，徹底消除高並發多租戶修改時的資料庫競態條件。 多模型持久化與身份管理： 建構了具高擴展性的角色權限控管（RBAC）生態系統，並整合多平台帳戶管理 API 與第三方雲端託管服務，以簡化 SaaS 資源組態（Provisioning）。實施多語系持久化架構，將結構嚴格的關聯式資料隔離於 MySQL，同時利用 MongoDB 處理具彈性的 3D 資產 Schema。 無伺服器可觀測性架構（Serverless Observability Architecture）：利用 AWS Lambda (Golang/Python) 與 CloudWatch 開發了一套事件驅動的健康度監控管線（Pipeline），用於持續驗證身份系統的可用性，實現即時故障偵測並確保系統的高可用性（High Availability）。 零信任 IoT 邊緣路由： 架構概念驗證方案，利用 AWS IoT（MQTT）管理大型 VR 裝置隊列的遙測資料，並整合憑證自動販售機（CVM）進行 X.509 裝置身份驗證。 資深軟體工程師暨技術主管 · InfoBoom Ltd（中研院衍生新創） 2020年2月 – 2020年9月 C\u0026#43;\u0026#43; · Node.js · MongoDB · RabbitMQ · WebRTC · GCP 與中央研究院合作開發的高安全性端對端加密 SaaS 通訊平台，並獲選於 CYBERSEC 2020 向總統簡報展示。\n非同步系統現代化： 解耦舊有企業級 WebRTC 單體系統，將核心後端架構升級為基於 Node.js 的高容錯、事件驅動的微服務生態系統，並透過 MongoDB 與 RabbitMQ 建立彈性的非同步服務間通訊管線。 加密架構與合規： 主導了第三代核心 C++ 加密函式庫的關鍵現代化重構。在橫跨後端、行動端及客戶端生態系中，嚴格維持了零退化（Zero-regression）的 API 相容性，並成功達成 ISO27001 與台灣 MAS 的完整合規要求。 技術團隊領導： 帶領 4 人工程團隊完成複雜的架構遷移，管理關鍵路徑以確保關鍵安全里程碑按時交付。 基礎設施自動化（CI/CD）： 引入並建構針對業務關鍵多平台加密函式庫的全自動化 CI/CD 部署管線，加速開發速度，平均部署時間縮短 3 倍。 產品開發工程師 · Synology 2016年12月 – 2018年6月 C\u0026#43;\u0026#43; · Python · Redis · ExtJS · CI/CD 架構並交付完整的郵件伺服器功能（含 SMTP Relay），整合底層 C++/Postfix/Dovecot 元件、Redis 資料結構、Web API 及 ExtJS 前端介面。 完整負責獨立 NAS 郵件伺服器產品，確保長期穩定性、安全修補管理，以及功能符合企業需求。 透過實施隱私保護資料處理機制及郵件工作流程的系統層級稽核控制，交付 GDPR 合規功能。 建構並維護自動化測試與部署的 CI/CD 管線；透過修補關鍵 CVE 漏洞（如 Dovecot、memcached）及解決競態條件與記憶體洩漏，提升系統安全性與可用性。 與支援部門緊密協作，直接解決升級的客戶事件（含後端資料修正、權限修復及郵件佇列管理），降低關鍵問題的平均解決時間（MTTR）。 跨職能協作，與產品管理、UX/UI 設計師、QA 工程師及技術文件撰寫者合作，確保所有面向使用者及系統功能的正確性、可用性與文件準確性。 研究助理 · 國立台灣大學 2014年5月 – 2014年8月 無線網路與嵌入式系統實驗室 專案：Information Delivery Middleware for Disaster Management over Heterogeneous Interwoven Communication Networks | 國家科學及技術委員會補助計畫\n分散式系統基礎設施（Distributed Systems Infrastructure）：架構並實作了基礎的分散式測試平台，以執行實驗室災害管理研究所需的大規模、異質網路模擬。 演算法驗證環境（Algorithm Validation Environment）：建構了專屬的測試環境，以對實驗性服務恢復演算法進行實證驗證（Empirical validation）。透過模擬網路基礎設施故障，確保在發布/訂閱（Pub/Sub）架構下能可靠地收集遙測數據（Telemetry）。 系統底層最佳化（Systems-Level Optimization）：調校底層 Linux 作業系統與網路環境組態，在確保 I/O 效能無衰退（Zero I/O degradation）的前提下，完美支援高並發的分散式系統測試。 技術技能 # 程式語言：Python、C++、Golang、Node.js、Elixir、SQL、JavaScript、TypeScript 系統架構：分散式系統、事件驅動架構、微服務、領域驅動設計（DDD）、前後端分離（BFF）、零信任資料管線 資料庫與狀態管理：MySQL、SQLite、PostgreSQL、MongoDB、Redis、Firestore 框架：FastAPI、LangChain、Gin、Node.js、Flask、React、TensorFlow、PyTorch 平台、工具與網路協定：Git、Docker、Kubernetes、AWS、GCP、Linux、Jenkins、CI/CD、WebRTC、gRPC、RESTful APIs、WebSockets、Terraform（IaC） 學歷 # 資訊工程學碩士 - 國立台灣大學 # 2014 – 2016 論文：具 QoS 保障的時效性訊息傳遞於命名資料網路 專案：Information Delivery Middleware for Disaster Management over Heterogeneous Interwoven Communication Networks | 國家科學及技術委員會補助計畫 專案： Real-Time High-Performance Miniature Sensing Systems for Sleep Apnea | 國家科學及技術委員會補助計畫 資訊工程學士（資訊電機組）- 國立交通大學 # 2009 – 2013 發表著作 # Data-driven IoT applications design for smart city and smart buildings # 2017 IEEE SmartWorld, Ubiquitous Intelligence \u0026amp; Computing, Advanced \u0026amp; Trusted Computed, Scalable Computing \u0026amp; Communications, Cloud \u0026amp; Big Data Computing, Internet of People and Smart City Innovation (SmartWorld/SCALCOM/UIC/ATC/CBDCom/IOP/SCI)\n作者： Chi-Sheng Shih；Kuo-Hsiu (Kourtney) Lee；Jyun-Jhe Chou；Kwei-Jay Lin · DOI： 10.1109/UIC-ATC.2017.8397394\n專利 # METHOD AND SYSTEM FOR MANAGING POSITION INFORMATION, AND COMPUTER READABLE STORAGE MEDIUM # 專利號碼： US-20250363378-A1\n發明人： Kuo-Hsiu, Lee · 申請日： 2024-05-22\n透過機器學習（強化學習）技術，減少分散式空間運算中的訊息交換量。專利申請亦已提交至以下地區：歐洲專利（EP24206749.4，2024-10-15）、中國（202411517088.4，2024-10-29）、台灣（113133801，2024-09-06）\nMETHOD FOR MANAGING MESSAGE TRANSMISSION, CONTROL DEVICE, AND COMPUTER READABLE STORAGE MEDIUM # 專利號碼： US-20250240262-A1\n發明人： Kuo-Hsiu, Lee · 申請日： 2024-06-27\n透過創新演算法實現分散式空間運算中的訊息量削減。實際應用測試顯示網路訊息量減少 80%。專利申請亦已提交至以下地區：中國（202411455975.3，2024-10-18）、台灣（113131471，2024-08-21）\n專案 # PantryLens | 邊緣路由 AI 視覺 PWA \u0026#8599; \u0026#8598; AI 驅動的漸進式網頁應用（PWA），專為處理非結構化視覺資料並即時生成食譜而設計。為展示安全低延遲的邊緣運算能力，本應用透過影像辨識自動解析食材庫存，針對特殊飲食需求生成結構化輸出，並將視覺任務派送至 Gemma 4 模型。建構了即時 Server-Sent Events（SSE）串流管線，並利用 Redis 實現滑動視窗 IP 速率限制。 Next.js Edge Computing Vercel Server-Sent Events (SSE) OpenRouter Gemma 4 Redis System Architecture. AuroraPath | 安全 AI 路由網頁應用程式 \u0026#8599; \u0026#8598; 現代全端應用，整合即時 NOAA 太空天氣資料與 Google Gemini，為極光觀賞行程生成旅遊路線，並透過 Vercel 部署至全球邊緣節點。展示安全 AI 整合架構——採用雙層身份驗證體系（Auth0 M2M），將終端使用者認證與 Gemini AI 推論代理隔離，透過 Upstash Redis 強制執行嚴格的 API 配額與遙測資料收集，並在面向消費者的產品中提供可擴展的邊緣路由能力。 TypeScript Next.js Vercel Auth0 System Architecture API Rate Limiting LLM Hackathon Sniper | 自主探索 AI 代理 \u0026#8599; \u0026#8598; 一個自主 AI 代理，能動態探索、評估並結構化科技活動資料。專為 Notion MCP Hackathon 打造，展示從傳統線性 API 及多重 Model Context Protocol（MCP）整合，邁向自主 AI 代理開發的技術演進。 Typescript Model Context Protocol (MCP) Autonomous Agents Groq API Integration System Architecture Iron Counsel：企業級 RAG 架構與對話式 AI \u0026#8599; \u0026#8598; 以 RAG 技術為核心的對話式 AI 應用，在複雜資料集上實現精準的 LLM 推論。技術架構：基於 GCP 的無伺服器 FastAPI 後端（透過 Terraform 建置）、本地 ONNX 嵌入模型、Firestore 向量搜尋，以及 LangChain 編排框架。 RAG LangChain LLM Vector Database FastAPI Python GCP CI/CD Terraform 企業標準：非同步 FastAPI 架構 \u0026#8599; \u0026#8598; 生產就緒的非同步 Python 微服務樣板，專為標準化企業後端開發、消除傳統 I/O 瓶頸而設計。透過整合 Docker 並強制要求 95% 測試覆蓋率基準，在組織內建立嚴格的工程文化。 Python FastAPI Microservices System Architecture MongoDB Docker Software Standardization ","externalUrl":null,"permalink":"/zh-tw/about.html","section":"Kourtney's Space","summary":"摘要 # 具備豐富後端分散式系統開發經驗的資深軟體開發工程師，擁有超過七年的業界經驗，專注於建構與擴展運算密集型服務及現代 AI 應用所需的高效能分散式系統。核心專長深植於穩健的後端架構設計與應用型 AI 開發 (Applied AI development)，同時具有全端與前端開發能力，能夠將複雜的智慧邏輯層與使用者介面進行無縫整合。\n","title":"關於我","type":"page"}]