高效定位 Go 應用問題:Go 可觀測性功能深度解析

作者:古琦

背景

自 2024 年 6 月 26 日,阿里云 ARMS 團隊正式推出面向 Go 應用的可觀測性監控功能以來,我們與程序語言及編譯器團隊攜手并進,持續深耕技術優化與功能拓展。這一創新性的解決方案旨在為開發者提供更為全面、深入且高效的應用性能監控體驗,助力企業在數字化轉型中實現卓越的系統穩定性與性能表現。

從商業化版本的首次亮相至今,我們已歷經五次重大版本迭代及若干次精細化的小版本更新。相較于初始版本,系統性能實現了翻倍提升,同時在功能層面亦展現出前所未有的豐富性與靈活性。新增特性包括但不限于智能化應用診斷、高度可定制的擴展能力、靈活的應用開關機制、接口全量采樣以及代碼熱點分析等模塊。這些功能的引入不僅顯著提升了系統的實用性,也贏得了廣大用戶的廣泛認可與積極反饋。而基于編譯時插樁(Compile-time Instrumentation)的技術路徑,更被實踐證明是 Go 語言應用監控領域的一次突破性創舉,堪稱當前最優解。

為進一步賦能用戶在復雜場景下快速定位與解決問題,我們結合近期發布的一系列全新功能,精心梳理了一套從接入到問題發現、再到問題排查與精準定位的最佳實踐指南。

應用接入

通過 ARMS 提供的 Instgo 工具,只需要在?go build?前添加 instgo 命令,無需用修改一行代碼,通過編譯時插樁的方式實現監控能力注入[1]。

instgo go build {arg1} {arg2} {arg3}

智能告警

應用接入到 ARMS 后,可以在應用列表查看到應用的名稱,點擊進去查看到應用詳情,包括了請求數、錯誤數、延遲等指標,還提供了每個接口的指標、以及依賴的接口指標,為了快速發現問題,可以通過配置應用的告警來第一時間發現問題。

可以創建對應的告警,如最近 1 分鐘調用響應時間大于等于 500ms 就報警。

應用詳情

通過監控告警第一時間發現問題后,到對應服務的詳情查看這個接口的平均耗時非常長,即知道了告警是由于這個接口導致的。

查看對應的調用鏈,可以按耗時排列,找到耗時最長的調用鏈:

點擊查看調用鏈詳情,可以看到它的子 span 調用時間都非常短,可以確定是這個接口本身慢導致的,而不是其他對外請求導致的。

應用診斷

通過上述應用詳情找到了請求慢的接口后,如何確認這時候的問題呢,我們可以通過應用診斷來發現問題,在應用監控中除了指標、鏈路、日志外,Profiling 的數據成為了應用監控的四大支柱之一。

通過 Profiling 數據能快速發現性能的瓶頸,ARMS Go 可觀測提供了 CPU、內存、代碼熱點三個 Profiling 功能,用于快速發現應用性能問題。

ARMS 的持續剖析能力跟通過類似?https://github.com/grafana/pyroscope?或者 go 提供的 pprof 等工具相比,ARMS 提供的 Profiling 能力可以做到隨開隨關,通過應用設置-持續性能剖析設置即可進行開關設置,無需重啟,直接生效。

CPU Profiling

CPU Profiling 用于收集和分析 Go 應用程序中的 CPU 使用情況,了解你的程序在運行時有多少時間花費在各個函數上。通過分析這些數據,開發者可以識別出程序中最耗費 CPU 時間的部分,ARMS 提供的 CPU Profiling 數據會采集每分鐘的 CPU ?運行情況,通過下面的火焰圖即可找到當前執行時間最長的函數。

除了每分鐘的數據之外,還提供了 CPU Profiling 數據的對比功能,對比前后 CPU 的消耗的不同,確定性能瓶頸。

內存 Profiling

跟 CPU Profiling 一樣,內存 Profiling 也提供了對比的功能,可以對比前后不同時刻內存分配的情況,找到內存分配的熱點。

除了通過內存 Profiling 找到內存分配熱點外,還可以通過 Runtime 監控,找到每個時刻 Goroutines 數量、以及堆對象的數量來看某個時刻是否異常,是否因為流量突增導致的數量增加。

代碼熱點

