← 返回博客列表 Stripe 2026 NG OA 真题:服务器负载均衡 5 个 Part 全解析|状态模拟系统
Stripe

Stripe 2026 NG OA 真题:服务器负载均衡 5 个 Part 全解析|状态模拟系统

2026-05-14

Stripe 2026 New Grad OA 是典型的"系统状态模拟题"——不考算法 trick,但考你能否同时维护 4-5 个互相依赖的状态:服务器连接数、连接 ID 与服务器的映射、objId 与服务器的亲和绑定、容量上限、SHUTDOWN 后连接的重路由。题目分 5 个递进 Part,每一 Part 都建立在前一 Part 的代码之上。本文给出 5 个 Part 的完整 Python 解法,并标注 HackerRank 隐藏测试常踩点。

Stripe NG OA 概览

维度 详情
平台 HackerRank(全程录屏 + 摄像头)
时长 60 分钟
题量 1 道大题,5 个 Part
难度 LeetCode Easy ~ Medium,重逻辑
通过线 5/5 隐藏 case 全绿
语言 Python / Java / C++ 自选

题目核心:Server Load Balancing System

输入

输出:所有被接受的连接日志(按发生顺序)

题目要求模拟服务器在以下规则下的负载均衡:

Part 1:纯 CONNECT 路由

规则:每个 CONNECT 请求路由到当前连接数最少的服务器;连接数相同则选 index 较小的。

class LoadBalancer:
    def __init__(self, num_server: int, max_connection: int):
        self.num_server = num_server
        self.max_connection = max_connection
        self.counts = [0] * num_server
        self.conn_to_server = {}
        self.log = []

    def _pick_server(self):
        min_idx = 0
        for i in range(1, self.num_server):
            if self.counts[i] < self.counts[min_idx]:
                min_idx = i
        return min_idx

    def connect(self, connect_id, user_id, obj_id):
        s = self._pick_server()
        self.counts[s] += 1
        self.conn_to_server[connect_id] = s
        self.log.append(("CONNECT", connect_id, s))

Part 1 核心:用 list 维护 counts,避免堆——后续 Part 还要按 objId / serverId 索引,堆不灵活。

Part 2:加上 DISCONNECT

规则:DISCONNECT 释放对应 connectId 占用的服务器槽位。

def disconnect(self, connect_id, user_id, obj_id):
    if connect_id not in self.conn_to_server:
        return
    s = self.conn_to_server.pop(connect_id)
    self.counts[s] -= 1
    self.log.append(("DISCONNECT", connect_id, s))

隐藏 case:DISCONNECT 不存在的 connectId(之前没 CONNECT 过 / 已被 SHUTDOWN 清掉)时,静默忽略而不是报错。

Part 3:按 objId 亲和路由

规则:相同 objId 的连接应尽量路由到同一台服务器

def __init__(self, ...):
    ...
    self.obj_to_server = {}  # objId -> serverId
    self.obj_count = defaultdict(int)  # objId 当前活动连接数

def connect(self, connect_id, user_id, obj_id):
    if obj_id in self.obj_to_server:
        s = self.obj_to_server[obj_id]
    else:
        s = self._pick_server()
        self.obj_to_server[obj_id] = s
    self.counts[s] += 1
    self.obj_count[obj_id] += 1
    self.conn_to_server[connect_id] = s
    self.conn_to_obj[connect_id] = obj_id
    self.log.append(("CONNECT", connect_id, s))

def disconnect(self, connect_id, user_id, obj_id):
    if connect_id not in self.conn_to_server:
        return
    s = self.conn_to_server.pop(connect_id)
    o = self.conn_to_obj.pop(connect_id)
    self.counts[s] -= 1
    self.obj_count[o] -= 1
    if self.obj_count[o] == 0:
        del self.obj_to_server[o]
    self.log.append(("DISCONNECT", connect_id, s))

