OpenTelemetry 的可觀察性工程:以 Sentry 為例

Observability

OpenTelemetry 的可觀察性工程:以 Sentry 為例

點進 OpenTelemetry 的官方文件,它最先映入眼中的句子是「什麼是 OpenTelemetry」。例如,它是套可觀察性框架,用於檢測、蒐集與導出遙測數據;它是開源且供應商中立,能搭配其他的開源工具,像 Jaeger 或 Prometheus;它能將應用程式與系統儀表化,無關是用 Go 還是 .NET 開發,也無關部署在 AWS 還是 GCP 上。 但是身為一名開發者,當下我們想的是:「公司常開發一些沒人要用的功能,聽說 OpenTelemetry 可以提高可觀察性,也許我們應該放棄開發功能,轉頭建立更好的開發環境。」「AWS 常常要不到需要的數據,也許我們應該改用另一套工具,像是 OpenTelemetry,來解決這件事。」我們想像 OpenTelemetry 「應該」要能解決目前面臨到的一些問題,就像在技術的鏡像中尋找願望一樣。 如果已經有在用 Sentry,還需要導入 OpenTelemetry

By Ken Chen
標準化之路:Go 1.23 中的迭代器

Go

標準化之路:Go 1.23 中的迭代器

Ian Lance Taylor 在 "Range Over Function Types" 這篇文章聊到 iterator 誕生的原因。如果我們有兩個容器,稱為集合(Set),想要取得這兩個集合中的不重複元素,加到新的集合中形成聯集,我們可以寫個 Union 函式來執行 // Set holds a set of elements. type Set[E comparable] struct { m map[E]struct{} } // Union returns the union of two sets. func Union[E comparable](s1, s2 *Set[

By Ken Chen
OAuth 2.0 的身份認證:OpenID Connect

Web

OAuth 2.0 的身份認證:OpenID Connect

OAuth 2 讓網路服務可以存取第三方的受保護資源,因此,有些開發者會進一步利用 OAuth 2 來進行使用者認證。但這中間存在著一些語義落差,因為 OAuth 2 當初設計目的是「授權」而不是「認證」,兩者關注的焦點會有些不同。OpenID Connect 是基於 OAuth 2 的一套身份認證協定,讓開發者可以在 OAuth 2 授權的基礎上,再加入標準的認證流程。在這篇文章中,我會說明授權跟認證的場景有何差異,並講解 OpenID Connect 如何滿足認證需求。 因為 OpenID Connect 是建構在 OAuth 2 的基礎上,我會假設這篇文章的讀者已經知道 OAuth 2 的組件與流程,如果你不熟悉,可以先閱讀另外兩篇文章 * OAuth 2.0:

By Ken Chen

Ken Chen's Blog

Ken Chen 的部落格,通常是後端、DevOps、資料庫等各種技術話題。目前關注的語言是 Go。

Latest

OAuth 2.0:用 Go 跟 Google 要資料

在上一篇的結論中,我們講到開發者通常最想知道,開發 OAuth 2.0 客戶端需要什麼知識。後端工程師要實現 OAuth 2.0,最常見的情境是開發一個客戶端應用,用來存取資源擁有者的受保護資源。因此在這篇中,我們將用 Go 來牛刀小試一番,開發一個網路應用,它會取得使用者同意後,跟 Google 拿取使用者姓名並顯示出來。 註冊客戶端 不是隨便哪個應用都能跟 Google 授權伺服器申請授權,要跟授權伺服器互動,首先要人家願意信任你。因此,在開始寫程式前,要先到 GCP 的 APIs & Services 中註冊客戶端,連結是這個 點選 CREATE CREDENTIALS 並選擇 OAuth client ID,創造一個新的客戶端憑證 我們要開發的是個網路應用,Application type 選

By Ken Chen
OAuth 2.0:授權許可

OAuth 2.0:授權許可

