Skip to main content

alun_task/
plugin.rs

1//! TaskPlugin —— 任务插件入口
2//!
3//! 实现 `alun_core::Plugin` trait,通过 `PluginManager` 管理生命周期。
4//! 不持有任何数据库连接——持久化通过 `TaskStorage` trait 委托给业务方。
5
6use std::sync::Arc;
7use async_trait::async_trait;
8use alun_core::{Plugin, Result as CoreResult};
9use tokio::task::JoinHandle;
10use tracing::{info, error};
11
12use crate::storage::TaskStorage;
13use crate::HandlerRegistry;
14use crate::TaskWorker;
15use crate::RetryScanner;
16use crate::TaskMetrics;
17use crate::TaskWorkerConfig;
18
19/// 任务插件
20///
21/// 管理 TaskWorker 和 RetryScanner 的生命周期。
22/// 持久化通过 `TaskStorage` trait 委托给业务方。
23///
24/// 实现 `alun_core::Plugin`,可通过 `PluginManager` 或 `App::plugin()` 统一管理。
25///
26/// # 使用示例
27///
28/// ```ignore
29/// // 配置从 config.toml 的 [task] section 读取
30/// let task_cfg: TaskWorkerConfig = app.config().get_section("task")?;
31/// let storage = Arc::new(DbTaskStorage::new());
32/// let task_plugin = TaskPlugin::new(task_cfg, storage, registry)?;
33/// app.plugin(task_plugin).scan().start().await
34/// ```
35pub struct TaskPlugin {
36    /// TaskWorker 运行时配置
37    config: TaskWorkerConfig,
38    /// 任务持久化接口(由业务方实现)
39    storage: Arc<dyn TaskStorage>,
40    /// 处理器注册中心
41    registry: HandlerRegistry,
42    /// tokio 任务句柄(用于停止时等待任务完成)
43    handles: parking_lot::Mutex<Vec<JoinHandle<()>>>,
44    /// 运行状态标志
45    running: Arc<std::sync::atomic::AtomicBool>,
46    /// 任务执行指标
47    metrics: Arc<TaskMetrics>,
48    /// 已注册的 Kafka topic 列表
49    topics: Vec<String>,
50}
51
52impl TaskPlugin {
53    /// 创建任务插件
54    ///
55    /// - `config`: TaskWorker 运行时配置(建议从 `[task]` section 读取,支持 Deserialize)
56    /// - `storage`: 由业务方实现的持久化接口
57    /// - `registry`: 已注册 handler 的注册中心
58    pub fn new(
59        config: TaskWorkerConfig,
60        storage: Arc<dyn TaskStorage>,
61        registry: HandlerRegistry,
62    ) -> Result<Self, String> {
63        let mut topics: Vec<String> = registry
64            .task_types()
65            .iter()
66            .filter_map(|tt| registry.get_config(*tt).map(|c| c.topic.clone()))
67            .collect();
68        topics.sort_unstable();
69        topics.dedup();
70
71        let metrics = Arc::new(TaskMetrics::new());
72
73        Ok(Self {
74            config,
75            storage,
76            registry,
77            handles: parking_lot::Mutex::new(Vec::new()),
78            running: Arc::new(std::sync::atomic::AtomicBool::new(true)),
79            metrics,
80            topics,
81        })
82    }
83
84    /// 返回任务指标(供外部查询)
85    pub fn metrics(&self) -> Arc<TaskMetrics> {
86        Arc::clone(&self.metrics)
87    }
88
89    /// 返回已注册的 topic 列表
90    pub fn topics(&self) -> &[String] {
91        &self.topics
92    }
93
94    fn signal_stop(&self) {
95        self.running.store(false, std::sync::atomic::Ordering::Relaxed);
96    }
97
98    async fn wait_for_tasks(&self) {
99        let handles: Vec<JoinHandle<()>> = {
100            let mut guard = self.handles.lock();
101            std::mem::take(&mut *guard)
102        };
103        for handle in handles {
104            let _ = handle.await;
105        }
106    }
107}
108
109#[async_trait]
110impl Plugin for TaskPlugin {
111    fn name(&self) -> &str {
112        "task"
113    }
114
115    fn depends_on(&self) -> &[&str] {
116        &[]
117    }
118
119    /// 启动插件:在后台 tokio task 中启动 Worker 和 RetryScanner
120    async fn start(&self) -> CoreResult<()> {
121        self.running.store(true, std::sync::atomic::Ordering::Relaxed);
122
123        let topics = self.topics.clone();
124        let running = Arc::clone(&self.running);
125        let metrics = Arc::clone(&self.metrics);
126
127        let worker = {
128            let w_config = self.config.clone();
129            let w_storage = Arc::clone(&self.storage);
130            let w_registry = self.registry.clone();
131            let w_metrics = Arc::clone(&metrics);
132            let w_running = Arc::clone(&running);
133            let w_topics = topics.clone();
134
135            tokio::spawn(async move {
136                let w_config_clone = w_config.clone();
137                let w_storage_clone = Arc::clone(&w_storage);
138                let w_registry_clone = w_registry.clone();
139                let worker = match TaskWorker::new(w_config_clone, w_storage_clone, w_registry_clone, w_metrics, &w_topics) {
140                    Ok(w) => Arc::new(w),
141                    Err(e) => {
142                        error!("TaskWorker 创建失败: {}", e);
143                        return;
144                    }
145                };
146
147                let worker_ref = Arc::clone(&worker);
148                let run_fut = worker_ref.run();
149
150                tokio::select! {
151                    result = run_fut => {
152                        if let Err(e) = result {
153                            error!("TaskWorker 运行异常: {}", e);
154                        }
155                    }
156                    _ = async {
157                        while w_running.load(std::sync::atomic::Ordering::Relaxed) {
158                            tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
159                        }
160                    } => {
161                        worker_ref.stop();
162                    }
163                }
164            })
165        };
166
167        let scanner = {
168            let s_brokers = self.config.brokers.clone();
169            let s_storage = Arc::clone(&self.storage);
170            let s_registry = self.registry.clone();
171            let s_interval = self.config.scan_interval_secs;
172            let s_batch = self.config.max_batch_size;
173            let s_running = Arc::clone(&running);
174
175            tokio::spawn(async move {
176                let scanner = match RetryScanner::new(
177                    &s_brokers, s_storage, s_registry, s_interval, s_batch,
178                ) {
179                    Ok(s) => Arc::new(s),
180                    Err(e) => {
181                        error!("RetryScanner 创建失败: {}", e);
182                        return;
183                    }
184                };
185
186                let scanner_ref = Arc::clone(&scanner);
187
188                tokio::select! {
189                    _ = scanner_ref.run() => {}
190                    _ = async {
191                        while s_running.load(std::sync::atomic::Ordering::Relaxed) {
192                            tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
193                        }
194                    } => {
195                        scanner_ref.stop();
196                    }
197                }
198            })
199        };
200
201        self.handles.lock().push(worker);
202        self.handles.lock().push(scanner);
203
204        info!(
205            "TaskPlugin 启动: topics={:?}, brokers={}, group={}",
206            self.topics, self.config.brokers, self.config.group_id
207        );
208        Ok(())
209    }
210
211    /// 停止插件:发送停止信号并等待后台任务完成
212    async fn stop(&self) -> CoreResult<()> {
213        info!("TaskPlugin 收到停止信号");
214        self.signal_stop();
215        self.wait_for_tasks().await;
216        info!("TaskPlugin 已停止");
217        Ok(())
218    }
219}