歡迎光臨
每天分享高質量文章

golang微服務開發與治理

張曉明 :深知科技

曾就職於中科院計算所,阿裡巴巴,閃電購等企業,目前就職於深知科技,擔任技術架構師職務。從事過c/c++.java,golang開發,目前專心golan

前言

今天的演講主題是:golang 微服務架構與治理實戰。次微服務架構目前已經成為主流的網際網路技術架構方案,深知科技在創業初始階段就採用了微服務架構來開發和部署線上服務,經過一年多的實戰演變,目前我們十幾人的開發團隊,維護和管理近百個微服務,實現了一套快速開發,部署,以及服務治理和追蹤的技術棧。這次我將主要給大家分享我們用到的技術內容以及開源產品的一些使用經驗。


       我們公司去年2月份成立,到現在大約一年半的時間,發展非常迅速,從一開始技術團隊只有3個人,發展到現在的20人左右。我們公司技術棧全部基於golang構建,偏演演算法側的東西很多,偏資料端的東西也很多,後端資料也全部是用golang構建的。我下麵會分享用golang建立這個公司業務中遇到的問題和經驗。創業公司的特點是業務需求變更比較快,就需要很快的把需求完成。

      今天分享的內容,主要是從系統構建,系統從0到1構建過程、微服務架構怎麼樣、微服務我們怎麼做追蹤和治理的,以及我們遇到的開元技術棧和一些總結。

系統從0->1的構建

       一開始構建我們的系統,一幫人在一起創業,做一個網際網路專案,部署這些東西的時候,第一個問題就是用什麼樣的程式語言建立我們這套系統。我們選了golang,因為golang確實在這些方面有些優勢。

      Why go?

       部署更輕量



       第一,我認為golang的部署更輕量,部署的輕量主要體現在兩個方面,第一個方面是它佔用的資源少,golang的微服務可以放在類似於BUSYBOX很輕量的容器裡面,再結合自動化運維工具來部署,管理整個叢集。這樣以來整個開發成本就會非常少,在創業一開始你的業務量其實並沒有那麼多,如果你配置很多的硬體資源成本都比較高昂。每個微服務資源的佔用比較小,體量比較小的時候,在運用運維工具都可以很均衡的把資源分配出去,能夠很好的解決資源分配的問題。

       在編譯和部署上面,時間上面的開銷也非常少,這就帶來了時間上的節省,開發效率更高。



       原始碼更可控

       第二,   golang的原始碼更可控,主要是golang生態圈裡面我們用到第三方工具包大部分是以開源的形式開放給我們,所以我們用到它的時候,我們很容易知道它的原始碼。另外如果它缺少一些特性,我們可以手動新增一些特性。當然這裡面最主要的一個原因,是因為golang從一開始設計的時候有一個很重要的思想,就是這個原始碼寫起來要簡單,不會同一個東西有很多種寫法。這樣在閱讀別人原始碼的時候更輕鬆一些,不會有原始碼看起來很費勁的情況。所以這也確實是golang的一個優勢,就算是在團隊內部互相去看其他同學程式碼的時候,也會發現比之前好很多。

       生態圈內部漸漸成熟,很多優秀的工具



   golang的生態圈裡面有非常多優秀的工具,golang的工具,很多工具都非常優秀,業界鼎鼎大名,包括做分散式日誌追蹤、etcd等等,在各自行業裡都非常好用,給了我們非常大的支援。拋開源本身,我覺得生態作用可能更大一點。

         系統構建與運維

       當時我們構建系統的時候遇到了一些問題,一開始我們沒有專門的運維人員,其實到目前為止我們也沒有專門的運維人員,開發人員數量也有限。在創業初期,我們對開發效率又看的比較高。當然我們有一個好處,在一開始業務量級不像大公司那麼大,所以一開始不太需要考慮大規模的挑戰。

       系統部署框架

       結合我們的業務場景,這是我們構建的部署方案,因為我們最終交付給使用者的產品是一個web端的東西,所以需要把前端程式碼部署在OSS上,把所有後端服務部署到k8s裡面去,大部分還是k8s請求,做各個微服務之間的轉法,微服務之間的通訊我們用的是GRPC和RMQ。

       這是我們整體的部署方案,部署方案完了,我們從一開始定位了運維和開發輔助工具,來幫助我們完成開發。



       在日誌跟蹤方面,因為微服務,你的服務數量又多,又都是分散式的。包括線上預警、資源配置、儲存管理。