在前一篇中,我們討論了 OAuth 2.0 的角色與信道,知道 OAuth 2.0 將授權模型劃分為四個角色,讓它們經由前/後端信道交流,完成整個授權許可流程。在這篇中,我們要進一步來討論,具體的授權許可是什麼?我們將改由時序的角度出發,探討模型中的物件如何交換訊息。如果覺得這段話太抽象,可以理解成,上一篇介紹了遊戲中的角色與道具,而在這篇,我們將來介紹遊戲的流程與規則。 底下的介紹會著重在授權碼許可(Authorization Code Grant)跟隱式許可(Implicit Grant)兩種 Web 應用場景的授權許可。至於資源擁有者憑證許可(Resource Owner Password Credentials)跟客戶端憑證許可(Client Credentials),雖然在 RFC 6749 有提到,但因資安風險較高,需要資源擁有者非常信賴客戶端且沒有其他方式情況下,才會拿來使用。 Authorization

By Ken Chen
OAuth 2.0:角色與信道

OAuth 2.0:角色與信道

使用者體驗是 B2C 重要的產品面向。通常一個網路服務,會要求使用者註冊帳戶後才能開始使用——以台灣金融保險法規為例,使用者需要建立帳戶後,才能得到報價。站在行銷觀點,註冊會降低用戶的轉換率,因為它需要填寫姓名、暱稱、生日、信箱等資料,步驟相當繁瑣,對行動場景,這百分百是個負面體驗。這讓人不禁想問,這個環節是可以優化的嗎? 事實上,我們可以合理假設使用者資訊已經存在社群媒體中,例如 Google、Facebook、Twitter、GitHub,而我們需要的只是請求使用者同意,讓我們可以代表使用者,存取社群媒體中受限制的資源。也就是,我們關注的是有沒有一個授權框架,可以讓第三方應用取得對資源的訪問權限。 OAuth 2.0 這就帶到 OAuth 2.0 想要解決的問題。在傳統的 Client-Server 認證架構中,當客戶端要存取伺服端資源時,需要提供使用者的帳號密碼。如果發起請求的是第三方應用,則資源擁有者要將帳號密碼提供給第三方應用。可以想像,你需要請人幫你收信,就需要把家裡鑰匙交給對方。

By Ken Chen
配置存放於環境:Go 應用的配置實踐

配置存放於環境:Go 應用的配置實踐

在雲原生的環境中,程式通常採用容器部署,而不同環境間所需要的配置也會不同,像是開發環境的資料需要與生產環境分離;金絲雀部署要分流生產環境的流量,但不會寫資料到生產環境中;開發環境為了除錯,要印出 level 低的 log;開發環境跟生產環境要拿取的 key vault 的 key 跟 version 不同;等等。在雲原生的時代前,開發人員或維運人員通常會各自維護一份執行的程式,開發在開發環境中驗證後,交付維運部署上線,一次性處理好配置;但在雲原生時代,部署變得越來越頻繁,幾乎不太可能手動管理。這時要把問題倒過來想,不是因應開發出來的程式來設定配置,而是有沒有可能,因應部署會遇到的問題來設計開發? Store config in the environment Heroku 基於 SaaS(Software-as-a-Service) 實踐,歸納出 12 條雲原生應用的設計原則,稱為 The Twelve-Factor App,其中關於配置,

By Ken Chen
關於消息的三層語義:以 RabbitMQ 為例

關於消息的三層語義:以 RabbitMQ 為例

