Apache Pulsar 之 Go Function

在学习如何编写、部署 Go Function 之前,先向大家介绍一下 Go Function 的实现思路。

在 一篇文章了解 Pulsar Function 中我们提到,Function 作为 Pulsar 的计算层,其实现思路主要有如下两种:

  • SDK
  • Plugin

在之前介绍的 Java Function 和 Python Function 使用 Plugin 的形式实现,本篇介绍的 Go Function 却使用 SDK 的形式提供给用户使用。为什么我们不与 Java Function 和 Python Function 采用相同的形式却要在 Go Function 中另辟蹊径,以 SDK 的形式提供给用户使用呢?

Go Function 使用 SDK 的原因

首先是语言层面本身的问题,Go 是一门静态类型的语言,虽然支持了反射的功能,但反射的支持相对较弱,不支持动态反射的功能。这一点与 Java 和 Python 不同,Python 是动态类型的语言,动态加载本身就是它的强项所在。Java 虽然是静态类型的语言,但支持 ClassLoader 可以实现类似动态反射的功能。所以在 Go 中我们没办法直接将用户的代码逻辑动态加载到 Pulsar Functions 中来执行。

Go1.8 官方实现了 Go Plugin,支持动态加载的特性,但是并不成熟,在调研过程中发现的主要问题如下:

  • 如果注入了一些非法模块,会带来一定的安全隐患,如何防范它们?
  • 给系统带来一些不稳定因素。如果模块出现问题,则会导致服务崩溃。
  • 它给版本管理带来了一定的困难,特别是在微服务场景下,相同的服务,加载了不同的插件,如何做版本的管理?
  • 社区相对不成熟。

基于以上考量,我们使用了 SDK 的形式来做 Go Function 的实现。

编写 Go Function

Go Function 使用 SDK 实现,将 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! 后打印,第一个参数为为一个context对象,当用户编写的 Function 需要与 Go Function 进行交互时,可以加入该参数,使用示例如下:

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() 中只能接收一个函数的名字。Go Function 会根据接收到的 function name 利用 go 的反射来验证用户实现的参数列表和返回值列表是否正确。为了方便验证,需要规定用户输入的参数列表与返回值列表具体为什么,Go Function 目前支持如下形式的函数样例:

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)

在一切准备就绪之后,Go Function 会启动 channel 来源源不断的接收从 inputs topic 中传入的数据。需要说明的是,用户无需关心该 channel 关闭的时机,因为在 Function 的使用场景下,一旦启动就会源源不断的来处理输入的数据,用户可以使用 Ctrl+C 来终止整个程序的运行,或者可以通过参数 killAfterIdleMs 来配置该 Function 运行的时长,单位为毫秒。

部署 Go Function

Go Function 的实现依赖于 pulsar-client-go,在运行 Go Function 之前,需要保证 pulsar-client-go 被正确安装,具体安装参照:pulsar-client-go 安装

Go Function 的实现形式虽然同 Java Function 与 Python Function 不同,但是为了降低用户的部署成本,对外暴露给用户的部署方式是相同的,具体操作如下:

  1. 启动 Pulsar。
  2. 编译 Go Function。
$ go build [your function file name].go 
  1. 启动 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 所上传的是一份编译好的可执行文件,其中包含了 user code 和 Function 本身的代码。

Go Function 目前不支持的功能如下:

  • Schema,目前 input 和 output 只允许为 []byte
  • Metrics
  • Secrets
  • thentication & Authorization

你可能感兴趣的:(Apache Pulsar 之 Go Function)