在出現應用請求超時、響應慢的時候,為了快速定位到性能問題,從提供服務找到出現響應慢的接口,跳轉到調用鏈,從調用鏈分析看出來對應接口在某些請求中響應的時間超出正常值很多,這時候如果還要進一步定位到這個請求執行過程中響應慢的函數是哪個,則無法通過單純的調用鏈分析獲取到,代碼熱點就是用來解決這個問題。點開對應的 Trace,通過放大鏡即可查看當前的調用 Profiling[2]:

可以看到 main 中的?onCpu?函數消耗時間長達 0.62 秒,這樣去排查這個函數的問題即可。

自定義擴展

通過上述方式可以查看到大部分問題,我們還提供了自定義擴展的功能[3],通過一個規則+一段待注入的代碼組成,通過 Go Agent 的能力,在編譯時完成代碼的插樁,而不需要去修改原始代碼,這個功能的優勢是對于一些非項目開發人員可以在不修改原始代碼的情況下完成相關功能實現。以下是我們經常會碰到的通過自定義擴展可以解決的問題:

日志打印

為了快速定位問題或者業務需求,日志可以記錄非常詳細的信息,比如函數的出入參數、Http 的返回的 body、sql 的請求語句參數等,以下是介紹打印?sql?請求的語句、參數:

第一步,創建 hook 文件夾,使用?go mod init hook?初始化該文件夾,然后新增下面的 hook.go 代碼,它是即將注入的代碼:

package hookimport ("database/sql""fmt""github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/api"
)func sqlQueryOnEnter(call api.CallContext, db *sql.DB, query string, args ...interface{}) {fmt.Println("sql is ", query)fmt.Println("sql arg is", args)
}

第二步,編寫測試 Demo。創建文件夾并使用?go mod init demo?初始化,然后添加 main.go

package mainimport ("context""database/sql""fmt"_ "github.com/go-sql-driver/mysql"
)func main() {mysqlDSN := "test:test@tcp(127.0.0.1:3306)/test"db, _ := sql.Open("mysql", mysqlDSN)db.ExecContext(context.Background(), `CREATE TABLE IF NOT EXISTS usersx (id char(255), name VARCHAR(255), age INTEGER)`)db.ExecContext(context.Background(), `INSERT INTO usersx (id, name, age) VALUE ( ?, ?, ?)`, "0", "foo", 10)maliciousAnd := "'foo' AND 1 = 1"injectedSql := fmt.Sprintf("SELECT * FROM userx WHERE id = '0' AND name = %s", maliciousAnd)db.Query(injectedSql, "abc")
}

第三步,在 Demo 文件夾下編寫下面的 conf.json 配置,告訴工具我們想要將 hook 代碼注入到?database/sql:😦*DB).Query()。

[{"ImportPath": "database/sql","Function": "Query","ReceiverType": "*DB","OnEnter": "sqlQueryOnEnter","Path": "/path/to/hook" # Path修改為hook代碼的本地路徑
}]

第四步,切換到 Demo 目錄,使用 instgo 工具編譯并執行程序,以驗證 SQL 注入保護的效果。

$ ./instgo set --rule=./conf.json
$ docker run -d -p 3306:3306 -p 33060:33060 -e MYSQL_USER=test -e MYSQL_PASSWORD=test -e MYSQL_DATABASE=test -e MYSQL_ALLOW_EMPTY_PASSWORD=yes mysql:8.0.36
$ ./instgo go build .
$ ./demo

可以看到,使用?instgo?工具編譯出的二進制文件成功檢測到了潛在的 SQL 注入攻擊,并打印出了相應日志:

sql is  SELECT * FROM userx WHERE id = '0' AND name = 'foo' AND 1 = 1
sql arg is [abc]

記錄Span

ARMS 鏈路追蹤記錄的 span 信息都是對開源的 SDK 進行埋點獲取的,用戶在業務中如果有關心的函數需要記錄可以通過自定義插件的功能,記錄當前函數的 span。

第一步,創建 hook文件夾,使用?go mod init hook?初始化該文件夾,然后新增下面的 hook.go 代碼,它是即將注入的代碼:

package hookimport ("context""fmt""github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/api""go.opentelemetry.io/otel""go.opentelemetry.io/otel/attribute"
)func requestDbOnEnter(call api.CallContext) {tracer := otel.GetTracerProvider().Tracer("")_, span := tracer.Start(context.Background(), "Client/User defined span")span.SetAttributes(attribute.String("client", "client-with-ot"))span.SetAttributes(attribute.Bool("user.defined", true))span.End()fmt.Println(span.SpanContext().SpanID().String())
}

第二步,編寫測試 Demo。創建文件夾并使用?go mod init demo?初始化,然后添加 main.go

package mainimport ("demo/common"_ "github.com/go-sql-driver/mysql"_ "go.opentelemetry.io/otel"
)func main() {common.RequestDb()
}

common?文件夾下增加?common.go?如下:

package commonimport ("context""database/sql""fmt"_ "github.com/go-sql-driver/mysql"
)func RequestDb() {mysqlDSN := "test:test@tcp(127.0.0.1:3306)/test"db, _ := sql.Open("mysql", mysqlDSN)db.ExecContext(context.Background(), `CREATE TABLE IF NOT EXISTS usersx (id char(255), name VARCHAR(255), age INTEGER)`)db.ExecContext(context.Background(), `INSERT INTO usersx (id, name, age) VALUE ( ?, ?, ?)`, "0", "foo", 10)maliciousAnd := "'foo' AND 1 = 1"injectedSql := fmt.Sprintf("SELECT * FROM userx WHERE id = '0' AND name = %s", maliciousAnd)db.Query(injectedSql, "abc")
}

第三步,在 Demo文件夾下編寫下面的 conf.json 配置,告訴工具我們想要將 hook 代碼注入到?common/RequestDb()。

[{"ImportPath": "demo/common","Function": "RequestDb","ReceiverType": "","OnEnter": "requestDbOnEnter","Path": "/path/to/hook" # Path修改為hook代碼的本地路徑
}]

第四步,切換到 Demo 目錄,使用 instgo 工具編譯并執行程序,以驗證 SQL 注入保護的效果。

$ ./instgo set --rule=./conf.json
$ docker run -d -p 3306:3306 -p 33060:33060 -e MYSQL_USER=test -e MYSQL_PASSWORD=test -e MYSQL_DATABASE=test -e MYSQL_ALLOW_EMPTY_PASSWORD=yes mysql:8.0.36
$ ./instgo go build .
$ ./demo

可以看到,使用?instgo?工具編譯出的二進制文件成功創建了 span,并打印出了相應 trace spanId:

0000000000000000

如果上報 span 到服務端,則可以看到自定義的 span。

流量回放

除了簡單的打印日志和創建 Span 外,還可以對生產的請求進行錄制,用于開發和測試階段回歸,提高測試質量,減少線上故障,以下是介紹通過對 Http 的請求、返回進行記錄,將這些數據可以記錄到日志或者數據庫中,用于下次測試回歸。

第一步,創建 hook 文件夾,使用?go mod init hook?初始化該文件夾,然后新增下面的 hook.go 代碼,它是即將注入的代碼:

package hookimport ("encoding/json""fmt""github.com/alibaba/opentelemetry-go-auto-instrumentation/pkg/api""io""net/http"
)func httpClientOnEnter(call api.CallContext, t *http.Transport, req *http.Request) {if req == nil {return}h, _ := json.Marshal(req.Header)fmt.Println("http request header is ", string(h))if req.GetBody == nil {return}requestBody, err := req.GetBody()if err != nil {return}defer requestBody.Close()requestData, err := io.ReadAll(requestBody)if err != nil {return}fmt.Println("http request body is ", string(requestData))
}

第二步,編寫測試 Demo。創建文件夾并使用?go mod init demo?初始化,然后添加 main.go

package mainimport ("bytes""context""encoding/json""net/http""time""unicode"
)func hello(w http.ResponseWriter, r *http.Request) {_, err := w.Write([]byte("Hello Http!"))if err != nil {panic(err)}
}func setupHttp() {http.Handle("/http-service1", http.HandlerFunc(hello))err := http.ListenAndServe(":9114", nil)if err != nil {panic(err)}
}// 定義一個結構體用于構造 JSON 數據
type RequestBody struct {Name  string `json:"name"`Email string `json:"email"`
}func requestServer() {ctx := context.Background()reqBody := RequestBody{Name:  "Alice",Email: "alice@example.com",}// 將結構體序列化為 JSON 格式jsonData, err := json.Marshal(reqBody)if err != nil {return}req, err := http.NewRequestWithContext(ctx, "POST", "http://localhost:9114/http-service1", bytes.NewBuffer(jsonData))if err != nil {panic(err)}req.Header.Add("Content-Type", "application/json")req.Header.Add("test-key", "log")req.Header.Add("hello", "arms")client := &http.Client{}resp, err := client.Do(req)if err != nil {panic(err)}defer resp.Body.Close()
}func Is(s string) bool {for i := 0; i < len(s); i++ {if s[i] > unicode.MaxASCII {return false}}return true
}
func main() {go setupHttp()time.Sleep(3 * time.Second)requestServer()
}

第三步,在 Demo文件夾下編寫下面的 conf.json 配置,告訴工具我們想要將 hook 代碼注入到?net/http:😦*Transport).RoundTrip()。

[{"ImportPath": "net/http","Function": "RoundTrip","ReceiverType": "*Transport","OnEnter": "httpClientOnEnter","OnExit": "","Path": "/path/to/hook" # Path修改為hook代碼的本地路徑
}]

第四步,切換到 Demo 目錄,使用 instgo 工具編譯并執行程序,以驗證 SQL 注入保護的效果。

$ ./instgo set --rule=./conf.json
$ ./instgo go build .
$ ./demo

可以看到,使用?instgo?工具編譯出的二進制文件成功獲取到了請求的 header 和 body,并打印出了相應日志:

http request header is  {"Content-Type":["application/json"],"Hello":["arms"],"Test-Key":["log"]}
http request body is  {"name":"Alice","email":"alice@example.com"}

通過自定義插件打印了日志,或者通過已有代碼的日志也可以進行快速查看問題,我們提供了 TraceID 和 SpanID 關聯到日志的能力[4]。

按需全采

針對一些重要的接口如果需要全采樣,可以通過應用設置-采樣設置配置接口名稱,也可以通過前綴、后綴匹配來配置,這樣這個接口的請求都會被采樣到,避免被丟掉。

后續

為了進一步提升系統的可觀測性與診斷能力,我們正致力于引入一系列高級性能分析工具,包括 Goroutine Profiling(協程剖析)、Mutex Profiling(互斥鎖剖析)、Block Profiling(阻塞剖析)以及 Go Trace(Go語言運行軌跡追蹤)。這些功能將為開發者提供更深入的洞察力,幫助他們在復雜的應用場景中精準定位性能瓶頸與潛在問題。

與此同時,我們將擴展對前沿技術的支持,特別是與大語言模型(LLM)相關的插件生態。例如,我們將集成 langchaingo 這一高效的語言處理框架,并引入 dify 的創新組件,如 dify-sandbox(沙盒環境)和 dify-plugin-daemon(插件守護進程),以滿足開發者在多樣化場景下的需求。

我們還計劃推出一套在線調試工具,旨在為用戶打造一個實時、交互式的問題診斷平臺。通過這一平臺,開發者可以快速定位并解決復雜問題,從而大幅縮短故障排查時間,提升系統的穩定性和可靠性。我們相信,這些能力的引入將為開發者帶來前所未有的便捷體驗,同時推動技術生態的進一步繁榮與發展。

最后誠邀大家試用我們的商業化產品,并加入我們的釘釘群(開源群:102565007776,商業化群:35568145) ,共同提升 Go 應用監控與服務治理能力。通過群策群力,我們相信能為 Golang開發者社區帶來更加優質的云原生體驗。

相關鏈接:

[1] instgo 工具介紹:

https://help.aliyun.com/zh/arms/application-monitoring/developer-reference/instgo-tool-introduction

[2]?代碼熱點:

https://help.aliyun.com/zh/arms/application-monitoring/user-guide/use-hotspot-code-to-diagnose-slow-calls-in-go-applications

[3]?自定義擴展:

https://help.aliyun.com/zh/arms/application-monitoring/use-cases/use-golang-agent-to-customize-scalability

[4] Go 應用日志 Trace 關聯:

https://help.aliyun.com/zh/arms/application-monitoring/use-cases/associate-trace-ids-with-business-logs-for-a-go-application

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/899960.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/899960.shtml
英文地址,請注明出處:http://en.pswp.cn/news/899960.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

