OmniSCP 操作指南
目录
组件概述
OmniSCP 实现了 3GPP TS 29.500 和 TS 23.501 中定义的服务通信代理 (SCP) 网络功能。SCP 在 5G 基于服务的架构 (SBA) 中充当 NF 消费者和 NF 生产者之间的 HTTP 反向代理。它提供委托的 NRF 发现、NF 生产者实例之间的负载均衡、失败重试和 NRF 发现结果缓存。
路由模式
SCP 支持三种路由模式,在每个传入请求中按优先顺序进行评估:
- 直接转发 — 存在
3gpp-Sbi-Target-apiRoot头。��求直接转发到指定的基本 URI,而不进行 NRF 查找。 - 委托发现 — 存在
3gpp-Sbi-Discovery-target-nf-type和3gpp-Sbi-Discovery-service-names头。SCP 查询 NRF(或命中缓存)并通过配置的负载均衡策略选择一个实例。 - 基于路径的推断 — 没有路由头。SCP 从路径前缀推断目标 NF 类型(例如,
/nudm-→ UDM)并执行委托发现。
3GPP 角色和规范参考
| 项目 | 参考 |
|---|---|
| SCP NF 定义 | 3GPP TS 23.501 第 7.3 节 |
| SCP 间接通信模型 | 3GPP TS 29.500 第 6.10 节 |
| 3gpp-Sbi-Discovery-* 头 | 3GPP TS 29.500 第 6.10.3 节 |
| 3gpp-Sbi-Target-apiRoot 头 | 3GPP TS 29.500 第 6.10.3.2 节 |
| 3gpp-Sbi-Producer-Id 头 | 3GPP TS 29.500 第 6.10.3.3 节 |
| SCP 负载均衡 | 3GPP TS 29.500 第 6.10.4 节 |
| NF 发现服务 | 3GPP TS 29.510 第 6.2 节 |
| NRF NF 状态通知 | 3GPP TS 29.510 第 6.3 节 |
| SBI 通用框架 | 3GPP TS 29.500 |
SBI 端点
OmniSCP 作为透明代理运行。只有一个本地处理的端点;所有其他路径都被代理到适当的 NF 生产者。
| 方法 | 路径 | 本地处理 | 描述 |
|---|---|---|---|
POST | /nnrf-nfm/v1/nf-status-notify | 是 | 接收 NRF NF 状态变化通知。在 NF_DEREGISTERED 或 NF_PROFILE_CHANGED 事件时,��效整个发现缓存。返回 204 无内容。 |
* | /*(所有其他路径) | 否 — 代理 | 任何不匹配上述的方式和路径都根据活动路由模式代理到解析的 NF 生产者。 |
代理错误响应
当 SCP 无法完成代理操作时,它会根据 TS 29.500 返回 ProblemDetails 主体。
| HTTP 状态 | 原因 | 条件 |
|---|---|---|
| 400 错误请求 | MANDATORY_IE_MISSING | 没有可用的路由信息:没有 3gpp-Sbi-Target-apiRoot,没有发现头,且路径无法映射到已知服务。 |
| 502 错误网关 | TARGET_NF_NOT_REACHABLE | 所有选定的 NF 生产者实例在重试后返回 5xx 或连接错误,或无法解析发现实例的 SBI URI。 |
| 504 网关超时 | NF_DISCOVERY_FAILURE | NRF 发现返回零个 NF 实例。 |
| 500 内部服务器错误 | SYSTEM_FAILURE | SCP 代理中的意外内部错误。 |
消耗的 3gpp-Sbi 头
| 头 | 描述 |
|---|---|
3gpp-Sbi-Target-apiRoot | 直接路由目标。在转发之前被剥离。 |
3gpp-Sbi-Discovery-target-nf-type | 要发现的 NF 类型(例如,UDM)。用于委托发现。在转发之前被剥离。 |
3gpp-Sbi-Discovery-service-names | 以逗号分隔的服务名称列表。第一个值用作主要值。在转发之前被剥离。 |
3gpp-Sbi-Discovery-requester-nf-type | NRF 查询范围的请求者 NF 类型。在转发之前被剥离。 |
3gpp-Sbi-Discovery-target-plmn-list | 目标 PLMN 列表。传递给 NRF 发现。在转发之前被剥离。 |
3gpp-Sbi-Discovery-requester-snssai-list | 请求者 S-NSSAI 列表。传递给 NRF 发现。在转发之前被剥离。 |
3gpp-Sbi-Discovery-nf-set-id | 用于发现的 NF 集 ID 过滤器。在转发之前被剥离。 |
3gpp-Sbi-Discovery-target-nf-instance-id | 要针对的特定 NF 实例 ID。在转发之前被剥离。 |
3gpp-Sbi-Discovery-requester-nf-instance-id | 请求者实例 ID。在转发之前被剥离。 |
产生的 3gpp-Sbi 头
| 头 | 描述 |
|---|---|
3gpp-Sbi-Producer-Id | 添加到每个代理响应中。包含处理请求的 NF 生产者的 nfInstanceId,使消费者与生产者的绑定得以实现,依据 TS 29.500 第 6.10.3.3 节。 |
配置参考
所有参数通过应用环境设置(通常是 config/runtime.exs)。
config :omniscp,
sbi_scheme: "http",
sbi_addr: "127.0.0.200",
sbi_port: 7777,
nrf_uri: "http://127.0.0.10:7777",
mcc: "999",
mnc: "70",
heartbeat_interval: 10_000,
discovery_cache_ttl: 60_000,
lb_strategy: :round_robin,
max_retries: 1,
upstream_timeout: 5_000
参数表
| 参数 | 默认值 | 类型 | 描述 |
|---|---|---|---|
sbi_scheme | "http" | 字符串 | SBI 监听器的传输方案。 |
sbi_addr | "127.0.0.200" | 字符串 | SBI HTTP 服务器绑定的 IP 地址。NF 消费者必须将 SBI 流量路由到此地址。 |
sbi_port | 7777 | 整数 | SBI HTTP 服务器监听的 TCP 端口。 |
nrf_uri | "http://127.0.0.10:7777" | 字符串 | NRF 的基本 URI。用于 NF 注册、心跳和代表消费者的 NF 发现查询。 |
mcc | "999" | 字符串 | 移动国家代码。包含在注册到 NRF 的 SCP NF 配置文件中。 |
mnc | "70" | 字符串 | 移动网络代码。包含在注册到 NRF 的 SCP NF 配置文件中。 |
heartbeat_interval | 10_000 | 整数(毫秒) | NRF 心跳请求之间的间隔。 |
discovery_cache_ttl | 60_000 | 整数(毫秒) | NRF 发现缓存条目的生存时间,按 {target_nf_type, service_name} 键入。过期条目在查找时懒惰地驱逐,并由后台清理任务每 30 秒执行一次。对于稳定的部署,增加此值;当 NF 配置文件频繁变化时,减少此值。 |
lb_strategy | :round_robin | 原子 | NF 生产者选择的负载均衡策略。有效值::round_robin、:weighted、:priority。有关语义,请参见负载均衡部分。 |
max_retries | 1 | 整数 | 当 NF 生产者返回 5xx 或连接错误时的最大重试次数。值为 1 表示一次原始尝试加一次重试。设置为 0 以禁用重试。 |
upstream_timeout | 5_000 | 整数(毫秒) | 对 NF 生产者的上游 HTTP 请求的超时(接收超时)。超过此超时的请求将被视为失败,并可能触发重试。 |
负载均衡策略
| 策略 | 描述 |
|---|---|
:round_robin | 按顺序循环遍历健康实例。状态在每个 {nf_type, service_name} 对中维护。这是默认的推荐策略,适用于均匀的 NF 部署。 |
:weighted | 选择具有最低 load - capacity 分数的实例。使用来自 NRF NF 配置文件的 load 和 capacity 字段。优先选择容量高且当前负载低的实例。 |
:priority | 选择具有最低 priority 值(最高优先级)的实例。适用于主动/备用部署。 |
在连续 3 次失败后,实例被标记为不健康,并在 30 秒冷却后自动恢复。当所有实例都不健康时,负载均衡器会回退到完整的实例列表。
关键程序
直接转发(模式 1)
委托发现和转发(模式 2)
NRF 状态通知(缓存失效)
基于路径的服务推断(模式 3)
当没有路由头���,SCP 从请求路径前缀提取服务名称,并使用以下内置表将其映射到 NF 类型:
| 路径前缀 | NF 类型 |
|---|---|
nudm- | UDM |
nausf- | AUSF |
namf- | AMF |
nsmf- | SMF |
npcf- | PCF |
nudr- | UDR |
nnssf- | NSSF |
nbsf- | BSF |
nnrf- | NRF |
注意:nchf-、nnef- 和 naf- 前缀不在内置映射中(限制 SCP-L1)。对 CHF、NEF 或 AF 服务的请求在使用模式 3 时需要显式的发现头。
可观察性
监测事件
| 事件 | 测量 | 标签 | 描述 |
|---|---|---|---|
[:omniscp, :proxy, :requests] | count, duration_ms | target_nf_type, result | 每个请求的代理结果 |
[:omniscp, :proxy, :result] | count, duration_ms | target_nf_type, result | 用于分布直方图的相同事件 |
[:omniscp, :discovery, :cache] | hits, misses | target_nf_type, service_name | 每个服务的缓存命中/未命中 |
[:omniscp, :cache, :hit] | count | — | 聚合缓存命中计数器 |
[:omniscp, :cache, :miss] | count | — | 聚合缓存未命中计数器 |
[:omniscp, :associations, :active] | count | — | 测量:活动代理关联 |
[:omni5g, :nrf, :registration] | status | nf_type | NRF 注册状态(1=已注册,0=未注册) |
结果标签值��success(2xx/3xx)、client_error(4xx)、server_error(5xx)、error(连接/超时)。
Prometheus 指标
SCP 代理指标
| 指标 | 类型 | 标签 | 描述 |
|---|---|---|---|
omni_scp.proxy.requests.count | counter | target_nf_type, result | 每个请求的代理计数 |
omni_scp.proxy.requests.duration_ms | summary | target_nf_type | 每个请求的代理持续时间 |
omni_scp.proxy_requests.total | counter | target_nf_type, result | 总代理请求 |
omni_scp.proxy_request.duration_ms | distribution | target_nf_type | 代理请求持续时间(桶:1、5、10、25、50、100、250、500、1000) |
omni_scp.active_associations.count | gauge | -- | 活动 NF 关联的数量 |
缓存指标
| 指标 | 类型 | 标签 | 描述 |
|---|---|---|---|
omni_scp.discovery.cache.hits | counter | target_nf_type, service_name | 每个服务的缓存命中 |
omni_scp.discovery.cache.misses | counter | target_nf_type, service_name | 每个服务的缓存未命中 |
omni_scp.cache_hits.total | counter | -- | 聚合缓存命中计数器 |
omni_scp.cache_misses.total | counter | -- | 聚合缓存未命中计数器 |
NRF 指标
| 指标 | 类型 | 标签 | 描述 |
|---|---|---|---|
omni_scp.nrf.registration.status | gauge | nf_type | NRF 注册状态(1=已注册,0=未注册) |
BEAM VM 指标
| 指标 | 类型 | 描述 |
|---|---|---|
beam.memory.total | gauge | 总 BEAM 内存(字节) |
beam.memory.processes | gauge | Erlang 进程使用的内存 |
beam.memory.processes_used | gauge | 进程实际使用的内存 |
beam.memory.system | gauge | 系统内存 |
beam.memory.atom | gauge | 总原子内存 |
beam.memory.atom_used | gauge | 使用的原子内存 |
beam.memory.binary | gauge | 二进制内存 |
beam.memory.code | gauge | 代码内存 |
beam.memory.ets | gauge | ETS 表内存 |
beam.processes.count | gauge | Erlang 进程数量 |
beam.ports.count | gauge | Erlang 端口数量 |
beam.atom.count | gauge | 原子数量 |
beam.vm.uptime | gauge | VM 运行时间(秒) |
日志模式
| 级别 | 模式 | 意义 |
|---|---|---|
info | Received NRF status notification | 收到 NF 状态通知 |
info | NRF notification: event=<E> nf=<URI> | 解析的通知事件 |
debug | SCP direct forward: <METHOD> <URL> | 模式 1 转发 |
debug | SCP delegated forward: <METHOD> <URL> (attempt <N>) | 模式 2/3 转发尝试 |
warning | SCP retrying after <STATUS> from <ID>... | 由于 5xx 触发的重试 |
warning | SCP retrying after error from <ID>... | 由于连接错误触发的重试 |
warning | SCP cannot determine target for <METHOD> <PATH> | 模式 3 路径不在服务映射中 |
warning | NRF discovery returned no instances for <NF>/<SVC> | 发现返回空列表 |
warning | All NF instances unhealthy, falling back to full list | LB 健康回退 |
error | NRF discovery failed: ... | NRF 查询错误 |
error | SCP proxy error: ... | 意外的代理失败 |
info | NF instance <ID> recovered after cooldown | 实例健康恢复 |
已知限制
| ID | 严重性 | 描述 |
|---|---|---|
| SCP-H4 | 高 | ECIES SUCI 解密未在底层 Omni5gEx 共享库中实现。需要 SCP 在转发之前解密 SUCI 的请求(在某些 AUSF/UDM 的间接通信模型中使用)将被转发而不进行解密。这不影响大多数 SBA 部署,因为解密由 AUSF 完成。 |
| SCP-M1 | 中 | 过载控制未实现。未生成或消耗 3gpp-Sbi-Oci(过载控制信息)头。在过载场景中,SCP 将继续转发请求,而不会减轻负载或对消费者施加压力。 |
| SCP-M2 | 中 | 负载控制指示未实现。未生成 3gpp-Sbi-Lci(负载控制信息)头。消费者无法使用 OmniSCP 获��� NF 负载提示以进行自己的负载控制决策。 |
| SCP-L1 | 低 | 基于路径的服务推断映射(模式 3)缺少 nchf-(CHF)、nnef-(NEF)和 naf-(AF)前缀条目。对这些服务的请求在没有显式发现头时将收到 400 错误请求,原因是 MANDATORY_IE_MISSING。解决方法:配置消费者为这些服务发送 3gpp-Sbi-Discovery-* 头。 |
| SCP-L3 | 低 | 事件为 NF_DEREGISTERED 或 NF_PROFILE_CHANGED 的 NRF 状态通知会清除整个发现缓存,而不仅仅是受影响的 {nf_type, service_name} 条目。在 NF 配置文件频繁变化的部署中,这会导致 NRF 重新发现查询的激增。 |
故障排除
400 错误请求 — MANDATORY_IE_MISSING
SCP 无法确定路由目标。检查:
- 消费者是否发送了
3gpp-Sbi-Target-apiRoot或同时发送了3gpp-Sbi-Discovery-target-nf-type和3gpp-Sbi-Discovery-service-names? - 如果依赖于基于路径的推断(模式 3),路径前缀是否出现在内置服务映射中?注意
nchf-、nnef-和naf-缺失(SCP-L1)。为这些服务添加显式头。
504 网关超时 — NF_DISCOVERY_FAILURE
NRF 返回了没有 NF 实例。检查:
- NRF 是否可以从 OmniSCP 访问?验证
nrf_uri和网络连接。 - 目标 NF 类型是否在 NRF 中注册?直接查询 NRF:
GET {nrf_uri}/nnrf-disc/v1/nf-instances?target-nf-type=<TYPE>。 - 检查是否刚刚有 NRF 状态通知清除了缓存(
NF_DEREGISTERED事件),并且 NF 尚未重新注册。
502 错误网关 — TARGET_NF_NOT_REACHABLE
所有 NF 生产者实例失败。检查:
- NF 生产者是否正在运行并且可以在其 NRF 配置文件中报告的 SBI 地址上访问?
- 检查
upstream_timeout。如果 NF 生产者响应缓慢,请增加此值。 - 查看
max_retries。如果设置为0,则单个失败将立即变为 502。 - 检查日志中的负载均衡器健康状态:查找
NF instance <ID> marked unhealthy after N failures。
发现缓存导致过时路由
如果 NF 生产者在没有适当的 NRF 注销的情况下更改地址或重启,缓存可能会保留过时的 SBI URI,直到 TTL 过期。选项:
- 减少
discovery_cache_ttl以限制过时窗口。 - 确保 NF 生产者在关闭时从 NRF 注销;这会触发 NRF 状态通知,清除 OmniSCP 缓存。
- OmniSCP 的进程重启会清除所有缓存状态。
高代理延迟
- 检查
omni_scp.proxy_request.duration_ms直方图以获取延迟分布。 - 比较缓存命中率(
omni_scp.cache_hits.total与omni_scp.cache_misses.total)。高未命中率意味着频繁的 NRF 查询。增加discovery_cache_ttl。 - 检查
upstream_timeout— 超时的请求在触发重试之前会将整个超时持续时间添加到延迟中。
NRF 注册未维护
检查 omni_scp.nrf.registration.status 指标。如果它读取为 0:
- 验证
nrf_uri是否正确且 NRF 可访问。 - 检查
mcc和mnc是否与 NRF PLMN 配置匹配。 - 在应用程序启动时查看 NRF 注册错误的日志。