作者 | 冉小龍
審校 | Anonymitaet
編輯 | Susan
閱讀本文需要約 8 分鐘。
- 導(dǎo)讀 -
在學(xué)習(xí)如何編寫熏迹、部署 Go Function 之前,先向大家介紹一下 Go Function 的實(shí)現(xiàn)思路。
在一篇文章了解 Pulsar Functions 中我們提到窿侈,F(xiàn)unction 作為 Pulsar 的計算層,其實(shí)現(xiàn)思路主要有如下兩種:
SDK
Plugin
之前介紹的 Java Function 和 Python Function 使用 Plugin 的形式實(shí)現(xiàn)秋茫,本篇介紹的 Go Function 卻使用 SDK 的形式提供給用戶使用史简。為什么我們不與 Java Function 和 Python Function 采用相同的形式卻要在 Go Function 中另辟蹊徑,以 SDK 的形式提供給用戶使用呢肛著?
Go Function 使用 SDK 的原因
1
Go 不支持動態(tài)反射圆兵。這是語言層面本身的問題,Go 是一門靜態(tài)類型的語言枢贿,雖然支持了反射的功能衙傀,但反射的支持相對較弱,不支持動態(tài)反射的功能萨咕。
這一點(diǎn)與 Java 和 Python 不同统抬,Python 是動態(tài)類型的語言,動態(tài)加載本身就是它的強(qiáng)項所在危队。Java 雖然是靜態(tài)類型的語言聪建,但支持 ClassLoader 可以實(shí)現(xiàn)類似動態(tài)反射的功能。
所以在 Go 中我們沒辦法直接將用戶的代碼邏輯動態(tài)加載到 Pulsar Functions 中來執(zhí)行茫陆。
2
Go Plugin 不成熟金麸。Go 1.8 官方實(shí)現(xiàn)了 Go Plugin,支持動態(tài)加載的特性簿盅,但是并不成熟挥下,在調(diào)研過程中發(fā)現(xiàn)的主要問題如下:
如果注入了一些非法模塊,會帶來一定的安全隱患桨醋,如何防范它們?
給系統(tǒng)帶來一些不穩(wěn)定因素棚瘟。如果模塊出現(xiàn)問題,則會導(dǎo)致服務(wù)崩潰喜最。
它給版本管理帶來了一定的困難偎蘸,特別是在微服務(wù)場景下,相同的服務(wù)瞬内,加載了不同的插件迷雪,如何做版本的管理?
社區(qū)相對不成熟虫蝶。
基于以上考量章咧,我們使用了 SDK 的形式來做 Go Function 的實(shí)現(xiàn)。
編寫 Go Function
Go Function 使用 SDK 實(shí)現(xiàn)能真,將 Function 的接口以 SDK 的形式對外暴露給用戶赁严,在使用 Go Function 之前需要 import "github.com/apache/pulsar/pulsar-function-go/pf"调限,使用方式具體如下:
import (
"context"
"fmt"
"github.com/apache/pulsar/pulsar-function-go/pf"
)
func HandleRequest(ctx context.Context, input []byte) error {
fmt.Println(string(input) + "!")
return nil
}
func main() {
pf.Start(HandleRequest)
}
在上述示例代碼中,將輸入的 input 加 ! 后打印误澳,第一個參數(shù)為一個 context 對象,當(dāng)用戶編寫的 Function 需要與 Go Function 進(jìn)行交互時秦躯,可以加入該參數(shù)忆谓,使用示例如下:
if fc, ok := pf.FromContext(ctx); ok {
fmt.Printf("function ID is:%s, ", fc.GetFuncID())
fmt.Printf("function version is:%s\n", fc.GetFuncVersion())
}
在 main() 中,用戶只需要將編寫的 Function name 注冊到 Start() 中踱承,需要注意的是倡缠,Start() 中只能接收一個函數(shù)的名字。Go Function 會根據(jù)接收到的 Function name 利用 Go 的反射來驗(yàn)證用戶實(shí)現(xiàn)的參數(shù)列表和返回值列表是否正確茎活。
為了方便驗(yàn)證昙沦,需要規(guī)定用戶輸入的參數(shù)列表與返回值列表具體為什么,Go Function 目前支持如下形式的函數(shù)樣例:
func ()
func () error
func (input) error
func () (output, error)
func (input) (output, error)
func (context.Context) error
func (context.Context, input) error
func (context.Context) (output, error)
func (context.Context, input) (output, error)
在一切準(zhǔn)備就緒之后载荔,Go Function 會啟動 channel 來源源不斷地接收從 inputs topic 中傳入的數(shù)據(jù)盾饮。需要說明的是,用戶無需關(guān)心該 channel 關(guān)閉的時機(jī)懒熙,因?yàn)樵?Function 的使用場景下丘损,一旦啟動就會源源不斷的來處理輸入的數(shù)據(jù),用戶可以使用 Ctrl+C 來終止整個程序的運(yùn)行工扎,或者可以通過參數(shù) killAfterIdleMs 來配置該 Function 運(yùn)行的時長徘钥,單位為毫秒。
部署 Go Function
Go Function 的實(shí)現(xiàn)依賴于 pulsar-client-go肢娘,在運(yùn)行 Go Function 之前呈础,需要保證 pulsar-client-go 正確安裝,具體安裝參考 pulsar-client-go 安裝橱健。
pulsar-client-go:
https://pulsar.apache.org/docs/en/client-libraries-go/
Go Function 的實(shí)現(xiàn)形式雖然同 Java Function 與 Python Function 不同而钞,但是為了降低用戶的部署成本,對外暴露給用戶的部署方式是相同的拘荡,具體操作如下:
啟動 Pulsar笨忌。
編譯 Go Function。
$ go build [your function file name].go
3\. 啟動 Go Function俱病。
./bin/pulsar-admin functions localrun/create
--go [your go function path]
--inputs [input topics]
--output [output topic]
--tenant [default:public]
--namespace [default:default]
--name [custom unique go function name]
注意:
與 Java Function 和 Python Function 不同的是官疲,在 Go Function 中,用戶無需指定 classname亮隙。
在 Java Function 中途凫,--jar 上傳的是打包好的 jar 文件;在 Python Function 中 --py 所上傳的是 python 文件溢吻;無論哪種形式维费,上傳的都屬于 user code果元。而 Go Function 中 --go 所上傳的是一份編譯好的可執(zhí)行文件,其中包含了 user code 和 Function 本身的代碼犀盟。
Go Function 目前不支持的功能如下:
Schema而晒,目前 input 和 output 只允許為 []byte
Metrics
Secrets
Authentication & Authorization
更多關(guān)于 Pulsar 的技術(shù)干貨和產(chǎn)品動態(tài),請關(guān)注 StreamNative 微信公眾號阅畴。
下一場 Pulsar meetup 于 6/29 在深圳舉辦倡怎,歡迎來現(xiàn)場了解 Pulsar。