構造超小程序

文章目錄 構造超小程序1 編譯器-大小優化2 編譯器-移除 C 異常3 鏈接器-移除所有依賴庫4 移除所有函數依賴_RTC_InitBase() _RTC_Shutdown()__security_cookie __security_check_cookie()__chkstk() 5 鏈接器-移除清單文件6 鏈接器-移除調試信息7 鏈接器-關閉隨機基址8 移除異常…

大語言模型開發框架——LangChain

什么是LangChain LangChain是一個開發由語言模型驅動的應用程序的框架&#xff0c;它提供了一套工具、組件和接口&#xff0c;可以簡化構建高級語言模型應用程序的過程。利用LangChain可以使應用程序具備兩個能力&#xff1a; 上下文感知 將語言模型與上下文&#xff08;提示…

自動化釋放linux服務器內存腳本

腳本說明 使用Linux的Cron定時任務結合Shell腳本來實現自動化的內存釋放。 腳本用到sync系統命令 sync的作用&#xff1a;sync 是一個 Linux 系統命令&#xff0c;用于將文件系統緩存中的數據強制寫入磁盤。 在你執行reboot、poweroff、shutdown命令時&#xff0c;系統會默認執…

Python Websockets庫深度解析:構建高效的實時Web應用

引言 在現代Web開發中&#xff0c;實時通信已經成為許多應用的核心需求。無論是聊天應用、在線游戲、金融交易平臺還是協作工具&#xff0c;都需要服務器和客戶端之間建立持久、雙向的通信通道。傳統的HTTP協議由于其請求-響應模式&#xff0c;無法有效滿足這些實時交互需求。…

【實用技巧】電腦重裝后的Office下載和設置

寫在前面&#xff1a;本博客僅作記錄學習之用&#xff0c;部分圖片來自網絡&#xff0c;如需引用請注明出處&#xff0c;同時如有侵犯您的權益&#xff0c;請聯系刪除&#xff01; 文章目錄 前言下載設置總結互動致謝參考目錄導航 前言 在數字化辦公時代&#xff0c;Windows和…

Node.js 技術原理分析系列 —— Node.js 調試能力分析

Node.js 技術原理分析系列 —— Node.js 調試能力分析 Node.js 作為一個強大的 JavaScript 運行時環境,提供了豐富的調試能力,幫助開發者診斷和解決應用程序中的問題。本文將深入分析 Node.js 的調試原理和各種調試技術。 1. Node.js 調試原理 1.1 V8 調試器集成 Node.js…

【圖論】最短路徑問題總結

一圖勝千言 單源最短路徑 正權值 樸素Dijkstra dijkstra算法思想是維護一個永久集合U&#xff0c;全部點集合V。 循環n -1次 從源點開始&#xff0c;在未被訪問的節點中&#xff0c;選擇距離源點最近的節點 t。 以節點 t 為中間節點&#xff0c;更新從起點到其他節點的最短…

【最佳實踐】win11使用hyper-v安裝ubuntu 22/centos,并配置固定ip,掃坑記錄

文章目錄 場景查看本機的win11版本啟用hyper-vhyper-v安裝ubuntu22虛擬機1.準備好個人的 iso文件。2. hyper-v 快速創建3.編輯設置分配內存自定義磁盤位置設置磁盤大小連接網絡修改虛擬機名稱自定義檢查點位置 和智能分頁件位置虛擬機第一次連接給ubuntu22配置固定ip遇到過的坑…

自然語言處理(25:(終章Attention 1.)Attention的結構?)

系列文章目錄 終章 1&#xff1a;Attention的結構 終章 2&#xff1a;帶Attention的seq2seq的實現 終章 3&#xff1a;Attention的評價 終章 4&#xff1a;關于Attention的其他話題 終章 5&#xff1a;Attention的應用 目錄 系列文章目錄 前言 Attention的結構 一.seq…

Git 命令大全:通俗易懂的指南

Git 命令大全&#xff1a;通俗易懂的指南 Git 是一個功能強大且廣泛使用的版本控制系統。對于初學者來說&#xff0c;它可能看起來有些復雜&#xff0c;但了解一些常用的 Git 命令可以幫助你更好地管理代碼和協作開發。本文將介紹一些常用的 Git 命令&#xff0c;并解釋它們的…

