- 云原生
- 后端
【免费下载链接】fission
Fast and Simple Serverless Functions for Kubernetes
本文基于 RFC-0023: Stateful functions — keyed state API and sticky routing 编写,结合当前仓库源码(
pkg/apis/core/v1、pkg/statestore、pkg/statesvc、pkg/fetcher、pkg/router)验证每一项设计与落地细节。RFC 状态为Implemented(PR #3593 合并于 2026-07-22,粘性路径分配跟随 PR #3619)。
本指南聚焦 Fission 平台新增的**键控有状态函数(keyed stateful functions)**能力:如何通过FunctionSpec.State为函数开启一个按 keyspace 隔离、带配额与 TTL、由平台侧强制 namespace 隔离的 KV 状态 API,以及第二阶段如何在路由器上通过HRW(rendezvous hash)粘性路由让同一请求键的所有请求稳定落在同一个 Pod 上。读完本文,你将掌握StateConfig/StickyConfig的完整字段语义、statesvc 的 HTTP 契约、令牌派生与注入机制、配额原子性保证(CountedKV/quota.tla),以及粘性路由在endpointcache.Index.Admit中的落地方式。
一、动机与定位:把“有状态”做进平台
“FaaS 是无状态的”这一惯性是大量工作负载离开 FaaS 平台的根本原因:凡是需要会话(session)、按用户计数器、购物车、限流器或 Agent 对话记忆的负载,都必须自带 Redis、把凭据手工写进每个环境镜像,并且每个团队都要重新实现一次 scope 隔离与配额。RFC-0023 的目标是平台只做一次这件事:
- 平台在 specialize 时已经具备向每个 Pod 注入 per-pod 配置的能力(fetcher specialize payload);
- 平台已经通过 HKDF 派生 scoped 服务密钥(
pkg/auth/hmac,tenant-controller 的 per-namespace keys); - RFC-0021 之后平台拥有自带租户隔离的 KV 底座(statestore
KVStore)。
三者结合后,函数代码只需对一个集群内本地 HTTP API调用state.get/set/cas/delete/list,即可跨环境、跨后端获得可移植的持久状态。这是 Cloudflare Durable Objects / Dapr state 同类的差异化能力(文档明确:不是 Lambda 平级,Lambda 至今没有对应物)。
目标(Goals):
- 一个最小状态 HTTP API(get/set/delete/cas/list),按函数隔离,TTL、大小配额、namespace 隔离全部由平台侧强制;
- 用户代码零凭据:能力以“注入的 URL + scoped bearer token”形式到达;
- node/python 环境的 SDK helper 只需 ~20 行纯 HTTP 封装,任何环境无需 SDK 也可用;
- Phase 3 提供按请求键的可选粘性路由(基于现有 ready-pod 索引),在持久状态之上实现一致的进程内缓存;
- 通过 memory 驱动在 RFC-0018 本地开发循环中可用。
非目标(Non-goals):v1 不支持跨函数共享状态(keyspace 属于单个函数);不支持跨 key 事务、查询/二级索引与大 blob(KV 值默认上限 256KiB,blob 应进对象存储);粘性路由不提供严格单写者保证(粘性是优化,持久真相是状态 API);不替代用户自建的关系型数据库。
二、Function CRD 上的 API 面:StateConfig 与 StickyConfig
FunctionSpec新增一个可空字段,存在即开关(与Tool、Streaming相同的约定):
// FunctionSpec 新增(presence = opt-in): State *StateConfig `json:"state,omitempty"`对应源码见 pkg/apis/core/v1/types.go 与 pkg/apis/core/v1/types.go。完整结构(含当前仓库源码中的精化注释):
// StateConfig 声明一个函数的键控状态 keyspace 与配额(RFC-0023)。 // 存在 FunctionSpec.State 即开关——没有单独 enabled 标志,内存零值与存储对象永不失配。 type StateConfig struct { // Keyspace 命名该函数读写持久键空间。默认取函数名;显式设置是为了函数改名时不孤立数据。 // 字符集刻意排除 ':'(令牌派生 info-string 分隔符)与 '#'(平台保留的 "<keyspace>#meta" 配额记账兄弟键)。 // +optional // +kubebuilder:validation:Pattern=`^[a-z0-9](https://link.gitcode.com/i/d56556a4835b8f0c60bbc81b38881c5c)?$` // +kubebuilder:validation:MaxLength=63 Keyspace string `json:"keyspace,omitempty"` // DefaultTTL 应用于未携带显式 TTL 的写入。必须 >= 0;零(或 nil)表示默认不过期。 // +optional DefaultTTL *metav1.Duration `json:"defaultTTL,omitempty"` // MaxValueBytes 限制单个值的字节数。0 表示平台默认(DefaultStateMaxValueBytes = 256KiB)。 // blob 属于对象存储。 // +optional // +kubebuilder:validation:Minimum=0 MaxValueBytes int64 `json:"maxValueBytes,omitempty"` // MaxKeys 限制 keyspace 的存活键数,随每次写入原子地强制(quota.tla S3)。 // 0 表示平台默认(DefaultStateMaxKeys)。 // +optional // +kubebuilder:validation:Minimum=0 MaxKeys int64 `json:"maxKeys,omitempty"` // Backend 为该 keyspace 选择一个具名 statestore 驱动。v1 中接受并校验,但尚未生效: // statesvc 用其单一配置驱动服务所有 keyspace(按函数选择后端是文档化的延后项)。 // +optional Backend string `json:"backend,omitempty"` // Sticky 非 nil 时把函数切入粘性路由:路由器把声明的请求键一致哈希到 ready-pod 集合, // 在 pod 集合稳定时同一键的请求落在同一 pod。尽力而为(是优化而非正确性依赖——S6): // 持久真相始终在状态 API 之后。 // +optional Sticky *StickyConfig `json:"sticky,omitempty"` } // StickySource 选择路由器从请求中提取粘性路由键的位置。 // +kubebuilder:validation:Enum=header;queryparam type StickySource string const ( StickySourceHeader StickySource = "header" // pkg/apis/core/v1/const.go#L179 StickySourceQueryParam StickySource = "queryparam" // pkg/apis/core/v1/const.go#L180 ) // StickyConfig 声明如何从请求中提取粘性路由键。缺少键的请求回退到默认 endpoint 选择。 type StickyConfig struct { Source StickySource `json:"source"` // 例如 "header" Name string `json:"name"` // 例如 "X-Session-Id" }默认值与有效值语义
| 字段 | 默认值 | 取值约束 | 备注 |
|---|---|---|---|
Keyspace | 函数名 | 1–63 字符,^[a-z0-9](https://link.gitcode.com/i/d56556a4835b8f0c60bbc81b38881c5c)?$,不含:# | 显式设置以支持函数改名不丢数据 |
DefaultTTL | 无(不过期) | >= 0 | 对无显式 TTL 的写入生效 |
MaxValueBytes | 262144(256KiB) | >= 0,0 = 平台默认 | const.goDefaultStateMaxValueBytes |
MaxKeys | 10000 | >= 0,0 = 平台默认 | const.goDefaultStateMaxKeys |
Backend | ""= 默认驱动 | 引用已配置驱动(否则仅警告) | v1 校验但语义延后 |
Sticky.Source | — | header/queryparam二选一 | PathParam延后(文档明确未实现) |
Sticky.Name | — | 非空 | 如X-Session-Id |
EffectiveKeyspace/EffectiveMaxValueBytes/EffectiveMaxKeys三个方法负责把零值解析成有效配置(见 validation.go)。
Webhook / 准入校验(Go + CRD marker 双重强制)
校验逻辑在 pkg/apis/core/v1/validation.go 与 validation_state_test.go:
- keyspace 字符集与长度:
stateKeyspaceRegexp镜像 kubebuilder marker,Go 侧再查一遍,让 CLI 也能客户端校验; - 配额非负:
MaxValueBytes、MaxKeys、DefaultTTL均须>= 0; - sticky source 枚举:仅
header/queryparam,且Sticky.Name非空; - executor 约束:容器执行器(container executor)函数被拒绝——没有 fetcher sidecar 来投递 scoped 令牌(validation.go);
allowedFunctionsPerContainer: infinite环境上的函数也被拒绝:一个共享 mount 服务多个函数,单一 per-pod 令牌文件无法隔离它们——webhook 需要拉取环境对象来强制这一点(RFC 注释明确)。
三、statesvc:面向函数的作用域化头部,而非新的数据库服务器
RFC-0021 已经提供了statestoresvc头部(--statestorePort,pkg/statestore/statestoresvc),但它服务的是原始的、无作用域的KVStore/EventLog/Queue——只给控制面 HTTP-client 驱动(嵌入式模式)用。它绝不能被函数 Pod 触达:一个函数打到 raw 端口就能读写任何所有者的键。因此 statesvc 是一个独立的、带作用域的函数侧表面,复用底层 substrate 而不再是第二份拷贝:
- 它是
fission-bundle的一个 head(--stateApiPort,ClusterIP 仅集群内的svc/statesvc),采用与已发布的statestoresvc/mcpheads 相同的形态:Options-onlyStart(ctx, clientGen, logger, mgr, opts),可注入net.Listener,/readyz依赖statestore.Capabilities.Ping(RFC-0021)与 Function-cache 同步,/healthz、/readyz绕过认证。Helm 组件见 charts/fission-all/templates/statesvc/; - 每个请求的 KV 访问都包在已发布的
scopedKV(statestore.NewScoped(inner, quotaResolver))里,scope 来自已验证的令牌——raw driver 的Capabilities从不交给函数; - 复用
hmac.ServiceStatestore派生家族,并沿用 statestore 的 NetworkPolicy 约定而非另起炉灶。
HTTP 契约(JSON over HTTP)
路由与头部定义在 pkg/statesvc/stateapi/stateapi.go(这是一个不依赖 controller-runtime 的轻量叶子包,CLI 可导入;头部或字段改名在此是每个客户端的编译期破坏):
GET /v1/state/{key} → 200 {value} + X-Fission-State-Version | 404 PUT /v1/state/{key} body=value; If-Match: <version> → Set with IfVersion(412 冲突); TTL via X-Fission-State-TTL DELETE /v1/state/{key} If-Match → Delete with ifVersion POST /v1/state/{key}/cas {expectVersion, value} — 为没有 If-Match 管道能力的客户端提供显式 CAS GET /v1/state?prefix=&cursor= → 分页键列表(List)- 请求头部:
X-Fission-State-Namespace、X-Fission-State-Keyspace、X-Fission-State-Version、X-Fission-State-TTL; - 管理面查询参数:
scope-namespace、scope-keyspace。它们骑在查询字符串而非头部——HMAC 签名覆盖请求 URI 而不覆盖头部,因此签名能绑定它们,statesvc 拒绝 header 携带的管理 scope(防重放改目标加固); - 机器可读错误码(
Error{error, code}JSON):bad_request/unauthorized/forbidden/not_found/version_conflict/quota_value_bytes/quota_keys/capability_unavailable/internal; - CAS 请求体:
{expectVersion, value},ExpectVersion == 0表示 create-only(仅当键不存在才写成功); - List 响应体:
{keys, cursor},cursor为空表示已穷尽。
KV 表面:没有独立 CAS 方法
底层statestore.KVStore只有Get/Set/Delete/List——不存在独立的CAS方法。比较并交换(compare-and-swap)是Set配合SetOptions.IfVersion实现的:
IfVersion == nil:无条件写入;IfVersion == 0:create-only(键不存在才写);IfVersion > 0:对指定版本做 CAS。
HTTP 层If-Match: <version>直接映射到IfVersion;缺失或不匹配即返回 412(version_conflict)。
Scope 从令牌来,绝不从请求来
scope 不是客户端提交的:scopedKV的Scope{Namespace, Owner, Keyspace}完全由已验证令牌派生。注意 pkg/statesvc/auth.go 的StateOwner是一个固定的Scope.Owner(刻意不是函数名):keyspace 显式存在于StateConfig,因此函数改名后其 keyspace 与数据可以保留。一个函数不能靠篡改头部来命名另一个函数的 keyspace——令牌校验失败即 403。
配额(MaxValueBytes、MaxKeys、namespace 字节预算)由scopedKV.Set强制,违反时返回带机器可读体的 413/429(错误码quota_value_bytes/quota_keys)。
四、配额原子性:quota.tla 与 CountedKV
竞态:check-then-act
RFC 明确指出scopedKV.Set的旧形态——countKeys之后Set——是先检查后执行:两个并发写者可能同时读到count = MaxKeys-1,都通过检查,都写入,从而突破预算。quota.tla精确建模了这个场景,其负配置AtomicQuota = FALSE可产出超标的 trace。设计要求(并要审计已发布scopedKV的修复)是:MaxKeys/ namespace 字节的强制必须与写入原子——对计数器键做 KV CAS,或让值写入以计数事务为条件——绝不采用裸的 read-check-then-write。这与队列的 epoch 列、游标的 version-CAS 属于同一类防护。
落地:CountedKV / SetCounted
当前仓库已按此落地:
- pkg/statestore/interfaces.go 定义可选的
CountedKV能力:SetCounted(ctx, scope, key, val, opts, maxKeys)是“带存活键预算的 Set”,预算强制与写入是一步; - pkg/statestore/scoped.go:
scopedKV.Set在MaxKeys > 0时统一走SetCounted——注释明确SetCounted是唯一的强制路径,Set与直接SetCounted在预算边界上必须行为一致; - 内存驱动 pkg/statestore/memory/kv.go:store mutex 使计数与写入成为单步;
- SQL 驱动 pkg/statestore/sqlstore/kv.go:事务先对
#meta计数键取锁再写入; statestoresvcHTTP head 把驱动包进NewScoped,handler 类型断言CountedKV来服务线上的maxKeys(pkg/statestore/httpapi/server.go);- conformance 测试要求每个驱动必须实现
CountedKV(pkg/statestore/statestoretest/conformance.go),且 scoped_quota_race_test.go 守卫“scoped store 仍满足CountedKV”这一不变量,用pgregory.net/rapid让 N 个并发写者竞速预算边界。
注意 RFC 描述的实现选项最终落为“值写入以计数事务为条件”(即
SetCounted),而非“计数器键 CAS”——后者需要 TTL 漂移自愈,其存活重数会与进行中的预留并发竞速(被 mixed-churn race 测试捕获)。
五、令牌派生与注入
派生:HKDF + keyspace-scoped info
每个函数的令牌是经pkg/auth/hmac的 HKDF 派生:与DeriveServiceKeyNS相同的hkdf.Key(sha256, master, nil, info, 32)构造,但info扩展到 keyspace 粒度:<KeyVersion>:statestore:<ns>:<keyspace>,走既有的ServiceStatestore通道,再用EncodeKeyForEnv十六进制编码后随环境传输(二进制 env-var 的 UTF-8 问题在 tenant-controller 工作中已经踩过——这正是 encode 步骤不可省略的原因)。
statesvc 侧重新派生并常量时间比较(verifyKeyspaceToken,见 pkg/statesvc/auth.go):无状态验证、零令牌存储,与ServiceRouterInternal/ServiceStatestore同一家族。这直接支撑不变量S1:为函数 A 派生的令牌永远无法读写函数 B 的 keyspace。
注入:按“函数无关 vs 每函数”切分
这是评审暴露出的真实约束:poolmgr 通用 Pod 的user-function 容器在函数身份已知之前就已经在运行,而环境变量无法注入运行中的容器。因此必须拆分:
FISSION_STATE_URL是函数无关的(svc/statesvc的 ClusterIP):作为普通环境变量,在通用 Pod 创建(poolmgr)与 Deployment 渲染(newdeploy/container)时设置——正是今天internalAuthEnvVars被添加的位置(pkg/fetcher/config);FISSION_STATE_TOKEN是每函数/keyspace 的,只能在 specialize 时派生:poolmgr 下它不能是容器环境变量,而随FunctionSpecializeRequest(已以-specialize-requestCLI 参数送达 fetcher,pkg/fetcher/config)传递;newdeploy/container 下 Pod 自创建起就是函数专属,令牌作为 Deployment 渲染时的普通 env var。
一个“两个都做成 env var”的朴素设计会在 poolmgr 温路径上静默失败——RFC 明确记录了这一点。
当前仓库的落地:fetcher 写令牌文件
实现最终选择了比“运行时写它”草图更好的方案:令牌由 fetcher 派生(它已持有主密钥)并写入共享挂载下的/userfunc/.fission-state-token,以{namespace, keyspace, token}JSON 形式存在,无需改变环境镜像契约。见 pkg/fetcher/statetoken.go:
StateTokenFileName = ".fission-state-token"(L22);StateCredentials{Namespace, Keyspace, Token}(L28-L32)——claims 随令牌一起传输,因为 statesvc 的无状态验证要根据声明的 (namespace, keyspace) 重新派生;writeStateTokenFile以 0444 权限写入(env 容器以不同用户运行、只读它),executor 通过FISSION_STATE_TOKEN_PATH把文件路径交给 SDK;- 无主密钥的开发集群写占位令牌,statesvc 的 pass-through 模式接受任意 bearer——SDK 契约保持一致(见 auth.go 的
passThrough分支)。
根密钥轮换会在下一次 specialize 时轮换所有令牌;旧+新密钥的双接受窗口覆盖运行中的 Pod(fuzz 测试的 rotation key 用例正是验证masterOld派生仍然通过——见 auth_fuzz_test.go),与 internal-auth 的轮换叙事一致。
六、Phase 3:粘性路由(HRW in Admit)
目标与语义边界
目标:在 Pod 集合稳定时,所有携带同一键的请求落在同一已 specialize 的 Pod 上,使持久状态 API 之上的进程内缓存保持相干,并让单写者模式免于 CAS 抖动。关键边界(不变量S6):粘性是优化,绝不是正确性依赖——任何请求合法地落在任何 Pod 上,持久真相始终在状态 API 后面。
接缝:Admit 内部的分支,而非策略对象
在已发布的数据平面里,endpoint 选择与并发准入是一个融合的原子操作:resolver(pkg/router/resolver_fallback.go的两个Admit调用点——poolmgr 温路径与 newdeploy/container 的 endpoint-LB 路径)调用endpointcache.Index.Admit(namespace, name, requestsPerPod),返回(Endpoint, release func(), AdmitResult)。Admit扫描不可变的 endpoint 快照,跳过 not-ready / quarantined / at-capacity 的 endpoint,今天默认取**最少在飞(least-outstanding)**的一个,再做有界 CAS 槽位占用。
没有可插拔的 pick 接口——least-outstanding 内联在Admit里。因此粘性路由给这个扫描加一个分支,而不是引入策略对象:Admit获得可选的 sticky key(resolver 按StickyConfig提取后传入);当 key 存在时,扫描按(key, endpoint) 的 rendezvous hash(HRW)对 ready endpoint 排序,取最高排名的可准入者,而不是 least-outstanding。HRW 的优点:Pod 增删时最小重排、无环状态——纯粹是 (key, ready endpoints) 的函数。返回形状(Endpoint, release, AdmitResult)不变,因此下面的记账接缝不受影响。
源码证据在 pkg/router/endpointcache/index.go:
Admit(namespace, name, version string, requestsPerPod int, stickyKey string)(L537);- 注释明确“默认 least-outstanding;stickyKey 非空时取最高 HRW 排名的可准入endpoint;饱和的 sticky 赢家不可准入,走正常溢出行为”(L518-L522);
- 命中检查在 L590,
ownerAddr是 sticky key 在 ready 集合中的真实 HRW 赢家(L567)。
记账不变量:因为粘性 pick 仍在Admit内部,router-admitted 与 executor-resolved 的切分不受影响——Release != nil ⟺ router-admitted依然成立,pkg/router/transport.go无需改动。
行为细节
- 缺 key 回退:请求缺失声明的 key 时走默认 pick(文档化行为,不是错误)——由
fission_router_sticky_key_missing_total计数(pkg/router/metrics.go,注释在 transport.go); - 饱和回退:哈希到的 endpoint 饱和时,
Admit回退到正常溢出行为而非在 sticky 目标上排队(粘性在饱和下尽力而为,文档化); - Churn 语义:扩容/缩容或 Pod 替换时,某个键的 owner 可能移动。因为持久真相是状态 API、内存只是缓存,这只是延迟事件(缓存预热),绝非正确性事件;想要更强围栏的函数可用 CAS 版本号作为 fencing token(文档化模式);
- 冷路径:索引没有 endpoint(冷函数)时走正常 executor 冷启动 RPC 路径,一旦存在 endpoint 粘性才生效;
- 遗留数据平面:
endpointSliceCache.mode=off不支持粘性(router 在准入时看不到数据平面模式,故在 RFC 中记录而非告警),保持 pinned-legacy CI 腿有意义。
指标与 teleport 检测
fission_router_sticky_requests_total:粘性请求计数;fission_router_sticky_key_missing_total:声明了但缺失 key 的请求计数;fission_router_sticky_teleports_total(endpointcache/metrics.go):粘性键发生了 Pod 迁移(teleport)的计数——由stickyLast感知:per-key-hash 的 256 bucket 原子数组(index.go,L478,每个粘性函数约 2KiB),noteStickyPick记录该键桶上次被准入的地址,下次同键被准入到不同地址即 teleport(L480-L501)。
区分:canary 权重里早已存在
stickyWeightHash(FNV-64a,pkg/router/canary.go,getCanaryBackend用其按权重选后端)——那是 canary 的按 key 稳定性,与本 RFC 的 endpoint HRW 粘性不同。endpointcache 的 HRW 分数有TestHrwScoreMatchesFNV64a钉住字节级一致性。
七、不变量与验证体系
RFC 定义六条不变量,当前仓库都有对应验证手段:
| 不变量 | 内容 | 验证 |
|---|---|---|
| S1 | scope 隔离:A 的令牌无法读写 B 的 keyspace | go test -fuzz对有效令牌做位翻转/截断/scope 拼接/重编码,断言只有精确派生的令牌通过——auth_fuzz_test.go |
| S2 | 无丢失更新:单键并发 CAS 可线性化,每版本恰一个赢家 | porcupine对真实 statesvc HTTP 面(非仅驱动)记录的并发历史做线性化检查 + get→cas 计数器集成测试(零丢失增量);per-key CAS 协议本身已被 substrate 的 TLC(eventlogsub.tla的 version-CAS 游标、workflowfold.tla的 CAS-append)覆盖,statesvc 继承而非重证 |
| S3 | 配额健全性:MaxValueBytes/MaxKeys/namespace 预算永不被突破(含并发竞速计数器) | TLC 检查而非仅属性测试:quota.tla建模并发写者竞速MaxKeys计数器,AtomicQuota = FALSE负配置产出 check-then-act 超额的 trace——设计源头真相;之上再用rapid序列竞速内存驱动配额边界 |
| S4 | 粘性确定性:pick 是 (key, ready endpoint set) 的纯函数,每台 router 副本独立得到同一 Pod | 纯函数rapid测试 |
| S5 | 最小重排:移除一个 Pod 只移动映射到它的键;新增只移动现在映射到它的键(HRW 性质) | 随机 churn 序列下断言“只有被移除 Pod 的键发生移动”的rapid测试 |
| S6 | 粘性是优化而非正确性依赖 | 架构性约定(文档化) |
TLA 规格位于 docs/rfc/specs/(quota.tla/quota.cfg、eventlogsub.tla、workflowfold.tla等)。其余:TTL 行为与令牌轮换双接受窗口跑在testing/synctest气泡里(虚拟时钟、确定性、无 sleep);sticky resolver 路径在并发 endpoint churn 下以-race运行(gorillaMethods()咬过的 build-vs-serve 竞态家族)。RFC-0020 bench 目标:状态 API 延迟 p99 < 5ms(集群内),分别测嵌入式模式与外部 Postgres;另有 sticky 缓存命中率场景。
八、备选方案对比(为何不这样做)
| 备选 | 结论 |
|---|---|
| Dapr sidecar 提供状态 | per-pod sidecar 注入与 poolmgr 通用池模型冲突(Pod 先于函数身份存在),且拖入第二个控制面——RFC-0021 已拒绝 |
| 直接注入 DSN(凭据进函数) | 每个环境都要带 DB 客户端;配额/租户不可强制;轮换一个泄露函数的访问等于轮换整个存储——拒绝 |
| 状态面挂在 router internal listener 上 | Pod 更少,但把请求热路径的可用性与 NetworkPolicy 面耦合到状态流量,且扩大 GHSA 加固监听的职责——独立 ClusterIP head 廉价且可独立扩缩 |
Kubernetes ServicesessionAffinity: ClientIP | 错误的键(客户端 IP ≠ 逻辑键);对 router 的直接 Pod endpoint 拨号不可用;对 resolver 的准入记账不可见 |
| 用 CRD 当 KV 后端 | etcd 写速率与对象上限——RFC-0021 Motivation 已拒绝 |
九、向后兼容与分阶段上线
向后兼容:纯增量——CRD 可选字段(nil = 完全今天的行
- 云原生
- 后端
【免费下载链接】fission
Fast and Simple Serverless Functions for Kubernetes
相关推荐
PVCNN 完全指南:从论文到代码,快速掌握 Point-Voxel CNN 核心技术
PVCNN 完全指南:从论文到代码,快速掌握 Point Voxel CNN 核心技术 PVCNN(Point Voxel CNN)是2019年NeurIPS会
Wouter与Redux:实现路由状态与全局状态的同步
Wouter与Redux:实现路由状态与全局状态的同步 你是否曾遇到单页面应用中路由跳转后用户信息丢失?刷新页面后购物车数据清空?这些问题的根源往往是路由状态与
前端React Router路由监控终极指南:实时监控路由状态和性能
React Router路由监控终极指南:实时监控路由状态和性能 React Router是React生态系统中最流行的路由解决方案,为单页应用提供强大的路由管
前端路由
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考