簡介: 杭州開課啦教育科技有限公司是一家致力於爲中小學生提供學習輔導的在線教育公司,目前公司後端服務基礎設施主要依託於阿里雲原生,其中包含計算、網絡、存儲以及 Kubernetes 服務。
2020 年是開課啦公司發展壯大的一年,整個公司團隊由原來的幾百人擴充至如今的幾千人,在集中使用的時候基本上會有幾千人同時在運營後臺進行操做,公司原有的內部後臺運營系統是用 PHP 搭建起來的,性能跟業務上已逐漸不能知足公司的需求規劃,加上目前開課啦公司開發部已經作了微服務拆分,主體對外服務是 java 語言的 Dubbo 集羣,後臺系統須要無縫對接 java 的 Dubbo 服務,因此 PHP 已經逐漸不能知足開課啦公司的需求。java
當時本身也調研過 PHP 的 Dubbo 項目,因爲項目已基本無人更新維護因此 pass 掉,後面本身對簡潔高性能的 go 語言感興趣,而後就關注到了 Dubbo-go 項目,通過一段時間的調研以後發現 Dubbo Go 符合咱們的業務須要,而且社區很是的活躍,後面便決定選用 Dubbo-go 做爲後臺的 pc 業務框架。node
可能也有同窗會問爲何不使用跨言支持程度更好的 gRPC 呢,由於不少公司最開始的 RPC 服務集羣都是基於 Dubbo 生態構建的,若是換框架成本太大,因此基本不會考慮,gRPC 雖然跨語言支持程度更好可是不少東西都須要本身造輪子,好比服務註冊,服務發現,日誌監控等。linux
當時在決定選用 Dubbo-go 的時候開發內部也有一些反對的聲音的,爲何不直接轉 java,轉 java 的話就沒有跨語言通訊的問題了,轉 java 的問題在於入門成本高,並且對於整個公司的技術棧來講,保持語言的多樣性,才能更加從容的應對將來的業務變化,Go 自己是一個不弱於 Java 的高性能語言,很是適合微服務架構。程序員
肯定了框架選型後,我接到的首要任務即是要搭建出一套可快速建立業務項目的腳手架,開發出基於 HTTP 協議的 RPC 代理服務,部署須要接入公司的容器化部署平臺,一切都是從零開始,在網上基本上找不到能夠借鑑的資料。docker
首先是要進行 Dubbo-go 項目的架構的規劃,肯定項目目錄結構,通過參考 Dubbo-go Demo 以及其它的 Go 項目最終肯定了項目的目錄結構,如下目錄結構可做爲參考。數據庫
爲了與 Java 服務註冊中心保持一致,Dubbo-go 在項目選型上選用以下組件:後端
爲了增長開發的效率咱們在 provider 服務初始化前能夠對配置進行精簡只保留最基礎的配置就能夠相似下面這種,provider 服務的編碼參考 Dubbo-go demo 就能夠了。centos
下面是服務啓動的 main 方法代碼:緩存
通常使用 Dubbo,provider 端須要暴露出接口和方法,consumer 端要十分明確服務使用的接口定義和方法定義,還有入參返參類型等等信息,還須要基於 provider 端提供的 API,兩端才能正常通訊調用。bash
然而網關的使用場景是並不關心要調用的接口的詳細定義,網關只關注要調用的方法、傳遞的參數、能接收返回結果就能夠了,實現網關代理的基礎是 Dubbo/Dubbo-go 的泛化調用特性。
下面是 Dubbo-go 官方給的 demo,泛化服務加載後須要等待 3 秒才能完成調用,然而在實際使用的時候確定是不能實時加載服務去等待 3 秒,因此在網關應用啓動時就須要加載緩存好須要泛化調的服務。
通過對 Dubbo-go 泛化調用 demo 的研究,發現用該特性設計 dubbo-go 網關是可行的,難點在於咱們須要把每個須要網關代理 RPC 服務方法的參數以及服務的路徑等配置獲取到並緩存起來,這樣才能在調用前初始化好泛化調用服務,一個服務的配置以下。
因爲是用 go 語言作的網關代理,因此不能經過 Java 的 jar 包來獲取到 Java RPC 服務配置,若是經過人工維護的話工做量太大,並且易出錯,顯然是不可接受的。通過一段時間的瞭解,Java 服務能夠經過註解來實現配置的獲取,Java 端在方法上加上註解後啓動服務的時候會將配置信息經過消息發送到 MQ,網關消費這些消息來實現獲取 Java RPC 服務的配置。
Dubbo Go 的 RPC 服務因爲 go 語言不支持註解,因此我通過思考本身寫了一個掃描代碼的小工具,在每一個 RPC 服務方法前加上對應的註釋,經過對註釋的掃描來獲取 RPC 服務的配置,獲取到配置後在項目目錄內生成 RPC 服務配置,啓動應用的時候讀取配置發送到 MQ。
網關代理實現以後還能夠在網關的基礎實現更多的功能,好比 token 驗證、白名單、限流、熔斷、日誌監控功能,網關代理請求實現效果以下:
公司內部的容器化部署環境爲阿里雲的 K8s,部署至 K8s 平臺只須要提供鏡像文件,因爲 Dubbo-go 編譯後是一個二進制的文件,不需任何額外的第三方庫,能在 Docker 環境下穩定運行。有 docker 鏡像文件以下圖所示,能夠用 centos 等任一 linux 發行版做爲 base 鏡像。
LABEL maintainer="<xxx@xx.com>" LABEL version="1.0" LABEL description="KKL-GO-NKO-BASE"` ARG envType=stable #設置環境變量 ENV envType ${envType} #編譯打包好的壓縮包 ADD ./target/nko-base-${envType}.tar.gz /app/ WORKDIR /app EXPOSE 20000
鏡像寫好後提供給發佈平臺,發佈平臺機器啓動鏡像並解壓打包文件,執行 Dubbo-Go 程序 。
Container entrypoint set to [bash, -c, tar -zxf nko-base-stable.tar.gz && SERVER_ENV=kubernetes && sh ./nko-base/bin/load.sh start -group=stable]
因爲開發測試到生產通常是有多個部署環境的,因此咱們須要改動的dubbo-go samples demo 裏的編譯腳本,讓其支持多環境打包。
另外,Dubbo-go 默認註冊的 IP 是 K8s pod 的虛擬 IP,不一樣 K8s 集羣之間網絡是不能互通的,因此若是須要跨集羣調用就須要修改默認註冊 IP,將默認註冊的 pod IP + 端口 修改成 Kubernetes 實體機的 IP 加對應端口,Kubernetes 會在 pod 內寫入實體機的 IP 加對應端口環境變量,應用程序能夠經過讀取環境變量獲取實體機的 IP加端口,若是須要實現此功能須要修改 Dubbo-go 的註冊邏輯。例如以 zookeeper 註冊中心爲例,咱們能夠經過擴展
registery/zookeeper/registry.go的 registerTempZookeeperNode 方法來實現修改註冊 IP 跟端口,代碼以下圖,Dubbo-go 官方將在後面的版本以配置的形式支持自定義註冊 IP 跟端口的功能。
func (r *zkRegistry) registerTempZookeeperNode(root string, node string) error { ... regIp = os.Getenv(constant2.RegistryEnvIP) //實體機的ip regPort = os.Getenv(constant2.RegistryEnvPort) //實體機的端口 urlNode, _ := common.NewURL(node) role, _ := strconv.Atoi(urlNode.GetParam(constant.ROLE_KEY, "")) if role == common.PROVIDER && regIp != "" && regPort != "" { urlNode.Ip = regIp urlNode.Port = regPort node = url.QueryEscape(urlNode.String()) } zkPath, err = r.client.RegisterTemp(root, node) ... }
做者:曾凡維, 一個有 9 年服務端業務開發經驗的一線程序員,曾在騰訊閱文等多家公司擔任後端開發工程師,目前就任杭州開課啦教育科技有限公司,從事 go 語言服務基礎架構和中間件及部分業務開發工做。
原文連接本文爲阿里雲原創內容,未經容許不得轉載