好的,我们立刻进入对3GPP TS 29.594规范第四章核心部分的深度剖析。在上一篇文章中,我们已经掌握了本规范的宏观架构和核心概念。现在,我们将聚焦于具体的服务操作,从Subscribe开始,一步步揭示PCF与CHF之间交互的详细逻辑。

深度解析 3GPP TS 29.594:4.2 Service Operations (Part 1 - Subscribe订阅操作)

本文技术原理深度参考了3GPP TS 29.594 V18.4.0 (2024-06) Release 18规范中,第四章“Service Operations”的核心章节,重点解读4.2.2节“Nchf_SpendingLimitControl_Subscribe service operation”。本文旨在为读者深度剖析PCF如何通过一次精巧的Subscribe操作,同时实现“获取初始状态”和“建立未来通知”两大目标,并拆解其背后详尽的信令流程和处理逻辑。

引言:一通“电话”的艺术——既问当下,也约未来

在我们的故事中,策略“大脑”PCF需要实时了解用户乐乐的套餐余量,以便为她的高清直播业务制定正确的QoS策略。Subscribe(订阅)操作,就是PCF打给计费“账房”CHF的那通关键“电话”。

但这通“电话”非同寻常。它不仅要问清楚“乐乐现在的‘高清直播流量’还剩多少?”(获取初始状态),还要与CHF约定:“未来只要这个流量包的状态有任何风吹草动(例如即将用尽),请立刻打电话通知我!”(建立订阅关系)。

Subscribe操作正是这样一个将“即时查询”与“长期订阅”完美融合于一体的复合型服务操作。本篇文章,我们将化身为PCF和CHF系统的内部逻辑检察官,一窥这通关键“电话”背后的每一个技术细节,从“拨号”到“挂机”,全程追踪信息的流转与处理。


1. 解读第4.2.2章 Nchf_SpendingLimitControl_Subscribe service operation (订阅服务操作)

本节是Subscribe操作的功能性总览,它将这个操作进一步细分为两个核心的子流程。

3GPP TS 29.594 - 4.2.2.1 General

The Nchf_SpendingLimitControl_Subscribe service operation is used by the NF service consumer to subscribe to notification of changes in the status of the policy counters… and to retrieve the status of the policy counters… The following procedures are related to the subscribe service operation:

  • initial spending limit retrieval; and
  • intermediate spending limit report retrieval.

这段话明确了Subscribe操作的两个主要用途/流程:

  • 初始消费限额获取 (initial spending limit retrieval): 这通常发生在PCF首次需要了解某个用户消费状态时,例如PDU会话建立之初。这个流程的目标是创建一个新的订阅,并立即获取一次当前状态。
  • 中间消费限额报告获取 (intermediate spending limit report retrieval): 这指的是在一个已存在的订阅生命周期内,PCF可能需要修改这个订阅。例如,增加对新计数器的监控,或者移除对某个不再关心的计数器的监控。

本篇文章我们将重点剖析第一个,也是最核心的流程——初始获取


2. 解读第4.2.2.2章 Initial spending limit retrieval (初始消费限额获取)

本节详细描述了PCF如何发起一个全新的订阅请求,其信令流程图是理解整个交互过程的关键。

2.1 Subscribe操作的信令流程图剖析

规范通过“Figure 4.2.2.2-1: NF service consumer subscribes to retrieve policy counter status and spending limit reporting”这张图,为我们呈现了完整的交互步骤。

如下文所述,规范原文中的“Figure 4.2.2.2-1”清晰地描绘了服务消费者(PCF)与CHF之间的订阅创建和初始状态获取流程。

步骤1:PCF 发起 HTTP POST 请求

Figure 4.2.2.2-1, Step 1: 1. POST.../subscriptions

场景链接:用户乐乐发起高清直播业务,SMF向PCF请求策略。PCF决定需要监控乐乐的LiveStream_Data_CounterTotal_Data_Counter。于是,PCF构造了一个HTTP POST请求,目标指向CHF的/subscriptions资源集合。

请求体 (SpendingLimitContext) 的深度剖析: 这个POST请求的Body是一个SpendingLimitContext JSON对象,它是PCF填写的“订阅申请表”。规范详细列出了其中必须包含(shall include)和可能包含(may include)的字段:

  • supi (M, Mandatory): 必须包含用户的SUPI。这是告诉CHF“我要订阅的是哪个用户的消费信息”。
  • notifUri (M, Mandatory): 必须包含PCF的回调地址。这是告诉CHF“未来有通知请送到这里”。
  • notifId (C, Conditional): 通知关联ID。在一个复杂的场景下,例如乐乐的手机同时接入了两个网络切片,PCF可能需要为同一个SUPI建立两个独立的订阅。此时,PCF可以为每个订阅分配一个唯一的notifId,CHF在后续的Notify中会回传这个ID,便于PCF区分通知的上下文。
  • gpsi (O, Optional): 用户的GPSI(如手机号码MSISDN),可以作为SUPI之外的辅助身份标识。
  • policyCounterIds (O, Optional): 策略计数器ID列表。这是PCF明确告知CHF自己感兴趣的“账本”列表。

    If the “policyCounterIds” attribute is omitted, the subscription is to all available policy counters.

    • 这是一个非常重要的细节:如果PCF不提供这个字段,则表示它希望订阅该用户所有可用的策略计数器!这是一种“全量订阅”模式。
  • expiry (O, Optional): 建议的订阅过期时间。

