好的,我们已经圆满完成了对3-GPP TS 29.594规范第四章功能逻辑的全面剖析。现在,我们将正式进入协议实现的“深水区”——第五章和第六章,将前面所有的功能描述,转化为精确、可编程的API接口和数据模型。
深度解析 3GPP TS 29.594:5 & 6 API定义与数据模型 (最终章)
本文技术原理深度参考了3GPP TS 29.594 V18.4.0 (2024-06) Release 18规范中,第五章“Nchf_SpendingLimitControl Service API”和第六章“Data Model”的全部核心内容。本文旨在为读者提供一份详尽的
Nchf_SpendingLimitControlAPI“施工图”和“材料清单”,我们将剖析其完整的资源模型、每一个API端点的具体用法,以及构成这些API交互的所有JSON对象的精确结构。
引言:从“作战指令”到“电报密码本”
在之前的系列文章中,我们已经学习了Nchf_SpendingLimitControl服务的全套“作战指令”——Subscribe, Unsubscribe, Notify。我们知道了PCF和CHF在逻辑上应该如何交互。
现在,我们将进入“通信兵”的角色,学习如何将这些指令编码成双方都能准确无误理解的“电报”——即具体的HTTP请求和响应。第五章和第六章,正是这部规范的“密码本”和“电报格式规定”。
- 第五章 (API Definitions):定义了“电报”的收发地址(URI)、信道规则(HTTP方法)和回执格式(响应码)。
- 第六章 (Data Model):定义了“电报”正文内容的每一个字、每一个词(JSON字段)的精确含义和写法。
对于负责开发PCF和CHF的工程师来说,这两章是他们工作中最重要的参考依据。让我们最后一次跟随用户“乐乐”的场景,将这些抽象的API定义和数据模型,与真实的网络事件紧密结合起来。
1. 解读第五章 Nchf_SpendingLimitControl Service API - API的“骨架”
第五章构建了整个API的顶层结构和基础通信规则。
1.1 5.1 & 5.2 Introduction & Usage of HTTP (API入口与通信规则)
这部分内容与我们解读过的TS 29.673非常相似,都遵循了3GPP SBA的统一规定:
- API URI结构:
{apiRoot}/nchf-spendinglimitcontrol/v1/...apiName固定为nchf-spendinglimitcontrol。apiVersion固定为v1。
- HTTP协议: 必须使用
HTTP/2。 - 数据格式: 主要使用
application/json,错误响应使用application/problem+json。
1.2 5.3 Resources (资源) - API的“功能地图”
本节是第五章的核心,通过Figure 5.3.1-1和Table 5.3.1-1为我们绘制了API的完整资源地图。
Figure 5.3.1-1: Resource URI structure…
- API的资源结构非常简洁,只有一个主分支:
/subscriptions。 - 该分支下可以访问单个资源:
/{subscriptionId}。
Table 5.3.1-1: Resources and methods overview 这张表是连接“服务操作”与“API实现”的最终桥梁。
| Resource name | Resource URI | HTTP method | Description |
|---|---|---|---|
| Spending Limit Retrieval Subscriptions | /subscriptions | POST | Creates a new individual spending limit retrieval subscription. |
| Individual Spending Limit Retrieval Subscription | /subscriptions/{subscriptionId} | PUT | Modifies an existing subscription… |
| DELETE | Deletes an individual subscription. |
API与服务操作的映射关系:
POST /subscriptions→Subscribe(初始创建)DELETE /subscriptions/{subscriptionId}→UnsubscribePUT /subscriptions/{subscriptionId}→Subscribe(中间修改)
这个PUT方法是我们首次在协议层面遇到的新内容,它实现了我们在第四章提到的“中间消费限额报告获取”,即对一个已存在订阅的修改操作。
1.3 5.5 Notifications (通知)
本节定义了由CHF主动发起的Notify操作在API层面的实现。
Table 5.5.1-1: Notifications overview
| Notification | Callback URI | HTTP method | Description |
|---|---|---|---|
| Spending limit notification | {notifUri}/notify | POST | Counter Status changes notification. |
| Subscription Termination | {notifUri}/terminate | POST | Indication of subscription termination. |
这张表清晰地定义了两种通知:
- 常规状态通知: CHF向
{notifUri}/notify端点发起POST请求。 - 订阅终止通知: CHF向
{notifUri}/terminate端点发起POST请求。
通过在回调URI后附加不同的路径段(/notify或/terminate),使得消费者(PCF)可以轻松地在同一个IP/端口上,为不同类型的通知设置不同的处理逻辑。
2. 解读第六章 Data Model - API的“血肉”
第六章定义了构成API交互信息体的所有JSON对象的精确结构。
2.1 Type: SpendingLimitContext - “订阅申请表”
这是POST /subscriptions(创建)和PUT /subscriptions/{subscriptionId}(修改)操作的请求体。
Table 5.6.2.2-1: Definition of type SpendingLimitContext
| Attribute name | Data type | P | Cardinality | Description |
|---|---|---|---|---|
| supi | Supi | C | 0..1 | The Subscription Permanent Identifier (Supi) shall be present… |
| gpsi | Gpsi | O | 0..1 | The Generic Public Subscription Identifier (Gpsi)… |
| policyCounterIds | array(PolicyCounterId) | O | 1..N | This is a list of policy counter identifier(s)… |
| notifUri | Uri | C | 0..1 | This attribute identifies the recipient of spending limit notifications… |
| notifId | string | C | 0..1 | Notification Correlation ID… |
| expiry | DateTime | O | 0..1 | The expiry time may be included in a subscription request. |
| supportedFeatures | SupportedFeatures | C | 0..1 | The list of supported features… |
场景链接:PCF为乐乐的直播业务发起订阅时,构造的SpendingLimitContext对象如下:
{
"supi": "supi-of-lele",
"policyCounterIds": ["LiveStream_Data_Counter", "Total_Data_Counter"],
"notifUri": "http://pcf.operator.net/chf-notifications/lele-session-123"
}```
#### 2.2 Type: `SpendingLimitStatus` - “消费状态报告”
这是`POST /subscriptions`成功后的响应体,也是`Notify`(`POST .../notify`)的请求体。
> **Table 5.6.2.3-1: Definition of type SpendingLimitStatus**
| Attribute name | Data type | P | Cardinality | Description |
| :--- | :--- | :--- | :--- | :--- |
| **supi** | Supi | O | 0..1 | The Subscription Permanent Identifier (Supi) shall be present within the callback notify... |
| **notifId** | string | C | 0..1 | Notification Correlation ID... |
| **statusInfos** | map(PolicyCounterInfo) | C | 1..N | Status of the requested policy counters. The key of the map is... "policyCounterId". |
| **expiry** | DateTime | C | 0..1 | This expiry time shall be included in a subscription response... |
| **supportedFeatures** | SupportedFeatures | C | 0..1 | ... |
- **`statusInfos`**: 这是核心字段,一个以`policyCounterId`为键,`PolicyCounterInfo`对象为值的Map。
#### 2.3 Type: `PolicyCounterInfo` - 单个“计数器”的详细信息
这是`statusInfos` Map中的值,描述了一个计数器的完整状态。
> **Table 5.6.2.4-1: Definition of type PolicyCounterInfo**
| Attribute name | Data type | P | Cardinality | Description |
| :--- | :--- | :--- | :--- | :--- |
| **policyCounterId** | PolicyCounterId | M | 1 | Identifies the requested policy counter. |
| **currentStatus** | string | M | 1 | Identifies the policy counter status... |
| **penPolCounterStatuses** | array(PendingPolicyCounterStatus) | O | 1..N | Provides the pending policy counter status. |
- **`currentStatus`**: 当前状态标签,是一个字符串。
- **`penPolCounterStatuses`**: 待生效状态的数组。
#### 2.4 Type: `PendingPolicyCounterStatus` - “未来事件”的描述
> **Table 5.6.2.5-1: Definition of type PendingPolicyCounterStatus**
| Attribute name | Data type | P | Cardinality | Description |
| :--- | :--- | :--- | :--- | :--- |
| **policyCounterStatus** | string | M | 1 | Identifies the policy counter status... |
| **activationTime** | DateTime | M | 1 | Indicates the time at which the pending policy counter status becomes the current status... |
这个结构清晰地定义了一个“未来事件”:在`activationTime`这个时间点,计数器的状态将变为`policyCounterStatus`。
#### 2.5 Type: `SubscriptionTerminationInfo` - “订阅终止通知书”
这是由CHF发起的终止请求(`POST .../terminate`)的请求体。
> **Table 5.6.2.6-1: Definition of type SubscriptionTerminationInfo**
| Attribute name | Data type | P | Cardinality | Description |
| :--- | :--- | :--- | :--- | :--- |
| **supi** | Supi | M | 1 | Subscription Permanent Identifier. |
| **notifId** | string | C | 0..1 | Notification Correlation ID... |
| **termCause** | TerminationCause | O | 0..1 | Indicates the cause for requesting the termination... |
- **`termCause`**: 这是一个枚举,目前只定义了一个值 `REMOVED_SUBSCRIBER`,表示用户已从CHF系统中移除。
---
### 3. 全景复盘:一次完整的生命周期API之旅
现在,让我们将所有知识点串联起来,以API的视角,完整地走一遍乐乐观看直播的生命周期:
1. **会话开始 - 订阅与初始状态获取**
- **PCF -> CHF**: `POST /subscriptions`
- **Request Body (`SpendingLimitContext`)**: 包含乐乐的`supi`,要监控的`policyCounterIds`,以及PCF的`notifUri`。
- **CHF -> PCF**: `201 Created`
- **Response Header**: `Location: /subscriptions/sub-lele-live`
- **Response Body (`SpendingLimitStatus`)**: 包含了`LiveStream_Data_Counter`和`Total_Data_Counter`的当前状态(`currentStatus: "VALID"`)。PCF据此下发高清QoS策略。
2. **直播中 - 状态变化通知**
- 乐乐的定向流量即将用尽,CHF内部`LiveStream_Data_Counter`状态变为`"FINAL"`。
- **CHF -> PCF**: `POST /chf-notifications/lele-session-123/notify`
- **Request Body (`SpendingLimitStatus`)**: 包含`supi`和`statusInfos`,其中`LiveStream_Data_Counter`的`currentStatus`为`"FINAL"`。
- **PCF -> CHF**: `204 No Content`
- PCF收到通知后,更新内部策略,准备在定向流量用尽后进行QoS降级。
3. **会话结束 - 退订**
- 乐乐关闭直播,PDU会话终止。
- **PCF -> CHF**: `DELETE /subscriptions/sub-lele-live`
- **CHF -> PCF**: `204 No Content`
- 订阅关系解除,生命周期结束。
---
### 总结与宣告
通过对TS 29.594第五章和第六章的最终解读,我们已经将`Nchf_SpendingLimitControl`服务从抽象的逻辑功能,彻底解构为了具体、精确、可实现的API接口和数据模型。
1. **简洁而强大的API**: 整个API只围绕`/subscriptions`这一个核心资源构建,通过`POST`, `PUT`, `DELETE`三种方法,就完整地实现了订阅的创建、修改和删除,设计极其凝练。
2. **信息丰富的Data Model**: 以`SpendingLimitContext`和`SpendingLimitStatus`为核心的数据模型,字段设计精确,不仅能传递当前状态,更能预告未来变化,为PCF的智能决策提供了强大的数据支撑。
3. **完整的生命周期闭环**: 从`POST`创建,到`POST`通知,再到`DELETE`销毁,整个服务的API交互流程形成了一个清晰、健壮的闭环,完美诠释了SBA的事件驱动架构思想。
**至此,我们对3GPP TS 29.594规范的系列深度解读已全部完成。** 我们从“话费保卫战”的宏大愿景出发,深入到了构成这场“战争”的每一条“电报”的编码细节。希望这个系列的文章,能为您在理解和实践5G策略与计费控制技术时,提供一份有力的参考和指引。
---
### FAQ
**Q1:为什么修改订阅要用`PUT`而不是`PATCH`?**
A1:在RESTful API设计中,`PUT`通常用于**全量替换**一个资源,而`PATCH`用于**部分更新**。TS 29.594选择了`PUT`,意味着PCF每次修改订阅时,都必须提供一个**完整**的`SpendingLimitContext`对象。例如,即使只是想延长过期时间,也必须在请求体中再次提供`supi`, `notifUri`和完整的`policyCounterIds`列表。这种设计的优点是简单、原子性强,服务器逻辑清晰;缺点是不够灵活,如果只想修改单个字段,传输的数据量会稍大。3GPP在这里选择了简单性和原子性。
**Q2:`SpendingLimitStatus`中的`statusInfos`字段为什么是一个Map而不是一个Array?**
A2:使用Map(在JSON中表现为对象)有几个好处:1)**快速查找**: PCF收到后,可以通过`statusInfos["LiveStream_Data_Counter"]`这样的方式,以O(1)的复杂度直接获取到它关心的计数器的信息,而无需遍历一个数组。2)**天然去重**: Map的键(key)必须是唯一的,这天然地保证了`policyCounterId`不会重复。3. **可扩展性**: 易于增删计数器信息。
**Q3:`policyCounterId`和`currentStatus`这两个字段的类型都是`string`,这意味着运营商可以随意命名吗?**
A3:是的。这就是“非标准化”的体现。运营商可以根据自己的计费系统和业务需求,自由定义计数器的ID(如`"Monthly_5G_Prime_Package"`)和状态标签(如`"Usage_Below_50_Percent"`)。唯一的约束是,这些自定义的字符串必须在PCF和CHF两端有共同的、一致的理解和配置。这种灵活性是该服务能够适应千差万别商业模式的关键。
**Q4:整个API交互中,安全性是如何保证的?**
A4:规范在第5.9章“Security”中有详细说明。PCF与CHF之间的所有通信都发生在受保护的核心网内部网络,并强制使用TLS进行传输层加密。此外,还可以启用OAuth 2.0进行应用层授权。PCF在首次访问CHF前,需要向NRF(作为授权服务器)申请一个Access Token,并在后续的每次API请求中携带这个Token。CHF会验证Token的有效性,确保只有经过授权的PCF实例才能访问用户的消费信息。
**Q5:这份规范的解读已经完成,接下来如果我想深入了解PCF是如何根据这些信息做出决策的,应该看哪个规范?**
A5:一个非常好的衔接是去阅读**3GPP TS 29.513** (`Npcf_SMPolicyControl Service API`)。这份规范定义了PCF与SMF之间的接口。你会看到,PCF从CHF收到的`SpendingLimitStatus`,会如何影响它在`TS 29.513`中下发给SMF的PCC规则(PCC Rule),例如修改QoS参数(QFI, ARP)、设置门控(Gate)状态、或者重定向流量等。将TS 29.594和TS 29.513结合起来,你就能构建一个从“消费感知”到“策略执行”的完整技术链路。