腾讯-微信支付C++ 二面 面经总结
1. 介绍一下你做过的最有挑战性的项目,遇到了什么技术难点,如何解决的?
回答框架:
- 项目背景:简要说明项目目标和技术栈
- 技术挑战:具体描述遇到的核心问题(性能瓶颈、并发问题、架构设计等)
- 解决方案:详细说明采用的技术方案和实现细节
- 效果量化:用数据说明优化效果(性能提升X%、响应时间降低等)
- 反思总结:学到了什么,还有哪些改进空间
示例:"我做过一个高并发的订单处理系统。最大的挑战是在秒杀场景下,数据库成为瓶颈,出现大量超时。我采用了多级缓存架构:Redis缓存库存,本地缓存热点数据,使用Lua脚本保证原子性扣减库存。同时引入消息队列异步处理订单,削峰填谷。最终QPS从2000提升到15000,响应时间从500ms降到50ms以内。"
2. 你的项目中使用了哪些设计模式?为什么选择这些模式?
常用设计模式及应用场景:
- 单例模式:数据库连接池、日志管理器、配置管理器。保证全局唯一实例,注意线程安全(双重检查锁、局部静态变量)
- 工厂模式:创建不同类型的对象,解耦对象创建和使用。如根据配置创建不同的消息处理器
- 策略模式:支付方式选择(微信、支付宝、银行卡),不同的加密算法。避免大量if-else
- 观察者模式:事件通知系统,订单状态变化通知多个模块
- 装饰器模式:给对象动态添加功能,如日志、性能监控、权限检查
- 责任链模式:请求处理流程,如参数校验→权限验证→业务处理→日志记录
选择原则:根据实际需求,不要为了用设计模式而用。重点是代码的可维护性、可扩展性和可读性。
3. 你们的系统是如何保证高可用的?如果服务器宕机了怎么办?
高可用架构设计:
- 冗余部署:多机房部署,异地容灾负载均衡(Nginx、LVS)分发流量无状态服务,方便水平扩展
- 故障检测:健康检查(心跳机制)监控告警(Prometheus、Grafana)日志收集分析(ELK)
- 故障恢复:自动重启机制服务降级:关闭非核心功能熔断机制:防止雪崩效应限流:保护系统不被压垮
- 数据可靠性:数据库主从复制、读写分离Redis哨兵或集群模式定期备份,快速恢复
- 灰度发布:小流量验证快速回滚机制
目标:99.99%可用性(年停机时间<53分钟)。
4. 如何设计一个分布式锁?需要考虑哪些问题?
实现方案:
方案一:基于Redis的分布式锁
// 加锁:SET key value NX PX 30000
bool lock(string key, string requestId, int expireTime) {
return redis.set(key, requestId, "NX", "PX", expireTime);
}
// 解锁:Lua脚本保证原子性
bool unlock(string key, string requestId) {
string script = R"(
if redis.call('get', KEYS[1]) == ARGV[1] then
return redis.call('del', KEYS[1])
else
return 0
end
)";
return redis.eval(script, {key}, {requestId});
}
方案二:基于Zookeeper
- 创建临时顺序节点
- 最小序号获得锁
- 监听前一个节点,避免羊群效应
需要考虑的问题:
- 互斥性:同一时刻只有一个客户端持有锁
- 死锁:设置过期时间,防止客户端崩溃导致锁无法释放
- 锁误删:使用唯一标识(UUID),只能删除自己的锁
- 原子性:加锁和设置过期时间必须原子操作
- 可重入:同一线程可以多次获取锁(记录持有者和计数)
- 锁续期:业务执行时间超过过期时间,需要自动续期(看门狗机制)
- 高可用:Redis集群模式,Redlock算法(多个独立Redis实例)
- 性能:自旋等待 vs 阻塞等待
推荐使用成熟的库如Redisson,已经处理了这些细节问题。
5. 你们的日志系统是怎么设计的?如何处理海量日志?
日志系统设计要点:
- 日志级别:DEBUG、INFO、WARN、ERROR、FATAL生产环境通常只记录INFO及以上
- 日志格式:时间戳、日志级别、线程ID、文件位置、消息内容结构化日志(JSON格式),便于解析
- 日志输出:异步写入,避免阻塞业务线程双缓冲机制:前台缓冲收集,后台缓冲写入日志轮转:按大小或时间切割文件
- 性能优化:使用线程局部存储减少锁竞争批量写入,减少系统调用内存映射文件(mmap)提高IO性能
- 海量日志处理:分布式日志收集(Filebeat、Fluentd)集中存储(Elasticsearch)可视化分析(Kibana)日志采样:高频日志只记录部分
- 关键信息:请求ID(traceId):串联整个调用链用户ID、订单号等业务标识耗时统计
实现示例:
class Logger {
void log(Level level, const string& msg) {
if (level < minLevel_) return;
LogEntry entry{time(), level, threadId(), msg};
frontBuffer_.append(entry);
if (frontBuffer_.full()) {
swap(frontBuffer_, backBuffer_);
notify_writer_thread();
}
}
};
6. 如果让你设计微信红包系统,你会怎么设计?
核心需求分析:
- 发红包:指定金额和个数,生成红包
- 抢红包:高并发场景,先到先得
- 查看红包:已领取列表、剩余金额
技术挑战:
- 高并发:春节期间QPS极高
- 一致性:金额不能超发
- 公平性:随机金额算法
- 性能:毫秒级响应
设计方案:
- 红包生成:二倍均值法:保证随机且公平预先生成所有金额,存入Redis列表红包ID、总金额、剩余个数、领取记录
- 抢红包流程:Redis原子操作(LPOP)获取金额乐观锁检查剩余个数异步写入数据库(消息队列)返回结果
- 数据存储:Redis:红包信息、剩余金额列表(热数据)MySQL:红包记录、领取记录(持久化)最终一致性:先Redis后DB,失败重试
- 高并发优化:分片:按红包ID哈希分散到不同Redis实例限流:单个红包QPS限制降级:超时直接返回"手慢了"
- 防刷机制:同一用户只能领一次(Redis Set记录)IP限流风控系统
- 监控告警:实时监控抢红包成功率金额对账:Redis和DB定期核对
核心代码逻辑:
bool grabRedPacket(string packetId, string userId) {
// 1. 检查是否已领取
if (redis.sismember("grabbed:" + packetId, userId)) {
return false; // 已领取
}
// 2. 原子获取金额
string amount = redis.lpop("amounts:" + packetId);
if (amount.empty()) {
return false; // 已抢完
}
// 3. 记录领取
redis.sadd("grabbed:" + packetId, userId);
// 4. 异步写DB
mq.send({packetId, userId, amount});
return true;
}
7. 说说你对微服务架构的理解,以及遇到的问题和解决方案
微服务架构特点
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
本专栏系统梳理C++技术面试核心考点,涵盖语言基础、面向对象、内存管理、STL容器、模板编程及经典算法。从引用指针、虚函数表、智能指针等底层原理,到继承多态、运算符重载等OOP特性从const、static、inline等关键字辨析,到动态规划、KMP算法、并查集等手写实现。每个知识点以面试答题形式呈现,注重原理阐述而非冗长代码,帮助你快速构建完整知识体系,从容应对面试官提问,顺利拿下offer。
