HarmonyOS分布式爬虫实战:Actor模型架构解析 原创

SameX
发布于 2025-6-27 13:13
浏览
0收藏

作为在鸿蒙分布式系统中摸爬滚打的开发者,曾用Actor模型构建过日均千万级请求的爬虫系统。本文分享从架构设计到容错优化的实战经验,帮你用Actor模型打造高效稳定的分布式爬虫。

一、核心架构:三角色Actor协同设计

1.1 爬虫节点Actor(负责网页抓取)

actor CrawlerNode {
    private var taskQueue: [String] = []
    private let aggregator: ActorRef<ResultAggregator>
    
    init(aggregator: ActorRef<ResultAggregator>) {
        this.aggregator = aggregator
    }
    
    receiver func addTask(url: String) {
        taskQueue.append(url)
        processTasks()
    }
    
    private func processTasks() {
        while !taskQueue.isEmpty {
            let url = taskQueue.removeFirst()
            if let content = fetchPage(url) {
                let data = parsePage(content)
                aggregator.send(StoreData(data))
            } else {
                // 失败任务重新入队
                taskQueue.append(url)
            }
        }
    }
    
    private func fetchPage(_ url: String) -> String? {
        // 带重试的网络请求
        for _ in 0..3 {
            do {
                return Http.get(url).content
            } catch {
                sleep(1)  // 重试间隔
            }
        }
        return nil
    }
}

1.2 任务调度器Actor(负载均衡)

actor TaskScheduler {
    private var nodes: [ActorRef<CrawlerNode>] = []
    private var taskQueue: [String] = []
    
    receiver func register(node: ActorRef<CrawlerNode>) {
        nodes.append(node)
        dispatchTasks()
    }
    
    receiver func addTask(url: String) {
        taskQueue.append(url)
        dispatchTasks()
    }
    
    private func dispatchTasks() {
        while !taskQueue.isEmpty {
            let node = nodes.min(by: { $0.load < $1.load })!
            node.send(addTask(taskQueue.removeFirst()))
        }
    }
}

1.3 结果聚合器Actor(数据处理)

actor ResultAggregator {
    private var dataStore: DataStore
    
    receiver func StoreData(data: [String]) {
        dataStore.save(data)
        if dataStore.count % 100 == 0 {
            flushToDB()
        }
    }
    
    private func flushToDB() {
        // 批量写入数据库
    }
}

二、容错机制:断点续爬与异常处理

2.1 断点续爬实现

actor CheckpointManager {
    private let db: Database
    
    func saveState(nodes: [ActorRef<CrawlerNode>]) {
        var tasks = [String]()
        for node in nodes {
            tasks.append(contentsOf: node.taskQueue)
        }
        db.save("crawler_tasks", tasks)
    }
    
    func restoreState() -> [String] {
        return db.load("crawler_tasks") ?? []
    }
}

2.2 异常重试策略

extension CrawlerNode {
    private func fetchWithRetry(url: String, retries: Int = 3) -> String? {
        if retries == 0 {
            log("Failed: \(url)")
            return nil
        }
        
        do {
            return Http.get(url, timeout: 5s).content
        } catch {
            sleep(1s * retries)  // 指数退避
            return fetchWithRetry(url, retries-1)
        }
    }
}

三、性能优化:从代码到架构

3.1 网络IO优化

  1. 连接池复用

  2. actor HttpPool {

  3. private let pool: ConnectionPool
    

    func getConnection() -> HttpConnection {
    return pool.borrow()
    }

    func release(connection: HttpConnection) {
    pool.release(connection)
    }
    }


2. **并发控制**:  
3. ```cj
4. actor CrawlerNode {
5.     private let semaphore = Semaphore(5)  // 限制并发请求数
    private func fetchPage(_ url: String) -> String? {
        semaphore.acquire()
        defer { semaphore.release() }
        
        // 请求处理...
    }
}

3.2 可视化监控

graph TD
A[Prometheus] -->|抓取指标| B(CrawlerNode)
A -->|调度指标| C(TaskScheduler)
A -->|存储指标| D(ResultAggregator)
E[Grafana] --> A

监控关键指标

  • 节点负载(任务处理耗时)
    • 网络请求成功率
    • 数据存储吞吐量

四、实战部署:从单机到集群

4.1 分布式部署架构

┌─────────────┐    ┌─────────────┐    ┌─────────────┐  
│ 节点1       │    │ 节点2       │    │ 节点3       │  
│ (Crawler)   │    │ (Crawler)   │    │ (Scheduler) │  
└─────────────┘    └─────────────┘    └─────────────┘  
    ↑     消息总线     ↑     消息总线     ↑  
    └────────────────┼────────────────┘  
             ┌──────────────────────┐  
             │ 分布式消息中间件      │  
             └──────────────────────┘  

4.2 集群扩展策略

  1. 动态扩缩容
  2. actor ClusterManager {
  3. func scaleOut() {
    
  4.     for _ in 0..3 {
    
  5.         spawn(CrawlerNode(aggregator))
    
  6.     }
    
  7. }
    
    func scaleIn() {
    // 选择低负载节点关闭
    }
    }

2. **故障转移**:  
3. ```cj
4. actor Scheduler {
5.     receiver func nodeFailed(node: ActorRef<CrawlerNode>) {
6.         nodes.remove(node)
7.         rebalanceTasks()
8.     }
9. }
10. ```  

## 五、避坑指南:分布式爬虫的生死线  

1. **任务重复抓取**:  
2.    - 用布隆过滤器去重URL,避免重复任务  
2. **网络拥塞**:  
3.    - 实现全局请求限速,按域名分桶控制  
3. **数据一致性**:  
4.    - 结果聚合器使用幂等存储,避免重复数据  
5. 






©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
分类
标签
收藏
回复
举报
回复
    相关推荐