Go 讓 Apache APISIX 如虎添翼

爲什麼是 Go

Apache APISIX 允許用戶通過插件的方式來拓展功能,如鑑權、限流、請求改寫等核心功能都是通過插件的方式實現的。雖然 Apache APISIX 核心代碼是使用 Lua 編寫的,但是 Apache APISIX 支持多語言開發插件,比如 Go 、Java。

這篇文章將詳細講解如何用 Go 來開發 Apache APISIX 插件。通過擁抱 Go 的生態圈,爲 Apache APISIX 開創一片新天地,希望 Go 能讓 Apache APISIX 如虎添翼!

安裝

採用庫的方式來使用 Go Runner,apisix-go-plugin-runner 中的 cmd/go-runner 官方給出的例子,展示該如何使用 Go Runner SDK。未來也會支持通過 Go Plugin 的機制加載預先編譯好的插件。

開發

使用 Go Runner SDK 進行開發

$ tree cmd/go-runner
cmd/go-runner
├── main.go
├── main_test.go
├── plugins
│   ├── say.go
│   └── say_test.go
└── version.go

上面是官方示例的目錄結構。main.go 是入口,其中最關鍵的部分在於:

cfg := runner.RunnerConfig{}
...
runner.Run(cfg)

RunnerConfig 可以用來控制日誌等級和日誌輸出位置。

runner.Run 會讓應用監聽目標位置,接收請求並執行註冊好的插件。應用會一直處於這一狀態直到退出。

打開 plugins/say.go

func init() {
  err := plugin.RegisterPlugin(&Say{})
  if err != nil {
     log.Fatalf("failed to register plugin say: %s", err)
  } 
}

由於 main.go 導入了 plugins 包,

import (
  ...
  _ "github.com/apache/apisix-go-plugin-runner/cmd/go-runner/plugins"
  ...
)

這樣就在執行 runner.Run 之前通過 plugin.RegisterPlugin 註冊了 Say

Say 需要實現以下方法:

Name 方法返回插件名。

func (p *Say) Name() string {
  return "say"
}

ParseConf 會在插件配置變化的時候調用,解析配置並返回插件特定的配置上下文。

func (p *Say) ParseConf(in []byte) (interface{}, error) {
  conf := SayConf{}
  err := json.Unmarshal(in, &conf)
  return conf, err
}

該插件的上下文是這樣的:

type SayConf struct {
  Body string `json:"body"`
}

Filter 會在每個配置了 say 插件的請求中執行。

func (p *Say) Filter(conf interface{}, w http.ResponseWriter, r pkgHTTP.Request) {
  body := conf.(SayConf).Body
  if len(body) == 0 {
     return
  }

  w.Header().Add("X-Resp-A6-Runner""Go")
  _, err := w.Write([]byte(body))
  if err != nil {
     log.Errorf("failed to write: %s", err)
  }
}

可以看到Filter 把配置裏面的Filter 的值作爲響應體。如果在插件中直接進行響應,就會中斷請求。

Go Runner SDK  API 文檔:https://pkg.go.dev/github.com/apache/apisix-go-plugin-runner

把應用構建起來後(在示例裏面是 make build),在運行時需要設置兩個環境變量:

  1. APISIX_LISTEN_ADDRESS=unix:/tmp/runner.sock

  2. APISIX_CONF_EXPIRE_TIME=3600

像這樣:

APISIX_LISTEN_ADDRESS=unix:/tmp/runner.sock APISIX_CONF_EXPIRE_TIME=3600 ./go-runner run

應用運行時會去監聽 /tmp/runner.sock

設置 Apache APISIX (開發)

首先要安裝 Apache APISIX,需要和 Go Runner 位於同一實例上。

上圖左邊是 Apache APISIX 的工作流程,右邊的 plugin runner 負責運行不同語言編寫的外部插件。apisix-go-plugin-runner 就是這樣支持 Go 語言的 runner。

當你在 Apache  APISIX 中配置一個 plugin runner 時,Apache  APISIX 會把 plugin runner 作爲自己的一個子進程,該子進程與 Apache  APISIX 進程屬於同一個用戶,當我們重啓或重新加載 Apache APISIX 時,plugin runner 也將被重啓。