Part 3 关键obj_count == 0移除 obj_to_server 映射——否则同一 objId 后续重连会卡在已被 SHUTDOWN 的服务器上(Part 5 会爆)。

Part 4:加上容量上限

规则:服务器达到 maxConnection 后拒绝新 CONNECT。

def connect(self, connect_id, user_id, obj_id):
    if obj_id in self.obj_to_server:
        s = self.obj_to_server[obj_id]
        if self.counts[s] >= self.max_connection:
            self.log.append(("REJECT", connect_id))
            return
    else:
        s = self._pick_available_server()
        if s is None:
            self.log.append(("REJECT", connect_id))
            return
        self.obj_to_server[obj_id] = s
    ...

def _pick_available_server(self):
    candidates = [i for i in range(self.num_server)
                  if self.counts[i] < self.max_connection]
    if not candidates:
        return None
    return min(candidates, key=lambda i: (self.counts[i], i))

Part 4 决策点:当绑定的 objId server 满了时,是否回退到其他 server?题目期望不回退,直接 REJECT——这是 Stripe 的"affinity 优先级 > 完成率"业务直觉。

Part 5:SHUTDOWN 重路由

规则["SHUTDOWN", serverId] 时:

  1. 该 server 上所有连接作废(不算 DISCONNECT)
  2. 该 server 上的 objId 绑定释放
  3. 后续 CONNECT 请求绑定到其他 server
def shutdown(self, server_id):
    self.counts[server_id] = 0
    objs_to_release = [
        obj for obj, srv in self.obj_to_server.items() if srv == server_id
    ]
    for obj in objs_to_release:
        del self.obj_to_server[obj]
        self.obj_count[obj] = 0
    conns_to_drop = [
        cid for cid, srv in self.conn_to_server.items() if srv == server_id
    ]
    for cid in conns_to_drop:
        del self.conn_to_server[cid]
        if cid in self.conn_to_obj:
            del self.conn_to_obj[cid]
    # 标记为下线(防止后续 _pick_available_server 选中)
    self.shut_down.add(server_id)
    self.log.append(("SHUTDOWN", server_id))

def _pick_available_server(self):
    candidates = [
        i for i in range(self.num_server)
        if i not in self.shut_down and self.counts[i] < self.max_connection
    ]
    if not candidates:
        return None
    return min(candidates, key=lambda i: (self.counts[i], i))

Part 5 三个隐藏 case

  1. SHUTDOWN 后该 server 的 objId 绑定要全部清掉(否则下次该 obj 再 CONNECT 会路由到下线 server)
  2. SHUTDOWN 不能影响 log 里的历史 CONNECT 记录
  3. SHUTDOWN 一个已经下线的 server 应当幂等(不报错)

完整代码(5 个 Part 整合)

from collections import defaultdict
from typing import List

