mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-09-03 01:25:37 +00:00
feat(stats): add by-instance traffic metrics (#2011)
This commit is contained in:
+190
-44
@@ -8,7 +8,6 @@ use std::{
|
||||
},
|
||||
};
|
||||
|
||||
use arc_swap::ArcSwapOption;
|
||||
use crossbeam::atomic::AtomicCell;
|
||||
use futures::{StreamExt, TryFutureExt};
|
||||
|
||||
@@ -34,7 +33,6 @@ use crate::{
|
||||
defer,
|
||||
error::Error,
|
||||
global_ctx::ArcGlobalCtx,
|
||||
stats_manager::{CounterHandle, LabelSet, LabelType, MetricName},
|
||||
PeerId,
|
||||
},
|
||||
peers::peer_session::{PeerSessionStore, SessionKey, UpsertResponderSessionReturn},
|
||||
@@ -59,6 +57,7 @@ use crate::{
|
||||
use super::{
|
||||
peer_conn_ping::PeerConnPinger,
|
||||
peer_session::{PeerSession, PeerSessionAction},
|
||||
traffic_metrics::AggregateTrafficMetrics,
|
||||
PacketRecvChan,
|
||||
};
|
||||
|
||||
@@ -284,13 +283,6 @@ impl PeerConnCloseNotify {
|
||||
}
|
||||
}
|
||||
|
||||
struct PeerConnCounter {
|
||||
traffic_tx_bytes: CounterHandle,
|
||||
traffic_rx_bytes: CounterHandle,
|
||||
traffic_tx_packets: CounterHandle,
|
||||
traffic_rx_packets: CounterHandle,
|
||||
}
|
||||
|
||||
pub struct PeerConn {
|
||||
conn_id: PeerConnId,
|
||||
|
||||
@@ -323,8 +315,6 @@ pub struct PeerConn {
|
||||
throughput: Arc<Throughput>,
|
||||
loss_rate_stats: Arc<AtomicU32>,
|
||||
|
||||
counters: ArcSwapOption<PeerConnCounter>,
|
||||
|
||||
peer_session_store: Arc<PeerSessionStore>,
|
||||
my_encrypt_algo: String,
|
||||
}
|
||||
@@ -413,8 +403,6 @@ impl PeerConn {
|
||||
throughput,
|
||||
loss_rate_stats: Arc::new(AtomicU32::new(0)),
|
||||
|
||||
counters: ArcSwapOption::new(None),
|
||||
|
||||
peer_session_store,
|
||||
my_encrypt_algo,
|
||||
}
|
||||
@@ -480,6 +468,7 @@ impl PeerConn {
|
||||
};
|
||||
|
||||
*need_retry = true;
|
||||
let rsp_len = rsp.buf_len() as u64;
|
||||
|
||||
let Some(peer_mgr_hdr) = rsp.peer_manager_header() else {
|
||||
return Err(Error::WaitRespError(format!(
|
||||
@@ -505,6 +494,8 @@ impl PeerConn {
|
||||
));
|
||||
}
|
||||
|
||||
self.record_control_rx(&rsp.network_name, rsp_len);
|
||||
|
||||
Ok(rsp)
|
||||
}
|
||||
|
||||
@@ -527,7 +518,11 @@ impl PeerConn {
|
||||
.await?
|
||||
}
|
||||
|
||||
async fn send_handshake(&self, send_secret_digest: bool) -> Result<(), Error> {
|
||||
async fn send_handshake(
|
||||
&self,
|
||||
send_secret_digest: bool,
|
||||
metric_network_name: &str,
|
||||
) -> Result<(), Error> {
|
||||
let network = self.global_ctx.get_network_identity();
|
||||
let mut req = HandshakeRequest {
|
||||
magic: MAGIC,
|
||||
@@ -555,11 +550,13 @@ impl PeerConn {
|
||||
PeerId::default(),
|
||||
PacketType::HandShake as u8,
|
||||
);
|
||||
let pkt_len = zc_packet.buf_len() as u64;
|
||||
|
||||
self.sink.send(zc_packet).await.map_err(|e| {
|
||||
tracing::warn!("send handshake request error: {:?}", e);
|
||||
Error::WaitRespError("send handshake request error".to_owned())
|
||||
})?;
|
||||
self.record_control_tx(metric_network_name, pkt_len);
|
||||
|
||||
// yield to send the response packet
|
||||
tokio::task::yield_now().await;
|
||||
@@ -663,6 +660,7 @@ impl PeerConn {
|
||||
pb: Msg,
|
||||
packet_type: PacketType,
|
||||
remote_peer_id: PeerId,
|
||||
metric_network_name: &str,
|
||||
hs: &mut snow::HandshakeState,
|
||||
) -> Result<(), Error> {
|
||||
tracing::info!(
|
||||
@@ -679,7 +677,10 @@ impl PeerConn {
|
||||
.map_err(|e| Error::WaitRespError(format!("noise write msg1 failed: {e:?}")))?;
|
||||
let mut pkt = ZCPacket::new_with_payload(&msg[..msg_len]);
|
||||
pkt.fill_peer_manager_hdr(self.my_peer_id, remote_peer_id, packet_type as u8);
|
||||
Ok(self.sink.send(pkt).await?)
|
||||
let pkt_len = pkt.buf_len() as u64;
|
||||
self.sink.send(pkt).await?;
|
||||
self.record_control_tx(metric_network_name, pkt_len);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Unified remote peer authentication verification.
|
||||
@@ -833,6 +834,7 @@ impl PeerConn {
|
||||
msg1_pb,
|
||||
PacketType::NoiseHandshakeMsg1,
|
||||
PeerId::default(),
|
||||
&network.network_name,
|
||||
&mut hs,
|
||||
)
|
||||
.await?;
|
||||
@@ -844,6 +846,7 @@ impl PeerConn {
|
||||
self.recv_next_peer_manager_packet(Some(PacketType::NoiseHandshakeMsg2)),
|
||||
)
|
||||
.await??;
|
||||
self.record_control_rx(&network.network_name, msg2.buf_len() as u64);
|
||||
let remote_peer_id = msg2.get_src_peer_id().expect("missing src peer id");
|
||||
if let Some(hint) = self.peer_id_hint {
|
||||
if hint != remote_peer_id {
|
||||
@@ -896,6 +899,7 @@ impl PeerConn {
|
||||
msg3_pb,
|
||||
PacketType::NoiseHandshakeMsg3,
|
||||
remote_peer_id,
|
||||
&network.network_name,
|
||||
&mut hs,
|
||||
)
|
||||
.await?;
|
||||
@@ -1063,6 +1067,7 @@ impl PeerConn {
|
||||
let remote_peer_id = first_msg1
|
||||
.get_src_peer_id()
|
||||
.expect("msg1 must have src peer id");
|
||||
let first_msg1_len = first_msg1.buf_len() as u64;
|
||||
|
||||
let msg1_pb = Self::decode_handshake_message::<PeerConnNoiseMsg1Pb>(
|
||||
PacketType::NoiseHandshakeMsg1,
|
||||
@@ -1070,6 +1075,7 @@ impl PeerConn {
|
||||
first_msg1,
|
||||
)?;
|
||||
let remote_network_name = msg1_pb.a_network_name.clone();
|
||||
self.record_control_rx(&remote_network_name, first_msg1_len);
|
||||
|
||||
// this may update my peer id
|
||||
handshake_recved(self, &remote_network_name)?;
|
||||
@@ -1122,6 +1128,7 @@ impl PeerConn {
|
||||
msg2_pb,
|
||||
PacketType::NoiseHandshakeMsg2,
|
||||
remote_peer_id,
|
||||
&remote_network_name,
|
||||
&mut hs,
|
||||
)
|
||||
.await?;
|
||||
@@ -1133,6 +1140,7 @@ impl PeerConn {
|
||||
self.recv_next_peer_manager_packet(Some(PacketType::NoiseHandshakeMsg3)),
|
||||
)
|
||||
.await??;
|
||||
self.record_control_rx(&remote_network_name, msg3_pkt.buf_len() as u64);
|
||||
let msg3_pb = Self::decode_handshake_message::<PeerConnNoiseMsg3Pb>(
|
||||
PacketType::NoiseHandshakeMsg3,
|
||||
Some(&mut hs),
|
||||
@@ -1260,11 +1268,13 @@ impl PeerConn {
|
||||
let rsp = Self::decode_handshake_packet(&first_pkt)?;
|
||||
handshake_recved(self, &rsp.network_name)?;
|
||||
tracing::info!("handshake request: {:?}", rsp);
|
||||
self.record_control_rx(&rsp.network_name, first_pkt.buf_len() as u64);
|
||||
self.info = Some(rsp);
|
||||
self.is_client = Some(false);
|
||||
|
||||
let send_digest = self.get_network_identity() == self.global_ctx.get_network_identity();
|
||||
self.send_handshake(send_digest).await?;
|
||||
self.send_handshake(send_digest, &self.get_network_identity().network_name)
|
||||
.await?;
|
||||
} else {
|
||||
return Err(Error::WaitRespError(format!(
|
||||
"unexpected packet type during handshake: {}",
|
||||
@@ -1296,7 +1306,8 @@ impl PeerConn {
|
||||
self.info = Some(handshake_rsp);
|
||||
self.is_client = Some(true);
|
||||
} else {
|
||||
self.send_handshake(true).await?;
|
||||
let network = self.global_ctx.get_network_identity();
|
||||
self.send_handshake(true, &network.network_name).await?;
|
||||
tracing::info!("waiting for handshake request from server");
|
||||
let rsp = self.wait_handshake_loop().await?;
|
||||
tracing::info!("handshake response: {:?}", rsp);
|
||||
@@ -1317,6 +1328,21 @@ impl PeerConn {
|
||||
self.info.is_some()
|
||||
}
|
||||
|
||||
fn control_metrics(&self, network_name: &str) -> AggregateTrafficMetrics {
|
||||
AggregateTrafficMetrics::control(
|
||||
self.global_ctx.stats_manager().clone(),
|
||||
network_name.to_string(),
|
||||
)
|
||||
}
|
||||
|
||||
fn record_control_tx(&self, network_name: &str, bytes: u64) {
|
||||
self.control_metrics(network_name).record_tx(bytes);
|
||||
}
|
||||
|
||||
fn record_control_rx(&self, network_name: &str, bytes: u64) {
|
||||
self.control_metrics(network_name).record_rx(bytes);
|
||||
}
|
||||
|
||||
pub async fn start_recv_loop(&mut self, packet_recv_chan: PacketRecvChan) {
|
||||
let mut stream = self.recv.lock().await.take().unwrap();
|
||||
let sink = self.sink.clone();
|
||||
@@ -1324,19 +1350,7 @@ impl PeerConn {
|
||||
let close_event_notifier = self.close_event_notifier.clone();
|
||||
let ctrl_sender = self.ctrl_resp_sender.clone();
|
||||
let conn_info_for_instrument = self.get_conn_info();
|
||||
|
||||
let stats_mgr = self.global_ctx.stats_manager();
|
||||
let label_set = LabelSet::new().with_label_type(LabelType::NetworkName(
|
||||
conn_info_for_instrument.network_name.clone(),
|
||||
));
|
||||
let counters = PeerConnCounter {
|
||||
traffic_tx_bytes: stats_mgr.get_counter(MetricName::TrafficBytesTx, label_set.clone()),
|
||||
traffic_rx_bytes: stats_mgr.get_counter(MetricName::TrafficBytesRx, label_set.clone()),
|
||||
traffic_tx_packets: stats_mgr
|
||||
.get_counter(MetricName::TrafficPacketsTx, label_set.clone()),
|
||||
traffic_rx_packets: stats_mgr.get_counter(MetricName::TrafficPacketsRx, label_set),
|
||||
};
|
||||
self.counters.store(Some(Arc::new(counters)));
|
||||
let control_metrics = self.control_metrics(&conn_info_for_instrument.network_name);
|
||||
|
||||
let is_foreign_network = conn_info_for_instrument.network_name
|
||||
!= self.global_ctx.get_network_identity().network_name;
|
||||
@@ -1355,8 +1369,6 @@ impl PeerConn {
|
||||
None
|
||||
};
|
||||
|
||||
let counters = self.counters.load_full().unwrap();
|
||||
|
||||
self.tasks.spawn(
|
||||
async move {
|
||||
tracing::info!("start recving peer conn packet");
|
||||
@@ -1370,10 +1382,6 @@ impl PeerConn {
|
||||
|
||||
let mut zc_packet = ret.unwrap();
|
||||
let buf_len = zc_packet.buf_len() as u64;
|
||||
|
||||
counters.traffic_rx_bytes.add(buf_len);
|
||||
counters.traffic_rx_packets.inc();
|
||||
|
||||
let Some(peer_mgr_hdr) = zc_packet.mut_peer_manager_header() else {
|
||||
tracing::error!(
|
||||
"unexpected packet: {:?}, cannot decode peer manager hdr",
|
||||
@@ -1383,11 +1391,15 @@ impl PeerConn {
|
||||
};
|
||||
|
||||
if peer_mgr_hdr.packet_type == PacketType::Ping as u8 {
|
||||
control_metrics.record_rx(buf_len);
|
||||
peer_mgr_hdr.packet_type = PacketType::Pong as u8;
|
||||
if let Err(e) = sink.send(zc_packet).await {
|
||||
tracing::error!(?e, "peer conn send req error");
|
||||
} else {
|
||||
control_metrics.record_tx(buf_len);
|
||||
}
|
||||
} else if peer_mgr_hdr.packet_type == PacketType::Pong as u8 {
|
||||
control_metrics.record_rx(buf_len);
|
||||
if let Err(e) = ctrl_sender.send(zc_packet) {
|
||||
tracing::error!(?e, "peer conn send ctrl resp error");
|
||||
}
|
||||
@@ -1422,6 +1434,7 @@ impl PeerConn {
|
||||
self.latency_stats.clone(),
|
||||
self.loss_rate_stats.clone(),
|
||||
self.throughput.clone(),
|
||||
self.control_metrics(&self.get_conn_info().network_name),
|
||||
);
|
||||
|
||||
let close_event_notifier = self.close_event_notifier.clone();
|
||||
@@ -1438,11 +1451,6 @@ impl PeerConn {
|
||||
}
|
||||
|
||||
pub async fn send_msg(&self, msg: ZCPacket) -> Result<(), Error> {
|
||||
let counters = self.counters.load();
|
||||
if let Some(ref counters) = *counters {
|
||||
counters.traffic_tx_bytes.add(msg.buf_len() as u64);
|
||||
counters.traffic_tx_packets.inc();
|
||||
}
|
||||
Ok(self.sink.send(msg).await?)
|
||||
}
|
||||
|
||||
@@ -1545,7 +1553,7 @@ impl Drop for PeerConn {
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use std::sync::Arc;
|
||||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
@@ -1555,8 +1563,10 @@ pub mod tests {
|
||||
use crate::common::global_ctx::GlobalCtx;
|
||||
use crate::common::new_peer_id;
|
||||
use crate::common::scoped_task::ScopedTask;
|
||||
use crate::common::stats_manager::{LabelSet, LabelType, MetricName};
|
||||
use crate::peers::create_packet_recv_chan;
|
||||
use crate::peers::recv_packet_from_chan;
|
||||
use crate::tunnel::common::tests::wait_for_condition;
|
||||
use crate::tunnel::filter::tests::DropSendTunnelFilter;
|
||||
use crate::tunnel::filter::PacketRecorderTunnelFilter;
|
||||
use crate::tunnel::ring::create_ring_tunnel_pair;
|
||||
@@ -1577,6 +1587,17 @@ pub mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn metric_value(global_ctx: &GlobalCtx, metric: MetricName, network_name: &str) -> u64 {
|
||||
global_ctx
|
||||
.stats_manager()
|
||||
.get_metric(
|
||||
metric,
|
||||
&LabelSet::new().with_label_type(LabelType::NetworkName(network_name.to_string())),
|
||||
)
|
||||
.map(|metric| metric.value)
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn peer_conn_handshake_same_id() {
|
||||
let ps = Arc::new(PeerSessionStore::new());
|
||||
@@ -1610,10 +1631,12 @@ pub mod tests {
|
||||
let s_peer_id = new_peer_id();
|
||||
|
||||
let ps = Arc::new(PeerSessionStore::new());
|
||||
let c_ctx = get_mock_global_ctx();
|
||||
let s_ctx = get_mock_global_ctx();
|
||||
|
||||
let mut c_peer = PeerConn::new(c_peer_id, get_mock_global_ctx(), Box::new(c), ps.clone());
|
||||
let mut c_peer = PeerConn::new(c_peer_id, c_ctx.clone(), Box::new(c), ps.clone());
|
||||
|
||||
let mut s_peer = PeerConn::new(s_peer_id, get_mock_global_ctx(), Box::new(s), ps.clone());
|
||||
let mut s_peer = PeerConn::new(s_peer_id, s_ctx.clone(), Box::new(s), ps.clone());
|
||||
|
||||
let (c_ret, s_ret) = tokio::join!(
|
||||
c_peer.do_handshake_as_client(),
|
||||
@@ -1629,6 +1652,47 @@ pub mod tests {
|
||||
assert_eq!(s_recorder.sent.lock().unwrap().len(), 1);
|
||||
assert_eq!(s_recorder.received.lock().unwrap().len(), 1);
|
||||
|
||||
assert_eq!(
|
||||
metric_value(&c_ctx, MetricName::TrafficControlBytesTx, "default"),
|
||||
c_recorder
|
||||
.sent
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|pkt| pkt.buf_len() as u64)
|
||||
.sum::<u64>()
|
||||
);
|
||||
assert_eq!(
|
||||
metric_value(&c_ctx, MetricName::TrafficControlBytesRx, "default"),
|
||||
c_recorder
|
||||
.received
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|pkt| pkt.buf_len() as u64)
|
||||
.sum::<u64>()
|
||||
);
|
||||
assert_eq!(
|
||||
metric_value(&s_ctx, MetricName::TrafficControlBytesTx, "default"),
|
||||
s_recorder
|
||||
.sent
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|pkt| pkt.buf_len() as u64)
|
||||
.sum::<u64>()
|
||||
);
|
||||
assert_eq!(
|
||||
metric_value(&s_ctx, MetricName::TrafficControlBytesRx, "default"),
|
||||
s_recorder
|
||||
.received
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|pkt| pkt.buf_len() as u64)
|
||||
.sum::<u64>()
|
||||
);
|
||||
|
||||
assert_eq!(c_peer.get_peer_id(), s_peer_id);
|
||||
assert_eq!(s_peer.get_peer_id(), c_peer_id);
|
||||
assert_eq!(c_peer.get_network_identity(), s_peer.get_network_identity());
|
||||
@@ -1673,6 +1737,47 @@ pub mod tests {
|
||||
c_ret.unwrap();
|
||||
s_ret.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
metric_value(&c_ctx, MetricName::TrafficControlBytesTx, "default"),
|
||||
c_recorder
|
||||
.sent
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|pkt| pkt.buf_len() as u64)
|
||||
.sum::<u64>()
|
||||
);
|
||||
assert_eq!(
|
||||
metric_value(&c_ctx, MetricName::TrafficControlBytesRx, "default"),
|
||||
c_recorder
|
||||
.received
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|pkt| pkt.buf_len() as u64)
|
||||
.sum::<u64>()
|
||||
);
|
||||
assert_eq!(
|
||||
metric_value(&s_ctx, MetricName::TrafficControlBytesTx, "default"),
|
||||
s_recorder
|
||||
.sent
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|pkt| pkt.buf_len() as u64)
|
||||
.sum::<u64>()
|
||||
);
|
||||
assert_eq!(
|
||||
metric_value(&s_ctx, MetricName::TrafficControlBytesRx, "default"),
|
||||
s_recorder
|
||||
.received
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|pkt| pkt.buf_len() as u64)
|
||||
.sum::<u64>()
|
||||
);
|
||||
|
||||
let c_info = c_peer.get_conn_info();
|
||||
let s_info = s_peer.get_conn_info();
|
||||
|
||||
@@ -2071,6 +2176,47 @@ pub mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn peer_conn_pingpong_records_control_metrics() {
|
||||
let (c, s) = create_ring_tunnel_pair();
|
||||
|
||||
let c_peer_id = new_peer_id();
|
||||
let s_peer_id = new_peer_id();
|
||||
|
||||
let c_ctx = get_mock_global_ctx();
|
||||
let s_ctx = get_mock_global_ctx();
|
||||
let ps = Arc::new(PeerSessionStore::new());
|
||||
let mut c_peer = PeerConn::new(c_peer_id, c_ctx.clone(), Box::new(c), ps.clone());
|
||||
let mut s_peer = PeerConn::new(s_peer_id, s_ctx.clone(), Box::new(s), ps.clone());
|
||||
|
||||
let (c_ret, s_ret) = tokio::join!(
|
||||
c_peer.do_handshake_as_client(),
|
||||
s_peer.do_handshake_as_server()
|
||||
);
|
||||
|
||||
assert!(c_ret.is_ok());
|
||||
assert!(s_ret.is_ok());
|
||||
|
||||
s_peer.start_recv_loop(create_packet_recv_chan().0).await;
|
||||
c_peer.start_pingpong();
|
||||
c_peer.start_recv_loop(create_packet_recv_chan().0).await;
|
||||
|
||||
wait_for_condition(
|
||||
|| {
|
||||
let c_ctx = c_ctx.clone();
|
||||
let s_ctx = s_ctx.clone();
|
||||
async move {
|
||||
metric_value(&c_ctx, MetricName::TrafficControlBytesTx, "default") > 0
|
||||
&& metric_value(&c_ctx, MetricName::TrafficControlBytesRx, "default") > 0
|
||||
&& metric_value(&s_ctx, MetricName::TrafficControlBytesTx, "default") > 0
|
||||
&& metric_value(&s_ctx, MetricName::TrafficControlBytesRx, "default") > 0
|
||||
}
|
||||
},
|
||||
Duration::from_secs(5),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn peer_conn_pingpong_timeout_not_close() {
|
||||
peer_conn_pingpong_test_common(3, 5, false, false).await;
|
||||
|
||||
Reference in New Issue
Block a user