基于yolov11的棉花品種分類檢測系統python源碼+pytorch模型+評估指標曲線+精美GUI界面

【算法介紹】 基于YOLOv11的棉花品種分類檢測系統是一種高效、準確的農作物品種識別工具。該系統利用YOLOv11深度學習模型&#xff0c;能夠實現對棉花主要品種&#xff0c;包括樹棉&#xff08;G. arboreum&#xff09;、海島棉&#xff08;G. barbadense&#xff09;、草棉&a…

論文:Generalized Category Discovery with Clustering Assignment Consistency

論文下載&#xff1a; https://arxiv.org/pdf/2310.19210 一、基本原理 該方法包括兩個階段:半監督表示學習和社區檢測。在半監督表示學習中&#xff0c;使用了監督對比損失來充分地推導標記信息。此外&#xff0c;由于對比學習方法與協同訓練假設一致&#xff0c;研究引入了…

Java高級JVM知識點記錄,內存結構,垃圾回收,類文件結構,類加載器

JVM是Java高級部分&#xff0c;深入理解程序的運行及原理&#xff0c;面試中也問的比較多。 JVM是Java程序運行的虛擬機環境&#xff0c;實現了“一次編寫&#xff0c;到處運行”。它負責將字節碼解釋或編譯為機器碼&#xff0c;管理內存和資源&#xff0c;并提供運行時環境&a…

MySQL 5.7 Online DDL 技術深度解析

14.13.1 在線DDL操作 索引操作主鍵操作列操作生成列操作外鍵操作表操作表空間操作分區操作 索引操作 下表概述了對索引操作的在線DDL支持情況。星號表示有附加信息、例外情況或依賴條件。有關詳細信息&#xff0c;請參閱語法和使用說明。 操作原地執行重建表允許并發DML僅修…

kafka 報錯消息太大解決方案 Broker: Message size too large

kafka-configs.sh --bootstrap-server localhost:9092 \ --alter --entity-type topics \ --entity-name sim_result_zy \ --add-config max.message.bytes10485880 學習營課程

HarmonyOS:ComposeTitleBar 組件自學指南

在日常的鴻蒙應用開發工作中&#xff0c;我們常常會面臨構建美觀且功能實用的用戶界面的挑戰。而標題欄作為應用界面的重要組成部分&#xff0c;它不僅承載著展示頁面關鍵信息的重任&#xff0c;還能為用戶提供便捷的操作入口。最近在參與的一個項目里&#xff0c;我就深深體會…

前端面試題之CSS中的box屬性

前幾天在面試中遇到面試官問了一個關于box的屬性面試題&#xff0c;平時都是直接AI沒有仔細去看過。來說說CSS中的常用box屬性&#xff1a; 1. box-sizing box-sizing 屬性定義了元素的寬度和高度是否包括內邊距&#xff08;padding&#xff09;和邊框&#xff08;border&…

前端開發時的內存泄漏問題

目錄 &#x1f50d; 什么是內存泄漏&#xff08;Memory Leak&#xff09;&#xff1f;&#x1f6a8; 常見的內存泄漏場景1?? 未清除的定時器&#xff08;setInterval / setTimeout&#xff09;2?? 全局變量&#xff08;變量未正確釋放&#xff09;3?? 事件監聽未清除4??…

Java 基礎-30-單例設計模式:懶漢式與餓漢式

在軟件開發中&#xff0c;單例設計模式&#xff08;Singleton Design Pattern&#xff09;是一種常用的設計模式&#xff0c;它確保一個類只有一個實例&#xff0c;并提供一個全局訪問點。這種模式通常用于管理共享資源&#xff08;如數據庫連接池、線程池等&#xff09;或需要…

為 MinIO AIStor 引入模型上下文協議(MCP)服務器

Anthropic 最近宣布的模型上下文協議 &#xff08;MCP&#xff09; 將改變我們與技術交互的方式。它允許自然語言通信替換許多任務的復雜命令行語法。不僅如此&#xff0c;語言模型還可以總結傳統工具的豐富輸出&#xff0c;并以人類可讀的形式呈現關鍵信息。MinIO 是世界領先的…