Skip to content

Commit 3d825d5

Browse files
committed
Add stream bus module and tokio dependency
1 parent 81974d0 commit 3d825d5

4 files changed

Lines changed: 211 additions & 0 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ rsp-rs = "0.3.1"
1818
oxigraph = "0.5"
1919
rumqttc = "0.25.1"
2020
serde_json = "1.0.145"
21+
tokio = "1.48.0"
2122

2223
[target.'cfg(not(windows))'.dependencies]
2324
rdkafka = "0.38.0"

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ pub mod parsing;
8383

8484
pub mod api;
8585

86+
pub mod stream_bus;
87+
8688
pub mod storage;
8789

8890
pub mod registry;

src/stream_bus/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub mod stream_bus;

src/stream_bus/stream_bus.rs

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
//! Stream Bus to read the RDF data from a file and publishing to a Kafka and Streaming Storage at the same time.
2+
//!
3+
//! The module implements a high-throughput event bus that does the following things:
4+
//! 1. Will read the RDF events from the file.
5+
//! 2. It will publish the event to the Kafka / MQTT topic.
6+
//! 3. It will write the event to the Janus Streaming Storage.
7+
//! 4. It provides replay rate defined and will replay the event.
8+
9+
use crate::core::RDFEvent;
10+
use crate::storage::segmented_storage::StreamingSegmentedStorage;
11+
use core::str;
12+
use rdkafka::config::ClientConfig;
13+
use rdkafka::producer::{FutureProducer, FutureRecord};
14+
use rumqttc::{AsyncClient, MqttOptions, QoS};
15+
use std::fmt::write;
16+
use std::fs::File;
17+
use std::io::{BufRead, BufReader};
18+
use std::path::Path;
19+
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
20+
use std::sync::Arc;
21+
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
22+
use tokio::runtime::{self, Runtime};
23+
use tokio::time::sleep;
24+
25+
/// Defining the Broker Type
26+
/// 1. Kafka
27+
/// 2. MQTT
28+
/// 3. None, in which case it won't write to a stream but rather only to the Segmented Storage.
29+
#[derive(Debug, Clone)]
30+
pub enum BrokerType {
31+
Kafka,
32+
Mqtt,
33+
None,
34+
}
35+
36+
/// Defining the KafkaConfiguration
37+
#[derive(Debug, Clone)]
38+
pub struct KafkaConfig {
39+
pub bootstrap_servers: String,
40+
pub client_id: String,
41+
pub message_timeout_ms: String,
42+
}
43+
44+
/// Definining the MQTT Configuration
45+
#[derive(Debug, Clone)]
46+
pub struct MqttConfig {
47+
pub host: String,
48+
pub port: u16,
49+
pub client_id: String,
50+
pub keep_alive_secs: u64,
51+
}
52+
53+
/// Configuration for the Stream Bus
54+
#[derive(Debug, Clone)]
55+
pub struct StreamBusConfig {
56+
pub input_file: String,
57+
pub broker_type: BrokerType,
58+
pub topics: Vec<String>,
59+
pub rate_of_publishing: u64,
60+
pub loop_file: bool,
61+
pub add_timestamps: bool,
62+
pub kafka_config: Option<KafkaConfig>,
63+
pub mqtt_config: Option<MqttConfig>,
64+
}
65+
66+
impl Default for KafkaConfig {
67+
fn default() -> Self {
68+
Self {
69+
bootstrap_servers: "localhost:9092".to_string(),
70+
client_id: "janus_stream_bus".to_string(),
71+
message_timeout_ms: "5000".to_string(),
72+
}
73+
}
74+
}
75+
76+
impl Default for MqttConfig {
77+
fn default() -> Self {
78+
Self {
79+
host: "localhost".to_string(),
80+
port: 1883,
81+
client_id: "janus_stream_bus".to_string(),
82+
keep_alive_secs: 30,
83+
}
84+
}
85+
}
86+
87+
/// Metrics collected by the Stream Bus.
88+
pub struct StreamBusMetrics {
89+
pub events_read: u64,
90+
pub events_published: u64,
91+
pub events_stored: u64,
92+
pub publish_errors: u64,
93+
pub storage_errors: u64,
94+
pub elapsed_seconds: f64,
95+
}
96+
97+
impl StreamBusMetrics {
98+
pub fn events_per_second(&self) -> f64 {
99+
if self.elapsed_seconds > 0.0 {
100+
self.events_read as f64 / self.elapsed_seconds
101+
} else {
102+
0.0
103+
}
104+
}
105+
106+
pub fn publish_success_rate(&self) -> f64 {
107+
if self.events_read > 0 {
108+
(self.events_published as f64 / self.events_read as f64) * 100.0
109+
} else {
110+
0.0
111+
}
112+
}
113+
114+
pub fn storage_success_rate(&self) -> f64 {
115+
if self.events_read > 0 {
116+
(self.events_stored as f64 / self.events_read as f64) * 100
117+
} else {
118+
0.0
119+
}
120+
}
121+
}
122+
123+
/// Main Stream Bus's Architecture
124+
pub struct StreamBus {
125+
config: StreamBusConfig,
126+
storage: Arc<StreamingSegmentedStorage>,
127+
runtime: Arc<Runtime>,
128+
events_read: Arc<AtomicU64>,
129+
events_published: Arc<AtomicU64>,
130+
events_stored: Arc<AtomicU64>,
131+
publish_errors: Arc<AtomicU64>,
132+
storage_erros: Arc<AtomicU64>,
133+
should_stop: Arc<AtomicBool>,
134+
}
135+
136+
/// Error types for the Stream Bus
137+
#[derive(Debug)]
138+
pub enum StreamBusError {
139+
FileError(String),
140+
BrokerError(String),
141+
ConfigError(String),
142+
}
143+
144+
impl std::fmt::Display for StreamBusError {
145+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
146+
match self {
147+
StreamBusError::FileError(msg) => write!(f, "File Error: {}", msg),
148+
StreamBusError::ConfigError(msg) => write!(f, "Config Error: {}", msg),
149+
StreamBusError::BrokerError(msg) => write!(f, "Broker Error: {}", msg),
150+
}
151+
}
152+
}
153+
154+
impl std::error::Error for StreamBusError {}
155+
156+
impl StreamBus {
157+
pub fn new(config: StreamBusConfig, storage: Arc<StreamingSegmentedStorage>) -> Self {
158+
let runtime = Arc::new(
159+
tokio::runtime::Builder::new_current_thread()
160+
.worker_threads(4)
161+
.enable_all()
162+
.build()
163+
.expect("Failed to create the runtime for Tokio."),
164+
);
165+
166+
Self {
167+
config,
168+
storage,
169+
runtime,
170+
events_read: Arc::new(AtomicU64::new(0)),
171+
events_published: Arc::new(AtomicU64::new(0)),
172+
events_stored: Arc::new(AtomicU64::new(0)),
173+
publish_errors: Arc::new(AtomicU64::new(0)),
174+
storage_erros: Arc::new(AtomicU64::new(0)),
175+
should_stop: Arc::new(AtomicBool::new(false)),
176+
}
177+
}
178+
179+
/// Start the stream bus.
180+
pub fn start(&self) -> Result<StreamBusMetrics, StreamBusError> {
181+
println!("Starting the Stream Bus");
182+
println!("Input: {}", self.config.input_file);
183+
println!("Broker: {:?}", self.config.broker_type);
184+
println!("Topics: {:?}", self.config.topics);
185+
println!("Rate of publishing: {} Hz", if self.config.rate_of_publishing == 0{
186+
"unlimited".to_string()
187+
} else {
188+
self.config.rate_of_publishing.to_string()
189+
});
190+
191+
println!("Loop: {}", self.config.loop_file);
192+
println!();
193+
194+
195+
let start_time = Instant::now();
196+
197+
match self.config.broker_type {
198+
BrokerType::Kafka => self.runtime.block_on(self.run_with_kafka())?,
199+
BrokerType::Mqtt => self.runtime.block_on(self.run_with_mqtt())?,
200+
BrokerType::None => self.runtime.block_on(self.run_storage_only())?,
201+
}
202+
203+
let elapsed = start_time.elapsed().as_secs_f64();
204+
205+
Ok(StreamBusMetrics { events_read: (), events_published: (), events_stored: (), publish_errors: (), storage_errors: (), elapsed_seconds: () })
206+
}
207+
}

0 commit comments

Comments
 (0)