Interface BeaconScheduling


public interface BeaconScheduling
本机调度 / 健康门面(FR-148):业务插件取调度候选与健康事实的唯一入口。

业务插件禁止直连 Beacon HTTP(直连不作为契约、随时可变);HTTP / JSON 实现只存在于 agent 适配器 ([ADR-0005] 延续),本接口不暴露任何传输细节,对业务插件只读——无改配置 / 改 zone 旁路。

降级语义(fail-static):控制面不可用时——acquireCandidate(java.lang.String) 走本地快照决策照常返回; candidatesInZone(java.lang.String, top.wcpe.beacon.agent.api.AdmissionScope) / healthOf(java.lang.String) 继续供给最后快照(含 agent 重启后落盘恢复);一切方法 不抛因控制面不可达导致的异常、不阻塞玩家进服链路;控制面恢复后自动切回在线决策并补报降级期决策。

  • Method Details

    • acquireCandidate

      CompletableFuture<ScheduleResult> acquireCandidate(String zone)
      在指定小区内取一台可调度候选。

      异步返回(内部走独立线程,绝不阻塞调用线程与 MC 主线程);控制面不可用时自动降级为本地快照决策, future 仍正常完成(fail-static,不因控制面不可达异常完成)。等价于 acquireCandidate(zone, null)。

      Parameters:
      zone - 目标小区名(namespace 内唯一)
      Returns:
      决策结果 future;ScheduleResult.chosen() 为 null 表示本次未取到候选
    • acquireCandidate

      CompletableFuture<ScheduleResult> acquireCandidate(String zone, String purpose)
      在指定小区内取一台可调度候选,并携带业务用途说明。
      Parameters:
      zone - 目标小区名
      purpose - 业务用途说明(可空,如 lobby-transfer),随决策记录入库供排查
      Returns:
      决策结果 future
    • acquireCandidate

      default CompletableFuture<ScheduleResult> acquireCandidate(String zone, String purpose, AdmissionScope scope)
      在指定小区内取一台可调度候选,并带上准入作用域——决策在生成候选的那一刻就按它收窄 (FR-244)。

      为什么要有它

      不带作用域时,"这台候选到底接不接我这个区 / 具不具备这个资格"只有调用方自己知道, 于是只能走「先选中、再由调用方校验并拒掉」:被排除的候选仍然占过一次被选中的机会, 结果是本次调用白跑一圈直接失败。带上作用域后,不满足条件的节点根本不进入候选集合。

      语义

      作用域是一组节点自声明键值标签的 AND 要求(见 AdmissionScope): 候选节点的自声明必须全部满足它才进入决策;空作用域(AdmissionScope.empty()) 与不带作用域的那条重载逐位等价。本仓不解释任何 key 的业务含义(FR-243 同口径)。

      作用域把候选全滤掉时:ScheduleResult.state() 为 ScheduleState.NO_CANDIDATE(稳定事实,重试无用), ScheduleResult.failReason() 为 no_candidate_in_scope—— 它与"这个小区本来就没有候选"(no_candidate)是两条不同的诊断, 但结论类别相同(都不可重试)。

      向后兼容

      本方法是 default:既有实现在不重写它的前提下仍能编译、仍能作为 BeaconAgent.scheduling() 的返回值使用。

      缺省实现按作用域分两路,方向由"会不会放宽准入"决定:

      • 空作用域(null 或 AdmissionScope.isEmpty()): 直接委派给 acquireCandidate(String, String)——空作用域不排除任何候选, 委派与实现方自己按空作用域决策逐位等价,不会放宽任何准入。 这条正是老实现方调 acquireCandidate(zone, purpose, AdmissionScope.empty()) 时应当得到的等价结果,而不是一个异常。
      • 非空作用域:抛 UnsupportedOperationException——因为"忽略作用域、照旧全量决策" 是静默放宽准入,比响亮地不支持更危险。调用方据此把这种情况归入"本实现不提供该能力"。

      既有三个方法(acquireCandidate(String) / acquireCandidate(String, String) / candidatesInZone(String))的签名一字未改。可观测取值有一处按 FR-244 口径更正: 实现方在"看不到"(门面未就绪 / 判据不可用)时原先回 failReason = "no_candidate" ("确实没有候选",稳定事实),现更正为 "unavailable" + ScheduleState.UNAVAILABLE (当前状态、可重试)——把可重试处境印成稳定结论会让调用方永久放弃一次本来只是环境未就绪的派房。

      Parameters:
      zone - 目标小区名(namespace 内唯一)
      purpose - 业务用途说明(可空)
      scope - 准入作用域;null 视为 AdmissionScope.empty()
      Returns:
      决策结果 future(fail-static 契约不变:不因控制面不可达而异常完成)
      Throws:
      UnsupportedOperationException - 本实现不支持按作用域收窄,且本次作用域非空 (结论不可用,调用方自行降级)
    • candidatesInZone

      default List<CandidateView> candidatesInZone(String zone, AdmissionScope scope)
      列出指定小区当前候选快照,并只保留满足准入作用域的那些(FR-244)。

      判定用的标签是候选节点自己声明的那一份(CandidateView.labels() 同一真源), 因此"这次读到的候选集合"与"决策会考虑的候选集合"是同一套条件, 不会出现"候选列表里有、决策却选不中"这种两处口径分叉。

      向后兼容:同为 default,缺省实现与 acquireCandidate(String, String, AdmissionScope) 同一条方向原则—— 空作用域(null 或 AdmissionScope.isEmpty())直接委派给 candidatesInZone(String)(空作用域不排除任何候选,等价且不放宽); 非空作用域抛 UnsupportedOperationException(忽略作用域返回全量候选是静默放宽准入)。 candidatesInZone(String) 的签名一字未改 (取值口径的更正见 acquireCandidate(String, String, AdmissionScope))。

      抛出契约(两类异常必须分别处置,不得合并)

      • UnsupportedOperationException:能力缺失——本实现不支持按作用域收窄 (例如旧实现、只看缓存字段集不全的实现)。这是"这条路本实现走不通", 调用方的正确动作是改用不带作用域的重载,并把准入校验放回自己那一侧 (或按自己的能力探测结果换实现),不是"没有候选"。
      • IllegalStateException:本帧判据不可用——本次收窄判不了, 典型情形是候选快照未携带节点自声明标签字段(对端尚未支持该字段), 即"判定依据这一帧看不到"。它是当前状态、可重试, 调用方必须按"不可用"处置(与 ScheduleState.UNAVAILABLE 同口径: 等下一帧刷新后重试或改走不带作用域的路径),不得把它读成"没有候选" 而永久放弃一次派房。实现方不得用返回空列表来代替本异常—— 那是拿"看不到"冒充"没有"。

      两者都不会因为"控制面不可达"而发生:控制面不可用时读本地快照照常返回 (fail-static),只有判据本身缺失才抛 IllegalStateException。

      Parameters:
      zone - 目标小区名
      scope - 准入作用域;null 视为 AdmissionScope.empty()(等价于不带作用域的重载)
      Returns:
      满足作用域的候选(顺序与原快照一致);缓存未覆盖该小区时为空列表(非 null)。 作用域非空时"空列表"是稳定事实(收窄后的确没有符合条件的候选), 与上面的 IllegalStateException(判不了)严格区分
      Throws:
      UnsupportedOperationException - 本实现不支持按作用域收窄,且本次作用域非空(能力缺失)
      IllegalStateException - 本次收窄的判据不可用(如候选快照未携带自声明标签字段,属可重试的当前状态)
    • candidatesInZone

      List<CandidateView> candidatesInZone(String zone)
      列出指定小区当前候选快照(本地缓存,O(1) 读,可在主线程调用)。

      非实时,最长滞后一个刷新周期;缓存未覆盖该小区时返回空列表(非 null)。

    • healthOf

      HealthView healthOf(String serverId)
      查询某台服务器的健康视图(本地候选缓存快照)。
      Returns:
      缓存未覆盖该服时返回 null
    • selfHealth

      HealthView selfHealth()
      查询本服自身健康视图(随每次指标上报响应刷新,约 5s 新鲜度)。
      Returns:
      从未上报成功时返回 null
    • dataSource

      DataSourceState dataSource()
      当前数据来源状态与快照年龄。