mirror of
https://github.com/0xfnzero/solana-streamer.git
synced 2026-07-27 17:37:45 +00:00
feat: enhance event filtering and monitoring system
- Add EventTypeFilter for precise event type filtering - Refactor metrics to track events by type (tx/account/block) - Add parser caching for improved performance - Enhance gRPC subscription management - Update examples and type definitions
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "solana-streamer-sdk"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
edition = "2021"
|
||||
authors = ["William <byteblock6@gmail.com>", "sgxiang <sgxiang@gmail.com>", "wei <1415121722@qq.com>"]
|
||||
repository = "https://github.com/0xfnzero/solana-streamer"
|
||||
|
||||
@@ -44,14 +44,14 @@ Add the dependency to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
# Add to your Cargo.toml
|
||||
solana-streamer-sdk = { path = "./solana-streamer", version = "0.3.1" }
|
||||
solana-streamer-sdk = { path = "./solana-streamer", version = "0.3.2" }
|
||||
```
|
||||
|
||||
### Use crates.io
|
||||
|
||||
```toml
|
||||
# Add to your Cargo.toml
|
||||
solana-streamer-sdk = "0.3.1"
|
||||
solana-streamer-sdk = "0.3.2"
|
||||
```
|
||||
|
||||
## Usage Examples
|
||||
@@ -71,13 +71,14 @@ This example demonstrates:
|
||||
|
||||
The example uses a predefined transaction signature and shows how to extract protocol-specific events from the transaction data.
|
||||
|
||||
### Advanced Usage with Batch Processing and Backpressure
|
||||
### Advanced Usage - Complete Example
|
||||
|
||||
```rust
|
||||
use solana_streamer_sdk::{
|
||||
match_event,
|
||||
streaming::{
|
||||
event_parser::{
|
||||
common::{filter::EventTypeFilter, EventType},
|
||||
protocols::{
|
||||
bonk::{
|
||||
parser::BONK_PROGRAM_ID, BonkGlobalConfigAccountEvent, BonkMigrateToAmmEvent,
|
||||
@@ -127,13 +128,8 @@ use solana_streamer_sdk::{
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("Starting Solana Streamer...");
|
||||
|
||||
// Test Yellowstone gRPC with performance monitoring
|
||||
test_grpc().await?;
|
||||
|
||||
// Test ShredStream with performance monitoring
|
||||
test_shreds().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -178,26 +174,33 @@ async fn test_grpc() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let account_exclude = vec![];
|
||||
let account_required = vec![];
|
||||
|
||||
println!("Starting to listen for events, press Ctrl+C to stop...");
|
||||
println!("Monitoring programs: {:?}", account_include);
|
||||
|
||||
println!("Starting subscription...");
|
||||
|
||||
// 监听交易数据
|
||||
// Transaction filter for monitoring transaction events
|
||||
let transaction_filter = TransactionFilter {
|
||||
account_include: account_include.clone(),
|
||||
account_exclude,
|
||||
account_required,
|
||||
};
|
||||
|
||||
// 监听属于owner程序的账号数据 -> 账号事件监听
|
||||
// Account filter for monitoring account state changes
|
||||
let account_filter = AccountFilter { account: vec![], owner: account_include.clone() };
|
||||
|
||||
// Event type filtering - optional
|
||||
// No event filtering, includes all events
|
||||
let event_type_filter = None;
|
||||
// Only include PumpSwapBuy and PumpSwapSell events
|
||||
// let event_type_filter = Some(EventTypeFilter { include: vec![EventType::PumpSwapBuy, EventType::PumpSwapSell] });
|
||||
|
||||
println!("Starting to listen for events, press Ctrl+C to stop...");
|
||||
println!("Monitoring programs: {:?}", account_include);
|
||||
|
||||
println!("Starting subscription...");
|
||||
|
||||
grpc.subscribe_events_immediate(
|
||||
protocols,
|
||||
None,
|
||||
transaction_filter,
|
||||
account_filter,
|
||||
event_type_filter,
|
||||
None,
|
||||
callback,
|
||||
)
|
||||
@@ -236,11 +239,11 @@ fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|
||||
|event: Box<dyn UnifiedEvent>| {
|
||||
println!("🎉 Event received! Type: {:?}, ID: {}", event.event_type(), event.id());
|
||||
match_event!(event, {
|
||||
// block meta
|
||||
// -------------------------- block meta -----------------------
|
||||
BlockMetaEvent => |e: BlockMetaEvent| {
|
||||
println!("BlockMetaEvent: {e:?}");
|
||||
},
|
||||
// bonk
|
||||
// -------------------------- bonk -----------------------
|
||||
BonkPoolCreateEvent => |e: BonkPoolCreateEvent| {
|
||||
// When using grpc, you can get block_time from each event
|
||||
println!("block_time: {:?}, block_time_ms: {:?}", e.metadata.block_time, e.metadata.block_time_ms);
|
||||
@@ -255,7 +258,7 @@ fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|
||||
BonkMigrateToCpswapEvent => |e: BonkMigrateToCpswapEvent| {
|
||||
println!("BonkMigrateToCpswapEvent: {e:?}");
|
||||
},
|
||||
// pumpfun
|
||||
// -------------------------- pumpfun -----------------------
|
||||
PumpFunTradeEvent => |e: PumpFunTradeEvent| {
|
||||
println!("PumpFunTradeEvent: {e:?}");
|
||||
},
|
||||
@@ -265,7 +268,7 @@ fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|
||||
PumpFunCreateTokenEvent => |e: PumpFunCreateTokenEvent| {
|
||||
println!("PumpFunCreateTokenEvent: {e:?}");
|
||||
},
|
||||
// pumpswap
|
||||
// -------------------------- pumpswap -----------------------
|
||||
PumpSwapBuyEvent => |e: PumpSwapBuyEvent| {
|
||||
println!("Buy event: {e:?}");
|
||||
},
|
||||
@@ -281,7 +284,7 @@ fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|
||||
PumpSwapWithdrawEvent => |e: PumpSwapWithdrawEvent| {
|
||||
println!("Withdraw event: {e:?}");
|
||||
},
|
||||
// raydium_cpmm
|
||||
// -------------------------- raydium_cpmm -----------------------
|
||||
RaydiumCpmmSwapEvent => |e: RaydiumCpmmSwapEvent| {
|
||||
println!("RaydiumCpmmSwapEvent: {e:?}");
|
||||
},
|
||||
@@ -294,7 +297,7 @@ fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|
||||
RaydiumCpmmWithdrawEvent => |e: RaydiumCpmmWithdrawEvent| {
|
||||
println!("RaydiumCpmmWithdrawEvent: {e:?}");
|
||||
},
|
||||
// raydium_clmm
|
||||
// -------------------------- raydium_clmm -----------------------
|
||||
RaydiumClmmSwapEvent => |e: RaydiumClmmSwapEvent| {
|
||||
println!("RaydiumClmmSwapEvent: {e:?}");
|
||||
},
|
||||
@@ -319,7 +322,7 @@ fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|
||||
RaydiumClmmOpenPositionV2Event => |e: RaydiumClmmOpenPositionV2Event| {
|
||||
println!("RaydiumClmmOpenPositionV2Event: {e:?}");
|
||||
},
|
||||
// raydium_amm_v4
|
||||
// -------------------------- raydium_amm_v4 -----------------------
|
||||
RaydiumAmmV4SwapEvent => |e: RaydiumAmmV4SwapEvent| {
|
||||
println!("RaydiumAmmV4SwapEvent: {e:?}");
|
||||
},
|
||||
@@ -380,6 +383,22 @@ fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|
||||
}
|
||||
```
|
||||
|
||||
### Event Filtering
|
||||
|
||||
The library supports flexible event filtering to reduce processing overhead:
|
||||
|
||||
```rust
|
||||
use solana_streamer_sdk::streaming::event_parser::common::{filter::EventTypeFilter, EventType};
|
||||
|
||||
// No filtering - receive all events
|
||||
let event_type_filter = None;
|
||||
|
||||
// Filter specific event types - only receive PumpSwap buy/sell events
|
||||
let event_type_filter = Some(EventTypeFilter {
|
||||
include: vec![EventType::PumpSwapBuy, EventType::PumpSwapSell]
|
||||
});
|
||||
```
|
||||
|
||||
## Supported Protocols
|
||||
|
||||
- **PumpFun**: Primary meme coin trading platform
|
||||
|
||||
+39
-15
@@ -44,14 +44,14 @@ git clone https://github.com/0xfnzero/solana-streamer
|
||||
|
||||
```toml
|
||||
# 添加到您的 Cargo.toml
|
||||
solana-streamer-sdk = { path = "./solana-streamer", version = "0.3.1" }
|
||||
solana-streamer-sdk = { path = "./solana-streamer", version = "0.3.2" }
|
||||
```
|
||||
|
||||
### 使用 crates.io
|
||||
|
||||
```toml
|
||||
# 添加到您的 Cargo.toml
|
||||
solana-streamer-sdk = "0.3.1"
|
||||
solana-streamer-sdk = "0.3.2"
|
||||
```
|
||||
|
||||
## 使用示例
|
||||
@@ -71,13 +71,14 @@ cargo run --example parse_tx_events
|
||||
|
||||
该示例使用预定义的交易签名,展示如何从交易数据中提取协议特定的事件。
|
||||
|
||||
### 高级用法示例
|
||||
### 高级用法 - 完整示例
|
||||
|
||||
```rust
|
||||
use solana_streamer_sdk::{
|
||||
match_event,
|
||||
streaming::{
|
||||
event_parser::{
|
||||
common::{filter::EventTypeFilter, EventType},
|
||||
protocols::{
|
||||
bonk::{
|
||||
parser::BONK_PROGRAM_ID, BonkGlobalConfigAccountEvent, BonkMigrateToAmmEvent,
|
||||
@@ -173,11 +174,6 @@ async fn test_grpc() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let account_exclude = vec![];
|
||||
let account_required = vec![];
|
||||
|
||||
println!("Starting to listen for events, press Ctrl+C to stop...");
|
||||
println!("Monitoring programs: {:?}", account_include);
|
||||
|
||||
println!("Starting subscription...");
|
||||
|
||||
// 监听交易数据
|
||||
let transaction_filter = TransactionFilter {
|
||||
account_include: account_include.clone(),
|
||||
@@ -188,11 +184,23 @@ async fn test_grpc() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// 监听属于owner程序的账号数据 -> 账号事件监听
|
||||
let account_filter = AccountFilter { account: vec![], owner: account_include.clone() };
|
||||
|
||||
// 事件过滤 - 可选
|
||||
// 不进行事件过滤,包含所有事件
|
||||
let event_type_filter = None;
|
||||
// 只包含PumpSwapBuy事件、PumpSwapSell事件
|
||||
// let event_type_filter = Some(EventTypeFilter { include: vec![EventType::PumpSwapBuy, EventType::PumpSwapSell] });
|
||||
|
||||
println!("Starting to listen for events, press Ctrl+C to stop...");
|
||||
println!("Monitoring programs: {:?}", account_include);
|
||||
|
||||
println!("Starting subscription...");
|
||||
|
||||
grpc.subscribe_events_immediate(
|
||||
protocols,
|
||||
None,
|
||||
transaction_filter,
|
||||
account_filter,
|
||||
event_type_filter,
|
||||
None,
|
||||
callback,
|
||||
)
|
||||
@@ -231,11 +239,11 @@ fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|
||||
|event: Box<dyn UnifiedEvent>| {
|
||||
println!("🎉 Event received! Type: {:?}, ID: {}", event.event_type(), event.id());
|
||||
match_event!(event, {
|
||||
// block meta
|
||||
// -------------------------- block meta -----------------------
|
||||
BlockMetaEvent => |e: BlockMetaEvent| {
|
||||
println!("BlockMetaEvent: {e:?}");
|
||||
},
|
||||
// bonk
|
||||
// -------------------------- bonk -----------------------
|
||||
BonkPoolCreateEvent => |e: BonkPoolCreateEvent| {
|
||||
// 使用grpc的时候,可以从每个事件中获取到block_time
|
||||
println!("block_time: {:?}, block_time_ms: {:?}", e.metadata.block_time, e.metadata.block_time_ms);
|
||||
@@ -250,7 +258,7 @@ fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|
||||
BonkMigrateToCpswapEvent => |e: BonkMigrateToCpswapEvent| {
|
||||
println!("BonkMigrateToCpswapEvent: {e:?}");
|
||||
},
|
||||
// pumpfun
|
||||
// -------------------------- pumpfun -----------------------
|
||||
PumpFunTradeEvent => |e: PumpFunTradeEvent| {
|
||||
println!("PumpFunTradeEvent: {e:?}");
|
||||
},
|
||||
@@ -260,7 +268,7 @@ fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|
||||
PumpFunCreateTokenEvent => |e: PumpFunCreateTokenEvent| {
|
||||
println!("PumpFunCreateTokenEvent: {e:?}");
|
||||
},
|
||||
// pumpswap
|
||||
// -------------------------- pumpswap -----------------------
|
||||
PumpSwapBuyEvent => |e: PumpSwapBuyEvent| {
|
||||
println!("Buy event: {e:?}");
|
||||
},
|
||||
@@ -276,7 +284,7 @@ fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|
||||
PumpSwapWithdrawEvent => |e: PumpSwapWithdrawEvent| {
|
||||
println!("Withdraw event: {e:?}");
|
||||
},
|
||||
// raydium_cpmm
|
||||
// -------------------------- raydium_cpmm -----------------------
|
||||
RaydiumCpmmSwapEvent => |e: RaydiumCpmmSwapEvent| {
|
||||
println!("RaydiumCpmmSwapEvent: {e:?}");
|
||||
},
|
||||
@@ -289,7 +297,7 @@ fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|
||||
RaydiumCpmmWithdrawEvent => |e: RaydiumCpmmWithdrawEvent| {
|
||||
println!("RaydiumCpmmWithdrawEvent: {e:?}");
|
||||
},
|
||||
// raydium_clmm
|
||||
// -------------------------- raydium_clmm -----------------------
|
||||
RaydiumClmmSwapEvent => |e: RaydiumClmmSwapEvent| {
|
||||
println!("RaydiumClmmSwapEvent: {e:?}");
|
||||
},
|
||||
@@ -314,7 +322,7 @@ fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|
||||
RaydiumClmmOpenPositionV2Event => |e: RaydiumClmmOpenPositionV2Event| {
|
||||
println!("RaydiumClmmOpenPositionV2Event: {e:?}");
|
||||
},
|
||||
// raydium_amm_v4
|
||||
// -------------------------- raydium_amm_v4 -----------------------
|
||||
RaydiumAmmV4SwapEvent => |e: RaydiumAmmV4SwapEvent| {
|
||||
println!("RaydiumAmmV4SwapEvent: {e:?}");
|
||||
},
|
||||
@@ -375,6 +383,22 @@ fn create_event_callback() -> impl Fn(Box<dyn UnifiedEvent>) {
|
||||
}
|
||||
```
|
||||
|
||||
### 事件过滤
|
||||
|
||||
库支持灵活的事件过滤以减少处理开销:
|
||||
|
||||
```rust
|
||||
use solana_streamer_sdk::streaming::event_parser::common::{filter::EventTypeFilter, EventType};
|
||||
|
||||
// 无过滤 - 接收所有事件
|
||||
let event_type_filter = None;
|
||||
|
||||
// 过滤特定事件类型 - 只接收 PumpSwap 买入/卖出事件
|
||||
let event_type_filter = Some(EventTypeFilter {
|
||||
include: vec![EventType::PumpSwapBuy, EventType::PumpSwapSell]
|
||||
});
|
||||
```
|
||||
|
||||
## 支持的协议
|
||||
|
||||
- **PumpFun**: 主要迷因币交易平台
|
||||
|
||||
@@ -97,7 +97,7 @@ async fn get_single_transaction_details(signature_str: &str) -> Result<()> {
|
||||
Protocol::RaydiumCpmm,
|
||||
Protocol::RaydiumAmmV4,
|
||||
];
|
||||
let parser: Arc<dyn EventParser> = Arc::new(MutilEventParser::new(protocols));
|
||||
let parser: Arc<dyn EventParser> = Arc::new(MutilEventParser::new(protocols, None));
|
||||
let start_time = std::time::Instant::now();
|
||||
let events = parser
|
||||
.parse_transaction(
|
||||
|
||||
+10
-2
@@ -2,6 +2,7 @@ use solana_streamer_sdk::{
|
||||
match_event,
|
||||
streaming::{
|
||||
event_parser::{
|
||||
common::{filter::EventTypeFilter, EventType},
|
||||
protocols::{
|
||||
bonk::{
|
||||
parser::BONK_PROGRAM_ID, BonkGlobalConfigAccountEvent, BonkMigrateToAmmEvent,
|
||||
@@ -97,16 +98,22 @@ async fn test_grpc() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let account_exclude = vec![];
|
||||
let account_required = vec![];
|
||||
|
||||
// 监听交易数据
|
||||
// Listen to transaction data
|
||||
let transaction_filter = TransactionFilter {
|
||||
account_include: account_include.clone(),
|
||||
account_exclude,
|
||||
account_required,
|
||||
};
|
||||
|
||||
// 监听属于owner程序的账号数据 -> 账号事件监听
|
||||
// Listen to account data belonging to owner programs -> account event monitoring
|
||||
let account_filter = AccountFilter { account: vec![], owner: account_include.clone() };
|
||||
|
||||
// Event filtering
|
||||
// No event filtering, includes all events
|
||||
let event_type_filter = None;
|
||||
// Only include PumpSwapBuy events and PumpSwapSell events
|
||||
// let event_type_filter = EventTypeFilter { include: vec![EventType::PumpSwapBuy, EventType::PumpSwapSell] };
|
||||
|
||||
println!("Starting to listen for events, press Ctrl+C to stop...");
|
||||
println!("Monitoring programs: {:?}", account_include);
|
||||
|
||||
@@ -117,6 +124,7 @@ async fn test_grpc() -> Result<(), Box<dyn std::error::Error>> {
|
||||
None,
|
||||
transaction_filter,
|
||||
account_filter,
|
||||
event_type_filter,
|
||||
None,
|
||||
callback,
|
||||
)
|
||||
|
||||
+180
-46
@@ -4,19 +4,37 @@ use tokio::sync::Mutex;
|
||||
use super::config::StreamClientConfig;
|
||||
use super::constants::*;
|
||||
|
||||
/// 单个事件类型的指标
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EventMetrics {
|
||||
pub process_count: u64,
|
||||
pub events_processed: u64,
|
||||
pub events_per_second: f64,
|
||||
pub events_in_window: u64,
|
||||
pub window_start_time: std::time::Instant,
|
||||
}
|
||||
|
||||
impl EventMetrics {
|
||||
fn new(now: std::time::Instant) -> Self {
|
||||
Self {
|
||||
process_count: 0,
|
||||
events_processed: 0,
|
||||
events_per_second: 0.0,
|
||||
events_in_window: 0,
|
||||
window_start_time: now,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 通用性能监控指标
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PerformanceMetrics {
|
||||
pub start_time: std::time::Instant,
|
||||
pub process_count: u64,
|
||||
pub events_processed: u64,
|
||||
pub events_per_second: f64,
|
||||
pub event_metrics: [EventMetrics; 3], // [Tx, Account, BlockMeta]
|
||||
pub average_processing_time_ms: f64,
|
||||
pub min_processing_time_ms: f64,
|
||||
pub max_processing_time_ms: f64,
|
||||
pub last_update_time: std::time::Instant,
|
||||
pub events_in_window: u64,
|
||||
pub window_start_time: std::time::Instant,
|
||||
}
|
||||
|
||||
impl Default for PerformanceMetrics {
|
||||
@@ -25,20 +43,89 @@ impl Default for PerformanceMetrics {
|
||||
}
|
||||
}
|
||||
|
||||
pub enum MetricsEventType {
|
||||
Tx,
|
||||
Account,
|
||||
BlockMeta,
|
||||
}
|
||||
|
||||
impl MetricsEventType {
|
||||
fn as_index(&self) -> usize {
|
||||
match self {
|
||||
MetricsEventType::Tx => 0,
|
||||
MetricsEventType::Account => 1,
|
||||
MetricsEventType::BlockMeta => 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PerformanceMetrics {
|
||||
pub fn new() -> Self {
|
||||
let now = std::time::Instant::now();
|
||||
Self {
|
||||
start_time: std::time::Instant::now(),
|
||||
process_count: 0,
|
||||
events_processed: 0,
|
||||
events_per_second: 0.0,
|
||||
start_time: now,
|
||||
event_metrics: [EventMetrics::new(now), EventMetrics::new(now), EventMetrics::new(now)],
|
||||
average_processing_time_ms: 0.0,
|
||||
min_processing_time_ms: 0.0,
|
||||
max_processing_time_ms: 0.0,
|
||||
last_update_time: now,
|
||||
events_in_window: 0,
|
||||
window_start_time: now,
|
||||
}
|
||||
}
|
||||
|
||||
/// 更新时间窗口指标
|
||||
fn update_window_metrics(
|
||||
&mut self,
|
||||
event_type: &MetricsEventType,
|
||||
now: std::time::Instant,
|
||||
window_duration: std::time::Duration,
|
||||
) {
|
||||
let index = event_type.as_index();
|
||||
let event_metric = &mut self.event_metrics[index];
|
||||
|
||||
if now.duration_since(event_metric.window_start_time) >= window_duration {
|
||||
let window_seconds = now.duration_since(event_metric.window_start_time).as_secs_f64();
|
||||
// 修复:正确计算每秒事件数,避免除零错误
|
||||
event_metric.events_per_second = if window_seconds > 0.001 {
|
||||
// 避免极小的时间差
|
||||
event_metric.events_in_window as f64 / window_seconds
|
||||
} else {
|
||||
0.0 // 时间太短时设为0,而不是事件总数
|
||||
};
|
||||
|
||||
// 重置窗口
|
||||
event_metric.events_in_window = 0;
|
||||
event_metric.window_start_time = now;
|
||||
}
|
||||
}
|
||||
|
||||
/// 计算实时每秒事件数(用于显示)
|
||||
fn calculate_real_time_events_per_second(
|
||||
&self,
|
||||
event_type: &MetricsEventType,
|
||||
now: std::time::Instant,
|
||||
) -> f64 {
|
||||
let index = event_type.as_index();
|
||||
let event_metric = &self.event_metrics[index];
|
||||
|
||||
let current_window_duration =
|
||||
now.duration_since(event_metric.window_start_time).as_secs_f64();
|
||||
|
||||
// 如果当前窗口有足够的时间和事件,使用当前窗口的数据
|
||||
if current_window_duration > 1.0 && event_metric.events_in_window > 0 {
|
||||
event_metric.events_in_window as f64 / current_window_duration
|
||||
}
|
||||
// 如果当前窗口时间太短或没有事件,使用上一个完整窗口的值
|
||||
else if event_metric.events_per_second > 0.0 {
|
||||
event_metric.events_per_second
|
||||
}
|
||||
// 如果都没有,计算总体平均值
|
||||
else {
|
||||
let total_duration = now.duration_since(self.start_time).as_secs_f64();
|
||||
if total_duration > 1.0 && event_metric.events_processed > 0 {
|
||||
event_metric.events_processed as f64 / total_duration
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,15 +156,46 @@ impl MetricsManager {
|
||||
/// 打印性能指标
|
||||
pub async fn print_metrics(&self) {
|
||||
let metrics = self.get_metrics().await;
|
||||
println!("📊 {} Performance Metrics:", self.stream_name);
|
||||
let event_names = ["TX", "Account", "Block Meta"];
|
||||
let event_types =
|
||||
[MetricsEventType::Tx, MetricsEventType::Account, MetricsEventType::BlockMeta];
|
||||
let now = std::time::Instant::now();
|
||||
|
||||
println!("\n📊 {} Performance Metrics", self.stream_name);
|
||||
println!(" Run Time: {:?}", metrics.start_time.elapsed());
|
||||
println!(" Process Count: {}", metrics.process_count);
|
||||
println!(" Events Processed: {}", metrics.events_processed);
|
||||
println!(" Events/Second: {:.2}", metrics.events_per_second);
|
||||
println!(" Avg Processing Time: {:.2}ms", metrics.average_processing_time_ms);
|
||||
println!(" Min Processing Time: {:.2}ms", metrics.min_processing_time_ms);
|
||||
println!(" Max Processing Time: {:.2}ms", metrics.max_processing_time_ms);
|
||||
println!("---");
|
||||
|
||||
// 打印表格头部
|
||||
println!("┌─────────────┬──────────────┬──────────────────┬─────────────────┐");
|
||||
println!("│ Event Type │ Process Count│ Events Processed │ Events/Second │");
|
||||
println!("├─────────────┼──────────────┼──────────────────┼─────────────────┤");
|
||||
|
||||
// 打印每种事件类型的数据
|
||||
for (i, name) in event_names.iter().enumerate() {
|
||||
let event_metric = &metrics.event_metrics[i];
|
||||
// 使用实时计算的每秒事件数,而不是窗口更新的值
|
||||
let real_time_eps = metrics.calculate_real_time_events_per_second(&event_types[i], now);
|
||||
|
||||
println!(
|
||||
"│ {:11} │ {:12} │ {:16} │ {:13.2} │",
|
||||
name,
|
||||
event_metric.process_count,
|
||||
event_metric.events_processed,
|
||||
real_time_eps
|
||||
);
|
||||
}
|
||||
|
||||
println!("└─────────────┴──────────────┴──────────────────┴─────────────────┘");
|
||||
|
||||
// 打印处理时间统计表格
|
||||
println!("\n⏱️ Processing Time Statistics");
|
||||
println!("┌─────────────────────┬─────────────┐");
|
||||
println!("│ Metric │ Value (ms) │");
|
||||
println!("├─────────────────────┼─────────────┤");
|
||||
println!("│ Average │ {:9.2} │", metrics.average_processing_time_ms);
|
||||
println!("│ Minimum │ {:9.2} │", metrics.min_processing_time_ms);
|
||||
println!("│ Maximum │ {:9.2} │", metrics.max_processing_time_ms);
|
||||
println!("└─────────────────────┴─────────────┘");
|
||||
println!();
|
||||
}
|
||||
|
||||
/// 启动自动性能监控任务
|
||||
@@ -100,29 +218,50 @@ impl MetricsManager {
|
||||
}
|
||||
|
||||
/// 更新处理次数
|
||||
pub async fn add_process_count(&self) {
|
||||
pub async fn add_process_count(&self, event_type: MetricsEventType) {
|
||||
if !self.config.enable_metrics {
|
||||
return;
|
||||
}
|
||||
let mut metrics = self.metrics.lock().await;
|
||||
metrics.process_count += 1;
|
||||
metrics.event_metrics[event_type.as_index()].process_count += 1;
|
||||
}
|
||||
|
||||
// 保持向后兼容的方法
|
||||
pub async fn add_tx_process_count(&self) {
|
||||
self.add_process_count(MetricsEventType::Tx).await;
|
||||
}
|
||||
|
||||
pub async fn add_account_process_count(&self) {
|
||||
self.add_process_count(MetricsEventType::Account).await;
|
||||
}
|
||||
|
||||
pub async fn add_block_meta_process_count(&self) {
|
||||
self.add_process_count(MetricsEventType::BlockMeta).await;
|
||||
}
|
||||
|
||||
/// 更新性能指标
|
||||
pub async fn update_metrics(&self, events_processed: u64, processing_time_ms: f64) {
|
||||
pub async fn update_metrics(
|
||||
&self,
|
||||
event_type: MetricsEventType,
|
||||
events_processed: u64,
|
||||
processing_time_ms: f64,
|
||||
) {
|
||||
// 检查是否启用性能监控
|
||||
if !self.config.enable_metrics {
|
||||
return; // 如果未启用性能监控,直接返回
|
||||
return;
|
||||
}
|
||||
|
||||
let mut metrics = self.metrics.lock().await;
|
||||
let now = std::time::Instant::now();
|
||||
let index = event_type.as_index();
|
||||
|
||||
// 更新事件计数
|
||||
metrics.event_metrics[index].events_processed += events_processed;
|
||||
metrics.event_metrics[index].events_in_window += events_processed;
|
||||
|
||||
metrics.events_processed += events_processed;
|
||||
metrics.events_in_window += events_processed;
|
||||
metrics.last_update_time = now;
|
||||
|
||||
// 更新最快和最慢处理时间
|
||||
// 更新处理时间统计
|
||||
if processing_time_ms < metrics.min_processing_time_ms
|
||||
|| metrics.min_processing_time_ms == 0.0
|
||||
{
|
||||
@@ -132,29 +271,24 @@ impl MetricsManager {
|
||||
metrics.max_processing_time_ms = processing_time_ms;
|
||||
}
|
||||
|
||||
// 计算平均处理时间
|
||||
if metrics.events_processed > 0 {
|
||||
metrics.average_processing_time_ms = (metrics.average_processing_time_ms
|
||||
* (metrics.events_processed - events_processed) as f64
|
||||
+ processing_time_ms)
|
||||
/ metrics.events_processed as f64;
|
||||
}
|
||||
// 计算平均处理时间 - 使用增量更新避免重复计算
|
||||
let total_events = metrics.event_metrics[index].events_processed;
|
||||
if total_events > 0 {
|
||||
let total_events_f64 = total_events as f64;
|
||||
let old_total = (total_events_f64 - events_processed as f64).max(0.0);
|
||||
|
||||
// 基于时间窗口计算每秒处理事件数
|
||||
let window_duration = std::time::Duration::from_secs(DEFAULT_METRICS_WINDOW_SECONDS);
|
||||
if now.duration_since(metrics.window_start_time) >= window_duration {
|
||||
let window_seconds = now.duration_since(metrics.window_start_time).as_secs_f64();
|
||||
if window_seconds > 0.0 && metrics.events_in_window > 0 {
|
||||
metrics.events_per_second = metrics.events_in_window as f64 / window_seconds;
|
||||
metrics.average_processing_time_ms = if old_total > 0.0 {
|
||||
(metrics.average_processing_time_ms * old_total
|
||||
+ processing_time_ms * events_processed as f64)
|
||||
/ total_events_f64
|
||||
} else {
|
||||
// 如果窗口内没有事件,保持之前的速率或设为0
|
||||
metrics.events_per_second = 0.0;
|
||||
}
|
||||
|
||||
// 重置窗口
|
||||
metrics.events_in_window = 0;
|
||||
metrics.window_start_time = now;
|
||||
processing_time_ms
|
||||
};
|
||||
}
|
||||
|
||||
// 更新时间窗口指标
|
||||
let window_duration = std::time::Duration::from_secs(DEFAULT_METRICS_WINDOW_SECONDS);
|
||||
metrics.update_window_metrics(&event_type, now, window_duration);
|
||||
}
|
||||
|
||||
/// 记录慢处理操作
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
use crate::streaming::event_parser::common::{
|
||||
types::EventType, ACCOUNT_EVENT_TYPES, BLOCK_EVENT_TYPES,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EventTypeFilter {
|
||||
pub include: Vec<EventType>,
|
||||
}
|
||||
|
||||
impl EventTypeFilter {
|
||||
pub fn include_transaction_event(&self) -> bool {
|
||||
self.include
|
||||
.iter()
|
||||
.any(|event| !ACCOUNT_EVENT_TYPES.contains(event) && !BLOCK_EVENT_TYPES.contains(event))
|
||||
}
|
||||
|
||||
pub fn include_account_event(&self) -> bool {
|
||||
self.include.iter().any(|event| ACCOUNT_EVENT_TYPES.contains(event))
|
||||
}
|
||||
|
||||
pub fn include_block_event(&self) -> bool {
|
||||
self.include.iter().any(|event| BLOCK_EVENT_TYPES.contains(event))
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
pub mod types;
|
||||
pub mod utils;
|
||||
pub mod filter;
|
||||
|
||||
/// 自动生成UnifiedEvent trait实现的宏
|
||||
#[macro_export]
|
||||
|
||||
@@ -180,6 +180,24 @@ pub enum EventType {
|
||||
Unknown,
|
||||
}
|
||||
|
||||
pub const ACCOUNT_EVENT_TYPES: &[EventType] = &[
|
||||
EventType::AccountRaydiumAmmV4AmmInfo,
|
||||
EventType::AccountPumpSwapGlobalConfig,
|
||||
EventType::AccountPumpSwapPool,
|
||||
EventType::AccountBonkPoolState,
|
||||
EventType::AccountBonkGlobalConfig,
|
||||
EventType::AccountBonkPlatformConfig,
|
||||
EventType::AccountBonkVestingRecord,
|
||||
EventType::AccountPumpFunBondingCurve,
|
||||
EventType::AccountPumpFunGlobal,
|
||||
EventType::AccountRaydiumClmmAmmConfig,
|
||||
EventType::AccountRaydiumClmmPoolState,
|
||||
EventType::AccountRaydiumClmmTickArrayState,
|
||||
EventType::AccountRaydiumCpmmAmmConfig,
|
||||
EventType::AccountRaydiumCpmmPoolState,
|
||||
];
|
||||
pub const BLOCK_EVENT_TYPES: &[EventType] = &[EventType::BlockMeta];
|
||||
|
||||
impl EventType {
|
||||
#[allow(clippy::inherent_to_string)]
|
||||
pub fn to_string(&self) -> String {
|
||||
|
||||
@@ -3,6 +3,7 @@ use std::sync::OnceLock;
|
||||
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
|
||||
use crate::streaming::event_parser::common::filter::EventTypeFilter;
|
||||
use crate::streaming::event_parser::common::{EventMetadata, EventType, ProtocolType};
|
||||
use crate::streaming::event_parser::core::traits::UnifiedEvent;
|
||||
use crate::streaming::event_parser::protocols::bonk::parser::BONK_PROGRAM_ID;
|
||||
@@ -34,7 +35,7 @@ static PROTOCOL_CONFIGS_CACHE: OnceLock<HashMap<Protocol, Vec<AccountEventParseC
|
||||
pub struct AccountEventParser {}
|
||||
|
||||
impl AccountEventParser {
|
||||
pub fn configs(protocols: Vec<Protocol>) -> Vec<AccountEventParseConfig> {
|
||||
pub fn configs(protocols: Vec<Protocol>, event_type_filter: Option<EventTypeFilter>) -> Vec<AccountEventParseConfig> {
|
||||
let protocols_map = PROTOCOL_CONFIGS_CACHE.get_or_init(|| {
|
||||
let mut map: HashMap<Protocol, Vec<AccountEventParseConfig>> = HashMap::new();
|
||||
map.insert(Protocol::PumpSwap, vec![
|
||||
@@ -145,7 +146,11 @@ impl AccountEventParser {
|
||||
|
||||
let mut configs = vec![];
|
||||
for protocol in protocols {
|
||||
configs.extend(protocols_map.get(&protocol).unwrap_or(&vec![]).clone());
|
||||
let protocol_configs = protocols_map.get(&protocol).unwrap_or(&vec![]).clone();
|
||||
let filtered_configs: Vec<AccountEventParseConfig> = protocol_configs.into_iter().filter(|config| {
|
||||
event_type_filter.as_ref().map(|filter| filter.include.contains(&config.event_type)).unwrap_or(true)
|
||||
}).collect();
|
||||
configs.extend(filtered_configs);
|
||||
}
|
||||
configs
|
||||
}
|
||||
@@ -154,8 +159,9 @@ impl AccountEventParser {
|
||||
protocols: Vec<Protocol>,
|
||||
account: AccountPretty,
|
||||
program_received_time_ms: i64,
|
||||
event_type_filter: Option<EventTypeFilter>,
|
||||
) -> Option<Box<dyn UnifiedEvent>> {
|
||||
let configs = Self::configs(protocols);
|
||||
let configs = Self::configs(protocols, event_type_filter);
|
||||
for config in configs {
|
||||
if account.owner == config.program_id.to_string()
|
||||
&& account.data[..config.account_discriminator.len()]
|
||||
|
||||
@@ -4,6 +4,7 @@ use prost_types::Timestamp;
|
||||
use solana_sdk::{instruction::CompiledInstruction, pubkey::Pubkey};
|
||||
use solana_transaction_status::UiCompiledInstruction;
|
||||
|
||||
use crate::streaming::event_parser::common::filter::EventTypeFilter;
|
||||
use crate::streaming::event_parser::{
|
||||
core::traits::{EventParser, GenericEventParseConfig, GenericEventParser, UnifiedEvent},
|
||||
EventParserFactory, Protocol,
|
||||
@@ -14,7 +15,7 @@ pub struct MutilEventParser {
|
||||
}
|
||||
|
||||
impl MutilEventParser {
|
||||
pub fn new(protocols: Vec<Protocol>) -> Self {
|
||||
pub fn new(protocols: Vec<Protocol>, event_type_filter: Option<EventTypeFilter>) -> Self {
|
||||
let mut inner = GenericEventParser::new(vec![], vec![]);
|
||||
// Configure all event types
|
||||
for protocol in protocols {
|
||||
@@ -22,12 +23,18 @@ impl MutilEventParser {
|
||||
|
||||
// Merge inner_instruction_configs, append configurations to existing Vec
|
||||
for (key, configs) in parse.inner_instruction_configs() {
|
||||
inner.inner_instruction_configs.entry(key).or_insert_with(Vec::new).extend(configs);
|
||||
let filtered_configs: Vec<GenericEventParseConfig> = configs.into_iter().filter(|config| {
|
||||
event_type_filter.as_ref().map(|filter| filter.include.contains(&config.event_type)).unwrap_or(true)
|
||||
}).collect();
|
||||
inner.inner_instruction_configs.entry(key).or_insert_with(Vec::new).extend(filtered_configs);
|
||||
}
|
||||
|
||||
// Merge instruction_configs, append configurations to existing Vec
|
||||
for (key, configs) in parse.instruction_configs() {
|
||||
inner.instruction_configs.entry(key).or_insert_with(Vec::new).extend(configs);
|
||||
let filtered_configs: Vec<GenericEventParseConfig> = configs.into_iter().filter(|config| {
|
||||
event_type_filter.as_ref().map(|filter| filter.include.contains(&config.event_type)).unwrap_or(true)
|
||||
}).collect();
|
||||
inner.instruction_configs.entry(key).or_insert_with(Vec::new).extend(filtered_configs);
|
||||
}
|
||||
|
||||
// Append program_ids (this is already appending)
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
|
||||
use super::types::EventPretty;
|
||||
use crate::common::AnyResult;
|
||||
use crate::streaming::common::{
|
||||
EventBatchProcessor as EventBatchCollector, MetricsManager, StreamClientConfig as ClientConfig,
|
||||
EventBatchProcessor as EventBatchCollector, MetricsEventType, MetricsManager,
|
||||
StreamClientConfig as ClientConfig,
|
||||
};
|
||||
use crate::streaming::event_parser::common::filter::EventTypeFilter;
|
||||
use crate::streaming::event_parser::core::account_event_parser::AccountEventParser;
|
||||
use crate::streaming::event_parser::core::common_event_parser::CommonEventParser;
|
||||
use crate::streaming::event_parser::EventParser;
|
||||
@@ -18,12 +20,29 @@ use crate::streaming::event_parser::{
|
||||
pub struct EventProcessor {
|
||||
pub(crate) metrics_manager: MetricsManager,
|
||||
pub(crate) config: ClientConfig,
|
||||
pub(crate) parser_cache: Arc<Mutex<Option<Arc<dyn EventParser>>>>,
|
||||
}
|
||||
|
||||
impl EventProcessor {
|
||||
/// 创建新的事件处理器
|
||||
pub fn new(metrics_manager: MetricsManager, config: ClientConfig) -> Self {
|
||||
Self { metrics_manager, config }
|
||||
Self { metrics_manager, config, parser_cache: Arc::new(Mutex::new(None)) }
|
||||
}
|
||||
|
||||
/// 获取或创建解析器,使用缓存机制避免重复创建
|
||||
fn get_or_create_parser(
|
||||
&self,
|
||||
protocols: Vec<Protocol>,
|
||||
event_type_filter: Option<EventTypeFilter>,
|
||||
) -> Arc<dyn EventParser> {
|
||||
let mut cache = self.parser_cache.lock().unwrap();
|
||||
if let Some(cached_parser) = cache.clone() {
|
||||
return cached_parser.clone();
|
||||
}
|
||||
let parser: Arc<dyn EventParser> =
|
||||
Arc::new(MutilEventParser::new(protocols.clone(), event_type_filter.clone()));
|
||||
*cache = Some(parser.clone());
|
||||
parser
|
||||
}
|
||||
|
||||
/// 使用性能监控处理事件交易
|
||||
@@ -33,19 +52,21 @@ impl EventProcessor {
|
||||
callback: &F,
|
||||
bot_wallet: Option<Pubkey>,
|
||||
protocols: Vec<Protocol>,
|
||||
event_type_filter: Option<EventTypeFilter>,
|
||||
) -> AnyResult<()>
|
||||
where
|
||||
F: Fn(Box<dyn UnifiedEvent>) + Send + Sync,
|
||||
{
|
||||
match event_pretty {
|
||||
EventPretty::Account(account_pretty) => {
|
||||
self.metrics_manager.add_process_count().await;
|
||||
self.metrics_manager.add_account_process_count().await;
|
||||
let start_time = std::time::Instant::now();
|
||||
let program_received_time_ms = chrono::Utc::now().timestamp_millis();
|
||||
let account_event = AccountEventParser::parse_account_event(
|
||||
protocols.clone(),
|
||||
account_pretty,
|
||||
program_received_time_ms,
|
||||
event_type_filter,
|
||||
);
|
||||
if let Some(event) = account_event {
|
||||
callback(event);
|
||||
@@ -53,21 +74,22 @@ impl EventProcessor {
|
||||
let processing_time = start_time.elapsed();
|
||||
let processing_time_ms = processing_time.as_millis() as f64;
|
||||
// 更新性能指标(如果启用)
|
||||
self.metrics_manager.update_metrics(1, processing_time_ms).await;
|
||||
self.metrics_manager
|
||||
.update_metrics(MetricsEventType::Account, 1, processing_time_ms)
|
||||
.await;
|
||||
// 记录慢处理操作
|
||||
self.metrics_manager.log_slow_processing(processing_time_ms, 1);
|
||||
}
|
||||
}
|
||||
EventPretty::Transaction(transaction_pretty) => {
|
||||
self.metrics_manager.add_process_count().await;
|
||||
self.metrics_manager.add_tx_process_count().await;
|
||||
let start_time = std::time::Instant::now();
|
||||
let program_received_time_ms = chrono::Utc::now().timestamp_millis();
|
||||
let slot = transaction_pretty.slot;
|
||||
let signature = transaction_pretty.signature.to_string();
|
||||
|
||||
// 直接创建解析器并处理事务
|
||||
let parser: Arc<dyn EventParser> =
|
||||
Arc::new(MutilEventParser::new(protocols.clone()));
|
||||
// 使用缓存获取解析器
|
||||
let parser = self.get_or_create_parser(protocols.clone(), event_type_filter);
|
||||
let all_events = parser
|
||||
.parse_transaction(
|
||||
transaction_pretty.tx.clone(),
|
||||
@@ -98,13 +120,15 @@ impl EventProcessor {
|
||||
let processing_time_ms = processing_time.as_millis() as f64;
|
||||
|
||||
// 更新性能指标(如果启用)
|
||||
self.metrics_manager.update_metrics(event_count as u64, processing_time_ms).await;
|
||||
self.metrics_manager
|
||||
.update_metrics(MetricsEventType::Tx, event_count as u64, processing_time_ms)
|
||||
.await;
|
||||
// 记录慢处理操作
|
||||
self.metrics_manager.log_slow_processing(processing_time_ms, event_count);
|
||||
}
|
||||
EventPretty::BlockMeta(block_meta_pretty) => {
|
||||
let start_time = std::time::Instant::now();
|
||||
self.metrics_manager.add_process_count().await;
|
||||
self.metrics_manager.add_block_meta_process_count().await;
|
||||
let block_time_ms = block_meta_pretty
|
||||
.block_time
|
||||
.map(|ts| ts.seconds * 1000 + ts.nanos as i64 / 1_000_000)
|
||||
@@ -119,7 +143,9 @@ impl EventProcessor {
|
||||
let processing_time = start_time.elapsed();
|
||||
let processing_time_ms = processing_time.as_millis() as f64;
|
||||
// 更新性能指标(如果启用)
|
||||
self.metrics_manager.update_metrics(1, processing_time_ms).await;
|
||||
self.metrics_manager
|
||||
.update_metrics(MetricsEventType::BlockMeta, 1, processing_time_ms)
|
||||
.await;
|
||||
// 记录慢处理操作
|
||||
self.metrics_manager.log_slow_processing(processing_time_ms, 1);
|
||||
}
|
||||
@@ -135,19 +161,21 @@ impl EventProcessor {
|
||||
batch_processor: &mut EventBatchCollector<F>,
|
||||
bot_wallet: Option<Pubkey>,
|
||||
protocols: Vec<Protocol>,
|
||||
event_type_filter: Option<EventTypeFilter>,
|
||||
) -> AnyResult<()>
|
||||
where
|
||||
F: Fn(Vec<Box<dyn UnifiedEvent>>) + Send + Sync + 'static,
|
||||
{
|
||||
match event_pretty {
|
||||
EventPretty::Account(account_pretty) => {
|
||||
self.metrics_manager.add_process_count().await;
|
||||
self.metrics_manager.add_account_process_count().await;
|
||||
let start_time = std::time::Instant::now();
|
||||
let program_received_time_ms = chrono::Utc::now().timestamp_millis();
|
||||
let account_event = AccountEventParser::parse_account_event(
|
||||
protocols.clone(),
|
||||
account_pretty,
|
||||
program_received_time_ms,
|
||||
event_type_filter,
|
||||
);
|
||||
if let Some(event) = account_event {
|
||||
(batch_processor.callback)(vec![event]);
|
||||
@@ -155,21 +183,22 @@ impl EventProcessor {
|
||||
let processing_time = start_time.elapsed();
|
||||
let processing_time_ms = processing_time.as_millis() as f64;
|
||||
// 实际调用性能指标更新
|
||||
self.metrics_manager.update_metrics(1, processing_time_ms).await;
|
||||
self.metrics_manager
|
||||
.update_metrics(MetricsEventType::Account, 1, processing_time_ms)
|
||||
.await;
|
||||
// 记录慢处理操作
|
||||
self.metrics_manager.log_slow_processing(processing_time_ms, 1);
|
||||
}
|
||||
}
|
||||
EventPretty::Transaction(transaction_pretty) => {
|
||||
self.metrics_manager.add_process_count().await;
|
||||
self.metrics_manager.add_tx_process_count().await;
|
||||
let start_time = std::time::Instant::now();
|
||||
let program_received_time_ms = chrono::Utc::now().timestamp_millis();
|
||||
let slot = transaction_pretty.slot;
|
||||
let signature = transaction_pretty.signature.to_string();
|
||||
|
||||
// 直接创建解析器并处理事务
|
||||
let parser: Arc<dyn EventParser> =
|
||||
Arc::new(MutilEventParser::new(protocols.clone()));
|
||||
// 使用缓存获取解析器
|
||||
let parser = self.get_or_create_parser(protocols.clone(), event_type_filter);
|
||||
let result = parser
|
||||
.parse_transaction(
|
||||
transaction_pretty.tx.clone(),
|
||||
@@ -224,13 +253,16 @@ impl EventProcessor {
|
||||
let processing_time_ms = processing_time.as_millis() as f64;
|
||||
|
||||
// 实际调用性能指标更新
|
||||
self.metrics_manager.update_metrics(total_events as u64, processing_time_ms).await;
|
||||
self.metrics_manager
|
||||
.update_metrics(MetricsEventType::Tx, total_events as u64, processing_time_ms)
|
||||
.await;
|
||||
|
||||
// 记录慢处理操作
|
||||
self.metrics_manager.log_slow_processing(processing_time_ms, total_events);
|
||||
}
|
||||
EventPretty::BlockMeta(block_meta_pretty) => {
|
||||
let start_time = std::time::Instant::now();
|
||||
self.metrics_manager.add_block_meta_process_count().await;
|
||||
let block_time_ms = block_meta_pretty
|
||||
.block_time
|
||||
.map(|ts| ts.seconds * 1000 + ts.nanos as i64 / 1_000_000)
|
||||
@@ -245,7 +277,9 @@ impl EventProcessor {
|
||||
let processing_time = start_time.elapsed();
|
||||
let processing_time_ms = processing_time.as_millis() as f64;
|
||||
// 更新性能指标(如果启用)
|
||||
self.metrics_manager.update_metrics(1, processing_time_ms).await;
|
||||
self.metrics_manager
|
||||
.update_metrics(MetricsEventType::BlockMeta, 1, processing_time_ms)
|
||||
.await;
|
||||
// 记录慢处理操作
|
||||
self.metrics_manager.log_slow_processing(processing_time_ms, 1);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ use super::types::AccountsFilterMap;
|
||||
use super::types::TransactionsFilterMap;
|
||||
use crate::common::AnyResult;
|
||||
use crate::streaming::common::StreamClientConfig as ClientConfig;
|
||||
use crate::streaming::event_parser::common::filter::EventTypeFilter;
|
||||
|
||||
/// 订阅管理器
|
||||
#[derive(Clone)]
|
||||
@@ -41,17 +42,27 @@ impl SubscriptionManager {
|
||||
/// 创建订阅请求并返回流
|
||||
pub async fn subscribe_with_request(
|
||||
&self,
|
||||
transactions: TransactionsFilterMap,
|
||||
transactions: Option<TransactionsFilterMap>,
|
||||
accounts: Option<AccountsFilterMap>,
|
||||
commitment: Option<CommitmentLevel>,
|
||||
event_type_filter: Option<EventTypeFilter>,
|
||||
) -> AnyResult<(
|
||||
impl Sink<SubscribeRequest, Error = mpsc::SendError>,
|
||||
impl Stream<Item = Result<SubscribeUpdate, Status>>,
|
||||
)> {
|
||||
let blocks_meta = if event_type_filter.is_some()
|
||||
&& event_type_filter.as_ref().unwrap().include_block_event()
|
||||
{
|
||||
hashmap! { "".to_owned() => SubscribeRequestFilterBlocksMeta {} }
|
||||
} else if event_type_filter.is_none() {
|
||||
hashmap! { "".to_owned() => SubscribeRequestFilterBlocksMeta {} }
|
||||
} else {
|
||||
hashmap! {}
|
||||
};
|
||||
let subscribe_request = SubscribeRequest {
|
||||
accounts: accounts.unwrap_or_default(),
|
||||
transactions,
|
||||
blocks_meta: hashmap! { "".to_owned() => SubscribeRequestFilterBlocksMeta {} },
|
||||
transactions: transactions.unwrap_or_default(),
|
||||
blocks_meta,
|
||||
commitment: if let Some(commitment) = commitment {
|
||||
Some(commitment as i32)
|
||||
} else {
|
||||
@@ -69,10 +80,16 @@ impl SubscriptionManager {
|
||||
&self,
|
||||
account: Vec<String>,
|
||||
owner: Vec<String>,
|
||||
event_type_filter: Option<EventTypeFilter>,
|
||||
) -> Option<AccountsFilterMap> {
|
||||
if account.len() == 0 && owner.len() == 0 {
|
||||
return None;
|
||||
}
|
||||
if event_type_filter.is_some()
|
||||
&& !event_type_filter.as_ref().unwrap().include_account_event()
|
||||
{
|
||||
return None;
|
||||
}
|
||||
let mut accounts = HashMap::new();
|
||||
accounts.insert(
|
||||
"".to_owned(),
|
||||
@@ -92,7 +109,13 @@ impl SubscriptionManager {
|
||||
account_include: Vec<String>,
|
||||
account_exclude: Vec<String>,
|
||||
account_required: Vec<String>,
|
||||
) -> TransactionsFilterMap {
|
||||
event_type_filter: Option<EventTypeFilter>,
|
||||
) -> Option<TransactionsFilterMap> {
|
||||
if event_type_filter.is_some()
|
||||
&& !event_type_filter.as_ref().unwrap().include_transaction_event()
|
||||
{
|
||||
return None;
|
||||
}
|
||||
let mut transactions = HashMap::new();
|
||||
transactions.insert(
|
||||
"client".to_string(),
|
||||
@@ -105,7 +128,7 @@ impl SubscriptionManager {
|
||||
account_required,
|
||||
},
|
||||
);
|
||||
transactions
|
||||
Some(transactions)
|
||||
}
|
||||
|
||||
/// 获取配置
|
||||
|
||||
@@ -9,6 +9,7 @@ use crate::common::AnyResult;
|
||||
use crate::streaming::common::{
|
||||
EventBatchProcessor, MetricsManager, PerformanceMetrics, StreamClientConfig,
|
||||
};
|
||||
use crate::streaming::event_parser::common::filter::EventTypeFilter;
|
||||
use crate::streaming::event_parser::{Protocol, UnifiedEvent};
|
||||
use crate::streaming::grpc::{EventPretty, EventProcessor, StreamHandler, SubscriptionManager};
|
||||
|
||||
@@ -111,13 +112,26 @@ impl YellowstoneGrpc {
|
||||
self.config.enable_metrics = enabled;
|
||||
}
|
||||
|
||||
/// 简化的即时事件订阅(推荐用于简单场景)
|
||||
/// Simplified immediate event subscription (recommended for simple scenarios)
|
||||
///
|
||||
/// # Parameters
|
||||
/// * `protocols` - List of protocols to monitor
|
||||
/// * `bot_wallet` - Optional bot wallet address for filtering related transactions
|
||||
/// * `transaction_filter` - Transaction filter specifying accounts to include/exclude
|
||||
/// * `account_filter` - Account filter specifying accounts and owners to monitor
|
||||
/// * `event_filter` - Optional event filter for further event filtering, no filtering if None
|
||||
/// * `commitment` - Optional commitment level, defaults to Confirmed
|
||||
/// * `callback` - Event callback function that receives parsed unified events
|
||||
///
|
||||
/// # Returns
|
||||
/// Returns `AnyResult<()>`, `Ok(())` on success, error information on failure
|
||||
pub async fn subscribe_events_immediate<F>(
|
||||
&self,
|
||||
protocols: Vec<Protocol>,
|
||||
bot_wallet: Option<Pubkey>,
|
||||
transaction_filter: TransactionFilter,
|
||||
account_filter: AccountFilter,
|
||||
event_type_filter: Option<EventTypeFilter>,
|
||||
commitment: Option<CommitmentLevel>,
|
||||
callback: F,
|
||||
) -> AnyResult<()>
|
||||
@@ -133,15 +147,18 @@ impl YellowstoneGrpc {
|
||||
transaction_filter.account_include,
|
||||
transaction_filter.account_exclude,
|
||||
transaction_filter.account_required,
|
||||
event_type_filter.clone(),
|
||||
);
|
||||
let accounts = self.subscription_manager.subscribe_with_account_request(
|
||||
account_filter.account,
|
||||
account_filter.owner,
|
||||
event_type_filter.clone(),
|
||||
);
|
||||
let accounts = self
|
||||
.subscription_manager
|
||||
.subscribe_with_account_request(account_filter.account, account_filter.owner);
|
||||
|
||||
// 订阅事件
|
||||
let (mut subscribe_tx, mut stream) = self
|
||||
.subscription_manager
|
||||
.subscribe_with_request(transactions, accounts, commitment)
|
||||
.subscribe_with_request(transactions, accounts, commitment, event_type_filter.clone())
|
||||
.await?;
|
||||
|
||||
// 创建通道,使用配置中的通道大小
|
||||
@@ -183,6 +200,7 @@ impl YellowstoneGrpc {
|
||||
&callback,
|
||||
bot_wallet,
|
||||
protocols.clone(),
|
||||
event_type_filter.clone(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
@@ -195,13 +213,32 @@ impl YellowstoneGrpc {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 高级模式订阅(包含批处理和背压处理)
|
||||
/// Advanced event subscription with batch processing and backpressure handling
|
||||
///
|
||||
/// # Parameters
|
||||
/// * `protocols` - List of protocols to monitor
|
||||
/// * `bot_wallet` - Optional bot wallet address for filtering related transactions
|
||||
/// * `transaction_filter` - Transaction filter specifying accounts to include/exclude
|
||||
/// * `account_filter` - Account filter specifying accounts and owners to monitor
|
||||
/// * `event_filter` - Optional event filter for further event filtering, no filtering if None
|
||||
/// * `commitment` - Optional commitment level, defaults to Confirmed
|
||||
/// * `callback` - Event callback function that receives parsed unified events
|
||||
///
|
||||
/// # Features
|
||||
/// * Batch processing for improved throughput
|
||||
/// * Backpressure handling to prevent memory overflow
|
||||
/// * Automatic performance monitoring (if enabled)
|
||||
/// * Configurable batch size and timeout
|
||||
///
|
||||
/// # Returns
|
||||
/// Returns `AnyResult<()>`, `Ok(())` on success, error information on failure
|
||||
pub async fn subscribe_events_advanced<F>(
|
||||
&self,
|
||||
protocols: Vec<Protocol>,
|
||||
bot_wallet: Option<Pubkey>,
|
||||
transaction_filter: TransactionFilter,
|
||||
account_filter: AccountFilter,
|
||||
event_type_filter: Option<EventTypeFilter>,
|
||||
commitment: Option<CommitmentLevel>,
|
||||
callback: F,
|
||||
) -> AnyResult<()>
|
||||
@@ -217,15 +254,18 @@ impl YellowstoneGrpc {
|
||||
transaction_filter.account_include,
|
||||
transaction_filter.account_exclude,
|
||||
transaction_filter.account_required,
|
||||
event_type_filter.clone(),
|
||||
);
|
||||
let accounts = self.subscription_manager.subscribe_with_account_request(
|
||||
account_filter.account,
|
||||
account_filter.owner,
|
||||
event_type_filter.clone(),
|
||||
);
|
||||
let accounts = self
|
||||
.subscription_manager
|
||||
.subscribe_with_account_request(account_filter.account, account_filter.owner);
|
||||
|
||||
// Subscribe to events
|
||||
let (mut subscribe_tx, mut stream) = self
|
||||
.subscription_manager
|
||||
.subscribe_with_request(transactions, accounts, commitment)
|
||||
.subscribe_with_request(transactions, accounts, commitment, event_type_filter.clone())
|
||||
.await?;
|
||||
|
||||
// Create channel
|
||||
@@ -280,6 +320,7 @@ impl YellowstoneGrpc {
|
||||
&mut batch_processor,
|
||||
bot_wallet,
|
||||
protocols.clone(),
|
||||
event_type_filter.clone(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
@@ -299,6 +340,10 @@ impl YellowstoneGrpc {
|
||||
// 实现 Clone trait 以支持模块间共享
|
||||
impl Clone for EventProcessor {
|
||||
fn clone(&self) -> Self {
|
||||
Self { metrics_manager: self.metrics_manager.clone(), config: self.config.clone() }
|
||||
Self {
|
||||
metrics_manager: self.metrics_manager.clone(),
|
||||
config: self.config.clone(),
|
||||
parser_cache: self.parser_cache.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,9 +45,12 @@ impl YellowstoneGrpc {
|
||||
account_include,
|
||||
account_exclude,
|
||||
addrs,
|
||||
None,
|
||||
);
|
||||
let (mut subscribe_tx, mut stream) =
|
||||
self.subscription_manager.subscribe_with_request(transactions, None, None).await?;
|
||||
let (mut subscribe_tx, mut stream) = self
|
||||
.subscription_manager
|
||||
.subscribe_with_request(transactions, None, None, None)
|
||||
.await?;
|
||||
let (mut tx, mut rx) = mpsc::channel::<EventPretty>(CHANNEL_SIZE);
|
||||
|
||||
let callback = Box::new(callback);
|
||||
|
||||
Reference in New Issue
Block a user