步骤2:CHF 返回 201 Created 响应

Figure 4.2.2.2-1, Step 2: 2. 201 Created

CHF收到PCF的请求后,执行一系列严谨的内部处理:

  • 用户和计数器校验:

    • CHF首先检查supi对应的用户是否存在。如果不存在,将返回400 Bad Request,并在ProblemDetails中附带cause: "USER_UNKNOWN"
    • 如果PCF提供了policyCounterIds列表,CHF会逐一检查这些ID是否有效。如果某个ID未知,CHF可以选择拒绝请求(返回400cause: "UNKNOWN_POLICY_COUNTERS"),或者接受请求但忽略无效的ID(具体行为取决于运营商配置)。
    • 如果用户存在,但没有任何可用的策略计数器,CHF将返回400 Bad Requestcause: "NO_AVAILABLE_POLICY_COUNTERS"
  • 创建订阅资源: 如果所有校验通过,CHF将:

    1. 创建一个新的订阅资源,并为其分配一个唯一的subscriptionId
    2. 存储PCF提供的notifUripolicyCounterIds等订阅上下文。
    3. 立即查询数据库,获取所请求的(或全部)策略计数器的当前状态
    4. 构造一个201 Created响应。

响应体 (SpendingLimitStatus) 的深度剖析: 这个201 Created响应的价值是“双重”的。

  1. HTTP头: Location头中包含了新创建订阅的URI(例如/subscriptions/sub-abcde),这是PCF管理该订阅的唯一句柄。
  2. HTTP Body: 响应体是一个SpendingLimitStatus JSON对象,它立即返回了计数器的当前状态。

The SpendingLimitStatus data structure provided in the response body shall include the status of the requested subscribed policy counters in the “statusInfos” map…

statusInfos是一个Map(或JSON对象),其键(key) 是策略计数器ID,值(value) 是一个PolicyCounterInfo对象。

PolicyCounterInfo对象包含了:

  • policyCounterId: 计数器ID。
  • currentStatus: 当前的状态标签(例如,"ABUNDANT")。
  • penPolCounterStatuses (Pending Policy Counter Statuses): 待生效的状态列表。这是一个非常精巧的设计。例如,运营商可能在午夜对套餐进行重置。CHF可以在白天就预先知道这个状态变化。它可以在响应中告诉PCF:“Total_Data_Counter当前状态是"EXHAUSTED"(已用尽),但请注意,在北京时间明天凌晨0点,它的状态将变为"ABUNDANT"(重置后余量充足)”。这使得PCF可以做出更具前瞻性的策略决策。

场景闭环

  1. PCF向CHF发送POST /subscriptions请求,请求体中包含乐乐的SUPI和policyCounterIds: ["LiveStream_Data_Counter", "Total_Data_Counter"]
  2. CHF校验通过,创建了一个ID为sub-lele-live的订阅。
  3. CHF查询后发现,定向流量剩15GB,通用流量剩80GB。CHF内部的配置将这个状态映射为"VALID"
  4. CHF返回201 Created响应。
    • Location头为 .../subscriptions/sub-lele-live
    • 响应体为:
      {
        "statusInfos": {
          "LiveStream_Data_Counter": {
            "policyCounterId": "LiveStream_Data_Counter",
            "currentStatus": "VALID"
          },
          "Total_Data_Counter": {
            "policyCounterId": "Total_Data_Counter",
            "currentStatus": "VALID"
          }
        },
        "expiry": "2025-10-14T10:00:00Z"
      }
  5. PCF收到响应,一方面记录下sub-lele-live这个订阅ID,另一方面立即解析statusInfos,得知当前余量充足,从而为乐乐的直播业务下发了高质量QoS策略。

至此,一次完美的“初始消费限额获取”流程完成。PCF不仅为未来建立了监控,也解决了当下的决策问题。


3. 解读第4.2.2.3章 Intermediate spending limit report retrieval (中间消费限额报告获取)

