天天看點

開課啦 dubbo-go 微服務更新實戰技術選型背景面臨的挑戰Dubbo-go RPC 服務網關設計容器化部署最後的話

開課啦 dubbo-go 微服務更新實戰技術選型背景面臨的挑戰Dubbo-go RPC 服務網關設計容器化部署最後的話

曾凡維  杭州開課啦教育科技有限公司進階開發工程師

來源 |

阿裡巴巴雲原生公衆号

杭州開課啦教育科技有限公司是一家緻力于為中國小生提供學習輔導的線上教育公司,目前公司後端服務基礎設施主要依托于阿裡雲原生,其中包含計算、網絡、存儲以及 Kubernetes 服務。

技術選型背景

2020 年是開課啦公司發展壯大的一年,整個公司團隊由原來的幾百人擴充至現在的幾千人,在集中使用的時候基本上會有幾千人同時在營運背景進行操作,公司原有的内部背景營運系統是用 PHP 搭建起來的,性能跟業務上已逐漸不能滿足公司的需求規劃,加上目前開課啦公司開發部已經做了微服務拆分,主體對外服務是 java 語言的 Dubbo 叢集,背景系統需要無縫對接 java 的 Dubbo 服務,是以 PHP 已經逐漸不能滿足開課啦公司的需求。

當時自己也調研過 PHP 的 Dubbo 項目,由于項目已基本無人更新維護是以 pass 掉,後面自己對簡潔高性能的 go 語言感興趣,然後就關注到了 Dubbo-go 項目,經過一段時間的調研之後發現 Dubbo Go 符合我們的業務需要,并且社群非常的活躍,後面便決定選用 Dubbo-go 作為背景的 pc 業務架構。

可能也有同學會問為什麼不使用跨言支援程度更好的 gRPC 呢,因為很多公司最開始的 RPC 服務叢集都是基于 Dubbo 生态建構的,如果換架構成本太大,是以基本不會考慮,gRPC 雖然跨語言支援程度更好但是很多東西都需要自己造輪子,比如服務注冊,服務發現,日志監控等。

當時在決定選用 Dubbo-go 的時候開發内部也有一些反對的聲音的,為什麼不直接轉 java,轉 java 的話就沒有跨語言通信的問題了,轉 java 的問題在于入門成本高,而且對于整個公司的技術棧來說,保持語言的多樣性,才能更加從容的應對未來的業務變化,Go 本身是一個不弱于 Java 的高性能語言,非常适合微服務架構。

面臨的挑戰

确定了架構選型後,我接到的首要任務便是要搭建出一套可快速建立業務項目的腳手架,開發出基于 HTTP 協定的 RPC 代理服務,部署需要接入公司的容器化部署平台,一切都是從零開始,在網上基本上找不到可以借鑒的資料。

首先是要進行 Dubbo-go 項目的架構的規劃,确定項目目錄結構,經過參考 Dubbo-go Demo 以及其它的 Go 項目最終确定了項目的目錄結構,以下目錄結構可作為參考。

開課啦 dubbo-go 微服務更新實戰技術選型背景面臨的挑戰Dubbo-go RPC 服務網關設計容器化部署最後的話

為了與 Java 服務注冊中心保持一緻,Dubbo-go 在項目選型上選用如下元件:

  • 使用 zookeeper 作為注冊中心
  • nacos 作為配置中心
  • 資料庫 orm 采用 gorm
  • 消息隊列使用 RocketMQ

為了增加開發的效率我們在 provider 服務初始化前可以對配置進行精簡隻保留最基礎的配置就可以類似下面這種,provider 服務的編碼參考 Dubbo-go demo 就可以了。

開課啦 dubbo-go 微服務更新實戰技術選型背景面臨的挑戰Dubbo-go RPC 服務網關設計容器化部署最後的話

下面是服務啟動的 main 方法代碼:

開課啦 dubbo-go 微服務更新實戰技術選型背景面臨的挑戰Dubbo-go RPC 服務網關設計容器化部署最後的話

Dubbo-go RPC 服務網關設計

一般使用 Dubbo,provider 端需要暴露出接口和方法,consumer 端要十分明确服務使用的接口定義和方法定義,還有入參返參類型等等資訊,還需要基于 provider 端提供的 API,兩端才能正常通信調用。