如果爲一個給定的路由配置了 ext-plugin-* 插件,擊中該路由的請求將觸發 Apache APISIX 通過 unix socket 向 plugin runner 執行 RPC 調用。調用細分爲兩個階段:

根據需要配置 plugin runner 的執行時機。

plugin runner 會處理 RPC 調用,在其內部創建一個模擬請求,然後運行其他語言編寫的插件,並將結果返回給 Apache APISIX。

這些插件的執行順序是在 ext-plugin-* 插件配置項中定義的。像其他插件一樣,它們可以被啓用並在運行中重新定義。

爲了展示如何開發 Go 插件,我們先設置 Apache  APISIX 進入開發模式。在 config.yaml 中增加以下配置:

ext-plugin:
  path_for_test: /tmp/runner.sock

這個配置的意思是,命中路由規則後,Apache APISIX 會向 /tmp/runner.sock 發起 RPC 請求。

接下來設置路由規則:

curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
  "uri": "/get",
  "plugins": {
    "ext-plugin-pre-req": {
       "conf": [
         {"name":"say", "value":"{\"body\":\"hello\"}"}
       ]
    } 
  },
  "upstream": {
         "type": "roundrobin",
         "nodes": {
             "127.0.0.1:1980": 1
         }
     }
}
'

注意插件名稱配置在 name 裏面,插件配置(經 JSON 序列化後)放在 value 裏面。

如果在開發過程中看到 Apache  APISIX 端有 refresh cache and try again 的 warning 和 Runner 端有 key not found 的 warning,這是因爲配置緩存不一致導致的。因爲開發狀態下,Runner 不是由 Apache  APISIX 管理的,所以內部狀態會有可能不一致。不用擔心,Apache  APISIX 會重試。

然後我們請求一下:curl 127.0.0.1:9080/get

$ curl http://127.0.0.1:9080/get
HTTP/1.1 200 OK
Date: Mon, 26 Jul 2021 11:16:11 GMT
Content-Type: text/plain; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
X-Resp-A6-Runner: Go
Server: APISIX/2.7

hello

可以看到接口返回 hello 而且沒有訪問到任何上游。

設置 Apache APISIX (運行)

這裏以 go-runner 爲例,只需把運行命令行配置在 ext-plugin 裏就可以運行了:

ext-plugin:
  \# path_for_test: /tmp/runner.sock
  cmd: ["/path/to/apisix-go-plugin-runner/go-runner""run"]

Apache APISIX 會把 plugin runner 作爲自己的一個子進程,管理它的整個生命週期。

注意:這時就不要配置 path_for_test 了。Apache APISIX 在啓動 runner 時會自動分配一個 unix socket 地址供 runner 監聽。APISIX_LISTEN_ADDRESS 和 APISIX_CONF_EXPIRE_TIME 這兩個環境變量也不用手動設置。

總結

目前 Go Plugin Runner 還處於早期開發階段,我們會陸續完善其功能。成功的開源項目離不開大家的貢獻,歡迎各位參與到 apisix-go-plugin-runner 的開發中來,讓我們一起共建 Apache  APISIX 和 Go 的橋樑!

apisix-go-plugin-runner 的項目地址:https://github.com/apache/apisix-go-plugin-runner

關於 Apache APISIX

===================

Apache APISIX 是一個動態、實時、高性能的開源 API 網關,提供負載均衡、動態上游、灰度發佈、服務熔斷、身份認證、可觀測性等豐富的流量管理功能。Apache APISIX 可以幫忙企業快速、安全的處理 API 和微服務流量,包括網關、Kubernetes Ingress 和服務網格等。

全球已有數百家企業使用 Apache APISIX 處理關鍵業務流量,涵蓋金融、互聯網、製造、零售、運營商等等,比如美國航空航天局(NASA)、歐盟的數字工廠、中國航信、中國移動、騰訊、華爲、微博、網易、貝殼找房、360、泰康、奈雪的茶等。

200 餘位貢獻者,一同締造了 Apache APISIX 這個世界上最活躍的開源網關項目。聰明的開發者們!快來加入這個活躍而多樣化的社區,一起來給這個世界帶來更多美好的東西吧!

👆🏻關注公衆號,第一時間瞭解 Apache APISIX 技術乾貨、社區活動。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/0m2fDheMuvMR8cHwmJYgeA