微服務架構

        微服務架構



       接下來,我來介紹一下在我們公司內部微服務架構使用的一些實踐。

       首先微服務的架構,現在微服務架構已經成為一個主流,幾乎很少有新的業務場景會使用單體應用的架構樣式,大部分都會從一開始都定義出微服務。就以典型的小電商網站為例,基本架構,以前我要做一個電商網站,程式碼是一套,部署也是一套,幾臺機器。現在我們做了拆分,我們會按照業務率,比如會員註冊、會員登陸、修改密碼,都在會員系統裡,會拆分成很多微服務。

       微服務架構優勢

      微服務有什麼好處?從領域模型緯度上,把原來很複雜的龐大系統拆分成了各個系統,各個系統之間透過這個介面來進行互動,這樣以來各個業務系統變得很清晰。另一個好處是從開發人員的角度,因為這些微服務的拆分,比如我是做交易開發的,我肯定是基於交易的實現。針對會員我只關心介面怎麼給我,但不關心具體實現,我編譯的程式碼也不會影響到它的系統。所以這是微服務拆分的好處。

        微服務粒度劃分

   微服務的粒度劃分,業界都認可的一種方式是按照業務領域模型進行劃分,比如會員系統和交易系統,它們倆在業務上有很強的隔離性,會員就管會員註冊登陸方面,交易主要完成交易。這時候劃分很清晰,大家也不會有什麼意見。但如果交易跟退款,這倆業務模型算不算一個獨立的業務模型?是分開還是合在一起?人們可能會認為它們兩個是獨立的業務領域,這是可以的。我一般的做法是按照開發人員緯度來配,比如退款有專門的團隊或者專門的同學來管理,這時候就會拆分。如果交易和退款是同一個團隊開發的,我覺得拆不拆無所謂,這時候不是很重要的。

       快速構建一個微服務

       再講一下在我們內部如何快速構建一個微服務。強調快速的原因,也是因為在我們內部構建一個微服務是一個很輕鬆平常的事,不像之前體量比較大的時候,或者微服務不那麼微的時候,這時候不太會去建立一個新的應用,除非你開一條新的應用線。在我們公司內部,平均每週會建立一個微服務,每週可能都要建立一個新的微服務,然後再部署上線。我們內部的微服務相對來說都比較簡單,重點是提供介面,第一步定義好微服務的介面,微服務主要分三步走,第一定義介面,第二實現介面定義上線。第一定義介面,在微服務裡面一旦把你的介面定義清楚,整個業務域就定義的很清楚了,介面定義清楚之後其他夥伴就可以拿介面做一些定向開發了。

       還有一個要談的話題是介面可拓展性問題,我們想象一下,在微服務沒有拆分的時候我們把所有程式碼都寫在一個工程裡,這時候這些微服務除了沒有互相隔離開,沒有互相部署,這時候沒有太大的差別。這時候互相呼叫,比如交易系統要去獲取會員的資訊,這時候也會呼叫會員的介面,只是這是一個純程式碼,編譯上就直接呼叫的東西。現在由於微服務的呼叫,我們強行把它部署到兩臺機器上。因為現在微服務的存在,會員已經上線了,交易這邊有一個介面,做一個改動就會帶來問題,所以介面擴充套件性非常重要。這時候如果想要擴充套件介面,只要多加一個欄位都可以。

       說起介面擴充套件性,確實很重要,以前也有參與國其他的,比如我們在定義這個介面的時候,我一開始定義輸入是一個引數端,這時候如果想再加一個引數,我這個介面就得再寫一個XXXV2這樣的版本。另外還有一種場景,我們想要統一去分流你的遠端呼叫的時候成本比較高,所以一開始設計好可擴充套件性很重要。

        至於實現介面部分就是寫具體業務邏輯的事項,這部分主要的意見是要給大家提供豐富的工具,能夠讓大家快速的實現,有一些配置性的東西要提供便捷的方式。比如有一個應用要裂變資料庫,最好你能提供獲取知識庫連線很便捷的方式。因為使用大量的微服務以後,微服務每天配的就會很多,你每天重覆配就會很煩,所以儘量用工具化的方式,以及約定大於配置的方式避免這些問題。  

       最後是配置上線,一個新人或者老人,最好能用自動化配置上線的方式,而不是傳統的方式,我有一個配置上線,我先找運維申請一臺機器資源,再去申請帳號密碼等等。這些東西我們在內部用了一些指令碼和工具自動化解決掉了,會自動分配這些東西。

      快速構建微服務閘道器

        這張圖講了閘道器,前面同學也提到閘道器,這個閘道器是把我們內部的服務轉變,因為我們微服務介面是兩個,所以這部分是透過閘道器來完成的,閘道器用了grpc-gateway的形式,這也是grpc生態下麵開源的庫。它的主要作用是同時提供gRPC服務,這樣就可以實現把IPC和http。典型的場景,像獲取使用者資訊,比如在前端頁面上可能有一個我的資訊,來獲取我自己的郵箱以及頭像資訊。交易系統可能也會獲取這樣的資訊,這時候它們可以走一套服務。另外gRPC還有一個功能,它能夠自動生成檔案。

      微服務間通訊

       proto

       在服務間通訊這個方面,gRPC設計的目的是實現跨語言遠端呼叫的協議,這是官方的示意圖,比如構建一個C++Service,在我們公司使用的場景是同語言的,因為我們內部服務大部分都是golang。我們把client和server統統包在k8s叢集內部,也就是後端服務全都是部署在k8s內部的,結合k8s的API來實現gRPC沒有實現的負載均衡和服務發現這個環節,比如這個客戶端要呼叫這個服務,它拿這個服務的名字去k8s的API查詢服務對應的IP,然後再透過這個傳回值呼叫匹配標的服務。

       RabbitMQ

       另外是RabbitMQ,訊息樣式在微服務間通訊也是非常重要的模型,在網際網路系統和微服務架構比較流行的情況下會非常適用,對於系統的解耦和服務非常有價值。比如當一個訂單支付成功的時候,我可能有其他三個微服務都去訂閱這個訂單支付成功這個訊息,這三個系統根據訂單支付成功分別做了自己的業務邏輯,比如同時賣家發貨等等,這主要是業務通訊的環節,也是微服務通訊中重要的一環。

服務追蹤與治理

       微服務的追蹤跟治理,當微服務數量比較多,而且人員又比較少的時候,服務追蹤、治理就顯得比較重要了。因為確實太多了,如果憑過去一個一個LOW的方式可能管不過來。這時候從服務標準化和工具上面,一方面是把服務變成非常標準,另外一方面是提供各種各樣的工具來管理和運維微服務。

      微服務標準化

       服務標準化,分為4個部分:

      第一是各種命名規範標準化

      第二是工程目錄標準化

      第三是標準化以後可以利用工具生來梳理

      第四是標準化以後開發的效率也會提升

      全鏈路日誌追蹤

      接下來介紹一下服務治理的工具。第一個是全鏈路的日誌追蹤,每個微服務輸入主要有三種,http、grpc、mq,以及會呼叫DB、redis、API、GRPC、MQ。

      這時候我們在每個節點上都打上了外掛,各個都打一些外掛,投入這些外掛採集出這些節點上面的輸入輸出,還有一些效能相關的資料,把這些資料透過一些格來儲存到ES上去。

       我們在這些外掛上幾乎記錄了所有的資訊,每個輸入、輸出以及異常情況。還有每個請求的執行時間、每個請求鏈路以及每個請求的背景關係環境。

      投入把這些鏈路記錄下來就很容易實現一些功能,比如我要回放之前的呼叫情況,我只要拿到我記錄下來的資料就可以進行回放。

      效能監控,異常報警,流量監控

      還有效能監控和異常率監控,比如每個介面錯誤率超過30%就會報警,這樣基於日誌就很容易做出來。還有業務異常監控,比如1萬單突然跌到1千單,就可以報警。還有呼叫鏈路梳理、依賴關係圖、定向問題排查,這些都有相應的定義,也會有相應的支援,包括在問題排查的時候也非常有用,這時候用這種工具回放排查這些問題就非常容易。      這是我們內部全鏈路日誌追蹤的系統,可以精準定位到某一個請求,當時我們傳回到輸入。我們還可以查詢它的兄弟節點以及下游節點的資訊,這樣整個可以在呼叫鏈路上進行遊走,精準找到問題的請求。

       這是uber日誌記錄,它是分散式日誌追蹤系統,現在被納入一個雲基金會的專案,最近也比較火。它可以把呼叫鏈路梳理的非常清楚,比如gRPC的呼叫下麵產生了四條呼叫,又呼叫了其他的gRPC,然後又做了其他的邏輯,裡面可以看到很詳細的資訊,這樣整個鏈路看得很清楚,並且你的日誌都有背景關係,非常容易對。在之前,如果你沒有加入關聯工具,挨個去找這些日誌也是一件非常頭疼的事情。

       這是基於全鏈路的日誌追蹤做的,像呼叫次數、呼叫耗時以及接入率的監控與報警。


開源技術棧使用經驗

       最後給大家講一下我們用到的一些開源的技術。在這一年過程中,確實感覺到開源技術給我們帶來的用處非常大,很多都是依賴這些東西,尤其是小公司沒有精力造輪子,往往是踩著別人的輪子。gRPC都講過,是遠端的呼叫。prest是一個很強大的東西,但是也有很大的風險,它可以把資料庫裡面的表或者試圖直接暴露出http的介面,基於這個業務場景就可以傳回一個表,我們完全可以不寫任何一個程式碼,透過prest暴露出來。它的前身是postgrest,但因為後面懂h語言的人越來越少,懂golang的人越來越多,於是就把它遷移到golang上面。gorm和etcd大家也瞭解。grafana支援現在所有主流的資料源,你可以配置一些監控報警,比如我前面給大家展示的那幾個圖上,都是透過它來做的。如果你有業務需求,自己內部業務系統的資料分析可以拿它當一個圖表工具來用。influxdb是一個資料庫,效能非常好,以及還有kubernetes。我們公司運維基本上沒有人,所以也沒有人專門做這個事情,開發的人也不願意做這些事情,基本上透過開發工具和指令碼來解決這些問題。

創業公司技術棧選擇

       最後是創業公司技術棧選擇的一些思考。創業公司相對來說在技術棧選擇上面可選的空間更大一點,因為它沒有那麼大的業務包袱,也沒有人員的包袱,這時候往往選技術棧就可以選擇更合適、更順手的,選擇的空間大一點。所以選一個好的輪子也就非常重要了,輪子選的好,業務和系統跑的就會更快一點,跑的塊才能活下來。在業務上面我們肯定要想辦法跑的快,效能儘量按需最佳化。構建好領域模型,我認為系統架構拋開那些常規的,比如效能、運維、前後端不談,內部實現最大的挑戰往往是在你的業務領域模型上面,你的業務是什麼樣子,你根據這個業務構建的系統應該是什麼樣子,這部分挑戰才是最大的,所以領域模型的構建是非常關鍵的一環。剩下就是常規的迭代速度和保持簡單的技術方案,不要用太複雜的設計,別人都看不懂,你設計一個東西別人讀起來也很麻煩,這樣無論團隊協作還是業務系統都不是很有利的方案。