然而網關的使用場景是并不關心要調用的接口的詳細定義,網關隻關注要調用的方法、傳遞的參數、能接收傳回結果就可以了,實作網關代理的基礎是 Dubbo/Dubbo-go 的泛化調用特性。

下面是 Dubbo-go 官方給的 demo,泛化服務加載後需要等待 3 秒才能完成調用,然而在實際使用的時候肯定是不能實時加載服務去等待 3 秒,是以在網關應用啟動時就需要加載緩存好需要泛化調的服務。

開課啦 dubbo-go 微服務更新實戰技術選型背景面臨的挑戰Dubbo-go RPC 服務網關設計容器化部署最後的話

經過對 Dubbo-go 泛化調用 demo 的研究,發現用該特性設計 dubbo-go 網關是可行的,難點在于我們需要把每一個需要網關代理 RPC 服務方法的參數以及服務的路徑等配置擷取到并緩存起來,這樣才能在調用前初始化好泛化調用服務,一個服務的配置如下。

開課啦 dubbo-go 微服務更新實戰技術選型背景面臨的挑戰Dubbo-go RPC 服務網關設計容器化部署最後的話

由于是用 go 語言做的網關代理,是以不能通過 Java 的 jar 包來擷取到 Java RPC 服務配置,如果通過人工維護的話工作量太大,而且易出錯,顯然是不可接受的。經過一段時間的了解,Java 服務可以通過注解來實作配置的擷取,Java 端在方法上加上注解後啟動服務的時候會将配置資訊通過消息發送到 MQ,網關消費這些消息來實作擷取 Java RPC 服務的配置。

Dubbo Go 的 RPC 服務由于 go 語言不支援注解,是以我經過思考自己寫了一個掃描代碼的小工具,在每個 RPC 服務方法前加上對應的注釋,通過對注釋的掃描來擷取 RPC 服務的配置,擷取到配置後在項目目錄内生成 RPC 服務配置,啟動應用的時候讀取配置發送到 MQ。

網關代理實作之後還可以在網關的基礎實作更多的功能,比如 token 驗證、白名單、限流、熔斷、日志監控功能,網關代理請求實作效果如下:

開課啦 dubbo-go 微服務更新實戰技術選型背景面臨的挑戰Dubbo-go RPC 服務網關設計容器化部署最後的話

容器化部署

公司内部的容器化部署環境為阿裡雲的 K8s,部署至 K8s 平台隻需要提供鏡像檔案,由于 Dubbo-go 編譯後是一個二進制的檔案,不需任何額外的第三方庫,能在 Docker 環境下穩定運作。有 docker 鏡像檔案如下圖所示,可以用 centos 等任一 linux 發行版作為 base 鏡像。

LABEL maintainer="<[email protected]>"
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)
  ...
}           

最後的話

如果使用 dubbo-go 過程中遇到一些問題,可以送出 issue 或者進社群釘釘群進行溝通交流。

個人建議:如果你做好了入坑 Dubbo-go 的準備,最好自己解決這些問題并給官方回報 PR,遇到問題解決問題你才能成長并有所收獲,不是嗎?

歡迎對 apache/dubbo-go 項目有興趣的同學通過搜尋釘釘群号 31363295 加入交流群。

作者簡介

曾凡維(github @jack15083), 一個有 9 年服務端業務開發經驗的一線程式員,曾在騰訊閱文等多家公司擔任後端開發工程師,目前就職杭州開課啦教育科技有限公司,從事 go 語言服務基礎架構和中間件及部分業務開發工作。

2021 年,原 SpringCloud Alibaba Meetup 正式更新為雲原生中間件 Meetup 從議題出品、活動形式、線上體驗三大次元重磅更新。所有話題的 Demo 都會放到知行動手實驗室 start.aliyun.com 供大家實操。

上海站亮點速遞:

  •  Dubbo 3.0 Preview 版正式釋出!

  •  Nacos 2.0 正式釋出!

  •  雲原生混沌工程控制台 ChaosBox 正式開源!

  •  Dapr V1.0 釋出!中文文檔上線!

點選即可報名活動!