这一节在后续版本的规范中,其功能主要由PUT /subscriptions/{subscriptionId}方法实现,用于修改一个已存在的订阅。我们将在后续解析第五章API时详细展开。其核心思想是,PCF可以使用HTTP PUT方法,向一个已存在的订阅URI(如/subscriptions/sub-lele-live)提交一个新的SpendingLimitContext对象,来更新订阅的参数(例如,增加对新计数器的监控,或延长过期时间)。CHF在成功处理PUT请求后,会返回200 OK,响应体中同样会包含更新后所有被监控计数器的当前状态。


总结

Nchf_SpendingLimitControl_Subscribe服务操作是整个消费限额控制服务的“发动机”和入口。通过对4.2.2节的深度剖析,我们掌握了其精巧的“二合一”设计:

  1. 复合型操作: Subscribe操作通过一次POST请求,同时完成了创建订阅资源获取初始状态两个核心任务,设计非常高效。

  2. 丰富的上下文信息: 订阅请求体SpendingLimitContext携带了丰富的上下文信息(SUPI, 通知URI, 计数器列表, 过期时间等),使得CHF能够精确地建立订阅关系。

  3. 即时与未来的状态反馈: 订阅成功后的响应体SpendingLimitStatus,不仅通过currentStatus字段提供了计数器的即时状态,更通过penPolCounterStatuses字段提供了未来待生效的状态,赋予了PCF进行前瞻性策略决策的能力。

  4. 健壮的错误处理: 规范定义了USER_UNKNOWN, UNKNOWN_POLICY_COUNTERS等清晰的错误原因,使得PCF能够准确地定位和处理订阅失败的情况。

我们已经完全理解了PCF是如何打出这通关键“电话”的。在下一篇文章中,我们将继续探索UnsubscribeNotify操作,看看这通电话建立的“约定”是如何被履行和终止的,从而构成一个完整的异步通信生命周期。


FAQ

Q1:PCF订阅时,如果不指定policyCounterIds,订阅所有计数器,会有性能问题吗? A1:可能会。如果一个用户关联了非常多的策略计数器(例如,办理了十几种不同的业务包),“全量订阅”会导致CHF需要监控所有这些计数器的状态变化,并可能产生大量Notify消息,其中很多可能是PCF并不关心的。最佳实践是,PCF应该根据当前业务的上下文,按需、精确地订阅其决策所必需的计数器,这是一种“最小权限”原则的应用,可以最大化系统效率。

Q2:notifId(通知关联ID)的具体作用是什么? A2:notifId主要用于上下文关联。想象一个场景,一个企业用户(同一个SUPI)通过一部手机同时使用了两条网络切片:一条用于办公(切片A),一条用于视频会议(切片B)。PCF可能会为这两条切片建立两个独立的PDU会话,并向CHF发起两次独立的订阅,一次监控办公切片的流量包,一次监控视频会议的流量包。PCF可以为这两个订阅分别设置notifId: "office"notifId: "meeting"。当CHF发来通知时,通知中会携带相应的notifId,PCF收到后就能立刻知道“哦,这是办公流量用超了”还是“会议流量用超了”,从而对正确的PDU会话执行策略,而无需进行复杂的内部状态查询。

Q3:penPolCounterStatuses(待生效状态)这个功能非常强大,它能预告多长时间之后的状态变化? A3:规范本身没有限制预告的时间跨度。这完全取决于CHF和其所连接的计费系统(CCS)的能力。一个强大的CCS可能能够预知周期性的事件,比如月度套餐重置(可以预告未来一个月内的某个时间点)、每日签到赠送流量(可以预告未来24小时内的某个时间点),或者用户通过APP预约了一个小时后生效的流量包。只要计费系统能预知,CHF就可以通过penPolCounterStatuses将这些“未来事件”告知PCF。

Q4:Subscribe操作使用POST,而修改订阅使用PUT,这是为什么? A4:这是遵循RESTful API设计原则。POST用于创建一个新资源,其URI由服务器分配。POST /subscriptions正是创建一个新的订阅,服务器会为其生成一个新的subscriptionId。而PUT用于替换/更新一个已存在的、URI已知的资源。PUT /subscriptions/{subscriptionId}的意图是“用我请求体中的新内容,完全替换掉ID为{subscriptionId}的这个订阅的全部配置”。这种方法的区别,清晰地反映了“创建”与“更新”两种不同的操作语义。

Q5:如果CHF在处理订阅请求时,部分policyCounterIds有效,部分无效,它会如何响应? A5:规范提到,这取决于CHF的配置。

  • 严格模式: CHF可以被配置为只要policyCounterIds列表中有一个ID无效,就拒绝整个请求,返回400 Bad Request"UNKNOWN_POLICY_COUNTERS"
  • 宽松模式: CHF也可以被配置为接受请求,它会为所有有效的ID建立监控,并在响应的statusInfos中,为那些无效的ID返回一个特殊的状态,例如"COUNTER_UNKNOWN""NOT_PROVISIONED",告知PCF这些计数器无法被监控。这种模式更加健壮,但需要PCF能够正确处理部分成功的场景。