Q&A;

       提問:我們現在也在做golang微服務的技術選型,您也提到了go-kit,現在有一些微服務開源的東西,像go-kit,您能不能大概講一下為什麼當初你們選型的時候沒有選這兩個,而選了go-kit?

   

   張曉明:我個人不太喜歡框架類的東西,框架的東西是什麼概念呢?他給你一個筐子你來填。我喜歡工具類的東西,他給你一個工具你來用,你想用就用,不想用就不用。因為我最註重開發效率,你怎麼樣能夠跑的更快,所以我們覺得它對我們效率支撐有限,另外我們希望能夠用工具化的方式來解決這些問題,其實本身golang的框架就已經很簡單了。比如我們有資料庫的管理服務,就可以為我們每個服務建立資料庫的帳號密碼。像這種東西通用的框架很難做,所以我們自己做,因為這個也花不了太多時間。

   

   提問:微服務現在對創業公司來說,可能我要把微服務玩轉還是有點難度,可能開發起來相對方便一點,但是出現問題呼叫鏈比較長,你們這邊有什麼比較好的經驗?

   

   張曉明:你可以加我微信,我一會兒發你具體的名字,它叫jaeger,他能夠把你所有的日誌都記錄下來,統一儲存,並且提供到這樣的介面中來做查詢。這些都可以點開,這就是一個典型的呼叫鏈,每個呼叫鏈上面點開以後可以看到裡面詳細的資訊,因為我們做的比較全,把每個請求全都記下來了,所以都能夠看見,你還能夠看見這個請求具體發生在哪裡,以及它當時的耗時等等問題。所以整個鏈路追蹤起來非常爽,完全不需要擔心這個問題,你以前即便是單體應用,這個日誌查起來也很麻煩,尤其是服務內部的呼叫鏈路,就很難去管理。用這種微服務再結合這種jaeger,這個呼叫鏈就很容易看見,看一眼就能夠知道它是乾什麼的,很清晰。

   

   提問:你說是全鏈路的日誌追蹤,所有的請求你都全部記錄,日誌安全能不能保證?因為推特資訊洩露就是從日誌當中洩露的。

   

   張曉明:你說的日誌安全是什麼意思?是伺服器被入侵了,日誌被偷走了?

   

   提問:大概是這個意思。

   

   張曉明:首先我這個日誌在我公司內部的,它的安全性跟資料庫的安全性是一樣的,如果我的伺服器被入侵了,資料庫一樣會洩露資訊。這些日誌的記錄,它是我們運維排查的工具,它跟資料庫是同樣的安全性。

   

   提問:我想補充一下,資料庫上面的密碼一般會加嚴,但日誌上的密碼有可能會明文打出來。

   

   張曉明:主要是在使用者登陸場景上面,他會輸入密碼並且傳遞過來,只有這個地方有風險,這個地方我們做了單獨的處理。

   

   主持人:你那邊說全鏈路追蹤,它會把呼叫記錄等等都會記錄下來,這個是在jaeger裡面把這些資訊記下來的嗎?

   

   張曉明:有很多節點,比如gorm會記,發訊息接訊息的端都會記等等。我們每個服務,比如http每個服務暴露都是統一的,比如我們基礎的服務框架,我們內部會分一下http,我們會把gorm包一下就能夠記下來了。像gRPC也是往裡面打plugin,gRPC會提供一個外掛的介面。資料庫訪問主要是註冊回呼事件,透過這種方式來記錄。

   

   主持人:所以每個請求都包了一層plugin?

   

   張曉明:對。

   

   主持人:你們現在全鏈路的東西基本上都是在你們應用層面裡面調一些中間庫的時候,這個中間庫裡包了一層?

   

   張曉明:是的。

   

   提問:微服務裡面使用者許可權怎麼控制?內部不控制還是怎麼樣?

   

   張曉明:我們內部是不控制的。

   

   提問:剛才說到訂閱,訂閱者掛掉一段時間怎麼補前面的訊息?

   

   張曉明:訊息機制有專門的伺服器,會維護訊息訂閱的狀態,這個訊息並不是直接投到訂閱,裡面會有訊息伺服器,訊息伺服器會負責發給訂閱者。

   

   提問:訊息比較多了,後面的比如……

   

   張曉明:訊息會有堆積的情況,比如這邊訂閱者掛了或者訂閱者消費能力非常弱,這時候有可能會出現訊息堆積,我們用RabbitMQ的介面做了監控和報警,所以訊息堆積也是微服務裡面經常出現的問題,尤其這裡面某一個服務出現了問題,這時候會導致訊息堆積,訊息堆積我們就用報警的方式,及時發現。

   

   提問:我並不關心真的訊息丟瞭如何模擬出來補起來的過程,因為以前碰到過,也沒有很好的辦法來解決。

   

   張曉明:這時候我們一般是拿一個指令碼補一下,直接拿一個指令碼發一個訊息是很容易的。

   

   主持人:golang裡面有nsq,當初你們的選擇,如果所有技術棧都是基於golang,選擇nsq可能更好一點,選擇RabbitmQ你們當時選擇的時候處於怎麼考慮?

   

   張曉明:一個是因為RabbitMQ用起來還蠻好用的,之前有使用經驗,也沒有出現過問題,另一個就是rmq有中間伺服器。



贊(0)

分享創造快樂