网易雷火 游戏服务器-C++ 一面

1. 请做一个简洁的自我介绍

2. 你在“高性能分布式日志接入与检索网关”项目里主要做了什么

我主要负责三块内容:

  1. 日志接入链路优化负责接收多来源日志上报;做协议解析、批量写入和异步转发。
  2. 对象池与内存复用模块针对日志消息对象、缓冲区对象做池化管理;降低频繁分配释放带来的锁竞争与碎片问题。
  3. 共享内存检索加速热点索引和统计信息通过共享内存供多个进程访问;减少重复反序列化和 IPC 拷贝开销。

这个项目的核心目标是:

  • 在高并发日志写入下保持低延迟;
  • 在热点查询下保证检索稳定性和资源可控。

3. 讲一下这个“高性能分布式日志接入与检索网关”的整体框架

整体可以拆成 5 层:

  1. 接入层负责 TCP 长连接管理;接收日志上报请求,做协议解码和校验。
  2. 缓存与对象管理层对消息对象、缓冲区、批量发送节点做对象池复用;降低堆分配开销。
  3. 写入调度层按日志类型、租户、索引分片进行路由;支持批量刷盘和异步落库。
  4. 检索加速层通过共享内存保存热点索引摘要与统计元数据;多进程直接读取,提高查询性能。
  5. 监控与治理层延迟打点、队列积压监控、丢日志率统计、内存监控。

代码:

class LogGateway {
public:
    void OnMessage(const TcpConnPtr& conn, Buffer* buf) {
        auto req = codec_.Decode(buf);
        auto* msg = msgPool_.Acquire();
        msg->Fill(req);

        auto shard = router_.Select(msg->tenantId, msg->logType);
        dispatcher_.Dispatch(shard, msg);

        metrics_.RecordIn(msg->tenantId, msg->size);
    }

private:
    Codec codec_;
    Router router_;
    MessagePool msgPool_;
    Dispatcher dispatcher_;
    Metrics metrics_;
};

4. 你这个对象池/内存复用模块是怎么做的?是直接照着 tcmalloc 实现的吗

不是完整照搬 tcmalloc,而是参考它的设计思想,做了面向业务对象的定制化池化。 因为这个项目里对象类型比较固定,比如:

  • 日志消息对象;
  • 批量发送缓冲对象;
  • 查询结果节点;
  • 索引构建临时对象。

我的设计重点不是做一个“通用内存分配器”,而是做一个低延迟对象池,解决以下问题:

  1. 高频小对象 new/delete 造成的锁竞争;
  2. 大量短生命周期对象导致的 allocator 抖动;
  3. 峰值流量下尾延迟不稳定。

设计上主要分两层:

  • 线程本地缓存优先复用当前线程已释放对象;减少跨线程锁争用。
  • 全局池线程本地不足时批量申请;空闲对象过多时再归还全局池。

代码:

template <class T>
class ObjectPool {
public:
    T* Acquire() {
        if (!local_.empty()) {
            T* obj = local_.back();
            local_.pop_back();
            return obj;
        }
        return GlobalAcquire();
    }

    void Release(T* obj) {
        obj->Reset();
        local_.push_back(obj);
    }

private:
    T* GlobalAcquire();
    thread_local static std::vector<T*> local_;
};

5. 这个项目的测试结果怎么样?你是如何设计压测方案的?

测试我主要分成三部分:

  1. 功能正确性测试检查日志是否丢失、乱序;检查对象池复用是否安全;检查异常情况下是否能正确回收资源。
  2. 性能压测模拟真实日志写入流量;测 QPS、平均耗时、P99/P999;观察不同对象大小分布下的性能差异。
  3. 稳定性测试长时间压测观察 RSS 变化;检查是否有泄漏、积压、抖动问题;看高峰流量结束后资源是否回落。

压测场景不是只测固定包长,而是模拟真实日志模型:

  • 70% 小包日志;
  • 20% 中等长度结构化日志;
  • 10% 大包异常堆栈日志。

6. 测试前后内存峰值有什么变化?为什么优化后 RSS 不一定明显下降?

改造前的现象通常是:

  • 高峰流量下频繁申请释放;
  • allocator 抖动明显;
  • 尾延迟高;
  • RSS 有时会持续上涨且波动剧烈。

改造后的变化通常是:

  1. 峰值更平滑;
  2. 高峰期分配耗时更稳定;
  3. 长尾延迟下降;
  4. RSS 不一定更低,但增长更可解释、回落更可控。

原因是:

  • 对象池会缓存一部分空闲对象;
  • 所以从操作系统视角看,RSS 可能没有立刻下降;
  • 但从系统稳定性看,延迟和吞吐往往更优。

7. 项目里如果发现内存持续上涨,你怎么判断是泄漏、碎片还是缓存保留?

答案: 这是线上排障很常见的问题,不能一看到 RSS 涨就说泄漏。 一般我会分三类判断:

  1. 真实内存泄漏活跃对象不该增加却持续增加;heap profile 能看到未释放对象累计。
  2. 分配器碎片业务对象已经释放;但 allocator 由于页保留、碎片合并不及时导致 RSS 不回落。
  3. 缓存/对象池保留对象池、连接池、缓存命中优化导致内存被保留;这不是泄漏,但需要设置上限和淘汰策略。

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

C++ 常考面试题总结 文章被收录于专栏

本专栏系统梳理C++方向, 大中厂高频高频面试考点 , 内容皆来自真实面试经历,从基础语法、内存管理、STL与设计模式,到操作系统与项目实战,结合真实面试题深度解析,帮助开发者高效查漏补缺,提升技术理解与面试通过率,打造扎实的C++工程能力.

全部评论

相关推荐

评论
1
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务