極速精簡 Go 版 Logstash

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"前言","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"今天來介紹 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"go-zero","attrs":{}}],"attrs":{}},{"type":"text","text":" 生態的另一個組件 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"go-stash","attrs":{}}],"attrs":{}},{"type":"text","text":"。這是一個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"logstash","attrs":{}}],"attrs":{}},{"type":"text","text":" 的 Go 語言替代版,我們用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"go-stash","attrs":{}}],"attrs":{}},{"type":"text","text":" 相比原先的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"logstash","attrs":{}}],"attrs":{}},{"type":"text","text":" 節省了2/3的服務器資源。如果你在用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"logstash","attrs":{}}],"attrs":{}},{"type":"text","text":",不妨試試,也可以看看基於 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"go-zero","attrs":{}}],"attrs":{}},{"type":"text","text":" 實現這樣的工具是多麼的容易,這個工具作者僅用了兩天時間。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"整體架構","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"先從它的配置中,我們來看看設計架構。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"yaml"},"content":[{"type":"text","text":"Clusters:\n - Input:\n Kafka:\n # Kafka 配置 --> 聯動 go-queue\n Filters:\n # filter action\n - Action: drop \n - Action: remove_field\n - Action: transfer \n Output:\n ElasticSearch:\n # es 配置 {host, index}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"看配置名:","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"kafka","attrs":{}}],"attrs":{}},{"type":"text","text":" 是數據輸出端,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"es","attrs":{}}],"attrs":{}},{"type":"text","text":" 是數據輸入端,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"filter","attrs":{}}],"attrs":{}},{"type":"text","text":" 抽象了數據處理過程。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對,整個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"go-stash","attrs":{}}],"attrs":{}},{"type":"text","text":" 就是如 config 配置中顯示的,所見即所得。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/51/51f4eabe016135354564fb3b3149c1a9.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"啓動","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"stash.go","attrs":{}}],"attrs":{}},{"type":"text","text":" 的啓動流程大致分爲幾個部分。因爲可以配置多個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"cluster","attrs":{}}],"attrs":{}},{"type":"text","text":",那從一個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"cluster","attrs":{}}],"attrs":{}},{"type":"text","text":" 分析:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"建立與 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"es","attrs":{}}],"attrs":{}},{"type":"text","text":" 的連接【傳入 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"es","attrs":{}}],"attrs":{}},{"type":"text","text":" 配置】","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"構建 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"filter processors","attrs":{}}],"attrs":{}},{"type":"text","text":"【","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"es","attrs":{}}],"attrs":{}},{"type":"text","text":" 前置處理器,做數據過濾以及處理,可以設置多個】","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"完善對 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"es","attrs":{}}],"attrs":{}},{"type":"text","text":" 中 索引配置,啓動 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"handle","attrs":{}}],"attrs":{}},{"type":"text","text":" ,同時將 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"filter","attrs":{}}],"attrs":{}},{"type":"text","text":" 加入handle【處理輸入輸出】","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"連接下游的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"kafka","attrs":{}}],"attrs":{}},{"type":"text","text":",將上面創建的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"handle","attrs":{}}],"attrs":{}},{"type":"text","text":" 傳入,完成 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"kafka","attrs":{}}],"attrs":{}},{"type":"text","text":" 和 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"es","attrs":{}}],"attrs":{}},{"type":"text","text":" 之間的數據消費和數據寫入","attrs":{}}]}]}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"MessageHandler","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在上面架構圖中,中間的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"filter","attrs":{}}],"attrs":{}},{"type":"text","text":" 只是從 config 中看到,其實更詳細是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MessageHandler","attrs":{}}],"attrs":{}},{"type":"text","text":" 的一部分,做數據過濾和轉換,下面來說說這塊。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以下代碼:https://github.com/tal-tech/go-stash/tree/master/stash/handler/handler.go","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"type MessageHandler struct {\n writer *es.Writer\n indexer *es.Index\n filters []filter.FilterFunc\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個就對應上面說的,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"filter","attrs":{}}],"attrs":{}},{"type":"text","text":" 只是其中一部分,在結構上 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MessageHandler","attrs":{}}],"attrs":{}},{"type":"text","text":" 是對接下游 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"es","attrs":{}}],"attrs":{}},{"type":"text","text":" ,但是沒有看到對 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"kafka","attrs":{}}],"attrs":{}},{"type":"text","text":" 的操作。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"別急,從接口設計上 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MessageHandler","attrs":{}}],"attrs":{}},{"type":"text","text":" 實現了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"go-queue","attrs":{}}],"attrs":{}},{"type":"text","text":" 中 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ConsumeHandler","attrs":{}}],"attrs":{}},{"type":"text","text":" 接口。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏,上下游就串聯了:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"MessageHandler","attrs":{}}],"attrs":{}},{"type":"text","text":" 接管了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"es","attrs":{}}],"attrs":{}},{"type":"text","text":" 的操作,負責數據處理到數據寫入","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"對上實現了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"kafka","attrs":{}}],"attrs":{}},{"type":"text","text":" 的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Consume","attrs":{}}],"attrs":{}},{"type":"text","text":" 操作。這樣在消費過程中執行 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"handler","attrs":{}}],"attrs":{}},{"type":"text","text":" 的操作,從而寫入 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"es","attrs":{}}],"attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"實際上,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Consume()","attrs":{}}],"attrs":{}},{"type":"text","text":" 也是這麼處理的:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"func (mh *MessageHandler) Consume(_, val string) error {\n var m map[string]interface{}\n // 反序列化從 kafka 中的消息\n if err := jsoniter.Unmarshal([]byte(val), &m); err != nil {\n return err\n }\n // es 寫入index配置\n index := mh.indexer.GetIndex(m)\n // filter 鏈式處理【因爲沒有泛型,整個處理都是 `map進map出`】\n for _, proc := range mh.filters {\n if m = proc(m); m == nil {\n return nil\n }\n }\n bs, err := jsoniter.Marshal(m)\n if err != nil {\n return err\n }\n // es 寫入\n return mh.writer.Write(index, string(bs))\n}\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"數據流","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"說完了數據處理,以及上下游的連接點。但是數據要從 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"kafka -> es","attrs":{}}],"attrs":{}},{"type":"text","text":" ,數據流出這個動作從 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"kafka","attrs":{}}],"attrs":{}},{"type":"text","text":" 角度看,應該是由開發者主動 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"pull data from kafka","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那麼數據流是怎麼動起來?我們回到主程序 https://github.com/tal-tech/go-stash/blob/master/stash/stash.go","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其實 ","attrs":{}},{"type":"link","attrs":{"href":"#%E5%90%AF%E5%8A%A8","title":"","type":null},"content":[{"type":"text","text":"啓動","attrs":{}}]},{"type":"text","text":" 整個流程中,其實就是一個組合模式:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"func main() {\n // 解析命令行參數,啓動優雅退出\n ...\n // service 組合模式\n group := service.NewServiceGroup()\n defer group.Stop()\n\n for _, processor := range c.Clusters {\n // 連接es\n ...\n // filter processors 構建\n ...\n // 準備es的寫入操作 {寫入的index, 寫入器writer}\n handle := handler.NewHandler(writer, indexer)\n handle.AddFilters(filters...)\n handle.AddFilters(filter.AddUriFieldFilter(\"url\", \"uri\"))\n // 按照配置啓動kafka,並將消費操作傳入,同時加入組合器\n for _, k := range toKqConf(processor.Input.Kafka) {\n group.Add(kq.MustNewQueue(k, handle))\n }\n }\n // 啓動這個組合器\n group.Start()\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"整個數據流,就和這個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"group","attrs":{}}],"attrs":{}},{"type":"text","text":" 組合器有關了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"group.Start()\n |- group.doStart()\n |- [service.Start() for service in group.services]\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那麼說明加入 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"group","attrs":{}}],"attrs":{}},{"type":"text","text":" 的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"service","attrs":{}}],"attrs":{}},{"type":"text","text":" 都是實現 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Start()","attrs":{}}],"attrs":{}},{"type":"text","text":"。也就是說 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"kafka","attrs":{}}],"attrs":{}},{"type":"text","text":" 端的啓動邏輯在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Start()","attrs":{}}],"attrs":{}},{"type":"text","text":":","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"func (q *kafkaQueue) Start() {\n q.startConsumers()\n q.startProducers()\n\n q.producerRoutines.Wait()\n close(q.channel)\n q.consumerRoutines.Wait()\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":1,"normalizeStart":1},"content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"啓動 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"kafka","attrs":{}}],"attrs":{}},{"type":"text","text":" 消費程序","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"啓動 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"kafka","attrs":{}}],"attrs":{}},{"type":"text","text":" 消費拉取端【可能會被名字迷惑,實際上是從 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"kafka","attrs":{}}],"attrs":{}},{"type":"text","text":" 拉取消息到 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"q.channel","attrs":{}}],"attrs":{}},{"type":"text","text":"】","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"消費程序終止,收尾工作","attrs":{}}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而我們傳入 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"kafka","attrs":{}}],"attrs":{}},{"type":"text","text":" 中的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"handler","attrs":{}}],"attrs":{}},{"type":"text","text":",上文說過其實是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Consume","attrs":{}}],"attrs":{}},{"type":"text","text":",而這個方法就是在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"q.startConsumers()","attrs":{}}],"attrs":{}},{"type":"text","text":" 中執行的:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"go"},"content":[{"type":"text","text":"q.startConsumers()\n |- [q.consumeOne(key, value) for msg in q.channel]\n |- q.handler.Consume(key, value)\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這樣整個數據流就徹底串起來了:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ac/aceeb73e3fb4d7154e6e9e1c6691a968.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"總結","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"作爲 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"go-stash","attrs":{}}],"attrs":{}},{"type":"text","text":" 第一篇文章,本篇從架構和設計上整體介紹 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"go-stash","attrs":{}}],"attrs":{}},{"type":"text","text":" ,有關性能和爲什麼我們要開發一個這樣的組件,我們下篇文章逐漸揭曉。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"https://github.com/tal-tech/go-stash","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"關於 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"go-zero","attrs":{}}],"attrs":{}},{"type":"text","text":" 更多的設計和實現文章,可以持續關注我們。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"https://github.com/tal-tech/go-zero","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"歡迎使用 go-zero 並 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"star","attrs":{}},{"type":"text","text":" 支持我們!","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"微信交流羣","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"關注『微服務實踐』公衆號並回復 進羣 獲取社區羣二維碼。","attrs":{}}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章