Add Polling architecture analysis
This commit is contained in:
+413
@@ -0,0 +1,413 @@
|
||||
# Polling 架構分析
|
||||
|
||||
## 概述
|
||||
|
||||
Polling 是 RAIDGuard X 用來定期檢測 RAID 控制器狀態的核心機制,負責:
|
||||
- 取得控制器狀態
|
||||
- 取得事件日誌
|
||||
- 檢測連線狀態
|
||||
- 發送 SNMP Trap
|
||||
|
||||
## 架構流程
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ Polling 架構 │
|
||||
├─────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────────────────────────────┐ │
|
||||
│ │ java.util.Timer (tmPolling) │ │
|
||||
│ │ ┌─────────────┬─────────────┬─────────────┬─────────────┐ │ │
|
||||
│ │ │ wkPolling1 │ wkPolling2 │ wkEventPolling │wkEventPoll │ │ │
|
||||
│ │ │ (12秒) │ (30秒) │ (18秒) │ forSNMP │ │ │
|
||||
│ │ │ │ │ │ (22秒) │ │ │
|
||||
│ │ └─────────────┴─────────────┴─────────────┴─────────────┘ │ │
|
||||
│ └─────────────────────────────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌────────────────────┼────────────────────┐ │
|
||||
│ ▼ ▼ ▼ │
|
||||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
||||
│ │ Polling() │ │ GetEvent() │ │EventPollFor │ │
|
||||
│ │ 狀態輪詢 │ │ 事件取得 │ │ SNMP() │ │
|
||||
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
|
||||
│ │ │ │ │
|
||||
│ ▼ ▼ ▼ │
|
||||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
||||
│ │ PageRefresh() │ │IssueCmdAgent2│ │IssueCmdAgent2│ │
|
||||
│ │ 取得頁面資料 │ │ 發送命令 │ │ 發送命令 │ │
|
||||
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
|
||||
│ │ │ │ │
|
||||
│ ▼ ▼ ▼ │
|
||||
│ ┌──────────────────────────────────────────────────────────────┐ │
|
||||
│ │ In-Band API (Port 8922) │ │
|
||||
│ │ - 0x01 GET_PAGE │ │
|
||||
│ │ - 0xFF/0x02 Get InBand Event │ │
|
||||
│ └──────────────────────────────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌──────────────────────────────────────────────────────────────┐ │
|
||||
│ │ RAID Controller │ │
|
||||
│ └──────────────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Polling 任務
|
||||
|
||||
### Timer 排程
|
||||
|
||||
```java
|
||||
// frmMain.java - 1460-1477
|
||||
java.util.Timer tmPolling = new java.util.Timer();
|
||||
|
||||
// 主動式輪詢 (線上狀態)
|
||||
tmPolling.schedule(new wkPolling1(this), 5000, 12001); // 每 12 秒
|
||||
tmPolling.schedule(new wkPolling2(this), 5000, 3001); // 每 3 秒
|
||||
|
||||
// 被動式輪詢 (離線狀態 - 檢查是否重新上線)
|
||||
tmPolling.schedule(new wkStatusRefresh(this), 5000, 1004); // 每 1 秒
|
||||
|
||||
// 事件輪詢
|
||||
tmPolling.schedule(new wkEventPolling(this), 5000, 18003); // 每 18 秒
|
||||
tmPolling.schedule(new wkEventPollingForSNMP(this), 5000, 22002); // 每 22 秒
|
||||
```
|
||||
|
||||
| 任務 | 週期 | 說明 |
|
||||
|------|------|------|
|
||||
| wkPolling1 | 12 秒 | 取得控制器頁面資料 |
|
||||
| wkPolling2 | 3 秒 | 取得控制器頁面資料 + GC |
|
||||
| wkStatusRefresh | 1 秒 | 狀態刷新 |
|
||||
| wkEventPolling | 18 秒 | 取得事件日誌 |
|
||||
| wkEventPollingForSNMP | 22 秒 | 取得事件並發送 SNMP Trap |
|
||||
|
||||
## Polling 類別
|
||||
|
||||
### wkPolling1 (12秒)
|
||||
|
||||
```java
|
||||
class wkPolling1 extends java.util.TimerTask {
|
||||
public void run() {
|
||||
if (PageDataDB.Polling_Flag) {
|
||||
fMainTmp.Polling(0x01); // iCareOffline = 0x01 (關心離線)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**行為:**
|
||||
- 每 12 秒執行一次
|
||||
- 只在 Polling_Flag = true 時執行
|
||||
- 關心離線狀態 (iCareOffline = 0x01)
|
||||
- 呼叫 Polling(0x01)
|
||||
|
||||
### wkPolling2 (3秒)
|
||||
|
||||
```java
|
||||
class wkPolling2 extends java.util.TimerTask {
|
||||
public void run() {
|
||||
if (PageDataDB.Polling_Flag) {
|
||||
fMainTmp.Polling(0x00); // iCareOffline = 0x00 (不關心離線)
|
||||
}
|
||||
Runtime.getRuntime().gc(); // 垃圾回收
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**行為:**
|
||||
- 每 3 秒執行一次
|
||||
- 不關心離線狀態 (iCareOffline = 0x00)
|
||||
- 呼叫 Polling(0x00)
|
||||
- 執行 GC 垃圾回收
|
||||
|
||||
### wkEventPolling (18秒)
|
||||
|
||||
```java
|
||||
class wkEventPolling extends java.util.TimerTask {
|
||||
public void run() {
|
||||
if (PageDataDB.Polling_Flag) {
|
||||
fMainTmp.GetEvent(); // 取得事件
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**行為:**
|
||||
- 每 18 秒執行一次
|
||||
- 呼叫 GetEvent() 取得事件日誌
|
||||
- 更新 UI 事件列表
|
||||
|
||||
### wkEventPollingForSNMP (22秒)
|
||||
|
||||
```java
|
||||
class wkEventPollingForSNMP extends java.util.TimerTask {
|
||||
public void run() {
|
||||
if (fMainTmp.jtHostList.getSelectedRow() >= 0) {
|
||||
fMainTmp.EventPollingForSNMP(); // 取得事件並發送 SNMP
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**行為:**
|
||||
- 每 22 秒執行一次
|
||||
- 需要有選中的控制器
|
||||
- 呼叫 EventPollingForSNMP() 取得事件並發送 SNMP Trap
|
||||
|
||||
## Polling() 方法
|
||||
|
||||
```java
|
||||
public void Polling(int iCareOffline) {
|
||||
// 遍歷所有已註冊的控制器
|
||||
for (int i = 0; i < jtHostList.getRowCount(); i++) {
|
||||
sHostName = jtHostList.getValueAt(i, iHostList_Host).toString();
|
||||
sIpAddr = jtHostList.getValueAt(i, iHostList_IP).toString();
|
||||
sSN = jtHostList.getValueAt(i, iHostList_SN).toString();
|
||||
|
||||
if (!sSN.equals("")) {
|
||||
// 檢查連線失敗次數
|
||||
if (GetHostCmdFailCnt(sSN) > iMaxCommandFail) {
|
||||
// 設定為離線狀態
|
||||
SetHostStatus(sSN, 0x00);
|
||||
jtHostList.setValueAt(imgRed, i, iHostList_Net);
|
||||
jtHostList.setValueAt("Disconnected", i, iHostList_Status);
|
||||
ClearEventLog(); // 清除事件日誌
|
||||
}
|
||||
|
||||
// 根據參數決定行為
|
||||
if (iCareOffline == 0x01) {
|
||||
// 只關心線上的控制器
|
||||
if (GetHostStatus(sSN) == 0x01) {
|
||||
PageRefresh(sIpAddr, sSN, new Point(0, 0));
|
||||
// 如果是 MAX Storage,刷新備援控制器
|
||||
if (Check_MAX_Storage(sSN)) {
|
||||
PageRefresh(sIpAddr, GetSlaveSerialNumber(sSN), new Point(0, 0));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 刷新所有控制器
|
||||
PageRefresh(sIpAddr, sSN, new Point(0, 0));
|
||||
if (Check_MAX_Storage(sSN)) {
|
||||
PageRefresh(sIpAddr, GetSlaveSerialNumber(sSN), new Point(0, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### iCareOffline 參數
|
||||
|
||||
| 值 | 行為 |
|
||||
|----|------|
|
||||
| 0x01 | 只刷新線上 (GetHostStatus == 0x01) 的控制器 |
|
||||
| 0x00 | 刷新所有控制器 (不管離線與否) |
|
||||
|
||||
## GetEvent() 方法
|
||||
|
||||
```java
|
||||
public void GetEvent() {
|
||||
// 取得目前選中的控制器事件
|
||||
GetEvent(sIpAddr, sSN);
|
||||
}
|
||||
|
||||
// PageDataDB.GetEvent()
|
||||
public static void GetEvent(String sIP, String sSN) {
|
||||
// 避免重複執行
|
||||
if (bIsGetEvent) return;
|
||||
bIsGetEvent = true;
|
||||
|
||||
// 建立命令
|
||||
CmdQueue cmdQueue = new CmdQueue();
|
||||
cmdQueue.NewCommand(sIP, sSN, 1, 1, 3);
|
||||
|
||||
// 設定命令內容
|
||||
bCmds[0] = 0xFF; // Phase Code
|
||||
bCmds[1] = 0x00; // Request Code
|
||||
bCmds[2] = 0x00; // Controller No
|
||||
bCmds[3] = 0x02; // InBand Event Cmd Code: Get InBand Event
|
||||
|
||||
// 發送命令
|
||||
issueCmdAgent2 ICA = new issueCmdAgent2();
|
||||
ICA.IssusCmd(sIP, sSN, cmdQueue);
|
||||
|
||||
// 處理回應
|
||||
ReplyTask(cmdQueue, iEventPollingCheckType);
|
||||
|
||||
bIsGetEvent = false;
|
||||
}
|
||||
```
|
||||
|
||||
## EventPollingForSNMP() 方法
|
||||
|
||||
```java
|
||||
public void EventPollingForSNMP() {
|
||||
// 遍歷所有控制器
|
||||
for (int iHost = 0; iHost < iMaxHosts; iHost++) {
|
||||
// 取得控制器資訊
|
||||
sTmpHostName = jtHostList.getValueAt(iHost, iHostList_Host);
|
||||
sTmpIP = jtHostList.getValueAt(iHost, iHostList_IP);
|
||||
sTmpSN = jtHostList.getValueAt(iHost, iHostList_SN);
|
||||
|
||||
// 發送事件查詢命令 (OpCode 0xFF/0x02)
|
||||
CmdQueue cmdQueue = new CmdQueue();
|
||||
cmdQueue.NewCommand(sTmpIP, sTmpSN, 1, 70, 3);
|
||||
|
||||
bCmds[3] = 0x02; // Get InBand Event
|
||||
|
||||
// 發送並處理回應
|
||||
issueCmdAgent2 ICA = new issueCmdAgent2();
|
||||
ICA.IssusCmd(sTmpIP, sTmpSN, cmdQueue);
|
||||
|
||||
// 處理 SNMP 回應
|
||||
ReplyTaskForSNMP(sTmpHostName, sTmpIP, sTmpSN, cmdQueue);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 通訊協定
|
||||
|
||||
### 取得事件 (Get InBand Event)
|
||||
|
||||
```
|
||||
Request:
|
||||
Phase Code: 0xFF
|
||||
Request Code: 0x00
|
||||
Controller: 0x00
|
||||
OpCode: 0x02 (Get InBand Event)
|
||||
|
||||
Response:
|
||||
- 事件資料 (最多 70 bytes)
|
||||
- 包含事件時間、事件 ID、事件描述
|
||||
```
|
||||
|
||||
## Polling 開關
|
||||
|
||||
```java
|
||||
// 選單功能 - Polling On/Off
|
||||
public void jmPollingSwitch_actionPerformed(ActionEvent e) {
|
||||
if (PageDataDB.Polling_Flag) {
|
||||
PageDataDB.Polling_Flag = false;
|
||||
jmPollingSwitch.setText("Polling Off");
|
||||
} else {
|
||||
PageDataDB.Polling_Flag = true;
|
||||
jmPollingSwitch.setText("Polling On");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Polling 狀態判斷
|
||||
|
||||
```
|
||||
主機狀態 (Host Status):
|
||||
0x00 - 離線 (Offline)
|
||||
0x01 - 線上 (Online)
|
||||
0x10 - 更新中 (Updating)
|
||||
|
||||
連線失敗計數:
|
||||
- GetHostCmdFailCnt(sSN) > iMaxCommandFail → 判定離線
|
||||
- 每次命令失敗計數 +1
|
||||
- 每次成功計數歸零
|
||||
```
|
||||
|
||||
## Code Flow
|
||||
|
||||
### 完整 Polling 流程
|
||||
|
||||
```
|
||||
1. Timer 觸發
|
||||
│
|
||||
▼
|
||||
2. 檢查 Polling_Flag
|
||||
│ true → 繼續
|
||||
│ false → 停止
|
||||
▼
|
||||
3. 選擇 Polling 任務
|
||||
│
|
||||
├── wkPolling1 ──► Polling(0x01) ──► PageRefresh() ──► In-Band API
|
||||
│
|
||||
├── wkPolling2 ──► Polling(0x00) ──► PageRefresh() ──► In-Band API
|
||||
│
|
||||
├── wkEventPolling ──► GetEvent() ──► In-Band API
|
||||
│
|
||||
└── wkEventPollingForSNMP ──► EventPollingForSNMP() ──► In-Band API ──► SNMP Trap
|
||||
```
|
||||
|
||||
### 事件處理流程
|
||||
|
||||
```
|
||||
控制器事件發生
|
||||
│
|
||||
▼
|
||||
Polling 偵測到 (Get InBand Event)
|
||||
│
|
||||
▼
|
||||
解析事件內容
|
||||
│
|
||||
├── 更新 UI Event List
|
||||
│
|
||||
├── 發送 SNMP Trap (EventPollingForSNMP)
|
||||
│
|
||||
└── 發送 Email Alert (如果啟用)
|
||||
```
|
||||
|
||||
## 關鍵變數
|
||||
|
||||
```java
|
||||
// PageDataDB.java
|
||||
public static boolean Polling_Flag = true; // Polling 開關
|
||||
public static boolean bIsGetEvent = false; // 防止重複取得事件
|
||||
public static int iMaxCommandFail = 3; // 最大允許連線失敗次數
|
||||
|
||||
// frmMain.java
|
||||
java.util.Timer tmPolling; // Polling 計時器
|
||||
```
|
||||
|
||||
## 效能優化
|
||||
|
||||
1. **分層輪詢**: 不同任務使用不同週期
|
||||
2. **離線加速**: 離線後改用較短週期 (3秒 vs 12秒)
|
||||
3. **事件鎖**: `bIsGetEvent` 防止重複取得
|
||||
4. **GC 排程**: wkPolling2 執行垃圾回收
|
||||
|
||||
## Rust 實作建議
|
||||
|
||||
```rust
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
use tokio::time::{interval, Duration};
|
||||
|
||||
pub struct PollingManager {
|
||||
polling_flag: Arc<AtomicBool>,
|
||||
// 其他狀態...
|
||||
}
|
||||
|
||||
impl PollingManager {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
polling_flag: Arc::new(AtomicBool::new(true)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start(&self) {
|
||||
let flag = self.polling_flag.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
let mut timer = interval(Duration::from_secs(12));
|
||||
loop {
|
||||
timer.tick().await;
|
||||
if flag.load(Ordering::SeqCst) {
|
||||
self.polling().await;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn stop(&self) {
|
||||
self.polling_flag.store(false, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
async fn polling(&self) {
|
||||
// 取得控制器狀態
|
||||
// 刷新頁面資料
|
||||
}
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user