對分散式系統來說,消息的可靠性非常重要,想想一個金融應用的場景,如果在支付時,消息遺失了,或是重複遞送了,都會造成使用者的困擾。當我們在系統中引入消息隊列時,我們同時引入了複雜度,這意思是,系統的「處理消息」跟你想的不一定是同一件事。從可靠性的角度來看,「處理消息」的語義可以分為三個層次,第一層是「最多一次」,當你請系統處理消息時,它會幫你進行,但最多一次,並且不保證是否完成;第二層是「最少一次」,系統會幫你處理消息,而且附帶必要的錯誤處理,確保消息至少被完成一次;第三層是「準確一次」,意指消息不多不少,恰恰好被準確處理並完成了一次。 當試著從語言學的角度來看待系統時,我們才能規劃出系統的整體面貌。儘管「準確處理一次」有最佳的可靠性,但因為其處理成本,降低了系統整體的吞吐量。在〈Starbucks Does Not Use Two-Phase Commit〉一文中,Gregor Hohpe 精確描繪了星巴克的異步系統。收銀員收費後,

By Ken Chen

如何優雅包裝錯誤:聊聊 Go 的 error

錯誤處理是 Golang 最常被討論的一個點。這有幾個因素,首先,這跟它「錯誤是值」的設計理念有關,開發者需要在業務流程中穿插錯誤處理,違反關注點分離的原則,當然會引發爭議。另外,在 1.13 前,Golang 標準 errors 庫的表現力有限,當需求較為複雜時,需要開發者自行發明錯誤處理輔助函式。這讓人不禁好奇,Golang 的錯誤處理設計原則是什麼?有沒有比較好的實踐?或者說,我們能不能找到一種方式,優雅地處理錯誤? 錯誤與異常 先來看看不同人的觀點,Robert Martin 在討論到錯誤處理時,是如此建議的 使用異常替代返回錯誤碼,錯誤處理代碼就能從主路徑代碼中分離出來,得到簡化 他給出的例子是 try { deletePage(page); registry.deleteReference(page.name); configKeys.deleteKey(page.name.

By Ken Chen

讓錯誤成為資源:gRPC 的錯誤處理模型

錯誤處理是所有 RPC 服務都要具備的設計,但是怎樣的錯誤處理模型,算是好的模型呢?從字面上來看,錯誤處理可以分解成「錯誤」跟「處理」,如果用 RESTful 的觀點,將錯誤當成是 Resource,一個好的模型應該要能匹配不同場景的 Resource,並根據場景需求來處理這些 Resource。 錯誤模型 在 RESTful 中,通常會用 HTTP Status Code 當錯誤訊息的分類(Category),錯誤內容則放在 Payload。這樣的好處是,只要看到分類,就能先進行大方向的處理,如果需要特定資訊,再從 Payload 拿取。通常錯誤內容的格式會自行定義,以支付服務 Stripe 的 API 為例,定義的格式就有 * type (string) * code (string) * decline_

By Ken Chen
在 GitLab 顯示測試覆蓋率:以 Go 為例

在 GitLab 顯示測試覆蓋率:以 Go 為例

對現代開發者來講,單元測試已經不是可選,而是必備了。單元測試能保護程式碼,讓錯誤提早現形,也能讓重構時更安心。通常我們在評估單元測試的執行狀況時,會用 coverage 當成其中一項指標。當然,coverage 還是會有一些使用的場合跟侷限,當談到專案落地,可能大家會想知道的是,coverage 該怎麼使用,才能幫助到專案? 開發、審查、回顧 我們先來看看什麼時候會需要知道 coverage?通常依照團隊的工作流程,將它分為三個階段:開發、審查、回顧。每個階段關注的場景會略有不同。 一個一個講。對開發中情形,開發者想知道的是剛寫完的邏輯是否能正常運行,有沒有對應的測試,覆蓋範圍是否已經足夠,如果還有條件分支沒覆蓋到的話,是哪裡?是不是每個錯誤都有處理了。這時最需要的是,codebase 要能 highlight 剛剛講的資訊,幫助開發者一眼掌握。 當開發完成,feature branch 被提交到原始碼管理系統,例如 GitLab,會需要一名

By Ken Chen

用 Fx 來替 Go 依賴注入吧

相信平常開發時,即使沒真的用到,也會聽別人提起「依賴注入」的概念。我們都知道依賴注入的目的是解耦模組間的依賴,但具體來說,依賴注入應該要怎麼進行呢?Go 對於依賴注入有什麼比較好的實踐呢?這篇就來談談 Go 相關的依賴注入話題。 常見的實踐方式 講到依賴注入,從 OOP 的觀點來看,可以回到 Martin Fowler 的 SOLID 原則,其中的 Dependency Inversion Principle 落實到編程中,就是依賴注入。James Grenning 曾經簡單扼要說明 DIP 原則的出發點 Martin tells us that high-level modules shouldn’t depend on low-level modules. 這裡,高層級的模組指的是商業邏輯的實現。依賴反轉原則之所以重要,

By Ken Chen

初探 Go 的單元測試:兼談 Stub 跟 Mock

測試是程式的防護網,能確保程式符合設計,而當開發者需要對程式進行重構,以增進品質時,測試也可以確保程式不會出現改 A 壞 B 的情況。從商業角度來看,測試能降低維護與改善程式的成本,進而提高軟體開發的競爭力。 既然測試這麼好,那為什麼常看到軟體專案中沒有測試?在我的經驗中,主要原因有兩個:首先是軟體開發初期,架構還不是很穩定,API 隨時有可能改變,在 API 不穩時,如果就開始寫大量測試,會造成後面很大的維護成本,試著想想,API 的改變,可能就牽涉到測試流程跟測試資料的改變,而之前的 Corner Case 很可能都變成沒有價值的投資,在這情況下寫測試沒有意義。 再來,程式中可能會引用到第三方套件,例如 ORM 或 HTTP 之類的外部依賴,如果要實際測試,就會需要建構測試環境,而這些也會有建置與維護成本,像是網路斷掉,可能就會在程式邏輯沒動到的狀況下,讓 HTTP 的測試失效,這些維護成本會讓寫測試的投資報酬率看起來不太划算。

By Ken Chen

自動生成重複代碼:使用 Go 的 Template

開發軟體時,常常會發現有些函式或方法很類似,例如對 Callback Function 來說,開發者都需要註冊回調函式,並在適當的時機,將資料交給回調函式處理,我們可以將這兩個動作,稱為 OnAction 跟 EmitAction。儘管繼承或組合能讓程式碼重複使用類似的組件,幫助開發者節省時間,但對於較複雜的情況,像是不同類(Class)的函式名稱也要不同時,仍需要仰賴開發者自行編寫。 試著想想,如果開發者僅僅寫設定檔(Config File),就有程式能根據設定檔來自動產生程式碼,不是很美好的事嗎?這是有的,在實務上,這類用於產生程式的程式被稱為 Code Generator,例如前面介紹過的 genny。Golang 有內建 generate 這個命令行工具,能幫助開發者將 Code Generator 跟編譯更密切結合在一起。 本文會用 Callback 的 Generator 當例子,講解如何開發並使用一套 Code Generator。

By Ken Chen
如何驗證使用者身分:使用 JWT

如何驗證使用者身分:使用 JWT

驗證與授權是開發網路應用時一定會遇到的問題,前者指的是確認使用者身分,讓 Server 明白請求者是真正的使用者,而不是其他人假冒;後者指的是該使用者有權限進行操作。JWT 處理的主要是前一個問題。 先來看看 JWT 出來前的做法。在傳統技術中,使用者會在登入時輸入帳號密碼,Server 由資料庫驗證無誤後,創建一組 Session ID,放入回應的 Cookie ,Client 後續請求都會在 Cookie 帶上 Session ID,方便 Server 檢驗。 由於只看 Session ID 無法說明使用者身分正確,還需要看該 Session ID 是否有儲存在 Server,而 Server 的數據通常儲存在資料庫,一來一往之間,就會造成 Server 端額外的開銷。JWT 只需要在 Server 儲存一組

By Ken Chen