- 云原生
- 后端
- 微服务
【免费下载链接】fn
The container native, cloud agnostic serverless platform.
本篇指南以 Fn Project 官方仓库的CONTRIBUTING.md为骨架,面向有意向 Fn Server(即本仓库fnproject/fn)提交代码的开发者,系统讲解三层核心内容:Fn Server 的贡献范围边界(什么该进核心、什么必须做成扩展)、如何从源码构建/运行/测试 Fn Server(含 Docker 方式),以及如何利用官方扩展点(listeners、middleware、API 端点)以正确姿势贡献功能。读完本文,你将具备在本地完整拉起一个可调试的 fnserver、跑通全链路测试,并写出符合官方规范扩展代码的实战能力。
一、Fn Server 的贡献边界:先想清楚"该不该进核心"
1.1 核心职责(属于本仓库的部分)
本仓库的CONTRIBUTING.md开宗明义:Fn Server 只承载整个 Fn 生态中最本质的部分,具体包括:
- 核心 API:apps(应用)、routes(路由)、calls(调用)、logs(日志)四大资源的管理接口;
- 函数执行(同步):即同步调用(sync invoke)路径,本地拉起容器执行函数;
- 扩展点:回调(callbacks)、中间件(middleware)、API 追加(API additions)三类可插拔机制。
从源码看,这一定位与仓库结构完全吻合:核心 API 的 handler 集中注册在 api/server/server.go 的bindHandlers中(/v2下的 apps、fns、triggers 的增删改查),而函数执行由api/agent包承担;扩展点则独立成fnext包与api/server中的AddXxx系列方法。
1.2 明确不属于核心的部分
原文档特别强调,以下内容不在Fn Server 核心范围内:
- 认证(authentication);
- 统计/指标(stats/metrics);
- 特殊或可选特性:triggers(触发器)、FDK(函数开发工具包)、workflows(工作流)、event sources(事件源)等;
- 可以论证的是,超出基础支持范围之外的额外 I/O 格式,也应作为插件(plugin)构建。
从仓库证据看,"统计/指标"并非完全缺席,但大多以可选方式接入:例如 cmd/fnserver/main.go 中的
registerViews只负责注册 OpenCensus 视图与 Prometheus 导出器,属于运行时观测能力而非 API 核心资源;而 triggers 虽然在本仓库api/server中有 CRUD 端点,但原文档将其定位为生态可选特性,贡献者应以扩展思路对待。
1.3 贡献铁律:能做成扩展,就绝不要塞进核心
原文档给出了一条最重要的取舍准则:
Rule of thumb: If it could be built as an extension, then build it as an extension.(经验法则:如果某个功能能做成扩展,就把它做成扩展。)
同时官方承诺两点:
- 扩展点优先接纳:为了支撑扩展开发,Fn Server愿意接受任何合理的扩展点新增(
We WILL accept any reasonable additions to extension points); - 尽力降低扩展成本:官方会尽一切可能让用户方便地添加扩展(简化构建,或支持 Go plugins 方式)。
1.4 晋升机制(Graduation)
原文档还定义了"扩展毕业"机制:当某个扩展在社区中变得普遍(即大多数用户都在使用)时,它可以晋升(graduate)进入核心。这意味着贡献者可以先以扩展形式验证需求与使用量,再申请并入核心,降低核心代码的膨胀风险。
二、构建依赖与仓库获取
2.1 依赖清单
按原文档,构建 Fn Server 的唯一硬依赖是:
- Go(需要Go >= 1.11.0才能使用 Go Modules)
对照当前仓库 go.mod 可以看到,模块声明为github.com/fnproject/fn,go指令版本已演进到go 1.24.6,并大量依赖gin-gonic/gin、fsouza/go-dockerclient、go-sqlite3、go.opencensus.io等库。因此实际编译请以本地 Go 版本满足 go.mod 要求为准。
2.2 获取仓库
原文档给出的克隆命令:
$ git clone https://github.com/fnproject/fn.git特别提醒:必须克隆fnproject/fn官方仓库,而不是你自己的 fork,否则构建无法正常工作。日常贡献流程是:以官方仓库为构建基准,在自己的 fork 上做修改后提交 PR。
# 进入仓库目录(如尚未进入) $ cd path/to/fnproject/fn三、本地构建与运行 Fn Server
3.1 只编译
make build对应 Makefile 中的目标:
build: go build -o fnserver ./cmd/fnserver注意产物是fnserver可执行文件(并非名为fn),编译入口为 cmd/fnserver/main.go。
3.2 编译并运行
原文档推荐:每次修改代码后都执行make run:
make run对应 Makefile:
run: build GIN_MODE=debug ./fnservermake run会先构建再用 debug 模式(Gin 框架的GIN_MODE=debug)启动服务。按原文档说明,启动后的 Fn Server:
- 使用内嵌的 sqlite3 数据库(默认连接串为
sqlite3://<当前目录>/data/fn.db,见 api/server/server.go 的NewFromEnv实现); - 监听8080端口(默认值
DefaultPort = 8080,见 api/server/server.go)。
3.3 入口代码与启动流程
启动逻辑非常简洁(cmd/fnserver/main.go):
func main() { ctx := context.Background() registerViews() funcServer := server.NewFromEnv(ctx) funcServer.Start(ctx) }server.NewFromEnv从环境变量装配全部配置(数据库、端口、节点类型、日志、Agent 等),并完成各类中间件与路由绑定;server.Start监听信号(os.Interrupt、SIGTERM),启动 Web/Admin/gRPC 服务并阻塞运行(见 api/server/server.go)。
此外 cmd/fnserver/main.go 通过 blank import 引入了api/server/defaultexts包,该包(见 api/server/defaultexts/defaultexts.go)以扩展形式自动加载了 docker 驱动、sql 数据存储及其 mysql/postgres/sqlite 子模块——这就是"默认二进制自带全部存储后端"的机制。
3.4 常用运行环境变量(源码级佐证)
从 api/server/server.go 的常量定义与NewFromEnv实现可确认以下最常用的环境变量:
| 环境变量 | 默认值 | 说明 |
|---|---|---|
FN_DB_URL | sqlite3://<cwd>/data/fn.db | 数据存储 URL,支持sqlite3、mysql、postgres三种 scheme |
FN_PORT | 8080 | Web/API 服务监听端口 |
FN_GRPC_PORT | 9190 | gRPC 服务端口(pure-runner 节点用) |
FN_LOG_LEVEL | info | 日志级别(如debug) |
FN_LOG_FORMAT | text | 日志格式(text/json) |
FN_LOG_DEST | stderr | 日志输出目标,支持udp、tcp、filescheme |
FN_NODE_TYPE | full | 节点类型:full/api/lb/pure-runner |
FN_MAX_REQUEST_SIZE | 0 | API 请求体大小上限(字节) |
FN_ZIPKIN_URL/FN_JAEGER_URL | 空 | 链路追踪导出地址 |
FN_RUNNER_ADDRESSES/FN_RUNNER_API_URL | 空 | LB 节点模式下 runner 地址/API URL |
FN_NODE_TYPE的四种取值对应ServerTypeFull / ServerTypeAPI / ServerTypeLB / ServerTypePureRunner四种模式(api/server/server.go),其中full同时承载 API 与任务执行,pure-runner仅运行 gRPC 执行服务。这些变量在本地调试与后续部署(如 LB + runner 分离架构)时非常关键。
四、测试体系:make test全链路拆解
原文档给出的测试命令:
make test对应 Makefile:
test: checkfmt pull-images test-basic test-middleware test-extensions test-systemmake test是一个完整的质量门禁流水线,下面逐段拆解。
4.1 checkfmt:代码格式门禁
checkfmt: ./go-fmt.shgo-fmt.sh 会遍历除vendor/、.git外的全部*.go文件执行gofmt -l,任何未格式化文件都会导致退出码非 0。提交 PR 前请务必先gofmt。
4.2 pull-images:拉取测试用镜像
pull-images: img-mysql img-postgres img-busybox由 helpers.sh 驱动,分别拉取mysql:9.5.0-oraclelinux9、postgres:16.2与busybox(可通过REMOTE_DOCKER_REPO覆盖 registry,默认docker.io),供测试中的数据库与函数容器使用。
4.3 test-basic:基础单元/集成测试
test-basic: checkfmt pull-images fn-test-utils fn-status-checker fn-test-volume ./test.shtest.sh 会:
- 设置
GOFLAGS=-mod=vendor(与 Makefile 的mod目标一致,使用 vendor 目录编译); - 通过 helpers.sh 的
spawn_postgres/spawn_mysql在 Docker 中拉起测试数据库,并把连接串注入POSTGRES_URL、MYSQL_URL环境变量; - 对排除 vendor、examples、test 目录后的所有包执行
go test(超时 360s); - 最后执行
go vet做静态检查。
4.4 test-middleware / test-extensions:示例工程编译验证
test-middleware: cd examples/middleware && go build test-extensions: cd examples/extensions && go build这两个目标确保 examples/middleware/main.go 与 examples/extensions/main.go 两个扩展示例始终可编译,是"扩展点必须好用"承诺的直接验证。
4.5 test-system:系统级端到端测试
test-system: ./system_test.sh sqlite3 ./system_test.sh mysql ./system_test.sh postgressystem_test.sh 针对三种数据库后端各跑一遍test/fn-system-tests下的端到端测试(如 test/fn-system-tests/exec_fn_test.go),并会输出 Prometheus 指标到prometheus.<db>.txt文件供核对。这解释了为什么make test需要 Docker:系统测试会真实拉起函数容器并执行。
4.6 其他相关测试目标
make test-datastore:仅对api/datastore包运行go test ./...;make test-build-arm:分别以GOARCH=arm(5/6/7)、GOARCH=arm64交叉编译,验证多架构构建可用性(见 Makefile)。
五、在 Docker 中运行与测试 Fn Server
5.1 一键在容器中启动
make docker-run对应 Makefile:
docker-run: docker-build docker run --rm --privileged -it -e NO_PROXY -e HTTP_PROXY -e FN_LOG_LEVEL=debug -e "FN_DB_URL=sqlite3:///app/data/fn.db" -v ${CURDIR}/data:/app/data -p 8080:8080 fnproject/fnserver要点:
--privileged:函数执行依赖 Docker-in-Docker 能力,需要特权模式;FN_DB_URL=sqlite3:///app/data/fn.db并挂载./data目录,使数据库文件持久化到宿主;- 映射
8080:8080,本地可直接访问 API; FN_LOG_LEVEL=debug便于调试。
5.2 先构建镜像,再用 fn CLI 指定版本启动
原文档还给出了另一种组合用法:
make docker-build fn start --version latestmake docker-build构建镜像fnproject/fnserver:latest(见 Makefile);fn start --version latest是fn CLI的命令(前提:本机已安装fnCLI),它会让本地 Fn Server 使用刚构建的latest版本镜像启动。
5.3 在 Docker 中跑测试
make docker-test对应 Makefile:挂载 Docker socket 与仓库目录,在fnproject/go:dev容器内对排除 vendor/examples/系统测试后的全部包执行go test -v。适用于不想污染本机 Go 环境的贡献者。
六、扩展点:贡献功能的"正确姿势"
原文档反复强调"能扩展就扩展",本节把官方提供的扩展机制结合源码逐一讲透。扩展相关接口集中在fnext包与api/server的注册方法中。
6.1 扩展的统一抽象:Extension接口
所有扩展必须实现 fnext/setup.go 定义的接口:
type Extension interface { Name() string Setup(s ExtServer) error }ExtServer接口(fnext/setup.go)限定了扩展能"触碰"的范围,可注册监听器、中间件、API 端点,并可读取当前 Datastore:
type ExtServer interface { AddAppListener(listener AppListener) AddCallListener(listener CallListener) AddAPIMiddleware(m Middleware) AddAPIMiddlewareFunc(m MiddlewareFunc) AddRootMiddleware(m Middleware) AddRootMiddlewareFunc(m MiddlewareFunc) AddEndpoint(method, path string, handler APIHandler) AddEndpointFunc(method, path string, handler func(w http.ResponseWriter, r *http.Request)) Datastore() models.Datastore }6.2 注册机制:RegisterExtension与AddExtension
api/server/extensions.go 提供了两套注册方式:
RegisterExtension(ext):仅登记到全局extensions映射,不初始化——适合在第三方扩展的init()中调用;AddExtensionByName(name):按名字完成Setup(s),供fn build-server这类 CLI 使用;AddExtension(ext):先注册再添加,适合扩展开发期或自定义 server 时直接调用。
6.3 Listeners:资源生命周期的回调
针对 App、Fn、Trigger、Call 四类资源,fnext/listeners.go定义了四组监听器接口,均采用Before/After成对钩子模式。例如:
AppListener:BeforeAppCreate/AfterAppCreate、BeforeAppUpdate/AfterAppUpdate、BeforeAppDelete/AfterAppDelete、BeforeAppGet/AfterAppGet、BeforeAppsList/AfterAppsList;FnListener:Fn 的增删改 Before/After 钩子;TriggerListener:Trigger 的增删改 Before/After 钩子;CallListener:BeforeCall(函数执行前)与AfterCall(函数完成后)。
从实现看,这些监听器通过 fnext/datastore.go 的NewDatastore包装层嵌入数据存储调用链:每次InsertApp、UpdateApp、RemoveTrigger等都会先触发 Before 钩子、再调用底层 Datastore、最后触发 After 钩子,任一 Before 返回错误都会中断操作。而api/server侧的appListeners/fnListeners/triggerListeners(见 api/server/app_listeners.go、api/server/fn_listeners.go、api/server/trigger_listeners.go)会把多个监听器串联成链,按注册顺序依次执行。
典型场景:在AfterAppCreate中为新建应用自动创建配套资源,或在BeforeAppDelete中做资源回收校验。
6.4 Middleware:拦截 API 与用户请求
fnext/middleware.go定义了标准中间件抽象:
type Middleware interface { Handle(next http.Handler) http.Handler } type MiddlewareFunc func(next http.Handler) http.Handler配套的注册方法(AddAPIMiddleware/AddRootMiddleware等)在 api/server/server.go 中被应用到 Gin 引擎:rootMiddlewareWrapper作用于所有路由,apiMiddlewareWrapper仅作用于/v2API 分组。
仓库自带的 examples/middleware/main.go 是一个完整可运行的认证中间件示例:它校验Authorization: Bearer <token>头(示例 token 为KlaatuBaradaNikto),不合法则返回 401 JSON,合法则向 context 注入用户信息并放行——这正是"认证不进核心、以中间件扩展实现"的官方示范。
6.5 追加 API 端点:AddEndpoint/AddEndpointFunc
api/server/extension_points.go实现了在/v2下追加自定义端点:
func (s *Server) AddEndpoint(method, path string, handler fnext.APIHandler) func (s *Server) AddEndpointFunc(method, path string, handler func(w http.ResponseWriter, r *http.Request))examples/extensions/main.go 演示了两种写法:实现APIHandler接口的结构体(custom1Handler),以及直接传入函数(custom2Handler):
funcServer.AddEndpoint("GET", "/custom1", &custom1Handler{}) funcServer.AddEndpointFunc("GET", "/custom2", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello func, %q", html.EscapeString(r.URL.Path)) })6.6 贡献建议总结
对照原文档与源码,贡献非核心功能时的推荐路径是:
- 优先以扩展实现,使用
fnext的 listeners / middleware / endpoint 三件套; - 需要绕过既有端点的行为时,可考虑
WithoutHTTPTriggerEndpoints()、WithoutFnInvokeEndpoints()等 Option(见 api/server/server.go)关闭默认端点,再替换为自己的实现; - 若确实需要新增扩展点基础设施,官方明确"愿意接受合理的新增";
- 当功能在社区普遍使用后,可申请**晋升(Graduation)**进入核心。
七、贡献流程速览
综合原文档与仓库工程实践,一次完整的贡献闭环大致如下:
- 克隆官方仓库:
git clonefnproject/fn(fork 用于开发,构建基准必须是官方仓库); - 环境准备:安装满足 go.mod 要求的 Go(原文档基线为 >= 1.11 以支持 go mod),并确保本机有 Docker(
make test/docker-run均依赖 Docker); - 编码与自测:每次改动后运行
make run本地验证,涉及格式问题先跑gofmt(make checkfmt); - 跑通测试:
make test(含格式门禁、基础测试、示例编译、三种数据库的系统测试);不想污染本机环境可改用make docker-test; - 遵守边界:功能优先做成扩展,扩展点不够时再提扩展点新增 PR;
- 提交 PR:说明改动动机、测试结果与扩展/核心归属判断。
遵循以上流程,既能保证提交质量,也能与 Fn Server "内核精简、扩展繁荣"的架构哲学保持一致。
参考资源(仓库内)
- 构建目标与测试编排:Makefile、go-fmt.sh
- 服务入口与装配:cmd/fnserver/main.go、api/server/defaultexts/defaultexts.go
- 环境变量与节点模式:api/server/server.go
- 扩展接口定义:fnext/、api/server/extensions.go、api/server/extension_points.go
- 官方示例:examples/extensions/main.go、examples/middleware/main.go
- 测试脚本:test.sh、system_test.sh、helpers.sh、test/fn-system-tests/
- 云原生
- 后端
- 微服务
【免费下载链接】fn
The container native, cloud agnostic serverless platform.
相关推荐
RabbitMQ Server 源码贡献实战指南:从构建、测试到本地多节点运行
RabbitMQ Server 源码贡献实战指南:从构建、测试到本地多节点运行 本指南以 deps/amqp10_client/CONTRIBUTING.md
后端消息队列消息路由Plunk 贡献开发指南:Turborepo 架构、本地环境搭建与代码规范全解析
Plunk 贡献开发指南:Turborepo 架构、本地环境搭建与代码规范全解析 Plunk 是一个开源的邮件平台(The Open Source Email
后端前端Sigma File Manager 源码开发与贡献指南:从环境搭建、本地运行到构建验证
Sigma File Manager 源码开发与贡献指南:从环境搭建、本地运行到构建验证 本文基于仓库根目录 CONTRIBUTING.md https://l
桌面应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考