class LoadBalancer:
    def __init__(self, num_server: int, max_connection: int):
        self.num_server = num_server
        self.max_connection = max_connection
        self.counts = [0] * num_server
        self.shut_down = set()
        self.conn_to_server = {}
        self.conn_to_obj = {}
        self.obj_to_server = {}
        self.obj_count = defaultdict(int)
        self.log = []

    def _pick_available_server(self):
        candidates = [
            i for i in range(self.num_server)
            if i not in self.shut_down and self.counts[i] < self.max_connection
        ]
        if not candidates:
            return None
        return min(candidates, key=lambda i: (self.counts[i], i))

    def connect(self, connect_id, user_id, obj_id):
        if obj_id in self.obj_to_server:
            s = self.obj_to_server[obj_id]
            if s in self.shut_down or self.counts[s] >= self.max_connection:
                self.log.append(("REJECT", connect_id))
                return
        else:
            s = self._pick_available_server()
            if s is None:
                self.log.append(("REJECT", connect_id))
                return
            self.obj_to_server[obj_id] = s
        self.counts[s] += 1
        self.obj_count[obj_id] += 1
        self.conn_to_server[connect_id] = s
        self.conn_to_obj[connect_id] = obj_id
        self.log.append(("CONNECT", connect_id, s))

    def disconnect(self, connect_id, user_id, obj_id):
        if connect_id not in self.conn_to_server:
            return
        s = self.conn_to_server.pop(connect_id)
        o = self.conn_to_obj.pop(connect_id)
        self.counts[s] -= 1
        self.obj_count[o] -= 1
        if self.obj_count[o] == 0 and o in self.obj_to_server:
            del self.obj_to_server[o]
        self.log.append(("DISCONNECT", connect_id, s))

    def shutdown(self, server_id):
        if server_id in self.shut_down:
            return
        self.shut_down.add(server_id)
        self.counts[server_id] = 0
        for obj in [o for o, srv in self.obj_to_server.items() if srv == server_id]:
            del self.obj_to_server[obj]
            self.obj_count[obj] = 0
        for cid in [c for c, srv in self.conn_to_server.items() if srv == server_id]:
            del self.conn_to_server[cid]
            if cid in self.conn_to_obj:
                del self.conn_to_obj[cid]
        self.log.append(("SHUTDOWN", server_id))

    def run(self, requests: List[List]):
        for req in requests:
            op = req[0]
            if op == "CONNECT":
                self.connect(req[1], req[2], req[3])
            elif op == "DISCONNECT":
                self.disconnect(req[1], req[2], req[3])
            elif op == "SHUTDOWN":
                self.shutdown(req[1])
        return self.log

时间复杂度:每次操作 O(n_server),整体 O(R · n_server),n_server ≤ 50
空间复杂度:O(R + obj 数量)

Stripe NG OA 拿满分的 4 个习惯

1)每加一 Part,先写 1-2 个手动测试

Part 5 的隐藏 case 多到无法只靠自查。每完成一个 Part,造一个 5-10 行的请求序列手动验证状态变化。

2)状态变量集中初始化

Stripe 评分会扫描"状态分散"的反模式。所有状态在 __init__ 初始化,不要在运行中动态加属性。

3)拒绝写日志保留

REJECT 不写入主 log,但 Stripe 部分版本要求返回拒绝列表。提交前确认题面要求。

4)连接幂等性

Part 5 SHUTDOWN 后的 server 应当能被再次 SHUTDOWN(幂等);同一 connectId 连续 DISCONNECT 也应静默处理。

FAQ

Stripe NG OA 题目每年都换吗?

题面会变,但结构高度稳定——5 个递进 Part、维护多个映射、最后一个 Part 是"重路由 / 撤销"。2024-2026 都遵循这个模板:从 brace expansion → shipping cost → server load balancing。

60 分钟够吗?

够,但要前 15 分钟做规划:把 Part 1-5 的状态变量列出来,规划数据结构。直接开写很容易在 Part 4-5 因为状态分散需要重构。

一定要用 Class 吗?

强烈推荐用 Class——5 个 Part 共享状态,函数式写法变量传参会膨胀到几十行。Stripe 评分对 OOP 风格有偏好。

Part 5 时间不够能拿到通过吗?

可以。完成 Part 1-4(约 80% 通过率)就达到 Stripe NG OA 通过线。Part 5 是 stretch goal。

这道题在 1point3acres 还能搜到讨论吗?

可以。关键词搜 "Stripe NG OA 5 part"、"Stripe server load balancing"、"Stripe 2026 OA",每周都有候选人 fresh 反馈。


正在准备 Stripe NG OA?

oavoservice 提供 Stripe NG OA 全流程实时辅助:拆 Part、状态变量规划、HackerRank 全过程协助。我们对 Stripe 历年的"5 Part 状态模拟"题型有完整复盘——server load balancing、subscription notifications、shipping cost、brace expansion 都能给出最优代码模板。

立即添加微信:Coding0201获取 Stripe NG OA 实战辅导

#Stripe #StripeOA #NewGrad #负载均衡 #状态模拟 #OA真题


联系方式

Email: [email protected]
Telegram: @OAVOProxy