1. 项目背景与挑战B站作为国内领先的视频社区平台拥有超过7亿注册用户。想要获取如此庞大的用户数据传统单机爬虫会遇到几个致命问题IP封锁、请求频率限制、数据存储压力以及任务调度复杂度。我在实际项目中遇到过这样的情况——单机爬取速度达到每秒1000请求时不到5分钟就会被封禁IP。分布式架构的核心思路是将任务拆分为三个层级调度服务器负责任务分配和状态管理工作节点执行实际爬取任务数据存储层处理海量数据写入这种架构下我们实测每天能稳定获取3000万条用户数据且不会对目标服务器造成过大压力。下面这段C代码展示了基础的任务结构体设计struct BiliTask { int clientId; // 工作节点ID unsigned midStart; // 起始用户ID unsigned midEnd; // 结束用户ID time_t ttl; // 任务有效期 };2. 核心架构设计2.1 代理IP池管理代理IP是爬虫的生命线。我们的方案采用动态评分机制新获取的IP初始分数为100分每次请求失败扣20分分数低于30分自动淘汰每小时补充新IP实测发现优质代理IP的可用时长差异巨大。有些只能坚持2-3分钟而稳定的IP能持续工作数小时。我们通过多供应商采购解决了这个问题以下是IP池的核心数据结构class ProxyPool { std::mapProxyInfo, int activeProxies; // IP信息, 当前分数 std::mutex poolMutex; // 线程安全锁 void refreshProxies() { // 从多个供应商获取新IP } };2.2 心跳检测机制分布式系统最怕节点假死。我们的解决方案包含三层检测主动心跳每30秒上报状态任务心跳每次请求任务视为一次心跳超时剔除连续3次无响应则移除节点当工作节点异常离线时调度器会自动将其未完成任务重新分配。这个过程对整体系统完全透明开发者只需关注业务逻辑。3. 关键技术实现3.1 任务分片算法面对7亿用户ID我们采用动态分片策略初始按10000个ID为单元分片根据节点性能动态调整分片大小失败任务自动拆分为更小单元这种设计使得8核机器能同时处理200个微任务CPU利用率保持在70%左右。分片调度器的核心逻辑如下class TaskScheduler { std::queueBiliTask pendingTasks; // 待处理任务 std::mapint, BiliTask activeTasks; // 执行中任务 void dispatchTask(WorkerNode node) { if(pendingTasks.empty()) { // 自动生成新任务批次 generateTasks(10000); } auto task pendingTasks.front(); node.assignTask(task); activeTasks[node.id] task; } };3.2 数据存储优化原始方案使用MySQL单表存储在数据量超过5000万条后性能急剧下降。我们最终采用的方案是按用户ID范围分表每200万用户一个表空数据单独压缩存储使用内存缓存热点数据这使写入速度从最初的2000条/秒提升到15000条/秒。以下是表结构设计示例CREATE TABLE bili_user_1_200w ( uid BIGINT PRIMARY KEY, name VARCHAR(64) CHARACTER SET utf8mb4, gender TINYINT, level TINYINT, sign TEXT, -- 其他字段... ) ENGINEInnoDB PARTITION BY KEY(uid);4. 性能调优实战4.1 连接池优化初期每个请求都新建连接导致TCP连接数爆炸。我们引入的连接池方案包含预建立50个HTTP连接异步IO复用通道智能DNS缓存调整后网络延迟从平均300ms降至80ms。关键实现代码class ConnectionPool { std::vectorCURL* handles; std::queueCURL* freeHandles; CURL* getHandle() { if(freeHandles.empty()) { return curl_easy_init(); } auto h freeHandles.front(); freeHandles.pop(); return h; } };4.2 内存管理技巧持续运行一周后出现内存泄漏我们通过以下手段解决使用智能指针管理资源每处理10万请求主动释放缓存定制化内存分配器Valgrind检测显示内存使用稳定在2GB左右不再持续增长。这是我们的内存监控方案class MemoryMonitor { static void checkMemoryUsage() { if(currentUsage threshold) { cleanupCache(); curl_global_cleanup(); // 其他清理操作... } } };5. 异常处理经验5.1 反爬虫策略应对B站的反爬机制会随时间变化我们总结出这些规律频繁访问相同API会触发验证码请求头缺失Referer会返回空数据异地登录行为会导致临时封禁解决方案是构建拟人行为模式随机延迟(100-500ms)轮换UserAgent模拟真实用户点击流5.2 数据一致性保障网络波动可能导致数据丢失我们采用本地SQLite临时存储断点续传机制数据校验哈希值这套方案使得即使在网络中断1小时后恢复工作也能从断点继续。6. 部署方案6.1 混合环境部署我们的生产环境包含1台调度服务器Linux3台高配工作节点Windows1台数据库服务器Linux多台廉价VPS作为代理节点这种组合既保证了核心服务稳定性又降低了代理IP成本。实测每天代理费用控制在50元以内。6.2 监控系统搭建使用PrometheusGrafana构建监控看板关键指标包括实时爬取速度代理IP健康度任务队列深度节点存活状态当系统出现异常时Telegram机器人会立即发送告警。7. 踩坑记录在开发过程中最耗时的三个问题CURL多线程崩溃需要设置CURLOPT_NOSIGNALMySQL连接耗尽调整wait_timeout参数代理IP突发失效实现快速切换机制有个有趣的发现工作节点在Windows下的性能比Linux高15%这可能与TCP协议栈实现差异有关。8. 扩展思考这套架构稍作修改就能适配其他场景电商价格监控替换解析模块新闻舆情分析增加NLP组件社交网络挖掘调整爬取策略最近我们在尝试将核心模块移植到Rust初步测试显示内存安全性有显著提升。