不一樣的go語言-athens源碼概覽


前言

  上一篇文章介紹了athens私服的安裝以及vgo download protocol的簡要介紹。本文着重介紹go proxy sever的實現原理以及athens是如何實現的。

go get原理

  當GOPROXY沒有設置的時候,通過-x參數,可以看到go get獲取module的詳細過程。

[eventer@localhost]# go get -x github.com/gin-gonic/gin@v1.3.0

  對於git來說,go依賴於git命令,通過git命令的組合獲取module庫的元數據及各版本源碼包。而其中的第一步在於向源碼倉庫獲取module的元數據。

[eventer@localhost]# curl -sSL 'https://swtch.com/testmod?go-get=1'
<!DOCTYPE html>
<meta name="go-import" content="swtch.com/testmod mod https://swtch.com/testmodproxy">
Nothing to see here.

  go cli根據返回的元數據從指定的地址獲取module,說白了就是在本地執行git的各個命令,跟大家平時從源碼庫拿代碼的過程差不多一樣。

  當GOPROXY被設置的時候,按照《Defining Go Modules》一文中關於proxy server的定義,情況發生了一些變化,而這也正是athens所要實現的內容。

athens概述&流程

  按照vgo download protocol中的定義,go proxy server是一個高效、可用、安全,且遵循module格式標准、下載協議、本地緩存以及支持按需下載的代理服務。顯然,這是一個構件系統的定義,而athens也正是朝着這個目標實現的。

  但由於go get與go mod命令的設定及其主動獲取這些特征,使得其與java陣營的nexus、jfrog不同。java的庫是由開發者主動deploy到公有倉庫或私有倉庫中,程序構建的時候再根據pom或gradle配置文件的聲明從倉庫獲取指定的package。而go則省略了第一步,直接在構建的時候由go get或go mod根據go.mod文件的聲明從源碼庫中獲取module。因而這就意味着athens首先必須實現從當前流行的源碼庫中獲取公開、私有的module,比如github、gitlab、bitbuckt;又要考慮如何從私有的源碼庫中獲取module。

  所以顯而易見,athens需要實現的功能列表如下:

功能項 性質 功能說明
下載協議 必需 4個必需接口,2個可選接口
本地存儲 必需 module存儲方式,本地磁盤or內存。athens都支持, 可配置
雲端存儲 增強 module存儲方式,支持gcp、minio、mongo、s3、AzureBlob,可配置
公倉用戶認證 必需 github token
私倉用戶認證 必需 SVN、Bazaar、Bitbucket、github、gitlab
版本控制 增強 提供方案控制哪些module的哪些版本使用代理、是否可用等
日志跟蹤 增強 使用opencensus實現
並發控制 增強 多個並發請求同一個module,處理第一個請求,后續請求等待並獲取結果
健康檢測 增強 實現服務狀態接口
管理功能 增加 實現查詢module若干接口

  那么按照預想,go get指令的流程如下:

image

  而再次獲取同一個版本的module時,流程如下:

image

  通過代理取包的過程其實也很簡單。athens按照約定,提供了4個或6個接口供go get指令使用。當GOPROXY被設置時,go get切換至新流程,如下(goproxy.io是proxy server):

  1. https://goproxy.io/github.com/gin-gonic/gin/@v/list
  2. https://goproxy.io/github.com/gin-gonic/gin/@v/v1.3.0.info
  3. https://goproxy.io/github.com/gin-gonic/gin/@v/v1.3.0.mod
  4. https://goproxy.io/github.com/gin-gonic/gin/@v/v1.3.0.zip

  這組協議對應至本地文件系統的一組目錄$GOPATH/pkg/mod/cache/download,這里保存了對應上述4個接口的文件,這4個文件內容可以到這個目錄下自行查看,它們的格式即是協議描述的內容。

接口實現

  athens本身是一個web服務,采用gorilla框架實現。main.go位於cmd/proxy包下,關鍵代碼讀取配置文件,然后根據配置文件參數初始化程序。

//讀入配置文件
conf, err := config.Load(*configFile)
if err != nil {
	log.Fatalf("could not load config file: %v", err)
}

//根據配置初始化程序
handler, err := actions.App(conf)
if err != nil {
	log.Fatal(err)
}

  在app.go文件中,配置了storage、github token、NETRCPath、HGRCPath、log、FilterFile、路由注冊。auth.go中的代碼將NETRCPath、HGRCPath聲明的文件內容轉寫到當前用戶home目錄下的預定位置;而關鍵的路由注冊,則由下面的代碼完成,調用的是app_proxy.go中的addProxyRoutes方法。

if err := addProxyRoutes(
	proxyRouter,
	store,
	lggr,
	conf,
); err != nil {
	err = fmt.Errorf("error adding proxy routes (%s)", err)
	return nil, err
}

  addProxyRoutes方法注冊了的路由如下:

路由 說明
/ 首頁
/healthz 健康檢測
/readyz -
/version athens版本
/catalog 所有module列表

  在這之后,定義了GoGetFetter用於處理module的下載、upstream vcs監聽器、並發控制器、vgo download protocol協議實現。

handlerOpts := &download.HandlerOpts{Protocol: dp, Logger: l}
//RegisterHanlders方法注冊了list、version.info、version.mod、version.zip這4個接口的路由
download.RegisterHandlers(r, handlerOpts)

  athens包說明

用途 描述
pkg/config 配置文件對應實體 存取配置文件參數
pkg/download/ vgo download protocol協議實現 核心入口
pkg/errors errors統一定義及堆棧跟蹤 良好的錯誤設計,可以快速定位到出錯的包、方法
pkg/log logrus日志框架集成 -
pkg/middleware 中間件 module緩存、日志、請求驗證、module獲取策略
pkg/module module獲取策略、倉庫源碼獲取、zip獲取實現 -
pkg/observ 日志及統計數據輸出 datadog
pkg/paths module path解析工具 -
pkg/stash module獲取與存儲包裝類及module並發請求控制 -
pkg/storage 存儲實現 包括內存、本地磁盤、數據庫(mongodb)、文件系統(afero抽象文件系統)、雲存儲(s3,gcp,minio)等

  module獲取策略

定義 說明
module.Exclude 排除 忽略對指定包的請求
module.Include - 不走代理,按常規模式獲取包,本地私倉使用此配置
module.Direct - 走代理

  獲取module流程圖

獲取module流程圖


  在athens的實現中,各個包之間的調用關系如下:

包調用關系


  GET baseURL/module/@v/list時序圖

GET baseURL/module/@v/list時序圖


  GET baseURL/module/@v/version.info時序圖

GET baseURL/module/@v/version.info時序圖


baseURL/module/@v/version.mod與baseURL/module/@v/version.zip的過程與baseURL/module/@v/version.info一致,只是調用不同的實現而已。

歡迎關注個人公眾號

不一樣的go語言


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM