From 654b193f09a839e118ee69eb9915e14e57e7f817 Mon Sep 17 00:00:00 2001 From: icycrystal4 Date: Thu, 7 Mar 2024 19:23:38 +0800 Subject: [PATCH 01/24] =?UTF-8?q?=E7=BB=9F=E4=B8=80ring=5Fslice=E7=9A=84?= =?UTF-8?q?=E6=9C=80=E5=9F=BA=E7=A1=80=E7=9A=84=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ci.sh | 9 +- ds/src/lib.rs | 42 +---- ds/src/mem/buffer.rs | 17 -- ds/src/mem/bytes.rs | 44 +++-- ds/src/mem/mod.rs | 2 + ds/src/mem/ring_slice.rs | 292 ++++++++++++++++---------------- ds/src/mem/slice/mod.rs | 39 +++++ rt/src/stream/mod.rs | 5 + tests/src/benches/ring_slice.rs | 16 +- tests/src/ring_buffer.rs | 25 +-- tests/src/ring_slice.rs | 33 +++- 11 files changed, 284 insertions(+), 240 deletions(-) create mode 100644 ds/src/mem/slice/mod.rs diff --git a/ci.sh b/ci.sh index ab214f8b4..191266b10 100755 --- a/ci.sh +++ b/ci.sh @@ -2,15 +2,11 @@ brz_home="/data1/ci/breeze" mkdir -p $brz_home -docker ps -a | grep breeze_ci_mysql4623 && docker rm -f breeze_ci_mysql4623 -docker ps -a | grep breeze_ci_mysql4624 && docker rm -f breeze_ci_mysql4624 +docker stop breeze_ci_mysql4623 breeze_ci_mysql4624 breeze_github_ci 2>/dev/null + docker run --rm --name breeze_ci_mysql4623 -p 4623:3306 -d parabala/mysqlci_with_schema:v0.0.2 docker run --rm --name breeze_ci_mysql4624 -p 4624:3306 -d parabala/mysqlci_with_schema:v0.0.2 - container_name=breeze_github_ci -docker ps -a | grep "$container_name" && docker rm -f "$container_name" - - docker run --rm -d -v $brz_home:/data1/resource/breeze --net="host" --name "$container_name" parabala/breeze:githubci108 # rm -rf $brz_home/* @@ -75,3 +71,4 @@ RUST_BACKTRACE=1 cargo test -p tests_integration --features github_workflow kill -9 $pid +docker stop breeze_ci_mysql4623 breeze_ci_mysql4624 breeze_github_ci diff --git a/ds/src/lib.rs b/ds/src/lib.rs index 06af2878d..9a06b7ad5 100644 --- a/ds/src/lib.rs +++ b/ds/src/lib.rs @@ -13,7 +13,6 @@ pub use cow::*; pub use mem::*; pub use vec::Buffer; mod switcher; -//pub use queue::PinnedQueue; pub use switcher::Switcher; pub use utf8::*; pub use waker::AtomicWaker; @@ -25,49 +24,16 @@ mod asserts; mod bits; pub use bits::*; -use std::ptr::copy_nonoverlapping as copy; - pub trait BufWriter { fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()>; #[inline] - fn write_seg_all(&mut self, buf0: &[u8], buf1: &[u8]) -> std::io::Result<()> { - self.write_all(buf0)?; - self.write_all(buf1) - } -} - -impl BufWriter for Vec { - #[inline] - fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> { - self.reserve(buf.len()); - let len = self.len(); - unsafe { self.set_len(len + buf.len()) }; - (&mut self[len..]).write_all(buf)?; - Ok(()) + fn write_all_hint(&mut self, buf: &[u8], _next: bool) -> std::io::Result<()> { + self.write_all(buf) } #[inline] fn write_seg_all(&mut self, buf0: &[u8], buf1: &[u8]) -> std::io::Result<()> { - let reserve_len = buf0.len() + buf1.len(); - self.reserve(reserve_len); - let len = self.len(); - unsafe { self.set_len(len + reserve_len) }; - (&mut self[len..]).write_seg_all(buf0, buf1)?; - Ok(()) - } -} -impl BufWriter for [u8] { - #[inline(always)] - fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> { - debug_assert!(self.len() >= buf.len(), "{} >= {}", self.len(), buf.len()); - unsafe { copy(buf.as_ptr(), self.as_mut_ptr(), buf.len()) }; - Ok(()) - } - #[inline(always)] - fn write_seg_all(&mut self, buf0: &[u8], buf1: &[u8]) -> std::io::Result<()> { - debug_assert!(self.len() >= buf0.len() + buf1.len()); - unsafe { copy(buf0.as_ptr(), self.as_mut_ptr(), buf0.len()) }; - unsafe { copy(buf1.as_ptr(), self.as_mut_ptr().add(buf0.len()), buf1.len()) }; - Ok(()) + self.write_all(buf0)?; + self.write_all(buf1) } } diff --git a/ds/src/mem/buffer.rs b/ds/src/mem/buffer.rs index ef685baa9..eaeb83ac5 100644 --- a/ds/src/mem/buffer.rs +++ b/ds/src/mem/buffer.rs @@ -73,23 +73,6 @@ impl RingBuffer { self.advance_write(read); out } - //// 返回可写入的buffer。如果无法写入,则返回一个长度为0的slice - //#[inline] - //fn as_mut_bytes(&mut self) -> &mut [u8] { - // if self.read + self.size == self.write { - // // 已满 - // unsafe { from_raw_parts_mut(self.data.as_ptr(), 0) } - // } else { - // let offset = self.mask(self.write); - // let read = self.mask(self.read); - // let n = if offset < read { - // read - offset - // } else { - // self.size - offset - // }; - // unsafe { from_raw_parts_mut(self.data.as_ptr().offset(offset as isize), n) } - // } - //} #[inline] pub fn data(&self) -> RingSlice { RingSlice::from(self.data.as_ptr(), self.size, self.read, self.write) diff --git a/ds/src/mem/bytes.rs b/ds/src/mem/bytes.rs index 1d5634ad3..0385eda31 100644 --- a/ds/src/mem/bytes.rs +++ b/ds/src/mem/bytes.rs @@ -27,12 +27,24 @@ pub trait ByteOrder { fn f32_le(&self, oft: usize) -> f32; fn f64_le(&self, oft: usize) -> f64; } +pub trait Slicer { + fn len(&self) -> usize; + fn with_seg(&self, r: R, v: impl FnMut(&[u8], usize, bool) -> O) -> O; +} +pub trait Merge { + fn merge(self, other: impl FnMut() -> Self) -> Self; +} pub trait Range { - fn range(&self, slice: &RingSlice) -> (usize, usize); + #[inline(always)] + fn r_len(&self, s: &S) -> usize { + let r = self.range(s); + r.1 - r.0 + } + fn range(&self, s: &S) -> (usize, usize); #[inline] - fn start(&self, slice: &RingSlice) -> usize { - self.range(slice).0 + fn start(&self, s: &S) -> usize { + self.range(s).0 } } @@ -55,37 +67,37 @@ impl bool> Visit for T { type Offset = usize; impl Range for Offset { #[inline(always)] - fn range(&self, slice: &RingSlice) -> (usize, usize) { - debug_assert!(*self <= slice.len()); - (*self, slice.len()) + fn range(&self, s: &S) -> (usize, usize) { + debug_assert!(*self <= s.len()); + (*self, s.len()) } } impl Range for std::ops::Range { #[inline(always)] - fn range(&self, slice: &RingSlice) -> (usize, usize) { - debug_assert!(self.start <= slice.len()); - debug_assert!(self.end <= slice.len()); + fn range(&self, s: &S) -> (usize, usize) { + debug_assert!(self.start <= s.len()); + debug_assert!(self.end <= s.len()); (self.start, self.end) } } impl Range for std::ops::RangeFrom { #[inline(always)] - fn range(&self, slice: &RingSlice) -> (usize, usize) { - debug_assert!(self.start <= slice.len()); - (self.start, slice.len()) + fn range(&self, s: &S) -> (usize, usize) { + debug_assert!(self.start <= s.len()); + (self.start, s.len()) } } impl Range for std::ops::RangeTo { #[inline(always)] - fn range(&self, slice: &RingSlice) -> (usize, usize) { - debug_assert!(self.end <= slice.len()); + fn range(&self, s: &S) -> (usize, usize) { + debug_assert!(self.end <= s.len()); (0, self.end) } } impl Range for std::ops::RangeFull { #[inline(always)] - fn range(&self, slice: &RingSlice) -> (usize, usize) { - (0, slice.len()) + fn range(&self, s: &S) -> (usize, usize) { + (0, s.len()) } } diff --git a/ds/src/mem/mod.rs b/ds/src/mem/mod.rs index 911e60203..4c035a10a 100644 --- a/ds/src/mem/mod.rs +++ b/ds/src/mem/mod.rs @@ -4,6 +4,8 @@ pub use buffer::*; mod resized; pub use resized::*; +mod slice; + mod ring_slice; pub use ring_slice::*; diff --git a/ds/src/mem/ring_slice.rs b/ds/src/mem/ring_slice.rs index cba2a673d..6b36c9186 100644 --- a/ds/src/mem/ring_slice.rs +++ b/ds/src/mem/ring_slice.rs @@ -1,5 +1,6 @@ use std::{ fmt::{Debug, Display, Formatter}, + ptr::copy_nonoverlapping, slice::from_raw_parts, }; @@ -15,22 +16,6 @@ pub struct RingSlice { mask: u32, } -macro_rules! with_segment { - ($self:ident, $range:expr, $noseg:expr, $seg:expr) => {{ - let (oft, end) = $range.range($self); - debug_assert!(oft <= end && end <= $self.len()); - let len = end - oft; - let oft_start = $self.mask($self.start() + oft); - if oft_start + len <= $self.cap() { - unsafe { $noseg($self.ptr().add(oft_start), len) } - } else { - let seg1 = $self.cap() - oft_start; - let seg2 = len - seg1; - unsafe { $seg($self.ptr().add(oft_start), seg1, $self.ptr(), seg2) } - } - }}; -} - impl RingSlice { #[inline] pub fn empty() -> Self { @@ -75,24 +60,22 @@ impl RingSlice { } #[inline] pub fn str_num(&self, r: impl Range) -> usize { - let (start, end) = r.range(self); - let mut num = 0usize; - for i in start..end { - num = num.wrapping_mul(10) + (self[i] - b'0') as usize; - } - num + self.fold(r, 0, |num, b| { + *num = num.wrapping_mul(10) + (b - b'0') as usize + }) } + // 如果有一个非数字的字符,则返回None #[inline] pub fn try_str_num(&self, r: impl Range) -> Option { - let (start, end) = r.range(self); - let mut num = 0usize; - for i in start..end { - if !self[i].is_ascii_digit() { - return None; + let mut digit = true; + let num = self.fold_r(r, 0usize, |num, b| { + digit &= b.is_ascii_digit(); + if digit { + *num = num.wrapping_mul(10) + (b - b'0') as usize; } - num = num.wrapping_mul(10) + (self[i] - b'0') as usize; - } - Some(num) + !digit + }); + digit.then_some(num) } #[inline] @@ -106,39 +89,65 @@ impl RingSlice { mask: self.mask, } } + // 这个方法是最核心的方法,所有的方法都是基于这个方法实现的 + // F:ptr, len, oft, segment + #[inline] + fn _visit(&self, r: R, mut f: F) -> O + where + O: Merge, + R: Range, + F: FnMut(*const u8, usize, usize, bool) -> O, + { + let (oft, end) = r.range(self); + debug_assert!(oft <= end && end <= self.len()); + let len = end - oft; + let oft_start = self.mask as usize & (self.start() + oft); + let seg1 = self.cap() - oft_start; + if len <= seg1 { + unsafe { f(self.ptr().add(oft_start), len, oft, false) } + } else { + let o0 = unsafe { f(self.ptr().add(oft_start), seg1, oft, true) }; + let seg2 = len - seg1; + o0.merge(|| f(self.ptr(), seg2, self.cap() - self.start(), true)) + } + } + #[inline(always)] + fn _visit_data(&self, r: R, mut f: F) -> O + where + O: Merge, + R: Range, + F: FnMut(&[u8], usize, bool) -> O, + { + self._visit(r, |p, len, oft, seg| unsafe { + f(from_raw_parts(p, len), oft, seg) + }) + } #[inline(always)] pub fn visit(&self, mut f: impl FnMut(u8)) { - self.visit_seg(0, |p, l| { - for i in 0..l { + self._visit(0usize, |p, len, _oft, _seg| { + for i in 0..len { unsafe { f(*p.add(i)) }; } - }); + }) } #[inline(always)] pub fn visit_seg(&self, r: R, mut f: impl FnMut(*const u8, usize)) { - with_segment!(self, r, |p, l| f(p, l), |p0, l0, p1, l1| { - f(p0, l0); - f(p1, l1) - }) + self._visit(r, |p, len, _, _| f(p, len)) } #[inline(always)] pub fn visit_data(&self, r: impl Range, mut f: impl FnMut(&[u8])) { - with_segment!(self, r, |p, l| f(from_raw_parts(p, l)), |p0, l0, p1, l1| { - { - f(from_raw_parts(p0, l0)); - f(from_raw_parts(p1, l1)); - } - }) + self._visit(r, |p, len, _, _| f(unsafe { from_raw_parts(p, len) })) } #[inline(always)] pub fn data_r(&self, r: impl Range) -> (&[u8], &[u8]) { static EMPTY: &[u8] = &[]; - with_segment!( - self, - r, - |ptr, len| (from_raw_parts(ptr, len), EMPTY), - |p0, l0, p1, l1| (from_raw_parts(p0, l0), from_raw_parts(p1, l1)) - ) + let mut seg = [EMPTY, EMPTY]; + let mut idx = 0; + self._visit(r, |p, l, _idx, _seg| { + seg[idx] = unsafe { from_raw_parts(p, l) }; + idx += 1; + }); + (&seg[0], &seg[1]) } #[inline(always)] pub fn data(&self) -> (&[u8], &[u8]) { @@ -149,46 +158,45 @@ impl RingSlice { pub unsafe fn data_dump(&self) -> &[u8] { from_raw_parts(self.ptr(), self.cap()) } + // 遍历,直到v返回true时. #[inline(always)] - pub fn fold_r bool>( - &self, - r: R, - mut init: I, - mut v: V, - ) -> I { - macro_rules! visit { - ($p:expr, $l:expr) => {{ - for i in 0..$l { - if !v(&mut init, *$p.add(i)) { - return false; - } + pub fn fold_r bool>(&self, r: R, init: I, mut v: V) -> I { + let mut init = init; + self._visit(r, |p, l, _oft, _seg| { + for i in 0..l { + if !v(&mut init, unsafe { *p.add(i) }) { + continue; } - true - }}; - } - with_segment!( - self, - r, - |p: *mut u8, l| visit!(p, l), - |p0: *mut u8, l0, p1: *mut u8, l1| { visit!(p0, l0) && visit!(p1, l1) } - ); + // 说明v返回true,则终止遍历 + return true; + } + // 尝试下一个seg + false + }); init } + // 遍历所有的数据。 #[inline(always)] pub fn fold(&self, r: R, init: I, mut v: impl FnMut(&mut I, u8)) -> I { self.fold_r(r, init, |i, b| { v(i, b); - true + false }) } #[inline] pub fn copy_to(&self, r: R, w: &mut W) -> std::io::Result<()> { - with_segment!( - self, - r, - |p, l| w.write_all(from_raw_parts(p, l)), - |p0, l0, p1, l1| { w.write_seg_all(from_raw_parts(p0, l0), from_raw_parts(p1, l1)) } - ) + self._visit(r, |p, len, _, seg| { + w.write_all_hint(unsafe { from_raw_parts(p, len) }, seg) + }) + } + #[inline] + fn _copy_to>(&self, r: R, mut o: T) { + let out = o.as_mut(); + let mut idx = 0; + self._visit(r, |p, len, _, _| { + unsafe { copy_nonoverlapping(p, out.as_mut_ptr().add(idx), len) }; + idx += len; + }); } #[inline] pub fn copy_to_w(&self, r: R, w: &mut W) { @@ -197,24 +205,29 @@ impl RingSlice { } #[inline] pub fn copy_to_vec(&self, v: &mut Vec) { - self.copy_to_w(.., v); + self.copy_to_v(.., v); } #[inline] pub fn copy_to_vec_with_len(&self, v: &mut Vec, len: usize) { - self.copy_to_w(..len, v); + self.copy_to_v(..len, v); } #[inline] - pub fn copy_to_vec_r(&self, v: &mut Vec, r: R) { - self.copy_to_w(r, v); + pub fn copy_to_v(&self, r: R, v: &mut Vec) { + let rlen = r.r_len(self); + v.reserve(rlen); + let len = v.len(); + unsafe { v.set_len(len + rlen) }; + let out = &mut v[len..]; + self._copy_to(r, out); } /// copy 数据到切片/数组中,目前暂时不需要oft,有需求后再加 #[inline] pub fn copy_to_slice(&self, s: &mut [u8]) { - self.copy_to_w(.., s); + self._copy_to(0, s) } #[inline] - pub fn copy_to_r(&self, s: &mut [u8], r: R) { - self.copy_to_w(r, s); + pub fn copy_to_r(&self, r: R, s: &mut [u8]) { + self._copy_to(r, s); } #[inline(always)] pub(super) fn cap(&self) -> usize { @@ -222,6 +235,7 @@ impl RingSlice { } #[inline(always)] pub(super) fn start(&self) -> usize { + debug_assert!(self.start < self.cap || self.start == 0, "{self}"); self.start as usize } @@ -235,6 +249,12 @@ impl RingSlice { self.len as usize } #[inline(always)] + pub fn first(&self) -> u8 { + debug_assert!(self.len > 0); + debug_assert!(self.start < self.cap); + unsafe { *self.ptr() } + } + #[inline(always)] pub fn at(&self, idx: usize) -> u8 { self[idx] } @@ -251,15 +271,18 @@ impl RingSlice { pub fn find(&self, offset: usize, b: u8) -> Option { self.find_r(offset, b) } + // 找到满足f.check(b, idx)的第一个字节位置,返回idx #[inline] pub fn find_r(&self, r: impl Range, mut f: impl Visit) -> Option { - let (start, end) = r.range(self); - for i in start..end { - if f.check(self[i], i) { - return Some(i); + self._visit(r, |p, len, oft, _seg| { + for i in 0..len { + let idx = oft + i; + if f.check(unsafe { *p.add(i) }, idx) { + return Some(idx); + } } - } - None + None + }) } // 跳过num个'\r\n',返回下一个字节地址 #[inline] @@ -269,73 +292,47 @@ impl RingSlice { } Some(oft) } - // 查找是否存在 '\r\n' ,返回匹配的第一个字节地址 + // 查找是否存在 '\r\n' ,返回'\r' 的位置 #[inline] pub fn find_lf_cr(&self, offset: usize) -> Option { self.find_r(offset..self.len() - 1, |b, idx| { + debug_assert!(idx < self.len() - 1, "{offset} => {idx}, {self} "); b == b'\r' && self[idx + 1] == b'\n' }) } #[inline] pub fn equal(&self, other: &[u8]) -> bool { - if self.len() != other.len() { - return false; - } - for i in 0..self.len() { - if self[i] != other[i] { - return false; - } - } - return true; + self.eq(other) } /// 判断是否相同,忽略大小写 #[inline] pub fn equal_ignore_case(&self, other: &[u8]) -> bool { - if self.len() != other.len() { - return false; - } - for i in 0..self.len() { - // 指令一般是大写 - if self[i].to_ascii_uppercase() != other[i].to_ascii_uppercase() { - return false; - } - } - return true; + self.len() == other.len() && self.compare(0, other, |a, b| a.eq_ignore_ascii_case(b)) + } + #[inline] + fn compare(&self, r: impl Range, s: &[u8], eq: impl Fn(&[u8], &[u8]) -> bool) -> bool { + debug_assert!(r.r_len(self) == s.len()); + let mut cmp = s; + let mut matched = true; + self._visit_data(r, |data, _, _| { + let l = data.len().min(cmp.len()); + let d = &data[..l]; + matched &= eq(d, &cmp[..l]); + cmp = &cmp[l..]; + // 还有未比对完成的数据,且当前数据不是最后一个数据,则继续比对 + cmp.len() == 0 || !matched + }); + matched } #[inline] pub fn start_with(&self, oft: usize, s: &[u8]) -> bool { - if oft + s.len() <= self.len() { - with_segment!( - self, - oft..oft + s.len(), - |p, _l| { from_raw_parts(p, s.len()) == s }, - |p0, l0, p1, _l1| from_raw_parts(p0, l0) == &s[..l0] - && from_raw_parts(p1, s.len() - l0) == &s[l0..] - ) - } else { - false - } + oft + s.len() <= self.len() && self.compare(oft..oft + s.len(), s, |a, b| a == b) } #[inline] pub fn start_ignore_case(&self, oft: usize, s: &[u8]) -> bool { - if oft + s.len() <= self.len() { - with_segment!( - self, - oft, - |p, _l| { from_raw_parts(p, s.len()).eq_ignore_ascii_case(s) }, - |p0, l0, p1, _l1| { - if l0 < s.len() { - from_raw_parts(p0, l0).eq_ignore_ascii_case(&s[..l0]) - && from_raw_parts(p1, s.len() - l0).eq_ignore_ascii_case(&s[l0..]) - } else { - from_raw_parts(p0, s.len()).eq_ignore_ascii_case(s) - } - } - ) - } else { - false - } + oft + s.len() <= self.len() + && self.compare(oft..oft + s.len(), s, |a, b| a.eq_ignore_ascii_case(b)) } // 读取一个u16的数字,大端 @@ -348,11 +345,8 @@ impl RingSlice { /// 展示所有内容,仅用于长度比较小的场景 fishermen #[inline] pub fn as_string_lossy(&self) -> String { - if self.len() >= 512 { - log::warn!("as_string_lossy: data too long: {:?}", self); - } let mut vec = Vec::with_capacity(self.len()); - self.copy_to_w(0, &mut vec); + self.copy_to_v(0, &mut vec); String::from_utf8(vec).unwrap_or_default() } } @@ -408,7 +402,7 @@ impl std::ops::IndexMut for RingSlice { impl PartialEq<[u8]> for super::RingSlice { #[inline] fn eq(&self, other: &[u8]) -> bool { - self.len() == other.len() && self.start_with(0, other) + self.len() == other.len() && self.compare(0, other, |a, b| a == b) } } // 内容相等 @@ -419,3 +413,15 @@ impl PartialEq for super::RingSlice { self.len() == other.len() && self.start_with(0, f) && self.start_with(f.len(), s) } } + +use crate::{Merge, Slicer}; +impl Slicer for RingSlice { + #[inline(always)] + fn len(&self) -> usize { + self.len as usize + } + #[inline] + fn with_seg(&self, r: R, v: impl FnMut(&[u8], usize, bool) -> O) -> O { + self._visit_data(r, v) + } +} diff --git a/ds/src/mem/slice/mod.rs b/ds/src/mem/slice/mod.rs new file mode 100644 index 000000000..56b5bf745 --- /dev/null +++ b/ds/src/mem/slice/mod.rs @@ -0,0 +1,39 @@ +use crate::{Merge, Range as Ranger, Slicer}; +use std::ops::Deref; +impl> Slicer for T { + #[inline(always)] + fn len(&self) -> usize { + self.deref().len() + } + #[inline(always)] + fn with_seg(&self, r: R, mut v: impl FnMut(&[u8], usize, bool) -> O) -> O { + let (start, end) = r.range(self); + debug_assert!(start <= end && end <= self.len() as usize); + v(&*self, start, false) + } +} + +impl Merge for Option { + #[inline(always)] + fn merge(self, other: impl FnMut() -> Self) -> Self { + self.or_else(other) + } +} +impl Merge for bool { + #[inline(always)] + fn merge(self, mut other: impl FnMut() -> Self) -> Self { + self || other() + } +} +impl Merge for () { + #[inline(always)] + fn merge(self, mut other: impl FnMut() -> Self) -> Self { + other() + } +} +impl Merge for Result<(), E> { + #[inline(always)] + fn merge(self, mut other: impl FnMut() -> Self) -> Self { + self.and_then(|_| other()) + } +} diff --git a/rt/src/stream/mod.rs b/rt/src/stream/mod.rs index d10f1932c..7b2663db7 100644 --- a/rt/src/stream/mod.rs +++ b/rt/src/stream/mod.rs @@ -159,6 +159,11 @@ impl ds::BufWriter for Stream { Ok(()) } #[inline] + fn write_all_hint(&mut self, buf: &[u8], next: bool) -> std::io::Result<()> { + self.buf.enable |= next; + self.write_all(buf) + } + #[inline] fn write_seg_all(&mut self, buf0: &[u8], buf1: &[u8]) -> std::io::Result<()> { self.buf.enable = true; self.write_all(buf0)?; diff --git a/tests/src/benches/ring_slice.rs b/tests/src/benches/ring_slice.rs index 19b67a630..0f5d8b67b 100644 --- a/tests/src/benches/ring_slice.rs +++ b/tests/src/benches/ring_slice.rs @@ -11,6 +11,7 @@ pub(super) fn bench_iter(c: &mut Criterion) { let len = slice.len(); let mut group = c.benchmark_group("ring_slice_iter"); let rs = RingSlice::from(slice.as_ptr(), slice.len(), 0, len); + let seg = ds::RingSlicer::from(slice.as_ptr(), slice.len(), 0, len); group.bench_function("vec", |b| { b.iter(|| { black_box({ @@ -83,6 +84,15 @@ pub(super) fn bench_iter(c: &mut Criterion) { }); }); }); + group.bench_function("fold_seg", |b| { + b.iter(|| { + black_box({ + seg.fold(0, 0u64, |t, v| { + *t += v as u64; + }) + }); + }); + }); group.bench_function("fold", |b| { b.iter(|| { black_box({ @@ -133,7 +143,7 @@ pub(super) fn bench_read_num(c: &mut Criterion) { let mut t = 0u64; for i in 0..runs { let mut buf = [0u8; 8]; - rs.copy_to_r(&mut buf[..], i..i + 8); + rs.copy_to_r(i..i + 8, &mut buf[..]); t = t.wrapping_add(u64::from_le_bytes(buf)); } t @@ -157,7 +167,7 @@ pub(super) fn bench_read_num(c: &mut Criterion) { let mut t = 0u64; for i in 0..runs { let mut buf = [0u8; 8]; - rs.copy_to_r(&mut buf[..], i..i + 8); + rs.copy_to_r(i..i + 8, &mut buf[..]); t = t.wrapping_add(u64::from_be_bytes(buf)); } t @@ -226,7 +236,7 @@ pub(super) fn bench_copy(c: &mut Criterion) { b.iter(|| { black_box({ for i in 0..runs { - rs.copy_to_r(&mut dst[..], i..i + 64); + rs.copy_to_r(i..i + 64, &mut dst[..]); } }); }); diff --git a/tests/src/ring_buffer.rs b/tests/src/ring_buffer.rs index 813b4fdab..f15c3091e 100644 --- a/tests/src/ring_buffer.rs +++ b/tests/src/ring_buffer.rs @@ -38,6 +38,7 @@ fn ring_buffer_basic() { let n = buf.write(&rs); assert_eq!(buf.len(), rs.len()); assert_eq!(buf.len(), n); + println!("=============\n\n"); assert_eq!(&buf.data(), &data[..]); buf.consume(rs.len()); assert_eq!(buf.len(), 0); @@ -81,18 +82,18 @@ fn ring_buffer_basic() { } // 随机从reader读取数据 - let runs = 1000; - let mut reader = RandomReader(rnd_bytes(cap * 32)); - for i in 0..runs { - let writtened = buf.copy_from(&mut reader); - let len = writtened.len(); - let src = buf.data().slice(buf.len() - len, len); - assert_eq!(writtened, src, "{}-th", i); - // 随机消费n条数据 - let n = rand::random::() as usize % buf.len(); - buf.consume(n); - } - buf.consume(buf.len()); + //let runs = 1000; + //let mut reader = RandomReader(rnd_bytes(cap * 32)); + //for i in 0..runs { + // let writtened = buf.copy_from(&mut reader); + // let len = writtened.len(); + // let src = buf.data().slice(buf.len() - len, len); + // assert_eq!(writtened, src, "{}-th", i); + // // 随机消费n条数据 + // let n = rand::random::() as usize % buf.len(); + // buf.consume(n); + //} + //buf.consume(buf.len()); } #[test] diff --git a/tests/src/ring_slice.rs b/tests/src/ring_slice.rs index f984552a1..7e990f373 100644 --- a/tests/src/ring_slice.rs +++ b/tests/src/ring_slice.rs @@ -76,6 +76,17 @@ fn test_ring_slice() { let _ = unsafe { Vec::from_raw_parts(ptr, 0, cap) }; } +#[test] +fn find_lr_cr() { + const CAP: usize = 32; + let mut data = [0u8; CAP]; + use std::ptr::copy_nonoverlapping as copy; + let b = [43u8, 79, 75, 13, 10, 43, 79, 75, 13, 10]; + unsafe { copy(b.as_ptr(), data.as_mut_ptr(), b.len()) }; + let rs = RingSlice::from(data.as_ptr(), CAP, 5, 10); + let o = rs.find_lf_cr(0); + assert_eq!(o, Some(3)); +} #[test] fn test_read_number() { @@ -137,6 +148,18 @@ fn copy_to_vec() { slice.copy_to_vec(&mut data); assert_eq!(data, vec![0, 1, 2, 0, 1, 2]); } +#[test] +fn copy_to_vec_seg() { + let data = "0123"; + let slice = RingSlice::from(data.as_ptr(), 4, 2, 6); + + let mut out: Vec = Vec::new(); + slice.copy_to_vec(&mut out); + assert_eq!(&*out, "2301".as_bytes()); + + assert_eq!(&slice, &out[..]); + assert_ne!(&slice, "2300".as_bytes()); +} #[test] fn copy_to_slice() { @@ -144,7 +167,7 @@ fn copy_to_slice() { let slice = RingSlice::from_vec(&data); let mut slice_short = [0_u8; 2]; - slice.copy_to_w(0..2, &mut slice_short[..]); + slice.copy_to_r(0..2, &mut slice_short[..]); assert_eq!(slice_short, [0, 1]); let mut slice_long = [0_u8; 6]; @@ -157,7 +180,6 @@ fn copy_to_slice() { let ptr = raw.as_ptr(); let mut rng = rand::thread_rng(); let mut dst = Vec::with_capacity(cap); - unsafe { dst.set_len(cap) }; for _i in 0..100 { let (start, end) = match rng.gen_range(0..10) { 0 => (0, cap), @@ -194,8 +216,9 @@ fn copy_to_slice() { (r_start, r_len) } }; - rs.copy_to_r(&mut dst, r_start..r_start + r_len); + rs.copy_to_v(r_start..r_start + r_len, &mut dst); assert_eq!(&dst[0..r_len], &slice[r_start..r_start + r_len]); + dst.clear(); } } } @@ -329,7 +352,7 @@ fn fold() { if ascii { *acc = acc.wrapping_mul(10).wrapping_add((v - b'0') as u64); } - ascii + !ascii }); assert_eq!(num, 12345678); let start = data.len() - 1; // '9' @@ -340,7 +363,7 @@ fn fold() { if ascii { *acc = acc.wrapping_mul(10).wrapping_add((v - b'0') as u64); } - ascii + !ascii }); assert_eq!(num, 912345678); } From 6c1c5a70c2b51a0afe0d093d2813eff507e88438 Mon Sep 17 00:00:00 2001 From: icycrystal4 Date: Thu, 7 Mar 2024 19:36:02 +0800 Subject: [PATCH 02/24] =?UTF-8?q?log:=20=E6=B6=88=E9=99=A4=E9=83=A8?= =?UTF-8?q?=E5=88=86=E7=BC=96=E8=AF=91=E8=AD=A6=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- log/src/disable.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/log/src/disable.rs b/log/src/disable.rs index 5895d8404..ef42f42e3 100644 --- a/log/src/disable.rs +++ b/log/src/disable.rs @@ -27,7 +27,7 @@ macro_rules! info { } #[macro_export] -macro_rules! _warn { +macro_rules! warn { ($($arg:tt)+) => { log::noop!($($arg)+) }; @@ -50,7 +50,6 @@ macro_rules! log_enabled { false }; } -pub use {_warn as warn, debug, error, fatal, info, trace}; use std::io::Write; pub fn init(path: &str, _l: &str) -> std::io::Result<()> { From da1628c8760f62588d09ece372e53965605dd5e9 Mon Sep 17 00:00:00 2001 From: icycrystal4 Date: Fri, 8 Mar 2024 10:57:27 +0800 Subject: [PATCH 03/24] =?UTF-8?q?ephemera=20vec:=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E6=A0=87=E5=87=86=E7=9A=84vec=E5=86=85=E5=AD=98=E5=88=86?= =?UTF-8?q?=E9=85=8D=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ds/src/mem/arena/mod.rs | 3 + ds/src/mem/arena/vec.rs | 132 ++++++++++++++++++++++++++ ds/src/vec.rs | 62 ------------ ds/src/vec/ephemera.rs | 95 +++++++++++++++++++ ds/src/vec/mod.rs | 54 +++++++++++ tests/src/all.rs | 2 +- tests/src/mem.rs | 204 ++++++++++++---------------------------- 7 files changed, 343 insertions(+), 209 deletions(-) create mode 100644 ds/src/mem/arena/vec.rs delete mode 100644 ds/src/vec.rs create mode 100644 ds/src/vec/ephemera.rs create mode 100644 ds/src/vec/mod.rs diff --git a/ds/src/mem/arena/mod.rs b/ds/src/mem/arena/mod.rs index 7ad95aa7e..ba4814312 100644 --- a/ds/src/mem/arena/mod.rs +++ b/ds/src/mem/arena/mod.rs @@ -4,6 +4,9 @@ pub use ephemera::*; mod cache; pub use cache::*; +mod vec; +pub(crate) use vec::*; + use std::ptr::NonNull; pub trait Allocator { fn alloc(&self, t: T) -> NonNull; diff --git a/ds/src/mem/arena/vec.rs b/ds/src/mem/arena/vec.rs new file mode 100644 index 000000000..0ee5cbb63 --- /dev/null +++ b/ds/src/mem/arena/vec.rs @@ -0,0 +1,132 @@ +use crate::BrzMalloc; + +use std::alloc::{GlobalAlloc, Layout}; +use std::sync::atomic::{fence, AtomicU8, AtomicUsize, Ordering::*}; +unsafe impl Sync for CacheArena {} +#[repr(align(64))] +pub(crate) struct CacheArena { + ptr: *mut u8, + // 指向当前访问的chunk + idx: AtomicU8, + // 1. 低32位目前已经分配的内存大小(指向的位置) + // 2. 高32位用来控制释放。是释放的数量,释放的数量到0时,说明当前的arena可以重新分配内存; + chunks: [Chunk; 2], +} +const ALIGN: usize = 64; +impl CacheArena { + pub(crate) fn new() -> Self { + let cap = CAP; + // 按页对齐,分配内存 + debug_assert!(cap.is_power_of_two() && cap & 4095 == 0); + let layout = Layout::array::(cap).unwrap(); + let ptr = unsafe { BrzMalloc.alloc(layout) }; + let each = cap / 2; + Self { + ptr, + idx: AtomicU8::new(0), + chunks: [Chunk::new(ptr, each), Chunk::new(ptr, each)], + } + } + #[inline(always)] + const fn cap(&self) -> usize { + CAP + } + #[inline] + fn layout(&self, size: usize) -> Layout { + unsafe { Layout::from_size_align_unchecked(size, ALIGN) } + } + #[inline(always)] + const fn max_alloc(&self) -> usize { + self.cap() / 32 + } + #[inline] + pub(crate) fn alloc(&self, size: usize) -> (*mut u8, usize) { + let size = (size + ALIGN - 1) & !(ALIGN - 1); + if size <= self.max_alloc() { + let idx = self.idx.load(Relaxed); + let chunk = &self.chunks[idx as usize]; + if let Some(p) = chunk.alloc(size).or_else(|| { + let idx = 1 - idx; + self.idx.store(idx, Relaxed); + self.get(idx as usize).alloc(size) + }) { + return p; + } + } + // 从堆上分配 + unsafe { (BrzMalloc.alloc(self.layout(size)), size) } + } + #[inline(always)] + fn get(&self, idx: usize) -> &Chunk { + debug_assert!(idx <= 1); + unsafe { &self.chunks.get_unchecked(idx) } + } + #[inline] + pub(crate) fn dealloc(&self, ptr: *mut u8, size: usize) { + debug_assert!(size & (ALIGN - 1) == 0); + if ptr >= self.ptr && ptr < unsafe { self.ptr.add(self.cap()) } { + let idx = ((ptr as usize - self.ptr as usize) >= self.cap()) as usize; + debug_assert!(idx <= 1); + self.get(idx).dealloc(ptr); + } else { + unsafe { BrzMalloc.dealloc(ptr, self.layout(size)) }; + } + } +} + +struct Chunk { + ptr: *mut u8, + alloc_oft: AtomicUsize, + cap: usize, +} + +impl Chunk { + fn new(ptr: *mut u8, cap: usize) -> Self { + Self { + ptr, + cap, + alloc_oft: AtomicUsize::new(0), + } + } + // 按64字节对齐 + #[inline(always)] + fn alloc(&self, size: usize) -> Option<(*mut u8, usize)> { + debug_assert!(size & (ALIGN - 1) == 0); + let oft = self.incr_size(size); + if oft + size <= self.cap { + Some((unsafe { self.ptr.add(oft) }, size)) + } else { + self.free_one(); + None + } + } + #[inline(always)] + fn incr_size(&self, size: usize) -> usize { + debug_assert!(size + self.cap < u32::MAX as usize); + let v = size + (1 << 32); + let alloc_oft = self.alloc_oft.fetch_add(v, Relaxed); + // 低32位为分配的偏移量,高32位为分配次数 + alloc_oft as u32 as usize + } + // 释放的时候统一释放 + #[inline(always)] + fn free_one(&self) { + let alloc_oft = self.alloc_oft.fetch_sub(1 << 32, Release); + if (alloc_oft >> 32) != 1 { + return; + } + fence(Acquire); + self.alloc_oft.store(0, Release); + } + #[inline(always)] + fn dealloc(&self, ptr: *mut u8) { + debug_assert!(ptr >= self.ptr && ptr < unsafe { self.ptr.add(self.cap) }); + self.free_one(); + } +} +// Drop +impl Drop for CacheArena { + fn drop(&mut self) { + unsafe { BrzMalloc.dealloc(self.ptr, Layout::array::(self.cap()).unwrap()) }; + } +} diff --git a/ds/src/vec.rs b/ds/src/vec.rs deleted file mode 100644 index e8a279442..000000000 --- a/ds/src/vec.rs +++ /dev/null @@ -1,62 +0,0 @@ -macro_rules! define_read_number { - ($($fn_name:ident, $type_name:tt);+) => { - pub trait Buffer { - fn write>(&mut self, data: D); - $( - fn $fn_name(&mut self, num:$type_name); - )+ - fn write_slice(&mut self, slice:&crate::RingSlice); - } - - impl Buffer for Vec { - #[inline] - fn write>(&mut self, data: D) { - let b = data.as_ref(); - use std::ptr::copy_nonoverlapping as copy; - self.reserve(b.len()); - unsafe { - copy( - b.as_ptr() as *const u8, - self.as_mut_ptr().offset(self.len() as isize), - b.len(), - ); - self.set_len(self.len() + b.len()); - } - } - #[inline] - fn write_slice(&mut self, data:&crate::RingSlice) { - data.copy_to_vec(self); - } - $( - #[inline] - fn $fn_name(&mut self, num: $type_name) { - self.write(num.to_be_bytes()); - } - )+ - } - }; -} - -// big endian -define_read_number!( - // 备注:write_u8 可以直接用push代替 - write_u16, u16; - write_u32, u32; - write_u64, u64 -); - -pub trait Add { - fn add(&mut self, t: T); -} - -impl Add for Vec -where - T: std::cmp::PartialEq, -{ - #[inline] - fn add(&mut self, e: T) { - if !self.contains(&e) { - self.push(e); - } - } -} diff --git a/ds/src/vec/ephemera.rs b/ds/src/vec/ephemera.rs new file mode 100644 index 000000000..5daff7e74 --- /dev/null +++ b/ds/src/vec/ephemera.rs @@ -0,0 +1,95 @@ +use std::ptr::copy_nonoverlapping; + +use crate::{arena::CacheArena, Buffer}; + +// 用来分配快速释放的Vec +// 1. cap大小固定, 不支持动态扩展; +// 2. 内存会优先从cache中分配,如果cache中没有足够的内存,会从堆中分配; +pub struct EphemeralVec { + ptr: *mut u8, + len: u32, + cap: u32, +} +impl From> for EphemeralVec { + #[inline] + fn from(vec: Vec) -> Self { + assert!(vec.capacity() < u32::MAX as usize); + let mut v = Self::fix_cap(vec.len()); + v.write(vec.as_slice()); + v + } +} + +impl EphemeralVec { + pub fn fix_cap(cap: usize) -> Self { + let (ptr, cap) = VEC_CACHE_ARENA.alloc(cap); + assert!(cap < u32::MAX as usize); + Self { + ptr, + len: 0, + cap: cap as u32, + } + } + #[inline(always)] + pub fn into_raw_parts(self) -> (*mut u8, usize, usize) { + let me = std::mem::ManuallyDrop::new(self); + (me.ptr, me.len as usize, me.cap as usize) + } + #[inline(always)] + pub fn from_raw_parts(ptr: *mut u8, len: usize, cap: usize) -> Self { + assert!(len <= cap && cap < u32::MAX as usize); + Self { + ptr, + len: len as u32, + cap: cap as u32, + } + } + #[inline(always)] + pub fn push(&mut self, val: u8) { + assert!(self.len < self.cap); + unsafe { *self.ptr.add(self.len as usize) = val }; + self.len += 1; + } + #[inline(always)] + pub fn len(&self) -> usize { + self.len as usize + } + #[inline(always)] + pub fn cap(&self) -> usize { + self.cap as usize + } +} +// 实现Deref<[u8]> +use std::ops::{Deref, DerefMut}; +impl Deref for EphemeralVec { + type Target = [u8]; + #[inline(always)] + fn deref(&self) -> &Self::Target { + unsafe { std::slice::from_raw_parts(self.ptr, self.len as usize) } + } +} +impl DerefMut for EphemeralVec { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { std::slice::from_raw_parts_mut(self.ptr, self.len as usize) } + } +} +impl Buffer for EphemeralVec { + #[inline(always)] + fn write>(&mut self, d: D) { + let data = d.as_ref(); + assert!(self.len() + data.len() <= self.cap()); + unsafe { copy_nonoverlapping(data.as_ptr(), self.ptr.add(self.len()), data.len()) }; + self.len += data.len() as u32; + } +} +// 实现Drop +// 当EphemeralVec被drop时,会自动释放内存 +impl Drop for EphemeralVec { + #[inline(always)] + fn drop(&mut self) { + VEC_CACHE_ARENA.dealloc(self.ptr, self.cap as usize); + } +} +#[ctor::ctor] +static VEC_CACHE_ARENA: CacheArena = CacheArena::new(); diff --git a/ds/src/vec/mod.rs b/ds/src/vec/mod.rs new file mode 100644 index 000000000..2d73ac857 --- /dev/null +++ b/ds/src/vec/mod.rs @@ -0,0 +1,54 @@ +mod ephemera; +pub use ephemera::*; + +pub trait Buffer: Sized { + fn write>(&mut self, data: D); + #[inline(always)] + fn write_u16(&mut self, num: u16) { + self.write(num.to_be_bytes()); + } + #[inline(always)] + fn write_u32(&mut self, num: u32) { + self.write(num.to_be_bytes()); + } + #[inline(always)] + fn write_u64(&mut self, num: u64) { + self.write(num.to_be_bytes()); + } + #[inline(always)] + fn write_slice(&mut self, slice: &crate::RingSlice) { + slice.copy_to_vec(self); + } +} +impl Buffer for Vec { + #[inline] + fn write>(&mut self, data: D) { + let b = data.as_ref(); + use std::ptr::copy_nonoverlapping as copy; + self.reserve(b.len()); + unsafe { + copy( + b.as_ptr() as *const u8, + self.as_mut_ptr().add(self.len()), + b.len(), + ); + self.set_len(self.len() + b.len()); + } + } +} + +pub trait Add { + fn add(&mut self, t: T); +} + +impl Add for Vec +where + T: std::cmp::PartialEq, +{ + #[inline] + fn add(&mut self, e: T) { + if !self.contains(&e) { + self.push(e); + } + } +} diff --git a/tests/src/all.rs b/tests/src/all.rs index 2331664f1..8318a357f 100644 --- a/tests/src/all.rs +++ b/tests/src/all.rs @@ -3,7 +3,7 @@ mod distribute; // mod hash_test; mod shard_test; //mod memcached_text; -//mod mem; +mod mem; mod protocols; //mod queue; // mod redis; diff --git a/tests/src/mem.rs b/tests/src/mem.rs index 959725256..dc14713fc 100644 --- a/tests/src/mem.rs +++ b/tests/src/mem.rs @@ -1,156 +1,68 @@ -use ds::{RingBuffer, RingSlice}; - -fn rnd_bytes(size: usize) -> Vec { - let data: Vec = (0..size).map(|_| rand::random::()).collect(); - data -} +use ds::EphemeralVec; #[test] -fn ring_buffer() { - let cap = 32; - let data = rnd_bytes(cap); - let rs = RingSlice::from(data.as_ptr(), cap, 0, 17); - let mut buf = RingBuffer::with_capacity(cap); - buf.write(&rs); - assert_eq!(buf.len(), 17); - assert!(&(buf.data()) == &data[0..17]); - let rs = RingSlice::from(data.as_ptr(), cap, 32, 32 + 32); - buf.advance_read(buf.len()); - assert_eq!(buf.len(), 0); - let n = buf.write(&rs); - assert_eq!(buf.len(), n); - assert_eq!(&buf.data(), &data[..]); - buf.advance_read(rs.len()); - assert_eq!(buf.len(), 0); - // 有折返的 - let rs = RingSlice::from(data.as_ptr(), cap, 27, 27 + 19); - assert_eq!(rs.len(), 19); - let n = buf.write(&rs); - assert_eq!(n, rs.len()); - assert_eq!(buf.len(), rs.len()); - assert_eq!(buf.len(), 19); - assert_eq!(buf.data(), rs); - let rs = RingSlice::from(data.as_ptr(), cap, 32, 32 + 32); - let n = buf.write(&rs); - assert_eq!(n, 32 - 19); - - let mut rrb = ds::ResizedRingBuffer::from(256, 4 * 1024, 1024); - assert_eq!(1024, rrb.cap()); - assert_eq!(0, rrb.len()); - - // 一次写满 - let buf = rrb.as_mut_bytes(); - assert_eq!(buf.len(), 1024); - assert_eq!(rrb.len(), 0); - assert_eq!(rrb.cap(), 1024); - rrb.advance_write(1024); - assert_eq!(rrb.len(), 1024); - assert_eq!(rrb.cap(), 1024); - - // 没有了,触发扩容 - let buf = rrb.as_mut_bytes(); - assert_eq!(buf.len(), 1024); - assert_eq!(rrb.cap(), 1024 * 2); - assert_eq!(rrb.len(), 1024); - - rrb.advance_read(1024); - - rrb.advance_write(1024); - let buf = rrb.as_mut_bytes(); - assert_eq!(buf.len(), 1024); - rrb.advance_write(1024); +fn ephemera_vec() { + use ds::Buffer; + let mut v = EphemeralVec::fix_cap(1024); + v.write(b"abcdefg"); + assert_eq!(v.len(), 7); + assert_eq!(&*v, b"abcdefg"); + v.write(b"hijklmnop"); + assert_eq!(v.len(), 16); + assert_eq!(&*v, b"abcdefghijklmnop"); - // 等待10ms。(默认是4ms) - std::thread::sleep(ds::time::Duration::from_millis(10)); - let buf = rrb.as_mut_bytes(); - assert_eq!(buf.len(), 1024); - rrb.advance_write(1024); - let buf = rrb.as_mut_bytes(); - assert_eq!(buf.len(), 1024); + drop(v); - // 缩容 - assert_eq!(rrb.cap(), 4 * 1024); - rrb.advance_read(2 * 1024); - //rrb.resize(2 * 1024); - //assert_eq!(rrb.cap(), 2 * 1024); -} - -// 随机生成器,生成的内存从a-z, A-Z, 0-9 循环。 -struct Reader { - num: usize, - source: Vec, - offset: usize, -} -impl ds::BuffRead for Reader { - type Out = usize; - fn read(&mut self, b: &mut [u8]) -> (usize, Self::Out) { - assert!(self.num > 0); - let mut w = 0; - while w < self.num { - let oft = self.offset % self.source.len(); - let l = b.len().min(self.num - w); - use std::ptr::copy_nonoverlapping as copy; - unsafe { copy(self.source.as_ptr().offset(oft as isize), b.as_mut_ptr(), l) }; - w += l; - self.offset += l; - } - (w, w) + // 单线程顺序分配,立即释放 + let mut n = 0; + // 20M数据。Cache大小是8M,可以循环分配 + use rand::{Rng, RngCore}; + let rnd = &mut rand::thread_rng(); + while n < 16 << 20 { + let len = rnd.gen_range(1..16 * 1024); + let mut v = EphemeralVec::fix_cap(len); + // 随机填充 + let mut buf = vec![0; len]; + rnd.fill_bytes(&mut buf[..]); + v.write(&buf); + assert_eq!(v.len(), len); + assert_eq!(&*v, &buf[..]); + n += len; } } #[test] -fn guarded_buffer() { - // 测试MemGuard - //let data: Vec = "abcdefg".into(); - //let s: &[u8] = &data; - //let slice: RingSlice = s.into(); - //let g0: MemGuard = slice.into(); - //assert_eq!(g0.read(0), &data); - //// 指向同一块内存 - //assert_eq!(g0.read(0).as_ptr() as usize, data.as_ptr() as usize); - //g0.recall(); - //// 内存回收,共享内存被释放。数据被复制,指向不同的内存。 - //assert_ne!(g0.read(0).as_ptr() as usize, data.as_ptr() as usize); - //// 但是数据一致 - //assert_eq!(g0.read(0), &data); - ////assert_eq!(g0.len(), data.len()); - //drop(data); +#[ignore] +fn test_ephemera_vec_thread() { + let secs = 3600; + let threads = 4; + let ths: Vec<_> = (0..threads) + .map(|_| { + std::thread::spawn(move || { + let start = std::time::Instant::now(); + use ds::Buffer; + use rand::{Rng, RngCore}; + let rnd = &mut rand::thread_rng(); + // 创建1..10个随机大小的vec + while start.elapsed().as_secs() < secs { + let num = rnd.gen_range(1..10); + let mut vecs = Vec::with_capacity(num); + for _i in 0..num { + let len = rnd.gen_range(1..64 * 1024); + let mut v = EphemeralVec::fix_cap(len); + let mut buf = vec![0; len]; + rnd.fill_bytes(&mut buf); + v.write(&buf); + assert_eq!(v.len(), len); + assert_eq!(&*v, &buf); + vecs.push(v); + } + } + }) + }) + .collect(); - let mut reader = Reader { - offset: 0, - source: Vec::from("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"), - num: 0, - }; - use ds::GuardedBuffer; - let mut guard = GuardedBuffer::new(128, 1024, 128); - let empty = guard.read(); - assert_eq!(empty.len(), 0); - reader.num = 24; - let n = guard.write(&mut reader); - assert_eq!(n, guard.len()); - assert_eq!(n, reader.num); - - let data = guard.read(); - assert_eq!(n, data.len()); - assert_eq!(data, reader.source[0..n]); - let len_g0 = 24; - let g0 = guard.take(len_g0); - assert_eq!(g0.len(), len_g0); - assert_eq!(guard.len(), n - len_g0); - let data = guard.read(); - assert_eq!(n - len_g0, data.len()); - //g0.read(0); - reader.num = 17; - guard.write(&mut reader); - let g1 = guard.take(10); - let g2 = guard.take(3); - let g3 = guard.take(3); - drop(g2); - drop(g1); - reader.num = 1; - guard.write(&mut reader); - drop(g3); - - drop(g0); - guard.gc(); + for th in ths { + th.join().expect("should not panic"); + } } From dcd18dad1ebc416aadcadf8a341ccab2dc003a18 Mon Sep 17 00:00:00 2001 From: icycrystal4 Date: Fri, 8 Mar 2024 14:34:22 +0800 Subject: [PATCH 04/24] =?UTF-8?q?ephemera=20vec:=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E6=A0=87=E5=87=86=E7=9A=84vec=E5=86=85=E5=AD=98=E5=88=86?= =?UTF-8?q?=E9=85=8D=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ds/src/io/mod.rs | 2 + ds/src/io/order.rs | 29 +++++++++++ ds/src/lib.rs | 8 ++- ds/src/mem/bytes.rs | 103 --------------------------------------- ds/src/mem/mod.rs | 4 +- ds/src/mem/ring_slice.rs | 8 +-- ds/src/mem/slice/mod.rs | 77 ++++++++++++++++++++++++++++- ds/src/vec/mod.rs | 9 +++- 8 files changed, 125 insertions(+), 115 deletions(-) create mode 100644 ds/src/io/mod.rs create mode 100644 ds/src/io/order.rs delete mode 100644 ds/src/mem/bytes.rs diff --git a/ds/src/io/mod.rs b/ds/src/io/mod.rs new file mode 100644 index 000000000..dfe906f25 --- /dev/null +++ b/ds/src/io/mod.rs @@ -0,0 +1,2 @@ +mod order; +pub use order::*; diff --git a/ds/src/io/order.rs b/ds/src/io/order.rs new file mode 100644 index 000000000..66fc50f44 --- /dev/null +++ b/ds/src/io/order.rs @@ -0,0 +1,29 @@ +use crate::RingSlice; +use procs::impl_number_ringslice; +// 如果方法名中没有包含be或者le,则默认为be +#[impl_number_ringslice(default = "be")] +pub trait ByteOrder { + fn i8(&self, oft: usize) -> i8; + fn u8(&self, oft: usize) -> u8; + fn u16_le(&self, oft: usize) -> u16; + fn i16_le(&self, oft: usize) -> i16; + fn u24_le(&self, oft: usize) -> u32; + fn i24_le(&self, oft: usize) -> i32; + fn u32_le(&self, oft: usize) -> u32; + fn i32_le(&self, oft: usize) -> i32; + fn u40_le(&self, oft: usize) -> u64; + fn i40_le(&self, oft: usize) -> i64; + fn u48_le(&self, oft: usize) -> u64; + fn i48_le(&self, oft: usize) -> i64; + fn u56_le(&self, oft: usize) -> u64; + fn i56_le(&self, oft: usize) -> i64; + fn u64_le(&self, oft: usize) -> u64; + fn i64_le(&self, oft: usize) -> i64; + + fn u16_be(&self, oft: usize) -> u16; + fn i24_be(&self, oft: usize) -> i32; + fn u32_be(&self, oft: usize) -> u32; + fn u64_be(&self, oft: usize) -> u64; + fn f32_le(&self, oft: usize) -> f32; + fn f64_le(&self, oft: usize) -> f64; +} diff --git a/ds/src/lib.rs b/ds/src/lib.rs index 9a06b7ad5..2177a1671 100644 --- a/ds/src/lib.rs +++ b/ds/src/lib.rs @@ -6,12 +6,13 @@ mod mem; //pub mod queue; pub mod rand; pub mod utf8; -pub mod vec; mod waker; +pub mod vec; +pub use vec::*; + pub use cow::*; pub use mem::*; -pub use vec::Buffer; mod switcher; pub use switcher::Switcher; pub use utf8::*; @@ -19,6 +20,9 @@ pub use waker::AtomicWaker; pub mod time; +mod io; +pub use io::*; + mod asserts; mod bits; diff --git a/ds/src/mem/bytes.rs b/ds/src/mem/bytes.rs deleted file mode 100644 index 0385eda31..000000000 --- a/ds/src/mem/bytes.rs +++ /dev/null @@ -1,103 +0,0 @@ -use crate::RingSlice; -use procs::impl_number_ringslice; -// 如果方法名中没有包含be或者le,则默认为be -#[impl_number_ringslice(default = "be")] -pub trait ByteOrder { - fn i8(&self, oft: usize) -> i8; - fn u8(&self, oft: usize) -> u8; - fn u16_le(&self, oft: usize) -> u16; - fn i16_le(&self, oft: usize) -> i16; - fn u24_le(&self, oft: usize) -> u32; - fn i24_le(&self, oft: usize) -> i32; - fn u32_le(&self, oft: usize) -> u32; - fn i32_le(&self, oft: usize) -> i32; - fn u40_le(&self, oft: usize) -> u64; - fn i40_le(&self, oft: usize) -> i64; - fn u48_le(&self, oft: usize) -> u64; - fn i48_le(&self, oft: usize) -> i64; - fn u56_le(&self, oft: usize) -> u64; - fn i56_le(&self, oft: usize) -> i64; - fn u64_le(&self, oft: usize) -> u64; - fn i64_le(&self, oft: usize) -> i64; - - fn u16_be(&self, oft: usize) -> u16; - fn i24_be(&self, oft: usize) -> i32; - fn u32_be(&self, oft: usize) -> u32; - fn u64_be(&self, oft: usize) -> u64; - fn f32_le(&self, oft: usize) -> f32; - fn f64_le(&self, oft: usize) -> f64; -} -pub trait Slicer { - fn len(&self) -> usize; - fn with_seg(&self, r: R, v: impl FnMut(&[u8], usize, bool) -> O) -> O; -} -pub trait Merge { - fn merge(self, other: impl FnMut() -> Self) -> Self; -} - -pub trait Range { - #[inline(always)] - fn r_len(&self, s: &S) -> usize { - let r = self.range(s); - r.1 - r.0 - } - fn range(&self, s: &S) -> (usize, usize); - #[inline] - fn start(&self, s: &S) -> usize { - self.range(s).0 - } -} - -pub trait Visit { - fn check(&mut self, b: u8, idx: usize) -> bool; -} -impl Visit for u8 { - #[inline(always)] - fn check(&mut self, b: u8, _idx: usize) -> bool { - *self == b - } -} -impl bool> Visit for T { - #[inline(always)] - fn check(&mut self, b: u8, idx: usize) -> bool { - self(b, idx) - } -} - -type Offset = usize; -impl Range for Offset { - #[inline(always)] - fn range(&self, s: &S) -> (usize, usize) { - debug_assert!(*self <= s.len()); - (*self, s.len()) - } -} - -impl Range for std::ops::Range { - #[inline(always)] - fn range(&self, s: &S) -> (usize, usize) { - debug_assert!(self.start <= s.len()); - debug_assert!(self.end <= s.len()); - (self.start, self.end) - } -} -impl Range for std::ops::RangeFrom { - #[inline(always)] - fn range(&self, s: &S) -> (usize, usize) { - debug_assert!(self.start <= s.len()); - (self.start, s.len()) - } -} -impl Range for std::ops::RangeTo { - #[inline(always)] - fn range(&self, s: &S) -> (usize, usize) { - debug_assert!(self.end <= s.len()); - (0, self.end) - } -} -impl Range for std::ops::RangeFull { - #[inline(always)] - fn range(&self, s: &S) -> (usize, usize) { - (0, s.len()) - } -} diff --git a/ds/src/mem/mod.rs b/ds/src/mem/mod.rs index 4c035a10a..e6416c783 100644 --- a/ds/src/mem/mod.rs +++ b/ds/src/mem/mod.rs @@ -5,6 +5,7 @@ mod resized; pub use resized::*; mod slice; +pub use slice::*; mod ring_slice; pub use ring_slice::*; @@ -20,9 +21,6 @@ pub use malloc::*; pub mod arena; -mod bytes; -pub use self::bytes::*; - use std::sync::atomic::{AtomicI64, Ordering::Relaxed}; pub static BUF_TX: Buffers = Buffers::new(); pub static BUF_RX: Buffers = Buffers::new(); diff --git a/ds/src/mem/ring_slice.rs b/ds/src/mem/ring_slice.rs index 6b36c9186..2c2c5b28c 100644 --- a/ds/src/mem/ring_slice.rs +++ b/ds/src/mem/ring_slice.rs @@ -230,17 +230,17 @@ impl RingSlice { self._copy_to(r, s); } #[inline(always)] - pub(super) fn cap(&self) -> usize { + pub(crate) fn cap(&self) -> usize { self.cap as usize } #[inline(always)] - pub(super) fn start(&self) -> usize { + pub(crate) fn start(&self) -> usize { debug_assert!(self.start < self.cap || self.start == 0, "{self}"); self.start as usize } #[inline(always)] - pub(super) fn mask(&self, oft: usize) -> usize { + pub(crate) fn mask(&self, oft: usize) -> usize { (self.mask & oft as u32) as usize } @@ -263,7 +263,7 @@ impl RingSlice { self[idx] = b; } #[inline(always)] - pub(super) fn ptr(&self) -> *mut u8 { + pub(crate) fn ptr(&self) -> *mut u8 { self.ptr as *mut u8 } diff --git a/ds/src/mem/slice/mod.rs b/ds/src/mem/slice/mod.rs index 56b5bf745..e6f31a81c 100644 --- a/ds/src/mem/slice/mod.rs +++ b/ds/src/mem/slice/mod.rs @@ -1,4 +1,3 @@ -use crate::{Merge, Range as Ranger, Slicer}; use std::ops::Deref; impl> Slicer for T { #[inline(always)] @@ -6,7 +5,7 @@ impl> Slicer for T { self.deref().len() } #[inline(always)] - fn with_seg(&self, r: R, mut v: impl FnMut(&[u8], usize, bool) -> O) -> O { + fn with_seg(&self, r: R, mut v: impl FnMut(&[u8], usize, bool) -> O) -> O { let (start, end) = r.range(self); debug_assert!(start <= end && end <= self.len() as usize); v(&*self, start, false) @@ -37,3 +36,77 @@ impl Merge for Result<(), E> { self.and_then(|_| other()) } } +pub trait Slicer { + fn len(&self) -> usize; + fn with_seg(&self, r: R, v: impl FnMut(&[u8], usize, bool) -> O) -> O; +} +pub trait Merge { + fn merge(self, other: impl FnMut() -> Self) -> Self; +} + +pub trait Range { + #[inline(always)] + fn r_len(&self, s: &S) -> usize { + let r = self.range(s); + r.1 - r.0 + } + fn range(&self, s: &S) -> (usize, usize); + #[inline] + fn start(&self, s: &S) -> usize { + self.range(s).0 + } +} + +pub trait Visit { + fn check(&mut self, b: u8, idx: usize) -> bool; +} +impl Visit for u8 { + #[inline(always)] + fn check(&mut self, b: u8, _idx: usize) -> bool { + *self == b + } +} +impl bool> Visit for T { + #[inline(always)] + fn check(&mut self, b: u8, idx: usize) -> bool { + self(b, idx) + } +} + +type Offset = usize; +impl Range for Offset { + #[inline(always)] + fn range(&self, s: &S) -> (usize, usize) { + debug_assert!(*self <= s.len()); + (*self, s.len()) + } +} + +impl Range for std::ops::Range { + #[inline(always)] + fn range(&self, s: &S) -> (usize, usize) { + debug_assert!(self.start <= s.len()); + debug_assert!(self.end <= s.len()); + (self.start, self.end) + } +} +impl Range for std::ops::RangeFrom { + #[inline(always)] + fn range(&self, s: &S) -> (usize, usize) { + debug_assert!(self.start <= s.len()); + (self.start, s.len()) + } +} +impl Range for std::ops::RangeTo { + #[inline(always)] + fn range(&self, s: &S) -> (usize, usize) { + debug_assert!(self.end <= s.len()); + (0, self.end) + } +} +impl Range for std::ops::RangeFull { + #[inline(always)] + fn range(&self, s: &S) -> (usize, usize) { + (0, s.len()) + } +} diff --git a/ds/src/vec/mod.rs b/ds/src/vec/mod.rs index 2d73ac857..714bd7232 100644 --- a/ds/src/vec/mod.rs +++ b/ds/src/vec/mod.rs @@ -17,7 +17,10 @@ pub trait Buffer: Sized { } #[inline(always)] fn write_slice(&mut self, slice: &crate::RingSlice) { - slice.copy_to_vec(self); + use crate::Slicer; + slice.with_seg(0, |data, _oft, _seg| { + self.write(data); + }); } } impl Buffer for Vec { @@ -35,6 +38,10 @@ impl Buffer for Vec { self.set_len(self.len() + b.len()); } } + #[inline(always)] + fn write_slice(&mut self, slice: &crate::RingSlice) { + slice.copy_to_vec(self); + } } pub trait Add { From e5a12bded7f020a13d1eb68ea2678b8c0b6f973b Mon Sep 17 00:00:00 2001 From: icycrystal4 Date: Fri, 8 Mar 2024 18:43:31 +0800 Subject: [PATCH 05/24] =?UTF-8?q?Writer:=20=E7=BB=9F=E4=B8=80writer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ds/src/io/buffer.rs | 27 ++++++++++++++++++++++ ds/src/io/mod.rs | 33 +++++++++++++++++++++++++++ ds/src/mem/guarded.rs | 11 +++++++++ ds/src/mem/slice/mod.rs | 21 +++++++++++++---- ds/src/vec/ephemera.rs | 10 ++++---- ds/src/vec/mod.rs | 43 ----------------------------------- protocol/src/redis/command.rs | 10 ++++---- protocol/src/stream.rs | 21 +++++++---------- rt/src/stream/mod.rs | 31 ++++++++----------------- stream/src/handler.rs | 2 +- 10 files changed, 116 insertions(+), 93 deletions(-) create mode 100644 ds/src/io/buffer.rs diff --git a/ds/src/io/buffer.rs b/ds/src/io/buffer.rs new file mode 100644 index 000000000..f58d474ec --- /dev/null +++ b/ds/src/io/buffer.rs @@ -0,0 +1,27 @@ +use crate::{RingSlice, Slicer}; +pub trait Buffer: Sized { + fn write(&mut self, data: &S); + #[inline(always)] + fn write_u16(&mut self, num: u16) { + self.write(&num.to_be_bytes()); + } + #[inline(always)] + fn write_u32(&mut self, num: u32) { + self.write(&num.to_be_bytes()); + } + #[inline(always)] + fn write_u64(&mut self, num: u64) { + self.write(&num.to_be_bytes()); + } + #[inline(always)] + fn write_slice(&mut self, slice: &RingSlice) { + self.write(slice) + } +} +use crate::Writer; +impl Buffer for T { + #[inline(always)] + fn write(&mut self, data: &S) { + self.write_r(0, data).expect("no err") + } +} diff --git a/ds/src/io/mod.rs b/ds/src/io/mod.rs index dfe906f25..c60e039eb 100644 --- a/ds/src/io/mod.rs +++ b/ds/src/io/mod.rs @@ -1,2 +1,35 @@ mod order; pub use order::*; + +mod buffer; +pub use buffer::*; + +use crate::{Range, Slicer}; +// 把一个Slicer的部分切片写入到一个Writer中。必须全部写入成功,否则返回错误。 +pub trait Writer { + fn write_all(&mut self, data: &[u8]) -> std::io::Result<()>; + #[inline(always)] + fn write_r(&mut self, r: R, slicer: &S) -> std::io::Result<()> { + slicer.with_seg(r, |seg, _oft, _seg| self.write_all(seg)) + } +} + +impl Writer for Vec { + #[inline] + fn write_all(&mut self, data: &[u8]) -> std::io::Result<()> { + self.reserve(data.len()); + use std::ptr::copy_nonoverlapping as copy; + unsafe { + let len = self.len(); + let ptr = self.as_mut_ptr().add(len); + copy(data.as_ptr(), ptr, data.len()); + self.set_len(len + data.len()); + } + Ok(()) + } + #[inline(always)] + fn write_r(&mut self, r: R, slicer: &S) -> std::io::Result<()> { + self.reserve(r.len(slicer)); + slicer.with_seg(r, |seg, _oft, _seg| self.write_all(seg)) + } +} diff --git a/ds/src/mem/guarded.rs b/ds/src/mem/guarded.rs index 832a77eed..90049106b 100644 --- a/ds/src/mem/guarded.rs +++ b/ds/src/mem/guarded.rs @@ -95,6 +95,17 @@ impl DerefMut for MemGuard { &mut self.mem } } +use crate::{Merge, Range, Slicer}; +impl Slicer for MemGuard { + #[inline] + fn len(&self) -> usize { + self.mem.len() + } + #[inline] + fn with_seg(&self, r: R, v: impl FnMut(&[u8], usize, bool) -> O) -> O { + self.mem.with_seg(r, v) + } +} impl MemGuard { #[inline] diff --git a/ds/src/mem/slice/mod.rs b/ds/src/mem/slice/mod.rs index e6f31a81c..9310d0312 100644 --- a/ds/src/mem/slice/mod.rs +++ b/ds/src/mem/slice/mod.rs @@ -1,14 +1,23 @@ -use std::ops::Deref; -impl> Slicer for T { +impl> Slicer for T { #[inline(always)] fn len(&self) -> usize { - self.deref().len() + self.as_ref().len() } #[inline(always)] fn with_seg(&self, r: R, mut v: impl FnMut(&[u8], usize, bool) -> O) -> O { let (start, end) = r.range(self); debug_assert!(start <= end && end <= self.len() as usize); - v(&*self, start, false) + v(self.as_ref(), start, false) + } +} +impl Slicer for str { + #[inline(always)] + fn len(&self) -> usize { + self.len() + } + #[inline(always)] + fn with_seg(&self, r: R, v: impl FnMut(&[u8], usize, bool) -> O) -> O { + self.as_bytes().with_seg(r, v) } } @@ -45,6 +54,10 @@ pub trait Merge { } pub trait Range { + #[inline(always)] + fn len(&self, s: &S) -> usize { + self.r_len(s) + } #[inline(always)] fn r_len(&self, s: &S) -> usize { let r = self.range(s); diff --git a/ds/src/vec/ephemera.rs b/ds/src/vec/ephemera.rs index 5daff7e74..9e5f1f2a1 100644 --- a/ds/src/vec/ephemera.rs +++ b/ds/src/vec/ephemera.rs @@ -1,6 +1,6 @@ use std::ptr::copy_nonoverlapping; -use crate::{arena::CacheArena, Buffer}; +use crate::{arena::CacheArena, Writer}; // 用来分配快速释放的Vec // 1. cap大小固定, 不支持动态扩展; @@ -15,7 +15,7 @@ impl From> for EphemeralVec { fn from(vec: Vec) -> Self { assert!(vec.capacity() < u32::MAX as usize); let mut v = Self::fix_cap(vec.len()); - v.write(vec.as_slice()); + v.write_all(vec.as_slice()).expect("err"); v } } @@ -74,13 +74,13 @@ impl DerefMut for EphemeralVec { unsafe { std::slice::from_raw_parts_mut(self.ptr, self.len as usize) } } } -impl Buffer for EphemeralVec { +impl crate::Writer for EphemeralVec { #[inline(always)] - fn write>(&mut self, d: D) { - let data = d.as_ref(); + fn write_all(&mut self, data: &[u8]) -> std::io::Result<()> { assert!(self.len() + data.len() <= self.cap()); unsafe { copy_nonoverlapping(data.as_ptr(), self.ptr.add(self.len()), data.len()) }; self.len += data.len() as u32; + Ok(()) } } // 实现Drop diff --git a/ds/src/vec/mod.rs b/ds/src/vec/mod.rs index 714bd7232..5c35ddcaf 100644 --- a/ds/src/vec/mod.rs +++ b/ds/src/vec/mod.rs @@ -1,49 +1,6 @@ mod ephemera; pub use ephemera::*; -pub trait Buffer: Sized { - fn write>(&mut self, data: D); - #[inline(always)] - fn write_u16(&mut self, num: u16) { - self.write(num.to_be_bytes()); - } - #[inline(always)] - fn write_u32(&mut self, num: u32) { - self.write(num.to_be_bytes()); - } - #[inline(always)] - fn write_u64(&mut self, num: u64) { - self.write(num.to_be_bytes()); - } - #[inline(always)] - fn write_slice(&mut self, slice: &crate::RingSlice) { - use crate::Slicer; - slice.with_seg(0, |data, _oft, _seg| { - self.write(data); - }); - } -} -impl Buffer for Vec { - #[inline] - fn write>(&mut self, data: D) { - let b = data.as_ref(); - use std::ptr::copy_nonoverlapping as copy; - self.reserve(b.len()); - unsafe { - copy( - b.as_ptr() as *const u8, - self.as_mut_ptr().add(self.len()), - b.len(), - ); - self.set_len(self.len() + b.len()); - } - } - #[inline(always)] - fn write_slice(&mut self, slice: &crate::RingSlice) { - slice.copy_to_vec(self); - } -} - pub trait Add { fn add(&mut self, t: T); } diff --git a/protocol/src/redis/command.rs b/protocol/src/redis/command.rs index b07603b17..b3ea56e3c 100644 --- a/protocol/src/redis/command.rs +++ b/protocol/src/redis/command.rs @@ -164,13 +164,13 @@ impl CommandProperties { cmd.push(b'*'); // 1个cmd, 1个key,1个value。一共3个bulk cmd.push((2 + self.has_val as u8) + b'0'); - cmd.write("\r\n"); + cmd.write(b"\r\n"); cmd.push(b'$'); cmd.write(&self.mname_len); - cmd.write("\r\n"); - cmd.write(self.mname); - cmd.write("\r\n"); - cmd.write_slice(data); + cmd.write(b"\r\n"); + cmd.write(&self.mname); + cmd.write(b"\r\n"); + cmd.write(data); //data.copy_to_vec(&mut cmd); use super::flag::RedisFlager; if first { diff --git a/protocol/src/stream.rs b/protocol/src/stream.rs index 3a842527e..25722f039 100644 --- a/protocol/src/stream.rs +++ b/protocol/src/stream.rs @@ -22,34 +22,29 @@ pub trait BufRead { fn reserve(&mut self, r: usize); } use super::Result; -pub trait Writer: ds::BufWriter + Sized { +pub trait Writer: ds::Writer + Sized { fn cap(&self) -> usize; fn pending(&self) -> usize; // 写数据,一次写完 - fn write(&mut self, data: &[u8]) -> Result<()>; + #[inline(always)] + fn write(&mut self, data: &[u8]) -> Result<()> { + self.write_r(0, &data)?; + Ok(()) + } #[inline] fn write_u8(&mut self, v: u8) -> Result<()> { self.write(&[v]) } #[inline] fn write_u16(&mut self, v: u16) -> Result<()> { - // let mut data = Vec::with_capacity(2); - // data.write_u16::(v)?; - // self.write(&data[0..]) self.write(&v.to_be_bytes()) } #[inline] fn write_u32(&mut self, v: u32) -> Result<()> { - // let mut data = Vec::with_capacity(4); - // data.write_u32::(v)?; - // self.write(&data[0..]) self.write(&v.to_be_bytes()) } #[inline] fn write_u64(&mut self, v: u64) -> Result<()> { - // let mut data = Vec::with_capacity(8); - // data.write_u64::(v)?; - // self.write(&data[0..]) self.write(&v.to_be_bytes()) } #[inline(always)] @@ -63,14 +58,14 @@ pub trait Writer: ds::BufWriter + Sized { #[inline] fn write_slice>(&mut self, data: &S, oft: usize) -> Result<()> { - (&*data).copy_to(oft, self)?; + self.write_r(oft, &**data)?; Ok(()) } // 暂时没发现更好的实现方式,先用这个实现 #[inline] fn write_ringslice(&mut self, data: &RingSlice, oft: usize) -> Result<()> { - data.copy_to(oft, self)?; + self.write_r(oft, data)?; Ok(()) } diff --git a/rt/src/stream/mod.rs b/rt/src/stream/mod.rs index 7b2663db7..8280c0b19 100644 --- a/rt/src/stream/mod.rs +++ b/rt/src/stream/mod.rs @@ -121,16 +121,6 @@ impl protocol::Writer for Stream { fn pending(&self) -> usize { self.buf.len() } - #[inline] - fn write(&mut self, data: &[u8]) -> protocol::Result<()> { - if data.len() <= 4 { - self.buf.write(data); - } else { - let mut ctx = Context::from_waker(&NOOP); - let _ = Pin::new(self).poll_write(&mut ctx, data); - } - Ok(()) - } // hint: 提示可能优先写入到cache #[inline] fn cache(&mut self, hint: bool) { @@ -147,27 +137,24 @@ impl protocol::Writer for Stream { self.rx_buf.pending() == 0 } } -impl ds::BufWriter for Stream { +use ds::{Range, Slicer}; +impl ds::Writer for Stream { #[inline] fn write_all(&mut self, data: &[u8]) -> std::io::Result<()> { if data.len() <= 4 { self.buf.write(data); } else { let mut ctx = Context::from_waker(&NOOP); - let _ = Pin::new(self).poll_write(&mut ctx, data); + let _ = Pin::new(self).poll_write(&mut ctx, data)?; } Ok(()) } - #[inline] - fn write_all_hint(&mut self, buf: &[u8], next: bool) -> std::io::Result<()> { - self.buf.enable |= next; - self.write_all(buf) - } - #[inline] - fn write_seg_all(&mut self, buf0: &[u8], buf1: &[u8]) -> std::io::Result<()> { - self.buf.enable = true; - self.write_all(buf0)?; - self.write_all(buf1) + #[inline(always)] + fn write_r(&mut self, r: R, slicer: &D) -> std::io::Result<()> { + slicer.with_seg(r, |data, _oft, seg| { + self.buf.enable |= seg; + self.write_all(data) + }) } } impl protocol::BufRead for Stream { diff --git a/stream/src/handler.rs b/stream/src/handler.rs index 2f9eca850..733435c5f 100644 --- a/stream/src/handler.rs +++ b/stream/src/handler.rs @@ -119,7 +119,7 @@ where while let Some(req) = ready!(self.data.poll_recv(cx)) { self.num.tx(); - self.s.write_slice(&*req, 0)?; + self.s.write_r(0, &**req)?; match req.on_sent() { Some(r) => self.pending.push_back((r, Instant::now())), From bb016604a243806d17cb7c5e0e72c2508649d680 Mon Sep 17 00:00:00 2001 From: icycrystal4 Date: Mon, 11 Mar 2024 12:50:13 +0800 Subject: [PATCH 06/24] =?UTF-8?q?dns:=20=E7=BA=A6=E6=9C=891.8k/s=E7=9A=84?= =?UTF-8?q?=E5=AF=B9=E8=B1=A1=E5=88=86=E9=85=8D=E6=98=AF=E7=94=B1dns=20res?= =?UTF-8?q?olver=E6=8F=90=E4=BE=9B=EF=BC=8C=E5=85=88=E5=B0=86lookup?= =?UTF-8?q?=E5=8D=95=E7=8B=AC=E6=8A=BD=E7=A6=BB=E5=88=B0=E7=8B=AC=E7=AB=8B?= =?UTF-8?q?=E7=9A=84module=E3=80=82=E5=90=8E=E7=BB=AD=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- discovery/Cargo.toml | 1 + discovery/src/dns/lookup.rs | 19 +++ discovery/src/{dns.rs => dns/mod.rs} | 169 ++++++++++++++------------- endpoint/src/dns.rs | 2 +- rt/src/task.rs | 7 ++ 5 files changed, 119 insertions(+), 79 deletions(-) create mode 100644 discovery/src/dns/lookup.rs rename discovery/src/{dns.rs => dns/mod.rs} (53%) diff --git a/discovery/Cargo.toml b/discovery/Cargo.toml index c80e3b2d5..072e12abf 100644 --- a/discovery/Cargo.toml +++ b/discovery/Cargo.toml @@ -32,3 +32,4 @@ trust-dns-resolver = {version = "0.23.0"} once_cell = "1.14.0" noop-waker = "0.1.0" ctor = "*" +dns-lookup = "*" diff --git a/discovery/src/dns/lookup.rs b/discovery/src/dns/lookup.rs new file mode 100644 index 000000000..f46d348d6 --- /dev/null +++ b/discovery/src/dns/lookup.rs @@ -0,0 +1,19 @@ +pub(super) struct Lookup { + resolver: TokioAsyncResolver, +} + +use trust_dns_resolver::TokioAsyncResolver; + +use std::net::IpAddr; +impl Lookup { + pub(super) fn new() -> Self { + Self { + resolver: TokioAsyncResolver::tokio_from_system_conf() + .expect("Failed to create the resolver"), + } + } + pub(super) async fn lookup(&self, host: &str) -> std::io::Result> { + let ips = self.resolver.lookup_ip(host).await?; + Ok(ips.into_iter().collect()) + } +} diff --git a/discovery/src/dns.rs b/discovery/src/dns/mod.rs similarity index 53% rename from discovery/src/dns.rs rename to discovery/src/dns/mod.rs index d4405fd62..5a4b6b060 100644 --- a/discovery/src/dns.rs +++ b/discovery/src/dns/mod.rs @@ -2,7 +2,7 @@ use once_cell::sync::OnceCell; use std::{ collections::HashMap, future::Future, - net::IpAddr, + net::Ipv4Addr as IpAddr, sync::{ atomic::{AtomicBool, AtomicUsize, Ordering}, Arc, @@ -10,7 +10,8 @@ use std::{ }; use tokio::sync::mpsc::{unbounded_channel, UnboundedSender as Sender}; -use trust_dns_resolver::TokioAsyncResolver; +mod lookup; +use lookup::Lookup; use ds::{ time::{interval, Duration, Instant}, @@ -19,7 +20,6 @@ use ds::{ static DNSCACHE: OnceCell> = OnceCell::new(); type RegisterItem = (String, Arc); -type Resolver = TokioAsyncResolver; fn get_dns() -> ReadGuard { //必须使用get进行线程间同步 @@ -35,61 +35,61 @@ pub fn lookup_ips<'a>(host: &str, mut f: impl FnMut(&[IpAddr])) { #[derive(Default, Clone, Debug)] struct Record { - host: String, subscribers: Vec>, ips: Vec, id: usize, - md5: u64, // 使用所有ip的和作为md5 + md5: u64, // 使用所有ip的和作为md5 + notify: bool, // true表示需要通知 } impl Record { fn watch(&mut self, s: Arc) { self.subscribers.push(s); } - fn update(&mut self, addr_md5: (Vec, u64)) { - (self.md5 != 0).then(|| log::info!("record changed: {self:?} => {addr_md5:?}")); - debug_assert_ne!(self.ips, addr_md5.0); - debug_assert_ne!(self.md5, addr_md5.1); - (self.ips, self.md5) = addr_md5; + fn need_notify(&self) -> bool { + self.notify } - fn notify(&self) { + fn notify(&mut self) { + assert!(self.notify); for update in self.subscribers.iter() { update.store(true, Ordering::Release); } + self.notify = false; + } + fn empty(&self) -> bool { + self.ips.len() == 0 } // 如果有更新,则返回lookup的ip。 // 无更新则返回None - async fn check_refresh(&self, r: &mut Resolver) -> Option<(Vec, u64)> { - let host = &self.host; - match r.lookup_ip(host).await { + async fn refresh(&mut self, host: &str, r: &mut Lookup) -> bool { + match r.lookup(host).await { Ok(ips) => { let mut md5 = 0u64; let mut cnt = 0; for ip in ips.iter() { - log::debug!("{} resolved ip {}", host, ip); - if let IpAddr::V4(v4) = ip { - cnt += 1; - let bits: u32 = v4.into(); - md5 += bits as u64; + match ip { + std::net::IpAddr::V4(ip) => { + md5 += u32::from(*ip) as u64; + cnt += 1; + } + std::net::IpAddr::V6(_ip) => {} } } log::debug!("{} resolved ips:{:?}, md5:{}", host, ips, md5); if cnt > 0 && (cnt != self.ips.len() || self.md5 != md5) { - let mut addrs = Vec::with_capacity(cnt); - for ip in ips.iter() { - addrs.push(ip); - } - return Some((addrs, md5)); + self.ips = ips + .into_iter() + .map(|ip| match ip { + std::net::IpAddr::V4(ip) => ip, + _ => unreachable!(), + }) + .collect(); + self.md5 = md5; + self.notify = true; + return true; } } Err(e) => log::info!("refresh host failed:{}, {:?}", host, e), } - None - } - async fn refresh(&mut self, resolver: &mut Resolver) -> bool { - if let Some((addrs, md5)) = self.check_refresh(resolver).await { - self.update((addrs, md5)); - return true; - } false } } @@ -123,62 +123,32 @@ impl IPPort for String { &self[idx..] } } - pub fn start_dns_resolver_refresher() -> impl Future { - let (reg_tx, reg_rx) = unbounded_channel(); + let (reg_tx, mut reg_rx) = unbounded_channel(); let mut local_cache = DnsCache::from(reg_tx); - let (tx, rx) = ds::cow(local_cache.clone()); - let _r = DNSCACHE.set(rx); + let (mut writer, reader) = ds::cow(local_cache.clone()); + let _r = DNSCACHE.set(reader); assert!(_r.is_ok(), "dns cache set failed"); async move { - let mut resolver = TokioAsyncResolver::tokio_from_system_conf().expect("resolver"); + let mut resolver = Lookup::new(); log::info!("task started ==> dns cache refresher"); - let mut writer = tx; - let mut rx = reg_rx; - const BATCH_CNT: usize = 128; let mut tick = interval(Duration::from_secs(1)); let mut idx = 0; - let mut need_notify = Vec::new(); loop { - if let Ok(reg) = rx.try_recv() { - let r = local_cache.register(reg.0, reg.1); - r.refresh(&mut resolver) - .await - .then(|| need_notify.push(r.host.clone())); - continue; + while let Ok((host, notify)) = reg_rx.try_recv() { + local_cache.register(host, notify); } - // 第一次增量更新,不等待tick - let notify = |writer: &mut ds::CowWriteHandle, - local_cache: &DnsCache, - need_notify: &mut Vec| { - if need_notify.len() > 0 { - writer.update(local_cache.clone()); - let hosts = &local_cache.hosts; - for host in need_notify.iter() { - hosts.get(host).map(|r| r.notify()); - } - need_notify.clear(); - } - }; - notify(&mut writer, &local_cache, &mut need_notify); - // 每一秒种tick一次,检查是否 - tick.tick().await; let start = Instant::now(); - for (host, record) in &mut local_cache.hosts { - assert_eq!(host, &record.host); - if idx == record.id % BATCH_CNT { - if let Some(addrs) = record.check_refresh(&mut resolver).await { - record.update(addrs); - need_notify.push(host.clone()); - } - } + let (num, cache) = local_cache.refresh_by_idx(idx, &mut resolver).await; + if num > 0 { + let new = local_cache.clone(); + writer.update(new); + local_cache.notify(cache.expect("cache none"), num); } - // 第二次增量更新,每个tick只更新一部分(1/BATCH_CNT) - notify(&mut writer, &local_cache, &mut need_notify); - need_notify.shrink_to(64); - idx = (idx + 1) % BATCH_CNT; + idx += 1; log::trace!("refresh dns elapsed:{:?}", start.elapsed()); + tick.tick().await; } } } @@ -192,7 +162,7 @@ impl DnsCache { fn from(tx: Sender) -> Self { Self { tx, - hosts: Default::default(), + hosts: HashMap::with_capacity(4096), } } fn watch(&self, addr: &str, notify: Arc) { @@ -201,15 +171,58 @@ impl DnsCache { log::error!("watcher failed to {} => {:?}", addr, _e); } } - fn register(&mut self, host: String, notify: Arc) -> &mut Record { + // 刷新所有id是idx的record。如果record的ips长度为空,也一共刷新。 + // 1. 返回刷新成功的数量; + // 2. 如果刷新成功的数量大于1,则返回第一个刷新的addr。 + // 通常不会有同时多个record刷新,可以使用这个减少flush_to时的遍历。 + async fn refresh_by_idx( + &mut self, + idx: usize, + resolver: &mut Lookup, + ) -> (usize, Option) { + let mut refreshes = 0usize; + let mut cache = None; + const BATCH_CNT: usize = 128; + let idx = idx % BATCH_CNT; + for (host, record) in &mut self.hosts { + // 新注册的host要及时刷新,从来没有解析成功的也一直要刷新 + if idx == record.id % BATCH_CNT || record.empty() { + if record.refresh(host, resolver).await { + (refreshes == 0).then(|| cache.insert(host.clone())); + refreshes += 1; + } + } + } + (refreshes, cache) + } + fn notify(&mut self, cache: String, refreshes: usize) { + assert!(refreshes >= 1); + // 先更新,再通知。 + // 通知 + if refreshes == 1 { + // shrink主要是减少cap,以提升遍历性能 + self.hosts.shrink_to_fit(); + let record = self.hosts.get_mut(&cache).unwrap(); + record.notify(); + } else { + // 遍历,notify为true的需要通知 + let mut n = 0; + for (_, record) in self.hosts.iter_mut() { + if record.need_notify() { + record.notify(); + n += 1; + } + } + assert_eq!(n, refreshes); + } + } + fn register(&mut self, host: String, notify: Arc) { log::debug!("host {} registered to cache", host); static SEQ: AtomicUsize = AtomicUsize::new(0); let id = SEQ.fetch_add(1, Ordering::Relaxed); let r = self.hosts.entry(host.clone()).or_default(); r.id = id; - r.host = host; r.watch(notify); - r } fn lookup(&self, host: &str) -> &[IpAddr] { static EMPTY: Vec = Vec::new(); diff --git a/endpoint/src/dns.rs b/endpoint/src/dns.rs index a4bddb7e7..b2f14f776 100644 --- a/endpoint/src/dns.rs +++ b/endpoint/src/dns.rs @@ -168,7 +168,7 @@ impl Lookup for GLookup { } } -use std::net::IpAddr; +use std::net::Ipv4Addr as IpAddr; pub trait Lookup { fn lookup(host: &str, f: impl FnMut(&[IpAddr])); } diff --git a/rt/src/task.rs b/rt/src/task.rs index b543fe9ab..a839e3c7a 100644 --- a/rt/src/task.rs +++ b/rt/src/task.rs @@ -9,3 +9,10 @@ where { tokio::spawn(future) } +pub fn spawn_blocking(f: F) -> JoinHandle +where + F: FnOnce() -> R + Send + 'static, + R: Send + 'static, +{ + tokio::task::spawn_blocking(f) +} From c2625582a8e88b0ab0f46ea2735e443f738068f2 Mon Sep 17 00:00:00 2001 From: icycrystal4 Date: Mon, 11 Mar 2024 14:39:25 +0800 Subject: [PATCH 07/24] =?UTF-8?q?dns:=20=E7=BA=A6=E6=9C=891.8k/s=E7=9A=84?= =?UTF-8?q?=E5=AF=B9=E8=B1=A1=E5=88=86=E9=85=8D=E6=98=AF=E7=94=B1dns=20res?= =?UTF-8?q?olver=E6=8F=90=E4=BE=9B=EF=BC=8C=E5=85=88=E5=B0=86lookup?= =?UTF-8?q?=E5=8D=95=E7=8B=AC=E6=8A=BD=E7=A6=BB=E5=88=B0=E7=8B=AC=E7=AB=8B?= =?UTF-8?q?=E7=9A=84module=E3=80=82=E5=90=8E=E7=BB=AD=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- discovery/src/dns/lookup.rs | 47 ++++++++++++++++-- discovery/src/dns/mod.rs | 98 ++++++++++++++++++------------------- 2 files changed, 91 insertions(+), 54 deletions(-) diff --git a/discovery/src/dns/lookup.rs b/discovery/src/dns/lookup.rs index f46d348d6..180eff82a 100644 --- a/discovery/src/dns/lookup.rs +++ b/discovery/src/dns/lookup.rs @@ -1,8 +1,9 @@ +use super::Record; pub(super) struct Lookup { resolver: TokioAsyncResolver, } -use trust_dns_resolver::TokioAsyncResolver; +use trust_dns_resolver::{lookup_ip::LookupIp, TokioAsyncResolver}; use std::net::IpAddr; impl Lookup { @@ -12,8 +13,48 @@ impl Lookup { .expect("Failed to create the resolver"), } } - pub(super) async fn lookup(&self, host: &str) -> std::io::Result> { + pub(super) async fn lookup(&self, host: &str) -> std::io::Result { let ips = self.resolver.lookup_ip(host).await?; - Ok(ips.into_iter().collect()) + Ok(IpAddrLookup::new(ips)) + } + pub(super) async fn lookups<'a, I>(&self, iter: I) -> (usize, Option) + where + I: Iterator, + { + let mut num = 0; + let mut cache = None; + for (host, r) in iter { + let ret = self.lookup(host).await; + if ret.is_err() { + log::error!("Failed to lookup ip for {} err:{:?}", host, ret.err()); + break; + } + let ips = ret.unwrap(); + if r.refresh(host, ips) { + num += 1; + if num == 1 { + assert!(cache.is_none()); + cache = Some(host.to_string()); + } + } + } + (num, cache) + } +} + +#[derive(Debug)] +pub(super) struct IpAddrLookup { + ips: LookupIp, +} +impl IpAddrLookup { + fn new(ips: LookupIp) -> Self { + Self { ips } + } + pub(super) fn visit_v4(&self, mut v: impl FnMut(std::net::Ipv4Addr)) { + for ip in self.ips.iter() { + if let IpAddr::V4(ip) = ip { + v(ip); + } + } } } diff --git a/discovery/src/dns/mod.rs b/discovery/src/dns/mod.rs index 5a4b6b060..f4ba44236 100644 --- a/discovery/src/dns/mod.rs +++ b/discovery/src/dns/mod.rs @@ -11,7 +11,7 @@ use std::{ use tokio::sync::mpsc::{unbounded_channel, UnboundedSender as Sender}; mod lookup; -use lookup::Lookup; +use lookup::*; use ds::{ time::{interval, Duration, Instant}, @@ -60,35 +60,20 @@ impl Record { } // 如果有更新,则返回lookup的ip。 // 无更新则返回None - async fn refresh(&mut self, host: &str, r: &mut Lookup) -> bool { - match r.lookup(host).await { - Ok(ips) => { - let mut md5 = 0u64; - let mut cnt = 0; - for ip in ips.iter() { - match ip { - std::net::IpAddr::V4(ip) => { - md5 += u32::from(*ip) as u64; - cnt += 1; - } - std::net::IpAddr::V6(_ip) => {} - } - } - log::debug!("{} resolved ips:{:?}, md5:{}", host, ips, md5); - if cnt > 0 && (cnt != self.ips.len() || self.md5 != md5) { - self.ips = ips - .into_iter() - .map(|ip| match ip { - std::net::IpAddr::V4(ip) => ip, - _ => unreachable!(), - }) - .collect(); - self.md5 = md5; - self.notify = true; - return true; - } - } - Err(e) => log::info!("refresh host failed:{}, {:?}", host, e), + fn refresh(&mut self, host: &str, ips: IpAddrLookup) -> bool { + let mut md5 = 0u64; + let mut cnt = 0; + ips.visit_v4(|ip| { + cnt += 1; + md5 += u32::from(ip) as u64; + }); + log::debug!("{} resolved ips:{:?}, md5:{}", host, ips, md5); + if cnt > 0 && (cnt != self.ips.len() || self.md5 != md5) { + self.ips.clear(); + ips.visit_v4(|ip| self.ips.push(ip)); + self.md5 = md5; + self.notify = true; + return true; } false } @@ -130,7 +115,7 @@ pub fn start_dns_resolver_refresher() -> impl Future { let _r = DNSCACHE.set(reader); assert!(_r.is_ok(), "dns cache set failed"); async move { - let mut resolver = Lookup::new(); + let resolver = Lookup::new(); log::info!("task started ==> dns cache refresher"); let mut tick = interval(Duration::from_secs(1)); let mut idx = 0; @@ -139,7 +124,7 @@ pub fn start_dns_resolver_refresher() -> impl Future { local_cache.register(host, notify); } let start = Instant::now(); - let (num, cache) = local_cache.refresh_by_idx(idx, &mut resolver).await; + let (num, cache) = resolver.lookups(local_cache.iter(idx)).await; if num > 0 { let new = local_cache.clone(); writer.update(new); @@ -175,25 +160,9 @@ impl DnsCache { // 1. 返回刷新成功的数量; // 2. 如果刷新成功的数量大于1,则返回第一个刷新的addr。 // 通常不会有同时多个record刷新,可以使用这个减少flush_to时的遍历。 - async fn refresh_by_idx( - &mut self, - idx: usize, - resolver: &mut Lookup, - ) -> (usize, Option) { - let mut refreshes = 0usize; - let mut cache = None; - const BATCH_CNT: usize = 128; - let idx = idx % BATCH_CNT; - for (host, record) in &mut self.hosts { - // 新注册的host要及时刷新,从来没有解析成功的也一直要刷新 - if idx == record.id % BATCH_CNT || record.empty() { - if record.refresh(host, resolver).await { - (refreshes == 0).then(|| cache.insert(host.clone())); - refreshes += 1; - } - } - } - (refreshes, cache) + fn iter(&mut self, idx: usize) -> HostRecordIter<'_> { + const PERIOD: usize = 128; + HostRecordIter::new(self, idx, PERIOD) } fn notify(&mut self, cache: String, refreshes: usize) { assert!(refreshes >= 1); @@ -232,3 +201,30 @@ impl DnsCache { .unwrap_or_else(|| &EMPTY) } } + +struct HostRecordIter<'a> { + // 当前遍历的hist的id的索引 + idx: usize, + iter: std::collections::hash_map::IterMut<'a, String, Record>, + period: usize, +} + +impl<'a> HostRecordIter<'a> { + fn new(cache: &'a mut DnsCache, idx: usize, period: usize) -> Self { + let idx = idx % period; + let iter = cache.hosts.iter_mut(); + Self { idx, iter, period } + } +} +// 实现Iterator trait +impl<'a> Iterator for HostRecordIter<'a> { + type Item = (&'a str, &'a mut Record); + fn next(&mut self) -> Option { + while let Some((host, record)) = self.iter.next() { + if record.id % self.period == self.idx || record.empty() { + return Some((host, record)); + } + } + None + } +} From 9d141a1ff913b9f869f62539550d5b66e8118a70 Mon Sep 17 00:00:00 2001 From: icycrystal4 Date: Mon, 11 Mar 2024 14:48:59 +0800 Subject: [PATCH 08/24] =?UTF-8?q?dns:=20=E7=BA=A6=E6=9C=891.8k/s=E7=9A=84?= =?UTF-8?q?=E5=AF=B9=E8=B1=A1=E5=88=86=E9=85=8D=E6=98=AF=E7=94=B1dns=20res?= =?UTF-8?q?olver=E6=8F=90=E4=BE=9B=EF=BC=8C=E5=85=88=E5=B0=86lookup?= =?UTF-8?q?=E5=8D=95=E7=8B=AC=E6=8A=BD=E7=A6=BB=E5=88=B0=E7=8B=AC=E7=AB=8B?= =?UTF-8?q?=E7=9A=84module=E3=80=82=E5=90=8E=E7=BB=AD=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- endpoint/src/dns.rs | 2 +- tests/src/dns.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/endpoint/src/dns.rs b/endpoint/src/dns.rs index b2f14f776..c0cdf0793 100644 --- a/endpoint/src/dns.rs +++ b/endpoint/src/dns.rs @@ -168,7 +168,7 @@ impl Lookup for GLookup { } } -use std::net::Ipv4Addr as IpAddr; +pub use std::net::Ipv4Addr as IpAddr; pub trait Lookup { fn lookup(host: &str, f: impl FnMut(&[IpAddr])); } diff --git a/tests/src/dns.rs b/tests/src/dns.rs index 1b5df7b29..d68ade7ba 100644 --- a/tests/src/dns.rs +++ b/tests/src/dns.rs @@ -1,5 +1,4 @@ use std::collections::HashMap; -use std::net::IpAddr; static mut CACHE: Option>> = None; struct Dns; @@ -15,7 +14,7 @@ impl Dns { } } -use endpoint::dns::{DnsLookup, Lookup}; +use endpoint::dns::{DnsLookup, IpAddr, Lookup}; impl Lookup for Dns { fn lookup(host: &str, mut f: impl FnMut(&[IpAddr])) { unsafe { From 2bce10d2ac19070f7097ffaf3e154f1ba5502f2c Mon Sep 17 00:00:00 2001 From: icycrystal4 Date: Mon, 11 Mar 2024 15:31:00 +0800 Subject: [PATCH 09/24] =?UTF-8?q?dns:=20=E7=BA=A6=E6=9C=891.8k/s=E7=9A=84?= =?UTF-8?q?=E5=AF=B9=E8=B1=A1=E5=88=86=E9=85=8D=E6=98=AF=E7=94=B1dns=20res?= =?UTF-8?q?olver=E6=8F=90=E4=BE=9B=EF=BC=8C=E5=85=88=E5=B0=86lookup?= =?UTF-8?q?=E5=8D=95=E7=8B=AC=E6=8A=BD=E7=A6=BB=E5=88=B0=E7=8B=AC=E7=AB=8B?= =?UTF-8?q?=E7=9A=84module=E3=80=82=E5=90=8E=E7=BB=AD=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- discovery/Cargo.toml | 1 + discovery/src/dns/lookup.rs | 34 ++++++++++++++++++++++------------ discovery/src/dns/mod.rs | 15 +++++++++++++-- 3 files changed, 36 insertions(+), 14 deletions(-) diff --git a/discovery/Cargo.toml b/discovery/Cargo.toml index 072e12abf..11c0a3d2a 100644 --- a/discovery/Cargo.toml +++ b/discovery/Cargo.toml @@ -33,3 +33,4 @@ once_cell = "1.14.0" noop-waker = "0.1.0" ctor = "*" dns-lookup = "*" +libc = "*" diff --git a/discovery/src/dns/lookup.rs b/discovery/src/dns/lookup.rs index 180eff82a..9fdefa723 100644 --- a/discovery/src/dns/lookup.rs +++ b/discovery/src/dns/lookup.rs @@ -13,9 +13,13 @@ impl Lookup { .expect("Failed to create the resolver"), } } - pub(super) async fn lookup(&self, host: &str) -> std::io::Result { + async fn lookup(&self, host: &str) -> std::io::Result { let ips = self.resolver.lookup_ip(host).await?; - Ok(IpAddrLookup::new(ips)) + Ok(IpAddrLookup::LookupIp(ips)) + } + fn dns_lookup(&self, host: &str) -> std::io::Result { + let ips = dns_lookup::lookup_host(host)?; + Ok(IpAddrLookup::AddrInfoIter(ips)) } pub(super) async fn lookups<'a, I>(&self, iter: I) -> (usize, Option) where @@ -24,7 +28,7 @@ impl Lookup { let mut num = 0; let mut cache = None; for (host, r) in iter { - let ret = self.lookup(host).await; + let ret = self.dns_lookup(host); if ret.is_err() { log::error!("Failed to lookup ip for {} err:{:?}", host, ret.err()); break; @@ -42,18 +46,24 @@ impl Lookup { } } -#[derive(Debug)] -pub(super) struct IpAddrLookup { - ips: LookupIp, +pub(super) enum IpAddrLookup { + LookupIp(LookupIp), + AddrInfoIter(Vec), } impl IpAddrLookup { - fn new(ips: LookupIp) -> Self { - Self { ips } - } pub(super) fn visit_v4(&self, mut v: impl FnMut(std::net::Ipv4Addr)) { - for ip in self.ips.iter() { - if let IpAddr::V4(ip) = ip { - v(ip); + match self { + Self::LookupIp(ips) => ips.iter().for_each(|ip| { + if let IpAddr::V4(ip) = ip { + v(ip); + } + }), + Self::AddrInfoIter(iter) => { + for ip in iter.iter() { + if let std::net::IpAddr::V4(ip) = ip { + v(*ip); + } + } } } } diff --git a/discovery/src/dns/mod.rs b/discovery/src/dns/mod.rs index f4ba44236..44ee40c85 100644 --- a/discovery/src/dns/mod.rs +++ b/discovery/src/dns/mod.rs @@ -33,7 +33,7 @@ pub fn lookup_ips<'a>(host: &str, mut f: impl FnMut(&[IpAddr])) { f(get_dns().lookup(host)) } -#[derive(Default, Clone, Debug)] +#[derive(Clone, Debug)] struct Record { subscribers: Vec>, ips: Vec, @@ -41,6 +41,17 @@ struct Record { md5: u64, // 使用所有ip的和作为md5 notify: bool, // true表示需要通知 } +impl Default for Record { + fn default() -> Self { + Record { + subscribers: Vec::with_capacity(2), + ips: Vec::with_capacity(2), + id: 0, + md5: 0, + notify: false, + } + } +} impl Record { fn watch(&mut self, s: Arc) { self.subscribers.push(s); @@ -67,12 +78,12 @@ impl Record { cnt += 1; md5 += u32::from(ip) as u64; }); - log::debug!("{} resolved ips:{:?}, md5:{}", host, ips, md5); if cnt > 0 && (cnt != self.ips.len() || self.md5 != md5) { self.ips.clear(); ips.visit_v4(|ip| self.ips.push(ip)); self.md5 = md5; self.notify = true; + log::debug!("{} resolved ips:{:?}, md5:{}", host, self.ips, md5); return true; } false From aa41764f9a7e7df0221e74d7480b75750a893ea4 Mon Sep 17 00:00:00 2001 From: icycrystal4 Date: Mon, 11 Mar 2024 15:36:45 +0800 Subject: [PATCH 10/24] =?UTF-8?q?dns:=20=E7=BA=A6=E6=9C=891.8k/s=E7=9A=84?= =?UTF-8?q?=E5=AF=B9=E8=B1=A1=E5=88=86=E9=85=8D=E6=98=AF=E7=94=B1dns=20res?= =?UTF-8?q?olver=E6=8F=90=E4=BE=9B=EF=BC=8C=E5=85=88=E5=B0=86lookup?= =?UTF-8?q?=E5=8D=95=E7=8B=AC=E6=8A=BD=E7=A6=BB=E5=88=B0=E7=8B=AC=E7=AB=8B?= =?UTF-8?q?=E7=9A=84module=E3=80=82=E5=90=8E=E7=BB=AD=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- discovery/src/dns/lookup.rs | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/discovery/src/dns/lookup.rs b/discovery/src/dns/lookup.rs index 9fdefa723..5d78d9317 100644 --- a/discovery/src/dns/lookup.rs +++ b/discovery/src/dns/lookup.rs @@ -1,22 +1,22 @@ use super::Record; pub(super) struct Lookup { - resolver: TokioAsyncResolver, + //resolver: TokioAsyncResolver, } -use trust_dns_resolver::{lookup_ip::LookupIp, TokioAsyncResolver}; +//use trust_dns_resolver::{lookup_ip::LookupIp, TokioAsyncResolver}; use std::net::IpAddr; impl Lookup { pub(super) fn new() -> Self { Self { - resolver: TokioAsyncResolver::tokio_from_system_conf() - .expect("Failed to create the resolver"), + //resolver: TokioAsyncResolver::tokio_from_system_conf() + // .expect("Failed to create the resolver"), } } - async fn lookup(&self, host: &str) -> std::io::Result { - let ips = self.resolver.lookup_ip(host).await?; - Ok(IpAddrLookup::LookupIp(ips)) - } + //async fn lookup(&self, host: &str) -> std::io::Result { + // let ips = self.resolver.lookup_ip(host).await?; + // Ok(IpAddrLookup::LookupIp(ips)) + //} fn dns_lookup(&self, host: &str) -> std::io::Result { let ips = dns_lookup::lookup_host(host)?; Ok(IpAddrLookup::AddrInfoIter(ips)) @@ -36,10 +36,7 @@ impl Lookup { let ips = ret.unwrap(); if r.refresh(host, ips) { num += 1; - if num == 1 { - assert!(cache.is_none()); - cache = Some(host.to_string()); - } + (num == 1).then(|| cache = Some(host.to_string())); } } (num, cache) @@ -47,17 +44,17 @@ impl Lookup { } pub(super) enum IpAddrLookup { - LookupIp(LookupIp), + //LookupIp(LookupIp), AddrInfoIter(Vec), } impl IpAddrLookup { pub(super) fn visit_v4(&self, mut v: impl FnMut(std::net::Ipv4Addr)) { match self { - Self::LookupIp(ips) => ips.iter().for_each(|ip| { - if let IpAddr::V4(ip) = ip { - v(ip); - } - }), + //Self::LookupIp(ips) => ips.iter().for_each(|ip| { + // if let IpAddr::V4(ip) = ip { + // v(ip); + // } + //}), Self::AddrInfoIter(iter) => { for ip in iter.iter() { if let std::net::IpAddr::V4(ip) = ip { From 005cf61973d34b5cb1465fafc6fb7e9dad336877 Mon Sep 17 00:00:00 2001 From: icycrystal4 Date: Tue, 12 Mar 2024 19:58:22 +0800 Subject: [PATCH 11/24] =?UTF-8?q?dns:=20=E4=BD=BF=E7=94=A8vec=E6=9B=BF?= =?UTF-8?q?=E6=8D=A2hashmap=EF=BC=8C=E6=8F=90=E5=8D=87cpu=E5=88=A9?= =?UTF-8?q?=E7=94=A8=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- discovery/src/dns/mod.rs | 151 ++++++++++++++++++++++++++++----------- 1 file changed, 108 insertions(+), 43 deletions(-) diff --git a/discovery/src/dns/mod.rs b/discovery/src/dns/mod.rs index 44ee40c85..9cdd83915 100644 --- a/discovery/src/dns/mod.rs +++ b/discovery/src/dns/mod.rs @@ -4,7 +4,7 @@ use std::{ future::Future, net::Ipv4Addr as IpAddr, sync::{ - atomic::{AtomicBool, AtomicUsize, Ordering}, + atomic::{AtomicBool, Ordering}, Arc, }, }; @@ -14,7 +14,7 @@ mod lookup; use lookup::*; use ds::{ - time::{interval, Duration, Instant}, + time::{interval, Duration}, CowReadHandle, ReadGuard, }; static DNSCACHE: OnceCell> = OnceCell::new(); @@ -37,7 +37,6 @@ pub fn lookup_ips<'a>(host: &str, mut f: impl FnMut(&[IpAddr])) { struct Record { subscribers: Vec>, ips: Vec, - id: usize, md5: u64, // 使用所有ip的和作为md5 notify: bool, // true表示需要通知 } @@ -46,7 +45,6 @@ impl Default for Record { Record { subscribers: Vec::with_capacity(2), ips: Vec::with_capacity(2), - id: 0, md5: 0, notify: false, } @@ -129,21 +127,18 @@ pub fn start_dns_resolver_refresher() -> impl Future { let resolver = Lookup::new(); log::info!("task started ==> dns cache refresher"); let mut tick = interval(Duration::from_secs(1)); - let mut idx = 0; loop { while let Ok((host, notify)) = reg_rx.try_recv() { local_cache.register(host, notify); } - let start = Instant::now(); - let (num, cache) = resolver.lookups(local_cache.iter(idx)).await; + // 处理新增的 + let (num, cache) = resolver.lookups(local_cache.iter()).await; if num > 0 { let new = local_cache.clone(); writer.update(new); local_cache.notify(cache.expect("cache none"), num); } - idx += 1; - log::trace!("refresh dns elapsed:{:?}", start.elapsed()); tick.tick().await; } } @@ -152,13 +147,19 @@ pub fn start_dns_resolver_refresher() -> impl Future { #[derive(Clone)] pub struct DnsCache { tx: Sender, - hosts: HashMap, + hosts: Hosts, + last_idx: usize, // 刷新到的idx + last_len: usize, // 上次刷新的长度 + cycle: usize, } impl DnsCache { fn from(tx: Sender) -> Self { Self { tx, - hosts: HashMap::with_capacity(4096), + hosts: Default::default(), + last_idx: 0, + last_len: 0, + cycle: 0, } } fn watch(&self, addr: &str, notify: Arc) { @@ -167,22 +168,39 @@ impl DnsCache { log::error!("watcher failed to {} => {:?}", addr, _e); } } - // 刷新所有id是idx的record。如果record的ips长度为空,也一共刷新。 - // 1. 返回刷新成功的数量; - // 2. 如果刷新成功的数量大于1,则返回第一个刷新的addr。 - // 通常不会有同时多个record刷新,可以使用这个减少flush_to时的遍历。 - fn iter(&mut self, idx: usize) -> HostRecordIter<'_> { + // 每16个tick执行一次empty,避免某一次刷新未解释成功导致需要等待下一个周期。 + // 其他情况下,每个tick只会刷新部分chunk数据。 + fn iter(&mut self) -> HostRecordIter<'_> { const PERIOD: usize = 128; - HostRecordIter::new(self, idx, PERIOD) + let ith = self.cycle % PERIOD; + // 如果当前是走空搜索,下一次扫描的时候会搜索2个chunk. + self.cycle += 1; + // 16是一个经验值。 + if self.hosts.len() > self.last_len || (self.cycle % 16 == 0 && self.hosts.has_empty()) { + self.last_len = self.hosts.len(); + HostRecordIter::empty_iter(self.hosts.hosts.iter_mut()) + } else { + // 刷新从上个idx开始的一个chunk长度的数据 + assert!(self.last_len == self.hosts.len()); + let len = self.last_len; + let chunk = (len + (PERIOD - 1)) / PERIOD; + // 因为chunk是动态变化的,所以不能用last_idx + chunk + let end = ((ith + 1) * chunk).min(len); + assert!(self.last_idx <= end); + let iter = self.hosts.hosts[self.last_idx..end].iter_mut(); + self.last_idx = end; + if self.last_idx == len { + self.last_idx = 0; + } + HostRecordIter::new(iter) + } } fn notify(&mut self, cache: String, refreshes: usize) { assert!(refreshes >= 1); // 先更新,再通知。 // 通知 if refreshes == 1 { - // shrink主要是减少cap,以提升遍历性能 - self.hosts.shrink_to_fit(); - let record = self.hosts.get_mut(&cache).unwrap(); + let record = self.hosts.get_mut(&cache); record.notify(); } else { // 遍历,notify为true的需要通知 @@ -198,44 +216,91 @@ impl DnsCache { } fn register(&mut self, host: String, notify: Arc) { log::debug!("host {} registered to cache", host); - static SEQ: AtomicUsize = AtomicUsize::new(0); - let id = SEQ.fetch_add(1, Ordering::Relaxed); - let r = self.hosts.entry(host.clone()).or_default(); - r.id = id; + let r = self.hosts.get_or_insert(host); r.watch(notify); } fn lookup(&self, host: &str) -> &[IpAddr] { static EMPTY: Vec = Vec::new(); - self.hosts - .get(host) - .map(|r| &r.ips) - .unwrap_or_else(|| &EMPTY) + self.hosts.get(host).unwrap_or_else(|| &EMPTY) + } +} + +#[derive(Clone)] +struct Hosts { + // 只有注册与lookup的时候才需要使用cache进行判断是否已经存在, 这个只在变更的时候使用。 + index: HashMap, + hosts: Vec<(String, Record)>, +} +impl Default for Hosts { + fn default() -> Self { + const CAP: usize = 2048; + Self { + index: HashMap::with_capacity(CAP), + hosts: Vec::with_capacity(CAP), + } + } +} +impl Hosts { + fn iter_mut(&mut self) -> std::slice::IterMut<'_, (String, Record)> { + self.hosts.iter_mut() + } + fn get_mut(&mut self, host: &String) -> &mut Record { + let &id = self.index.get(host).expect("not register"); + assert!(id < self.hosts.len()); + &mut self.hosts[id].1 + } + fn get(&self, host: &str) -> Option<&[IpAddr]> { + self.index.get(host).map(|&id| { + assert!(id < self.hosts.len()); + &self.hosts[id].1.ips[..] + }) + } + fn get_or_insert(&mut self, host: String) -> &mut Record { + let id = match self.index.get(&host) { + Some(id) => *id, + None => { + let id = self.hosts.len(); + self.hosts.push((host.clone(), Record::default())); + self.index.insert(host, id); + id + } + }; + assert!(id < self.hosts.len()); + &mut self.hosts[id].1 + } + fn len(&self) -> usize { + self.hosts.len() + } + // 是否有ips为空的记录 + fn has_empty(&self) -> bool { + self.hosts.iter().any(|(_, record)| record.ips.is_empty()) } } struct HostRecordIter<'a> { - // 当前遍历的hist的id的索引 - idx: usize, - iter: std::collections::hash_map::IterMut<'a, String, Record>, - period: usize, + iter: Box + 'a>, } impl<'a> HostRecordIter<'a> { - fn new(cache: &'a mut DnsCache, idx: usize, period: usize) -> Self { - let idx = idx % period; - let iter = cache.hosts.iter_mut(); - Self { idx, iter, period } + fn empty_iter + 'a>(iter: I) -> Self { + let iter = iter.filter(|(_, r)| r.empty()); + HostRecordIter { + iter: Box::new(iter), + } + } + fn new + 'a>(iter: I) -> Self { + HostRecordIter { + iter: Box::new(iter), + } } } -// 实现Iterator trait + impl<'a> Iterator for HostRecordIter<'a> { type Item = (&'a str, &'a mut Record); + fn next(&mut self) -> Option { - while let Some((host, record)) = self.iter.next() { - if record.id % self.period == self.idx || record.empty() { - return Some((host, record)); - } - } - None + self.iter.next().map(|(k, v)| (k.as_str(), v)) } } +unsafe impl<'a> Send for HostRecordIter<'a> {} +unsafe impl<'a> Sync for HostRecordIter<'a> {} From b3379dd2a19f9ef1441b339aae3e3b8e3e1ed9e1 Mon Sep 17 00:00:00 2001 From: icycrystal4 Date: Tue, 12 Mar 2024 20:24:31 +0800 Subject: [PATCH 12/24] =?UTF-8?q?metrics:=20f64=E5=9F=BA=E4=BA=8E0?= =?UTF-8?q?=E5=86=85=E5=AD=98=E5=88=86=E9=85=8D=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- metrics/src/lib.rs | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/metrics/src/lib.rs b/metrics/src/lib.rs index 09f435012..59aca02c2 100644 --- a/metrics/src/lib.rs +++ b/metrics/src/lib.rs @@ -121,29 +121,32 @@ impl WriteTo for i64 { v.with_str(|s| w.put_slice(s)); } } -// impl WriteTo for f64 { -// #[inline] -// fn write_to(&self, w: &mut W) { -// let mut trunc = self.trunc() as i64; -// if *self < 0.0 { -// w.put_slice(b"-"); -// trunc = -trunc; -// } -// (trunc as usize).with_str(|s| w.put_slice(s)); -// let fraction = ((self.fract() * 1000.0) as i64).abs() as usize; -// if fraction > 0 { -// w.put_slice(b"."); -// fraction.with_str(|s| w.put_slice(s)); -// } -// } -// } impl WriteTo for f64 { #[inline] fn write_to(&self, w: &mut W) { - let s = format!("{:.3}", *self); - w.put_slice(s); + let trunc = self.trunc() as i64; + trunc.write_to(w); + + let fraction = ((self.fract() * 1000.0) as i64).abs() as usize; + if fraction > 0 { + static PAD: &[u8; 4] = b".000"; + fraction.with_str(|s| { + // s长度不够,需要补零 + // 比如 6 => .006 + debug_assert!(s.len() <= 3); + w.put_slice(&PAD[0..4 - s.len()]); + w.put_slice(s) + }); + } } } +//impl WriteTo for f64 { +// #[inline] +// fn write_to(&self, w: &mut W) { +// let s = format!("{:.3}", *self); +// w.put_slice(s); +// } +//} pub(crate) trait ItemWriter { fn put_slice>(&mut self, data: S); From 9571ca47f929af2094651d108f976294ef6b22e1 Mon Sep 17 00:00:00 2001 From: icycrystal4 Date: Fri, 15 Mar 2024 19:31:21 +0800 Subject: [PATCH 13/24] =?UTF-8?q?mc:=20=E9=95=BF=E5=BA=A6=E6=A3=80?= =?UTF-8?q?=E6=B5=8B=E6=97=B6=E7=9B=B4=E6=8E=A5=E6=96=AD=E8=A8=80=EF=BC=8C?= =?UTF-8?q?=E8=80=8C=E4=B8=8D=E6=98=AF=E8=BF=94=E5=9B=9E=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- endpoint/src/cacheservice/topo.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/endpoint/src/cacheservice/topo.rs b/endpoint/src/cacheservice/topo.rs index 953941f70..1715668c7 100644 --- a/endpoint/src/cacheservice/topo.rs +++ b/endpoint/src/cacheservice/topo.rs @@ -114,13 +114,8 @@ where // TODO 有点怪异,先实现,晚点调整,这个属性直接从request获取更佳? fishermen req.retry_on_rsp_notok(req.can_retry_on_rsp_notok()); *req.mut_context() = ctx.ctx; - if idx >= self.streams.len() { - req.on_err(protocol::Error::TopChanged); - return; - } - log::debug!("+++ request sent prepared:{} - {} {}", idx, req, self); - debug_assert!(idx < self.streams.len(), "{} {} => {:?}", idx, self, req); + assert!(idx < self.streams.len(), "{} {} => {:?}", idx, self, req); unsafe { self.streams.get_unchecked(idx).send(req) }; } From d63c015c36ea8c1dcd11662cb6807756bd41b44a Mon Sep 17 00:00:00 2001 From: icycrystal4 Date: Fri, 15 Mar 2024 19:52:17 +0800 Subject: [PATCH 14/24] =?UTF-8?q?pipeline:=20=E7=AE=80=E5=8C=96Self?= =?UTF-8?q?=E7=9A=84mut=20capture.=20rust=E8=80=81=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E4=B8=8D=E6=94=AF=E6=8C=81Self=E7=9A=84fields=E8=A2=AB?= =?UTF-8?q?=E5=90=8C=E6=97=B6mut=E5=BC=95=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- stream/src/pipeline.rs | 81 +++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 52 deletions(-) diff --git a/stream/src/pipeline.rs b/stream/src/pipeline.rs index e7741eedd..0baf564cf 100644 --- a/stream/src/pipeline.rs +++ b/stream/src/pipeline.rs @@ -115,36 +115,26 @@ where if self.client.len() == 0 { return Ok(()); } - let Self { - client, - top, - parser, - pending, - waker, - first, - arena, - .. - } = self; // 解析请求,发送请求,并且注册回调 let mut processor = Visitor { - pending, - waker, - top, - first, - arena, - retry_on_rsp_notok: parser.config().retry_on_rsp_notok, + pending: &mut self.pending, + waker: &mut self.waker, + top: &self.top, + first: &mut self.first, + arena: &mut self.arena, + retry_on_rsp_notok: self.parser.config().retry_on_rsp_notok, }; - parser - .parse_request(client, top, &mut processor) + self.parser + .parse_request(&mut self.client, &self.top, &mut processor) .map_err(|e| { - log::info!("+++ parse request error: {:?} on client:{:?}", e, client); + log::info!("+++ parse error: {:?} :{:?}", e, self.client); match e { FlushOnClose(ref emsg) => { // 此处只处理FLushOnClose,用于发送异常给client - let _write_rs = client.write_all(emsg); - let _flush_rs = client.flush(); - log::warn!("+++ flush emsg[{:?}], client:[{:?}]", emsg, client); + let _write_rs = self.client.write_all(emsg); + let _flush_rs = self.client.flush(); + log::warn!("+++ flush emsg[{:?}], client:[{:?}]", emsg, self.client); e } _ => e, @@ -154,48 +144,35 @@ where // 处理pending中的请求,并且把数据发送到buffer #[inline] fn process_pending(&mut self) -> Result<()> { - let Self { - // 修改处理流程后,不再用pedding - client, - pending, - parser, - start, - start_init, - metrics, - flush, - .. - } = self; // 处理回调 - client.cache(pending.len() > 1); - while let Some(ctx) = pending.front_mut() { + self.client.cache(self.pending.len() > 1); + while let Some(ctx) = self.pending.front_mut() { // 当前请求是第一个请求 - if !*start_init { - *start = ctx.start_at(); - *start_init = true; + if !self.start_init { + self.start = ctx.start_at(); + self.start_init = true; } if !ctx.complete() { break; } - let mut ctx = pending.pop_front().expect("front"); + let mut ctx = self.pending.pop_front().expect("front"); let last = ctx.last(); // 当前不是最后一个值。也优先写入cache - if !last { - client.cache(true); - } + (!last).then(|| self.client.cache(true)); - *metrics.key() += 1; + *self.metrics.key() += 1; let mut response = ctx.take_response(); - parser.write_response( - &mut ResponseContext::new(&mut ctx, metrics, |hash| self.top.shard_idx(hash)), + self.parser.write_response( + &mut ResponseContext::new(&mut ctx, &self.metrics, |hash| self.top.shard_idx(hash)), response.as_mut(), - client, + &mut self.client, )?; let op = ctx.request().operation(); if let Some(rsp) = response { if ctx.is_write_back() && rsp.ok() { - ctx.async_write_back(parser, rsp, self.top.exp_sec(), metrics); + ctx.async_write_back(&self.parser, rsp, self.top.exp_sec(), &mut self.metrics); self.async_pending.push_back(ctx); } } @@ -203,12 +180,12 @@ where // 数据写完,统计耗时。当前数据只写入到buffer中, // 但mesh通常与client部署在同一台物理机上,buffer flush的耗时通常在微秒级。 if last { - let elapsed = start.elapsed(); - *metrics.ops(op) += elapsed; + let elapsed = self.start.elapsed(); + *self.metrics.ops(op) += elapsed; // 统计整机耗时 - *metrics.rtt() += elapsed; - *flush = true; - *start_init = false; + *self.metrics.rtt() += elapsed; + self.flush = true; + self.start_init = false; } } Ok(()) From faad62d52361c496c5da215c1b2c1f61b997a9a5 Mon Sep 17 00:00:00 2001 From: icycrystal4 Date: Sat, 16 Mar 2024 16:09:05 +0800 Subject: [PATCH 15/24] =?UTF-8?q?protocol=20mc:=20write=20response?= =?UTF-8?q?=E4=B9=8B=E5=89=8D=EF=BC=8C=E6=A3=80=E6=9F=A5req=E4=B8=8Erespon?= =?UTF-8?q?se=E7=9A=84seq=E6=98=AF=E5=90=A6=E4=B8=80=E8=87=B4=EF=BC=8C?= =?UTF-8?q?=E4=B8=8D=E4=B8=80=E8=87=B4=E7=9B=B4=E6=8E=A5panic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- protocol/src/memcache/binary/mod.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/protocol/src/memcache/binary/mod.rs b/protocol/src/memcache/binary/mod.rs index 8695a100f..2e84e1813 100644 --- a/protocol/src/memcache/binary/mod.rs +++ b/protocol/src/memcache/binary/mod.rs @@ -130,9 +130,7 @@ impl Protocol for MemcacheBinary { assert!(rsp.len() > 0, "empty rsp:{:?}", rsp); // 验证Opaque是否相同. 不相同说明数据不一致 - if ctx.request().opaque() != rsp.opaque() { - ctx.metric().inconsist(1); - } + self.check(ctx.request(), rsp); // 如果quite 请求没拿到数据,直接忽略 //if QUITE_GET_TABLE[old_op_code as usize] == 1 && !rsp.ok() { @@ -140,7 +138,6 @@ impl Protocol for MemcacheBinary { return Ok(()); } log::debug!("+++ will write mc rsp:{:?}", rsp.data()); - //let data = rsp.data_mut(); rsp.restore_op(old_op_code); w.write_slice(rsp, 0)?; From 3324b75de6dd83d626fe2ed5d97cb4ea975056e0 Mon Sep 17 00:00:00 2001 From: icycrystal4 Date: Wed, 20 Mar 2024 17:02:30 +0800 Subject: [PATCH 16/24] =?UTF-8?q?prometheus:=20register=E6=97=B6=EF=BC=8C?= =?UTF-8?q?=E5=85=A8=E5=B1=80=E5=85=B1=E4=BA=ABbody?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- agent/src/prometheus.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent/src/prometheus.rs b/agent/src/prometheus.rs index 20da3f692..cc7b7a32e 100644 --- a/agent/src/prometheus.rs +++ b/agent/src/prometheus.rs @@ -54,10 +54,10 @@ pub(crate) fn register_target(ctx: &context::Context) { "target": "{local_ip}:{port}" }}"# ); + let body: &'static str = Box::leak(body.into_boxed_str()); let client = Client::new(); let mut interval = interval(Duration::from_secs(60)); loop { - let body = body.clone(); let req = Request::builder() .method("PUT") .uri(&url) From b301597ef5ca6466553636a37599e1084d7be528 Mon Sep 17 00:00:00 2001 From: icycrystal4 Date: Wed, 20 Mar 2024 19:05:57 +0800 Subject: [PATCH 17/24] =?UTF-8?q?prometheus:=20=E5=8D=95=E6=AC=A1=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E5=85=B1=E4=BA=ABext=20buf?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- metrics/src/prometheus.rs | 83 ++++++++++++++++++++++----------------- 1 file changed, 47 insertions(+), 36 deletions(-) diff --git a/metrics/src/prometheus.rs b/metrics/src/prometheus.rs index f15b914b1..455abf4db 100644 --- a/metrics/src/prometheus.rs +++ b/metrics/src/prometheus.rs @@ -1,21 +1,34 @@ use crate::{ItemWriter, WriteTo}; -use std::sync::{ - atomic::{AtomicUsize, Ordering::AcqRel}, - Arc, -}; pub struct Prometheus { secs: f64, - idx: Arc, - left: Vec, + idx: usize, + host: bool, // host相关的metrics是否已经发送 + ext_buf: Vec, // 扩展的buffer,如果ReadBuf没有足够的空间,则使用这个buffer + ext_oft: usize, } impl Prometheus { pub fn new(secs: f64) -> Self { - let idx = Arc::new(AtomicUsize::new(0)); Self { - idx, + idx: 0, secs, - left: Default::default(), + ext_buf: Default::default(), + host: false, + ext_oft: 0, + } + } + // 把ext_buf中的数据拷贝到buf中 + fn copy_buf(&mut self, buf: &mut ReadBuf<'_>) { + if self.ext_buf.len() > 0 { + let left = self.ext_buf.len() - self.ext_oft; + debug_assert!(left > 0); + let n = left.min(buf.remaining()); + buf.put_slice(&self.ext_buf[self.ext_oft..self.ext_oft + n]); + self.ext_oft += n; + if self.ext_oft >= self.ext_buf.len() { + self.ext_buf.clear(); + self.ext_oft = 0; + } } } } @@ -30,48 +43,46 @@ impl AsyncRead for Prometheus { _cx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll> { + // 1. copy buffer + self.copy_buf(buf); let metrics = crate::get_metrics(); let len = metrics.len(); - if self.left.len() > 0 { - let n = std::cmp::min(self.left.len(), buf.remaining()); - let left = self.left.split_off(n); - buf.put_slice(&self.left); - self.left = left; - } - while buf.remaining() > 0 { - let idx = self.idx.fetch_add(1, AcqRel); - if idx >= len { - break; - } - let mut w = PrometheusItemWriter::new(buf); - if idx == 0 { - let mut host = HOST.try_lock().expect("host lock"); - host.snapshot(&mut w, self.secs); - } - let (id, item) = metrics.get_item_id(idx); - item.snapshot(id, &mut w, self.secs); - self.left = w.left(); + let secs = self.secs; + let Self { + host, idx, ext_buf, .. + } = &mut *self; + // 2. write host metrics + let mut w = PrometheusItemWriter::new(buf, ext_buf); + if !*host { + *host = true; + HOST.try_lock().expect("host lock").snapshot(&mut w, secs); + }; + // 3. write metrics by idx + while w.remaining() > 0 && *idx < len { + let (id, item) = metrics.get_item_id(*idx); + item.snapshot(id, &mut w, secs); + *idx += 1; } Poll::Ready(Ok(())) } } -struct PrometheusItemWriter<'a, 'r> { - left: Vec, +struct PrometheusItemWriter<'a, 'r, 'b> { + left: &'b mut Vec, buf: &'a mut ReadBuf<'r>, first: bool, // 在lable中,第一个k/v前面不输出 ',' } -impl<'a, 'r> PrometheusItemWriter<'a, 'r> { - fn new(buf: &'a mut ReadBuf<'r>) -> Self { +impl<'a, 'r, 'b> PrometheusItemWriter<'a, 'r, 'b> { + fn new(buf: &'a mut ReadBuf<'r>, ext: &'b mut Vec) -> Self { Self { - left: Vec::new(), + left: ext, buf, first: true, } } #[inline] - fn left(self) -> Vec { - self.left + fn remaining(&self) -> usize { + self.buf.remaining() } #[inline] fn put_label(&mut self, name: &str, val: &[u8]) { @@ -88,7 +99,7 @@ impl<'a, 'r> PrometheusItemWriter<'a, 'r> { } } } -impl<'a, 'r> ItemWriter for PrometheusItemWriter<'a, 'r> { +impl<'a, 'r, 'b> ItemWriter for PrometheusItemWriter<'a, 'r, 'b> { #[inline] fn put_slice>(&mut self, data: S) { let data = data.as_ref(); From 257016583882d12f6067be21673fcf806c9d25d6 Mon Sep 17 00:00:00 2001 From: icycrystal4 Date: Wed, 20 Mar 2024 20:35:37 +0800 Subject: [PATCH 18/24] =?UTF-8?q?dns=20lookup:=20=E6=AF=8F=E6=AC=A1?= =?UTF-8?q?=E6=89=AB=E6=8F=8F=E5=AE=8C=E6=89=80=E6=9C=89=E7=9A=84host?= =?UTF-8?q?=E5=90=8E=EF=BC=8C=E5=91=A8=E6=9C=9F=E4=BB=8E0=E5=BC=80?= =?UTF-8?q?=E5=A7=8B=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- agent/src/main.rs | 20 +++++++++++--------- context/src/lib.rs | 7 +++++++ discovery/src/dns/mod.rs | 1 + 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/agent/src/main.rs b/agent/src/main.rs index 570b094ae..873c252da 100644 --- a/agent/src/main.rs +++ b/agent/src/main.rs @@ -69,15 +69,17 @@ async fn discovery_init(ctx: &Context, rx: Receiver let service_pool_socks_url = ctx.service_pool_socks_url(); //watch_discovery之前执行清理sock - let mut dir = tokio::fs::read_dir(&ctx.service_path).await?; - while let Some(child) = dir.next_entry().await? { - let path = child.path(); - //从vintage获取socklist,或者存在unixsock,需要事先清理 - if service_pool_socks_url.len() > 1 - || path.to_str().map(|s| s.ends_with(".sock")).unwrap_or(false) - { - log::info!("{:?} exists. deleting", path); - let _ = tokio::fs::remove_file(path).await; + if !ctx.disable_clean_service_path { + let mut dir = tokio::fs::read_dir(&ctx.service_path).await?; + while let Some(child) = dir.next_entry().await? { + let path = child.path(); + //从vintage获取socklist,或者存在unixsock,需要事先清理 + if service_pool_socks_url.len() > 1 + || path.to_str().map(|s| s.ends_with(".sock")).unwrap_or(false) + { + log::info!("{:?} exists. deleting", path); + let _ = tokio::fs::remove_file(path).await; + } } } diff --git a/context/src/lib.rs b/context/src/lib.rs index 25f35bc91..2fabab0ec 100644 --- a/context/src/lib.rs +++ b/context/src/lib.rs @@ -73,6 +73,13 @@ pub struct ContextOption { )] pub service_path: String, + #[clap( + long, + help("clean path for unix domain socket ."), + default_value("false") + )] + pub disable_clean_service_path: bool, + #[clap(short, long, help("starting in upgrade mode"))] upgrade: bool, diff --git a/discovery/src/dns/mod.rs b/discovery/src/dns/mod.rs index 9cdd83915..ae7e5a5ab 100644 --- a/discovery/src/dns/mod.rs +++ b/discovery/src/dns/mod.rs @@ -190,6 +190,7 @@ impl DnsCache { let iter = self.hosts.hosts[self.last_idx..end].iter_mut(); self.last_idx = end; if self.last_idx == len { + self.cycle = 0; self.last_idx = 0; } HostRecordIter::new(iter) From 29ae2fecedfede5db33be7df120ba26e541547fa Mon Sep 17 00:00:00 2001 From: icycrystal4 Date: Fri, 22 Mar 2024 12:08:11 +0800 Subject: [PATCH 19/24] =?UTF-8?q?top=20discovery:=20=E7=AE=80=E5=8C=96?= =?UTF-8?q?=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.toml | 2 +- agent/src/main.rs | 9 +- agent/src/service.rs | 8 +- discovery/Cargo.toml | 1 - discovery/src/dns/lookup.rs | 23 +-- discovery/src/dns/mod.rs | 19 ++- discovery/src/lib.rs | 6 +- discovery/src/topology.rs | 4 +- discovery/src/update.rs | 301 ++++++++++++++++++++++++++++++------ endpoint/src/dns.rs | 2 +- stream/src/checker.rs | 4 +- stream/src/reconn.rs | 4 +- 12 files changed, 290 insertions(+), 93 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d6c0577b2..63e553186 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,5 +44,5 @@ codegen-units = 256 [workspace.dependencies] -tokio = { version = "1.32", features = ["rt", "fs", "net", "rt-multi-thread", "time", "sync", "signal"], default-features = false } +tokio = { version = "1.36", features = ["io-util", "rt", "fs", "net", "rt-multi-thread", "time", "sync", "signal"], default-features = false } serde = { version = "1.0.126", features = ["derive"], default-features = false } diff --git a/agent/src/main.rs b/agent/src/main.rs index 873c252da..608dc3e7a 100644 --- a/agent/src/main.rs +++ b/agent/src/main.rs @@ -1,4 +1,4 @@ -use ds::{chan::Receiver, BrzMalloc}; +use ds::BrzMalloc; #[global_allocator] static GLOBAL: BrzMalloc = BrzMalloc {}; @@ -13,10 +13,7 @@ mod init; use ds::time::{sleep, Duration}; use rt::spawn; -use protocol::{Parser, Result}; -use stream::{Backend, Request}; -type Endpoint = Backend; -type Topology = endpoint::TopologyProtocol; +use protocol::Result; // 默认支持 fn main() -> Result<()> { @@ -58,7 +55,7 @@ async fn run() -> Result<()> { } } -async fn discovery_init(ctx: &Context, rx: Receiver>) -> Result<()> { +async fn discovery_init(ctx: &Context, rx: service::Receiver) -> Result<()> { // 将dns resolver的初始化放到外层,提前进行,避免并发场景下顺序错乱 fishermen let discovery = discovery::Discovery::from_url(&ctx.discovery); let snapshot = ctx.snapshot_path.to_string(); diff --git a/agent/src/service.rs b/agent/src/service.rs index 8ce002c8b..2df885121 100644 --- a/agent/src/service.rs +++ b/agent/src/service.rs @@ -5,7 +5,6 @@ use rt::spawn; use std::sync::Arc; use discovery::{TopologyReadGuard, TopologyWriteGuard}; -use ds::chan::Sender; use metrics::Path; use protocol::{Parser, Result}; use stream::pipeline::copy_bidirectional; @@ -16,15 +15,18 @@ type Topology = endpoint::TopologyProtocol; use metrics::Status; // 一直侦听,直到成功侦听或者取消侦听(当前尚未支持取消侦听) // 1. 尝试侦听之前,先确保服务配置信息已经更新完成 +type Sender = ds::chan::Sender<(String, TopologyWriteGuard)>; +pub(super) type Receiver = ds::chan::Receiver<(String, TopologyWriteGuard)>; pub(super) async fn process_one( quard: &Quadruple, - discovery: Sender>, + discovery: Sender, ) -> std::result::Result<(), Box> { let p = Parser::try_from(&quard.protocol())?; let top = endpoint::TopologyProtocol::try_from(p.clone(), quard.endpoint())?; let (tx, rx) = discovery::topology(top, &quard.service()); // 注册,定期更新配置 - discovery.send(tx).await.map_err(|e| e.to_string())?; + let service = quard.service().to_string(); + discovery.send((service, tx)).await?; let path = Path::new(vec![quard.protocol(), &quard.biz()]); let mut metrics = StreamMetrics::new(&path); diff --git a/discovery/Cargo.toml b/discovery/Cargo.toml index 11c0a3d2a..354d2d292 100644 --- a/discovery/Cargo.toml +++ b/discovery/Cargo.toml @@ -28,7 +28,6 @@ serde_json = "1.0.65" rand = "0.8.4" md5 = "0.7" bs58 = "0.4" -trust-dns-resolver = {version = "0.23.0"} once_cell = "1.14.0" noop-waker = "0.1.0" ctor = "*" diff --git a/discovery/src/dns/lookup.rs b/discovery/src/dns/lookup.rs index 5d78d9317..c09b96f3f 100644 --- a/discovery/src/dns/lookup.rs +++ b/discovery/src/dns/lookup.rs @@ -1,27 +1,16 @@ use super::Record; -pub(super) struct Lookup { - //resolver: TokioAsyncResolver, -} - -//use trust_dns_resolver::{lookup_ip::LookupIp, TokioAsyncResolver}; +pub(super) struct Lookup {} use std::net::IpAddr; impl Lookup { pub(super) fn new() -> Self { - Self { - //resolver: TokioAsyncResolver::tokio_from_system_conf() - // .expect("Failed to create the resolver"), - } + Self {} } - //async fn lookup(&self, host: &str) -> std::io::Result { - // let ips = self.resolver.lookup_ip(host).await?; - // Ok(IpAddrLookup::LookupIp(ips)) - //} fn dns_lookup(&self, host: &str) -> std::io::Result { let ips = dns_lookup::lookup_host(host)?; Ok(IpAddrLookup::AddrInfoIter(ips)) } - pub(super) async fn lookups<'a, I>(&self, iter: I) -> (usize, Option) + pub(super) fn lookups<'a, I>(&self, iter: I) -> (usize, Option) where I: Iterator, { @@ -44,17 +33,11 @@ impl Lookup { } pub(super) enum IpAddrLookup { - //LookupIp(LookupIp), AddrInfoIter(Vec), } impl IpAddrLookup { pub(super) fn visit_v4(&self, mut v: impl FnMut(std::net::Ipv4Addr)) { match self { - //Self::LookupIp(ips) => ips.iter().for_each(|ip| { - // if let IpAddr::V4(ip) = ip { - // v(ip); - // } - //}), Self::AddrInfoIter(iter) => { for ip in iter.iter() { if let std::net::IpAddr::V4(ip) = ip { diff --git a/discovery/src/dns/mod.rs b/discovery/src/dns/mod.rs index ae7e5a5ab..104feeaf5 100644 --- a/discovery/src/dns/mod.rs +++ b/discovery/src/dns/mod.rs @@ -124,7 +124,7 @@ pub fn start_dns_resolver_refresher() -> impl Future { let _r = DNSCACHE.set(reader); assert!(_r.is_ok(), "dns cache set failed"); async move { - let resolver = Lookup::new(); + let mut resolver = Lookup::new(); log::info!("task started ==> dns cache refresher"); let mut tick = interval(Duration::from_secs(1)); loop { @@ -132,12 +132,17 @@ pub fn start_dns_resolver_refresher() -> impl Future { local_cache.register(host, notify); } // 处理新增的 - let (num, cache) = resolver.lookups(local_cache.iter()).await; - if num > 0 { - let new = local_cache.clone(); - writer.update(new); - local_cache.notify(cache.expect("cache none"), num); - } + (writer, resolver, local_cache) = tokio::task::spawn_blocking(move || { + let (num, cache) = resolver.lookups(local_cache.iter()); + if num > 0 { + let new = local_cache.clone(); + writer.update(new); + local_cache.notify(cache.expect("cache none"), num); + } + (writer, resolver, local_cache) + }) + .await + .expect("no err"); tick.tick().await; } diff --git a/discovery/src/lib.rs b/discovery/src/lib.rs index eb84b154c..67cb52918 100644 --- a/discovery/src/lib.rs +++ b/discovery/src/lib.rs @@ -1,5 +1,5 @@ -pub(crate) mod cache; -pub(crate) mod cfg; +//pub(crate) mod cache; +//pub(crate) mod cfg; pub mod distance; pub mod socks; @@ -10,7 +10,7 @@ pub use update::*; pub mod dns; mod fixed; mod path; -mod sig; +//mod sig; pub use fixed::Fixed; pub use topology::*; diff --git a/discovery/src/topology.rs b/discovery/src/topology.rs index 9adf66cbb..5c87ef67c 100644 --- a/discovery/src/topology.rs +++ b/discovery/src/topology.rs @@ -14,8 +14,8 @@ use crate::path::GetNamespace; pub trait TopologyWrite { fn update(&mut self, name: &str, cfg: &str); #[inline] - fn disgroup<'a>(&self, _path: &'a str, cfg: &'a str) -> Vec<(&'a str, &'a str)> { - vec![("", cfg)] + fn disgroup<'a>(&self, path: &'a str, cfg: &'a str) -> Vec<(&'a str, &'a str)> { + vec![(path, cfg)] } // 部分场景下,配置更新后,还需要load 命名服务,比如dns。 #[inline] diff --git a/discovery/src/update.rs b/discovery/src/update.rs index 7725b1635..5472194b2 100644 --- a/discovery/src/update.rs +++ b/discovery/src/update.rs @@ -1,28 +1,24 @@ // 定期更新discovery. -use super::{Discover, ServiceId, TopologyWrite}; +use super::{Discover, TopologyWrite}; use ds::chan::Receiver; use ds::time::{interval, Duration}; -use crate::cache::DiscoveryCache; -use crate::path::ToName; use std::collections::HashMap; -use std::sync::atomic::{AtomicUsize, Ordering::*}; pub async fn watch_discovery( snapshot: String, discovery: D, - rx: Receiver, + rx: Receiver<(String, T)>, tick: Duration, cb: super::fixed::Fixed, ) where - T: Send + TopologyWrite + ServiceId + 'static + Sync, + T: Send + TopologyWrite + 'static + Sync, D: Send + Sync + Discover + Unpin + 'static, { let tick = Duration::from_secs(3).max(tick); - let cache = DiscoveryCache::new(discovery); let mut refresher = Refresher { snapshot, - discovery: cache, + discovery, rx, tick, cb, @@ -31,17 +27,17 @@ pub async fn watch_discovery( } struct Refresher { - discovery: DiscoveryCache, + discovery: D, snapshot: String, tick: Duration, - rx: Receiver, + rx: Receiver<(String, T)>, cb: super::fixed::Fixed, } impl Refresher where D: Discover + Send + Unpin + Sync, - T: Send + TopologyWrite + ServiceId + 'static + Sync, + T: Send + TopologyWrite + 'static + Sync, { async fn watch(&mut self) { log::info!("task started ==> topology refresher"); @@ -49,51 +45,266 @@ where let period = Duration::from_secs(1); let cycle = (self.tick.as_secs_f64() / period.as_secs_f64()).ceil() as usize; let mut cycle_i = 0usize; - let mut first_cycle = true; let mut tick = interval(period); - self.cb.with_discovery(self.discovery.inner()).await; - let mut services = HashMap::new(); - // 每秒钟处理一次,一次只处理部分service。 + self.cb.with_discovery(&self.discovery).await; + let mut services = Services::new(&self.snapshot); // 每个周期结束清理缓存 loop { - while let Ok(t) = self.rx.try_recv() { - let service = t.service().name(); - if services.contains_key(&service) { - log::error!("service duplicatedly registered:{}", service); - } else { - static SEQ: AtomicUsize = AtomicUsize::new(0); - let id = SEQ.fetch_add(1, Relaxed); - log::debug!("service path:{:?} registered ", service); - let mut t: crate::cfg::Config = t.into(); - t.init(&self.snapshot, &mut self.discovery).await; - services.insert(service, (id, t)); + while let Ok((name, t)) = self.rx.try_recv() { + log::info!("receive new service: {}", name); + services.register(name, t, &self.discovery).await; + } + cycle_i += 1; + + // 部分服务更新配置后,需要重新加载 + if cycle_i <= cycle || cycle_i % cycle == 0 { + services.check_load().await; + if !self.cb.inited() || cycle_i % cycle == 0 { + self.cb.with_discovery(&self.discovery).await; } } - for (_name, (id, service)) in services.iter_mut() { - // 每秒钟只更新部分。 - if cycle_i == *id % cycle { - service - .check_update(&self.snapshot, &mut self.discovery) - .await; + let i = cycle_i % cycle; + // 更新部分 + for (idx, group) in services.groups.iter_mut().enumerate() { + if idx % cycle == i { + group.refresh(&self.snapshot, &self.discovery).await; + } else { + group.refresh_new(&self.snapshot, &self.discovery).await; } } + // 更新新注册的 + tick.tick().await; + } + } +} - for (_service, (_id, t)) in services.iter_mut() { - t.try_load(); +struct Services { + snapshot: String, + indices: HashMap, + groups: Vec>, +} +impl Services { + fn new(snapshot: &str) -> Self { + Self { + snapshot: snapshot.to_string(), + groups: Vec::with_capacity(64), + indices: HashMap::with_capacity(64), + } + } + async fn check_load(&mut self) { + for g in self.groups.iter_mut() { + g.load(); + } + } + fn get_group(&mut self, group: &str) -> Option<&mut ServiceGroup> { + let &idx = self.indices.get(group)?; + assert!(idx < self.groups.len()); + Some(&mut self.groups[idx]) + } + async fn register(&mut self, name: String, top: T, d: &D) { + // name的格式为 group_path:namespace + // 其中,group_path是一个路径,如:分隔符为'+'。 + let ns_idx = name.rfind(':'); + let (path_group, namespace) = match ns_idx { + Some(i) => (&name[..i], Some(&name[i + 1..])), + None => (&name[..], None), + }; + let g_name = path_group.split('+').last().expect("not valid"); + + let group = match self.get_group(g_name) { + Some(g) => g, + None => { + let idx = self.groups.len(); + let local_path = path_group.to_string(); + let remote_path = path_group.replace('+', "/"); + let name = g_name.to_string(); + self.groups + .push(ServiceGroup::new(name, local_path, remote_path)); + self.indices.insert(g_name.to_string(), idx); + let g = self.groups.last_mut().unwrap(); + assert_eq!(g.name, g_name); + g.init(&self.snapshot, d).await; + g } - cycle_i += 1; - if cycle_i == cycle { - self.cb.with_discovery(self.discovery.inner()).await; - cycle_i = 0; - // 清空缓存 - self.discovery.clear(); + }; + log::info!("register service: {} => {}", name, g_name); + let service = namespace + .map(|s| s.to_string()) + .unwrap_or_else(|| g_name.to_string()); + group.register(service, top); + } +} + +use serde::{Deserialize, Serialize}; +// 一个group会有一个或者多个namespace,共享一个group配置。 +#[derive(Serialize, Deserialize)] +struct ServiceGroup { + changed: bool, + name: String, // group的名称。 + local_path: String, + path: String, // 从discover获取配置时使用group的path。 + sig: String, + cfg: String, + #[serde(skip_serializing, skip_deserializing)] + cache: HashMap, + #[serde(skip_serializing, skip_deserializing)] + namespaces: Vec>, +} +use tokio::fs::File; +use tokio::io::AsyncWriteExt; +impl ServiceGroup { + // 先从snapshot获取配置。然后再从discover刷新。 + async fn init(&mut self, snapshot: &str, d: &D) { + self.load_from_snapshot(snapshot).await; + self.load_from_discover(snapshot, d).await; + } + fn load(&mut self) { + for s in &mut self.namespaces { + if s.top.need_load() { + s.top.load(); } - // 第一个循环处于冷启动期,需要加快固定任务的加载频率,避免请求失败导致其他问题(如socks) - if first_cycle && !self.cb.inited() { - self.cb.with_discovery(self.discovery.inner()).await; - first_cycle = false; + } + } + // 第一行是sig + // 第二行是group name + // 后面是cfg + async fn load_from_snapshot(&mut self, snapshot: &str) -> Option<()> { + let path = format!("{}/{}", snapshot, self.local_path); + // 返回第一行与剩余的内容 + fn take_line(mut s: String) -> Option<(String, String)> { + let idx = s.find("\n")?; + let left = s.split_off(idx + 1); + s.pop(); + if s.len() > 0 && s.as_bytes()[s.len() - 1] == b'\r' { + s.pop(); } - tick.tick().await; + Some((s, left)) + } + let content = tokio::fs::read_to_string(&path) + .await + .map_err(|e| log::info!("load snapshot {path} err:{e:?}")) + .ok()?; + let (sig, group_cfg) = take_line(content)?; + let (group, cfg) = take_line(group_cfg)?; + + if group != self.name { + log::warn!("group name changed: {path} '{}' -> '{}'", self.name, group); + return None; + } + log::info!("load from snapshot: {} {} => {}", path, self.sig, sig); + self.sig = sig; + self.cfg = cfg; + self.changed = true; + Some(()) + } + // 第一行是sig + // 第二行是group name + // 后面是cfg + async fn _dump_to_snapshot(&self, snapshot: &str) -> std::io::Result<()> { + let path = format!("{}/{}", snapshot, self.local_path); + log::info!("dump to snapshot: {}", path); + let mut file = File::create(path).await?; + file.write_all(&self.sig.as_bytes()).await?; + file.write_all(b"\n").await?; + file.write_all(&self.name.as_bytes()).await?; + file.write_all(b"\n").await?; + file.write_all(&self.cfg.as_bytes()).await?; + Ok(()) + } + async fn dump_to_snapshot(&self, snapshot: &str) { + if let Err(e) = self._dump_to_snapshot(snapshot).await { + log::warn!("failed to dump to snapshot: {}, {}", self.name, e); + } + } + async fn load_from_discover(&mut self, snapshot: &str, d: &D) { + log::debug!("load from discover: {}", self.path); + use crate::Config; + match d.get_service::(&self.path, &self.sig).await { + Ok(Config::NotFound) => log::warn!("service not found: {}", self.path), + Ok(Config::NotChanged) => log::debug!("service not changed: {}", self.path), + Ok(Config::Config(sig, cfg)) => { + log::info!("service changed: {} sig {} => {}", self.path, self.sig, sig); + self.sig = sig; + self.cfg = cfg; + self.dump_to_snapshot(snapshot).await; + self.cache.clear(); + self.changed = true; + } + Err(e) => log::error!("failed to get service: {}, {}", self.path, e), + } + } + async fn refresh_new(&mut self, snapshot: &str, d: &D) { + if self.cfg.len() == 0 { + self.load_from_discover(snapshot, d).await; + } + self.update_all(true); + } + async fn refresh(&mut self, snapshot: &str, d: &D) { + self.load_from_discover(snapshot, d).await; + self.update_all(false); + } + // new: 只更新新注册的服务 + fn update_all(&mut self, new: bool) { + if self.changed && self.namespaces.len() > 0 { + let s = self.namespaces.first().expect("empty"); + self.cache = s + .top + .disgroup(&self.name, &self.cfg) + .into_iter() + .map(|(k, v)| (k.to_string(), v.to_string())) + .collect(); + for s in self.namespaces.iter_mut() { + // 如果是新注册的,则不更新 + if new && s.cfg.len() > 0 { + continue; + } + let ns = &s.name; + match self.cache.get(ns) { + Some(cfg) => s.update(cfg), + None => log::warn!("namespace {} not found", ns), + } + } + self.changed = false; + } + } + fn register(&mut self, ns: String, top: T) { + assert!(self.namespaces.iter().find(|s| s.name == ns).is_none()); + let service = Service::new(ns, top); + self.namespaces.push(service); + self.changed = true; + } + fn new(name: String, local_path: String, path: String) -> Self { + Self { + name, + path, + local_path, + sig: String::new(), + cfg: String::new(), + namespaces: Vec::new(), + cache: HashMap::new(), + changed: false, + } + } +} + +struct Service { + name: String, + top: T, + cfg: String, +} +impl Service { + fn new(name: String, top: T) -> Self { + Self { + name, + top, + cfg: String::new(), + } + } + fn update(&mut self, cfg: &str) { + if cfg != self.cfg { + self.cfg = cfg.to_string(); + self.top.update(&self.name, cfg); + log::info!("Updated {}", self.name); } } } diff --git a/endpoint/src/dns.rs b/endpoint/src/dns.rs index c0cdf0793..23f1ac947 100644 --- a/endpoint/src/dns.rs +++ b/endpoint/src/dns.rs @@ -122,7 +122,7 @@ impl LoadGuard { log::warn!("{} clear_status failed", self._service); } if !f() { - log::info!("{} load failed", self._service); + log::info!("{} load not complete", self._service); if let Err(_e) = self.guard.compare_exchange(false, true, AcqRel, Relaxed) { log::warn!("{} renotified failed => {}", self._service, _e); } diff --git a/stream/src/checker.rs b/stream/src/checker.rs index 21709ce08..b38ed08fd 100644 --- a/stream/src/checker.rs +++ b/stream/src/checker.rs @@ -65,7 +65,7 @@ impl BackendChecker { if stream.is_none() { // 连接失败,按策略sleep log::debug!("+++ connected failed to:{}", self.addr); - reconn.conn_failed().await; + reconn.conn_failed(&self.addr).await; self.init.on(); continue; } @@ -87,7 +87,7 @@ impl BackendChecker { stream.cancel(); //当作连接失败处理,不立马重试 //todo:可以尝试将等待操作统一提取到循环开头 - reconn.conn_failed().await; + reconn.conn_failed(&self.addr).await; continue; } } diff --git a/stream/src/reconn.rs b/stream/src/reconn.rs index 9cb1357d3..f891cd57f 100644 --- a/stream/src/reconn.rs +++ b/stream/src/reconn.rs @@ -14,13 +14,13 @@ impl ReconnPolicy { // 连接失败,为下一次连接做准备:sleep一段时间,避免无意义的重连 // 第一次快速重连 - pub async fn conn_failed(&mut self) { + pub async fn conn_failed(&mut self, addr: &str) { self.conns += 1; // 第一次失败的时候,continue_fails为0,因此不会sleep let sleep_mills = (self.continue_fails * 500).min(6000); log::info!( - "{}-th conn {} sleep:{}ms", + "{}-th conn to {addr} {} sleep:{}ms", self.conns, self.continue_fails, sleep_mills, From 9994d15ae590c5d54b8e20652c506a857f8f13c6 Mon Sep 17 00:00:00 2001 From: icycrystal4 Date: Fri, 22 Mar 2024 15:42:07 +0800 Subject: [PATCH 20/24] =?UTF-8?q?top=20discovery:=20=E7=AE=80=E5=8C=96?= =?UTF-8?q?=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- discovery/src/update.rs | 48 +++++++++++++++++------------------------ tests/Cargo.toml | 1 + tests/src/dns.rs | 32 +++++++++++++-------------- 3 files changed, 36 insertions(+), 45 deletions(-) diff --git a/discovery/src/update.rs b/discovery/src/update.rs index 5472194b2..96112b7eb 100644 --- a/discovery/src/update.rs +++ b/discovery/src/update.rs @@ -101,43 +101,40 @@ impl Services { assert!(idx < self.groups.len()); Some(&mut self.groups[idx]) } + // name的格式。 dir0+dir1+dir2+...+group:namespace + // 规范:最后一个+号之后的是group:namespace + // namespace是可选。 + // namespace之前的是group的路径,如:分隔符为'+'。 async fn register(&mut self, name: String, top: T, d: &D) { - // name的格式为 group_path:namespace - // 其中,group_path是一个路径,如:分隔符为'+'。 - let ns_idx = name.rfind(':'); - let (path_group, namespace) = match ns_idx { - Some(i) => (&name[..i], Some(&name[i + 1..])), - None => (&name[..], None), + let group = name.split('+').last().expect("name"); + let mut group_namespace = group.split(':'); + let group = group_namespace.next().expect("group"); + let (path, service) = match group_namespace.next() { + Some(ns) => (&name[..name.len() - ns.len() - 1], ns), + None => (name.as_str(), group), }; - let g_name = path_group.split('+').last().expect("not valid"); - let group = match self.get_group(g_name) { + let g = match self.get_group(group) { Some(g) => g, None => { let idx = self.groups.len(); - let local_path = path_group.to_string(); - let remote_path = path_group.replace('+', "/"); - let name = g_name.to_string(); + let local = path.to_string(); + let remote = path.replace('+', "/"); self.groups - .push(ServiceGroup::new(name, local_path, remote_path)); - self.indices.insert(g_name.to_string(), idx); - let g = self.groups.last_mut().unwrap(); - assert_eq!(g.name, g_name); + .push(ServiceGroup::new(group.to_string(), local, remote)); + self.indices.insert(group.to_string(), idx); + let g = self.groups.get_mut(idx).expect("not push"); + assert_eq!(g.name, group); g.init(&self.snapshot, d).await; g } }; - log::info!("register service: {} => {}", name, g_name); - let service = namespace - .map(|s| s.to_string()) - .unwrap_or_else(|| g_name.to_string()); - group.register(service, top); + log::info!("register service: {} => {}", name, group); + g.register(service.to_string(), top); } } -use serde::{Deserialize, Serialize}; // 一个group会有一个或者多个namespace,共享一个group配置。 -#[derive(Serialize, Deserialize)] struct ServiceGroup { changed: bool, name: String, // group的名称。 @@ -145,9 +142,7 @@ struct ServiceGroup { path: String, // 从discover获取配置时使用group的path。 sig: String, cfg: String, - #[serde(skip_serializing, skip_deserializing)] cache: HashMap, - #[serde(skip_serializing, skip_deserializing)] namespaces: Vec>, } use tokio::fs::File; @@ -180,10 +175,7 @@ impl ServiceGroup { } Some((s, left)) } - let content = tokio::fs::read_to_string(&path) - .await - .map_err(|e| log::info!("load snapshot {path} err:{e:?}")) - .ok()?; + let content = tokio::fs::read_to_string(&path).await.ok()?; let (sig, group_cfg) = take_line(content)?; let (group, cfg) = take_line(group_cfg)?; diff --git a/tests/Cargo.toml b/tests/Cargo.toml index ddfcd3e40..195236fb5 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -24,6 +24,7 @@ tokio.workspace = true ctor = "0.1.23" mysql_async ="0.31.3" chrono = "0.4" +lazy_static = "*" mysql = "*" base64 = "0.21.0" diff --git a/tests/src/dns.rs b/tests/src/dns.rs index d68ade7ba..2c2f965e1 100644 --- a/tests/src/dns.rs +++ b/tests/src/dns.rs @@ -1,32 +1,30 @@ use std::collections::HashMap; -static mut CACHE: Option>> = None; +use std::sync::{Mutex, MutexGuard}; +lazy_static::lazy_static! { + static ref CACHE: Mutex>> = Mutex::new(Default::default()); +} struct Dns; impl Dns { + fn get(&mut self) -> MutexGuard<'_, HashMap>> { + CACHE.try_lock().expect("run in multithread runtime") + } fn insert(&mut self, host: &str, ip: &str) { - unsafe { - let ips = CACHE - .get_or_insert(Default::default()) - .entry(host.to_string()) - .or_insert(Vec::new()); - ips.push(ip.to_string()); - } + let mut cache = self.get(); + let ips = cache.entry(host.to_string()).or_insert(Vec::new()); + ips.push(ip.to_string()); } } use endpoint::dns::{DnsLookup, IpAddr, Lookup}; impl Lookup for Dns { fn lookup(host: &str, mut f: impl FnMut(&[IpAddr])) { - unsafe { - if let Some(cache) = &CACHE { - if let Some(ips) = cache.get(host) { - let mut addrs = Vec::with_capacity(ips.len()); - for ip in ips { - addrs.push(ip.parse().unwrap()); - } - f(&addrs); - } + if let Some(ips) = Dns.get().get(host) { + let mut addrs = Vec::with_capacity(ips.len()); + for ip in ips { + addrs.push(ip.parse().unwrap()); } + f(&addrs); } } } From eba149e0e39889da9e3bab4e7138379b208290ec Mon Sep 17 00:00:00 2001 From: icycrystal4 Date: Fri, 22 Mar 2024 19:46:39 +0800 Subject: [PATCH 21/24] =?UTF-8?q?=E5=88=A0=E9=99=A4=E9=83=A8=E5=88=86?= =?UTF-8?q?=E4=B8=8D=E5=BF=85=E8=A6=81=E7=9A=84=E6=96=87=E4=BB=B6=EF=BC=8C?= =?UTF-8?q?=E6=8A=8Atop=20update=E7=9A=84=E7=A7=BB=E5=8A=A8=E5=88=B0update?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- discovery/src/cache.rs | 98 ------------------------- discovery/src/cfg.rs | 139 ------------------------------------ discovery/src/dns/lookup.rs | 44 ++++++------ discovery/src/dns/mod.rs | 122 ++++++++++++++++++++++++------- discovery/src/lib.rs | 2 +- discovery/src/path.rs | 62 ---------------- discovery/src/sig.rs | 46 ------------ discovery/src/topology.rs | 7 -- discovery/src/update.rs | 5 ++ tests/src/dns.rs | 35 +++++++++ 10 files changed, 162 insertions(+), 398 deletions(-) delete mode 100644 discovery/src/cache.rs delete mode 100644 discovery/src/cfg.rs delete mode 100644 discovery/src/path.rs delete mode 100644 discovery/src/sig.rs diff --git a/discovery/src/cache.rs b/discovery/src/cache.rs deleted file mode 100644 index cdbce1692..000000000 --- a/discovery/src/cache.rs +++ /dev/null @@ -1,98 +0,0 @@ -use std::collections::HashMap; - -use crate::path::{GetNamespace, ToName, ToPath}; -use crate::sig::Sig; -use crate::Discover; - -pub(crate) trait Cache { - // 有更新,并且成功获取则返回对应的值 - async fn get( - &mut self, - name: &str, - sig: &Sig, - p: &G, - ) -> Option<(String, Sig)>; -} - -impl DiscoveryCache { - pub(crate) fn new(d: D) -> Self { - Self { - discovery: d, - cache: Default::default(), - sigs: Default::default(), - } - } -} - -pub(crate) struct DiscoveryCache { - discovery: D, - cache: HashMap, // 这里面的key存储的是ServiceId::name。 - sigs: HashMap, // 这里面存储的key是Service::path -} - -impl Cache for DiscoveryCache { - async fn get( - &mut self, - name: &str, - sig: &Sig, - p: &G, - ) -> Option<(String, Sig)> { - let path = name.path(); - let sig = sig.group_sig(); - // 从discovery获取。 - if !self.sigs.contains_key(&path) { - // 说明当前是第一次从snapshot获取到的值。 - self.sigs.insert(path.clone(), sig.to_string()); - } - // 先检查 sig 是否相等 - let path_sig = self.sigs.get_mut(&path).expect("not init"); - if path_sig.len() > 0 && *path_sig == sig { - log::debug!("cache hit group not changed. {}", name); - return None; - } - // 一个服务只会访问一次。直接删除即可。 - if let Some((cfg, s)) = self.cache.remove(name) { - log::debug!("cache hit. name:{} sig:{:?}", name, s); - return Some((cfg, s)); - } - if let Ok(crate::Config::Config(s, c)) = - self.discovery.get_service::(&path, sig).await - { - *path_sig = s; - // 一个path可能会对应于多个name。一次解析,后续直接缓存。 - if sig != *path_sig { - // 有更新 - let groups = p.disgroup(&path, &c); - log::info!("service {} updated from '{}' to {}", path, sig, path_sig); - for (n, v) in groups { - // n: 是namespace。 v: 对应的配置 - let name = path.cat(n).name(); - let hash = Sig::new(md5::compute(v).into(), path_sig.clone()); - self.cache.insert(name, (v.to_string(), hash)); - } - return self.cache.remove(name).or_else(|| { - log::warn!("group cfg found, but namespace missed:{}", name); - let empty_sig = Sig::new(md5::compute(&[]).into(), path_sig.clone()); - Some((String::new(), empty_sig)) - }); - } - log::warn!("this should not happen. cfg not found for {}", name); - } - if path_sig != sig { - *path_sig = sig.to_string(); - } - log::debug!("{} not changed by discovery", name); - None - } -} -impl DiscoveryCache { - // 清空缓存 - pub(crate) fn clear(&mut self) { - for (_path, sig) in self.sigs.iter_mut() { - sig.clear(); - } - } - pub(crate) fn inner(&self) -> &D { - &self.discovery - } -} diff --git a/discovery/src/cfg.rs b/discovery/src/cfg.rs deleted file mode 100644 index fb9396d00..000000000 --- a/discovery/src/cfg.rs +++ /dev/null @@ -1,139 +0,0 @@ -use std::io::{Error, ErrorKind, Result}; -use std::path::PathBuf; -use ds::time::{Duration, Instant}; - -use tokio::fs::File; -use tokio::io::{AsyncReadExt, AsyncWriteExt}; - -use super::{ServiceId, TopologyWrite}; -use crate::cache::Cache; -use crate::path::{GetNamespace, ToName}; -use crate::sig::Sig; -pub(crate) struct Config { - sig: Sig, - inner: T, - last_update: Instant, // 上一次更新的时间。 - last_load: Instant, -} - -impl From for Config { - #[inline] - fn from(inner: T) -> Self { - Self { - sig: Default::default(), - inner, - last_update: Instant::now(), - last_load: Instant::now(), - } - } -} - -impl Config -where - T: Send + TopologyWrite + ServiceId + 'static + Sync, -{ - // 初始化。 - pub(crate) async fn init(&mut self, snapshot: &str, discovery: &mut C) { - match self.load_from_snapshot(snapshot).await { - Ok((cfg, sig)) => self.update(&cfg, sig), - Err(_e) => self.check_update(snapshot, discovery).await, - } - } - pub(crate) async fn check_update(&mut self, snapshot: &str, discovery: &mut D) { - let service = self.service(); - if let Some((cfg, sig)) = discovery.get(&service.name(), &self.sig, &self.inner).await { - let dump = self.sig != sig; - let update = self.sig.digest != sig.digest; - if !dump && !update { - return; - } - log::info!("+++ service:{}, dump:{}, update:{}", service, dump, update); - if update { - log::info!("updating {:?} => {:?} cfg: {:?}", self, sig, cfg); - self.update(&cfg, sig); - } else { - self.sig = sig; - } - self.dump(snapshot, &cfg).await; - } - } - pub(crate) fn try_load(&mut self) { - const CYCLE: Duration = Duration::from_secs(15); - // 刚刚更新完成,或者长时间未load。则进行一次load - if self.last_update.elapsed() <= CYCLE || self.last_load.elapsed() >= CYCLE { - if self.need_load() { - self.load(); - self.last_load = Instant::now(); - } - } - } - fn update(&mut self, cfg: &str, sig: Sig) { - self.sig = sig; - let name = self.service().namespace().to_string(); - self.inner.update(&name, cfg); - self.last_update = Instant::now(); - } - fn encoded_path(&self, snapshot: &str) -> PathBuf { - let base = self.service().name(); - let mut pb = PathBuf::new(); - pb.push(snapshot); - pb.push(base); - pb - } - pub async fn load_from_snapshot(&self, name: &str) -> Result<(String, Sig)> { - let mut contents = Vec::with_capacity(8 * 1024); - File::open(self.encoded_path(name)) - .await? - .read_to_end(&mut contents) - .await?; - let mut contents = String::from_utf8(contents) - .map_err(|_e| Error::new(ErrorKind::Other, "not a valid utf8 file"))?; - // 内容的第一行是签名,第二行是往后是配置 - let idx = contents.find('\n').unwrap_or(0); - let cfg = contents.split_off(idx); - let sig: String = contents; - let sig: Sig = sig.try_into()?; - log::debug!("{} snapshot loaded:sig:{:?} cfg:{}", name, sig, cfg.len()); - Ok((cfg, sig)) - } - async fn dump(&self, snapshot: &str, cfg: &str) { - if let Err(_e) = self.try_dump(snapshot, cfg).await { - log::warn!("failed to dump {:?} len:{} err:{:?}", self, cfg.len(), _e) - } - } - async fn try_dump(&self, snapshot: &str, cfg: &str) -> Result<()> { - let sig_str = self.sig.serialize(); - log::debug!("dump {:?} cfg:{} sig_str:{}", self, cfg.len(), sig_str); - let path = self.encoded_path(snapshot); - if let Some(parent) = path.parent() { - if !parent.exists() { - tokio::fs::create_dir_all(parent).await?; - } - } - let mut file = File::create(path).await?; - file.write_all(sig_str.as_bytes()).await?; - file.write_all(b"\n").await?; - file.write_all(cfg.as_bytes()).await?; - Ok(()) - } -} - -use std::fmt::{self, Debug, Formatter}; -impl Debug for Config { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!(f, "sig:{:?} name:{:?}", self.sig, self.service()) - } -} - -use std::ops::{Deref, DerefMut}; -impl Deref for Config { - type Target = T; - fn deref(&self) -> &Self::Target { - &self.inner - } -} -impl DerefMut for Config { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.inner - } -} diff --git a/discovery/src/dns/lookup.rs b/discovery/src/dns/lookup.rs index c09b96f3f..2c2adbab1 100644 --- a/discovery/src/dns/lookup.rs +++ b/discovery/src/dns/lookup.rs @@ -1,14 +1,31 @@ -use super::Record; +use super::{Ipv4Vec, Record}; pub(super) struct Lookup {} +use dns_lookup::{getaddrinfo, AddrInfoHints}; +use libc::SOCK_STREAM; + use std::net::IpAddr; impl Lookup { pub(super) fn new() -> Self { Self {} } - fn dns_lookup(&self, host: &str) -> std::io::Result { - let ips = dns_lookup::lookup_host(host)?; - Ok(IpAddrLookup::AddrInfoIter(ips)) + fn dns_lookup(&self, host: &str) -> std::io::Result { + let hints = AddrInfoHints { + socktype: SOCK_STREAM as i32, + ..AddrInfoHints::default() + }; + + let mut ipv4vec = super::Ipv4Vec::new(); + + let addrs = getaddrinfo(Some(host), None, Some(hints))?; + for ip in addrs { + let ip = ip?; + match ip.sockaddr.ip() { + IpAddr::V4(ip) => ipv4vec.push(ip), + IpAddr::V6(_) => {} + } + } + Ok(ipv4vec) } pub(super) fn lookups<'a, I>(&self, iter: I) -> (usize, Option) where @@ -23,7 +40,7 @@ impl Lookup { break; } let ips = ret.unwrap(); - if r.refresh(host, ips) { + if r.refresh(ips) { num += 1; (num == 1).then(|| cache = Some(host.to_string())); } @@ -32,19 +49,4 @@ impl Lookup { } } -pub(super) enum IpAddrLookup { - AddrInfoIter(Vec), -} -impl IpAddrLookup { - pub(super) fn visit_v4(&self, mut v: impl FnMut(std::net::Ipv4Addr)) { - match self { - Self::AddrInfoIter(iter) => { - for ip in iter.iter() { - if let std::net::IpAddr::V4(ip) = ip { - v(*ip); - } - } - } - } - } -} +pub type IpAddrLookup = Ipv4Vec; diff --git a/discovery/src/dns/mod.rs b/discovery/src/dns/mod.rs index 104feeaf5..102e657eb 100644 --- a/discovery/src/dns/mod.rs +++ b/discovery/src/dns/mod.rs @@ -30,22 +30,22 @@ pub fn register(host: &str, notify: Arc) { get_dns().watch(host, notify) } pub fn lookup_ips<'a>(host: &str, mut f: impl FnMut(&[IpAddr])) { - f(get_dns().lookup(host)) + if let Some(ips) = get_dns().lookup(host) { + f(ips.as_slice()) + } } #[derive(Clone, Debug)] struct Record { subscribers: Vec>, - ips: Vec, - md5: u64, // 使用所有ip的和作为md5 + ips: Ipv4Vec, notify: bool, // true表示需要通知 } impl Default for Record { fn default() -> Self { Record { subscribers: Vec::with_capacity(2), - ips: Vec::with_capacity(2), - md5: 0, + ips: Ipv4Vec::new(), notify: false, } } @@ -69,22 +69,14 @@ impl Record { } // 如果有更新,则返回lookup的ip。 // 无更新则返回None - fn refresh(&mut self, host: &str, ips: IpAddrLookup) -> bool { - let mut md5 = 0u64; - let mut cnt = 0; - ips.visit_v4(|ip| { - cnt += 1; - md5 += u32::from(ip) as u64; - }); - if cnt > 0 && (cnt != self.ips.len() || self.md5 != md5) { - self.ips.clear(); - ips.visit_v4(|ip| self.ips.push(ip)); - self.md5 = md5; + fn refresh(&mut self, ips: IpAddrLookup) -> bool { + if ips.len() > 0 && self.ips != ips { + self.ips = ips; self.notify = true; - log::debug!("{} resolved ips:{:?}, md5:{}", host, self.ips, md5); - return true; + true + } else { + false } - false } } @@ -225,9 +217,8 @@ impl DnsCache { let r = self.hosts.get_or_insert(host); r.watch(notify); } - fn lookup(&self, host: &str) -> &[IpAddr] { - static EMPTY: Vec = Vec::new(); - self.hosts.get(host).unwrap_or_else(|| &EMPTY) + fn lookup(&self, host: &str) -> Option<&Ipv4Vec> { + self.hosts.get(host) } } @@ -255,10 +246,10 @@ impl Hosts { assert!(id < self.hosts.len()); &mut self.hosts[id].1 } - fn get(&self, host: &str) -> Option<&[IpAddr]> { + fn get(&self, host: &str) -> Option<&Ipv4Vec> { self.index.get(host).map(|&id| { assert!(id < self.hosts.len()); - &self.hosts[id].1.ips[..] + &self.hosts[id].1.ips }) } fn get_or_insert(&mut self, host: String) -> &mut Record { @@ -310,3 +301,86 @@ impl<'a> Iterator for HostRecordIter<'a> { } unsafe impl<'a> Send for HostRecordIter<'a> {} unsafe impl<'a> Sync for HostRecordIter<'a> {} + +// 一个高效的ipv4数组。 +// 小于等于5个元素时,直接使用cache,大于5个元素时,使用ext。 +use std::net::Ipv4Addr; +#[derive(Debug, Clone)] +pub struct Ipv4Vec { + len: u32, + cache: [Ipv4Addr; 5], + ext: Option>>, +} +impl Ipv4Vec { + pub fn new() -> Self { + Self { + len: 0, + cache: [Ipv4Addr::from(0u32); 5], + ext: None, + } + } + #[inline(always)] + pub fn len(&self) -> usize { + self.len as usize + } + pub fn push(&mut self, ip: Ipv4Addr) { + if self.len() < self.cache.len() { + self.cache[self.len()] = ip; + } else { + let ext = self + .ext + .get_or_insert_with(|| Box::new(Vec::with_capacity(16))); + if ext.len() == 0 { + ext.extend_from_slice(&self.cache[..]) + } + ext.push(ip); + } + self.len += 1; + } + pub fn iter(&self) -> Ipv4VecIter { + let iter = if self.len() <= self.cache.len() { + self.cache[..self.len as usize].iter() + } else { + self.ext.as_ref().expect("ext").iter() + }; + Ipv4VecIter { iter } + } + fn md5(&self) -> u64 { + self.iter().fold(0u64, |acc, ip| acc + u32::from(ip) as u64) + } + fn is_empty(&self) -> bool { + self.len == 0 + } + pub fn as_slice(&self) -> &[Ipv4Addr] { + if self.len() <= self.cache.len() { + &self.cache[..self.len as usize] + } else { + self.ext.as_ref().expect("ext").as_slice() + } + } +} +pub struct Ipv4VecIter<'a> { + iter: std::slice::Iter<'a, Ipv4Addr>, +} +impl<'a> Iterator for Ipv4VecIter<'a> { + type Item = Ipv4Addr; + fn next(&mut self) -> Option { + self.iter.next().map(|ip| *ip) + } +} +// 为Ipv4Vec实现一个Equal trait,这样就可以比较两个Ipv4Vec是否相等 +impl PartialEq for Ipv4Vec { + fn eq(&self, other: &Self) -> bool { + self.len == other.len && { + match self.len { + 0 => true, + 1 => self.cache[0] == other.cache[0], + 2 => { + self.cache[..2] == other.cache[..2] + || (self.cache[0] == other.cache[1] && self.cache[1] == other.cache[0]) + } + _ => self.md5() == other.md5(), + } + } + } +} diff --git a/discovery/src/lib.rs b/discovery/src/lib.rs index 67cb52918..af1765861 100644 --- a/discovery/src/lib.rs +++ b/discovery/src/lib.rs @@ -9,7 +9,7 @@ mod vintage; pub use update::*; pub mod dns; mod fixed; -mod path; +//mod path; //mod sig; pub use fixed::Fixed; diff --git a/discovery/src/path.rs b/discovery/src/path.rs deleted file mode 100644 index 1f120ba45..000000000 --- a/discovery/src/path.rs +++ /dev/null @@ -1,62 +0,0 @@ -pub trait ToPath { - fn path(&self) -> String; -} -pub trait ToName { - fn name(&self) -> String; -} -pub trait GetNamespace { - fn namespace(&self) -> &str; - fn cat(&self, ns: &str) -> String; -} - -impl ToPath for T { - #[inline] - fn path(&self) -> String { - let mut path = self.to_string(); - if path.find('+').is_some() { - path = path.replace('+', "/"); - } - // 去掉namespace。 - let idx = path.rfind(':').unwrap_or(path.len()); - let last_path_idx = path.rfind('/').unwrap_or(path.len()); - //配置可能是127.0.0.1:8080/config/to/config,这种情况不截断 - if last_path_idx < idx { - path.truncate(idx); - } - path - } -} -impl ToName for T { - #[inline] - fn name(&self) -> String { - let mut name = self.to_string(); - if name.find('/').is_some() { - name = name.replace('/', "+"); - } - name - } -} -impl> GetNamespace for T { - // 取base name。如果base name中有冒号区分,则取冒号后面的内容。 - #[inline] - fn namespace(&self) -> &str { - let name = self.as_ref(); - let base = name - .rfind(|c| c == '+' || c == '/') - .map(|idx| &name[idx + 1..]) - .unwrap_or_else(|| name); - // 再看是否有namespace - base.rfind(':') - .map(|idx| &base[idx + 1..]) - .unwrap_or_else(|| base) - } - #[inline] - fn cat(&self, ns: &str) -> String { - if ns.len() > 0 { - let name = self.as_ref().to_string(); - name + ":" + ns - } else { - self.as_ref().to_string() - } - } -} diff --git a/discovery/src/sig.rs b/discovery/src/sig.rs deleted file mode 100644 index 235370d97..000000000 --- a/discovery/src/sig.rs +++ /dev/null @@ -1,46 +0,0 @@ -// 签名包含2个部分。 -// 1. 内容的摘要; -// 2. 内容提供方(vintage)的唯一id -#[derive(Default, Debug, PartialEq, Eq)] -pub(crate) struct Sig { - pub(crate) digest: [u8; 16], - pub(crate) sig: String, -} -impl Sig { - pub(crate) fn new(digest: [u8; 16], sig: String) -> Self { - Self { digest, sig } - } - pub(crate) fn serialize(&self) -> String { - let mut buf = String::with_capacity(32 + 1 + self.sig.len()); - let sig_str = bs58::encode(self.digest).into_string(); - buf.push_str(&sig_str); - buf.push(' '); - buf.push_str(&self.sig); - buf - } - pub(crate) fn group_sig(&self) -> &str { - &self.sig - } -} - -use std::convert::TryFrom; -use std::io::{Error, ErrorKind}; -// 反序列化 -// 用空格区分为两部分。 -// 第一部分:是base58编码的digest. -// 第二部分:sig -impl TryFrom for Sig { - type Error = std::io::Error; - fn try_from(ser: String) -> std::result::Result { - let (digest, sig) = ser - .find(' ') - .map(|idx| (ser[0..idx].to_string(), ser[idx + 1..].to_string())) - .unwrap_or((ser, String::new())); - let digest: [u8; 16] = bs58::decode(digest) - .into_vec() - .map_err(|e| Error::new(ErrorKind::InvalidData, format!("into vec:{:?}", e)))? - .try_into() - .map_err(|e| Error::new(ErrorKind::InvalidData, format!("into sig:{:?}", e)))?; - Ok(Sig { digest, sig }) - } -} diff --git a/discovery/src/topology.rs b/discovery/src/topology.rs index 5c87ef67c..e53fa6070 100644 --- a/discovery/src/topology.rs +++ b/discovery/src/topology.rs @@ -1,5 +1,4 @@ use ds::{cow, CowReadHandle, CowWriteHandle}; -use metrics::{Metric, Path}; use std::{ ops::Deref, @@ -9,8 +8,6 @@ use std::{ }, }; -use crate::path::GetNamespace; - pub trait TopologyWrite { fn update(&mut self, name: &str, cfg: &str); #[inline] @@ -37,14 +34,12 @@ where let updates = Arc::new(AtomicUsize::new(0)); - let path = Path::new(vec!["any", service.namespace()]); ( TopologyWriteGuard { updating: None, inner: tx, service: service.to_string(), updates: updates.clone(), - update_num: path.num("top_updated"), }, TopologyReadGuard { inner: rx, updates }, ) @@ -79,7 +74,6 @@ where inner: CowWriteHandle, service: String, updates: Arc, - update_num: Metric, } impl Deref for TopologyReadGuard { @@ -113,7 +107,6 @@ where T: Clone, { fn update_inner(&mut self, f: impl Fn(&mut T) -> bool) -> bool { - self.update_num += 1; let mut t = self.updating.take().unwrap_or_else(|| self.inner.copy()); if !f(&mut t) { let _ = self.updating.insert(t); diff --git a/discovery/src/update.rs b/discovery/src/update.rs index 96112b7eb..c5e8bbc52 100644 --- a/discovery/src/update.rs +++ b/discovery/src/update.rs @@ -1,3 +1,4 @@ +use metrics::Metric; // 定期更新discovery. use super::{Discover, TopologyWrite}; use ds::chan::Receiver; @@ -283,19 +284,23 @@ struct Service { name: String, top: T, cfg: String, + metric: Metric, } impl Service { fn new(name: String, top: T) -> Self { + let metric = metrics::Path::new(vec!["any", name.as_str()]).num("top_updated"); Self { name, top, cfg: String::new(), + metric, } } fn update(&mut self, cfg: &str) { if cfg != self.cfg { self.cfg = cfg.to_string(); self.top.update(&self.name, cfg); + self.metric += 1; log::info!("Updated {}", self.name); } } diff --git a/tests/src/dns.rs b/tests/src/dns.rs index 2c2f965e1..4e7a1bec0 100644 --- a/tests/src/dns.rs +++ b/tests/src/dns.rs @@ -116,3 +116,38 @@ fn dns_lookup() { ] ); } + +#[test] +fn test_ipv4_vec() { + use discovery::dns::Ipv4Vec; + assert_eq!(std::mem::size_of::(), 32); + use std::net::Ipv4Addr; + let mut ips = Ipv4Vec::new(); + let ip0: Ipv4Addr = "10.0.0.1".parse().expect("invalid ip"); + ips.push(ip0); + let mut iter = ips.iter(); + assert_eq!(iter.next(), Some(ip0)); + assert_eq!(iter.next(), None); + + let vec = vec![ + Ipv4Addr::new(10, 0, 0, 1), + Ipv4Addr::new(10, 0, 0, 2), + Ipv4Addr::new(10, 0, 0, 3), + Ipv4Addr::new(10, 0, 0, 4), + Ipv4Addr::new(10, 0, 0, 5), + Ipv4Addr::new(10, 0, 0, 6), + Ipv4Addr::new(10, 0, 0, 7), + Ipv4Addr::new(10, 0, 0, 8), + ]; + let mut ips = discovery::dns::Ipv4Vec::new(); + vec.iter().for_each(|ip| ips.push(*ip)); + assert_eq!(ips.len(), 8); + let mut iter = ips.iter(); + for i in 0..vec.len() { + assert_eq!(iter.next(), Some(vec[i])); + } + let mut other = Ipv4Vec::new(); + vec.iter().rev().for_each(|ip| other.push(*ip)); + assert_eq!(other.len(), 8); + assert!(ips == other); +} From 89480e72b79909b9543e2fb5f306f285179ea321 Mon Sep 17 00:00:00 2001 From: parabala Date: Thu, 28 Mar 2024 15:25:01 +0800 Subject: [PATCH 22/24] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E4=B8=8D=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E7=9A=84=E5=8F=98=E9=87=8Fdisable=5Fclean=5Fservice?= =?UTF-8?q?=5Fpath?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- agent/src/main.rs | 20 +++++++++----------- context/src/lib.rs | 2 -- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/agent/src/main.rs b/agent/src/main.rs index 608dc3e7a..22c46bc39 100644 --- a/agent/src/main.rs +++ b/agent/src/main.rs @@ -66,17 +66,15 @@ async fn discovery_init(ctx: &Context, rx: service::Receiver) -> Result<()> { let service_pool_socks_url = ctx.service_pool_socks_url(); //watch_discovery之前执行清理sock - if !ctx.disable_clean_service_path { - let mut dir = tokio::fs::read_dir(&ctx.service_path).await?; - while let Some(child) = dir.next_entry().await? { - let path = child.path(); - //从vintage获取socklist,或者存在unixsock,需要事先清理 - if service_pool_socks_url.len() > 1 - || path.to_str().map(|s| s.ends_with(".sock")).unwrap_or(false) - { - log::info!("{:?} exists. deleting", path); - let _ = tokio::fs::remove_file(path).await; - } + let mut dir = tokio::fs::read_dir(&ctx.service_path).await?; + while let Some(child) = dir.next_entry().await? { + let path = child.path(); + //从vintage获取socklist,或者存在unixsock,需要事先清理 + if service_pool_socks_url.len() > 1 + || path.to_str().map(|s| s.ends_with(".sock")).unwrap_or(false) + { + log::info!("{:?} exists. deleting", path); + let _ = tokio::fs::remove_file(path).await; } } diff --git a/context/src/lib.rs b/context/src/lib.rs index 2fabab0ec..be3bf87c5 100644 --- a/context/src/lib.rs +++ b/context/src/lib.rs @@ -78,8 +78,6 @@ pub struct ContextOption { help("clean path for unix domain socket ."), default_value("false") )] - pub disable_clean_service_path: bool, - #[clap(short, long, help("starting in upgrade mode"))] upgrade: bool, From cd26ad5e47f709c0969df24ce071d364dc80acd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E6=B3=A2?= Date: Thu, 28 Mar 2024 18:54:31 +0800 Subject: [PATCH 23/24] =?UTF-8?q?=E5=AF=B9kv=E4=B8=AD=E6=97=A0=E5=93=8D?= =?UTF-8?q?=E5=BA=94=E7=9A=84req=E6=9E=84=E5=BB=BA=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E5=93=8D=E5=BA=94body?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- protocol/src/kv/mod.rs | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/protocol/src/kv/mod.rs b/protocol/src/kv/mod.rs index 9bcfb4b97..e0570be6d 100644 --- a/protocol/src/kv/mod.rs +++ b/protocol/src/kv/mod.rs @@ -53,6 +53,10 @@ pub mod prelude { // 如果没有找到value,且无异常,则返回该响应内容 lazy_static! { static ref NOT_FOUND: Vec = b"not found".to_vec(); + static ref TOP_INVALID_RSP: RingSlice = + RingSlice::from_slice(b"invalid request: year out of index"); + static ref REQ_INVALID_RSP: RingSlice = RingSlice::from_slice(b"invalid request"); + static ref UNKNOWN_RSP: RingSlice = RingSlice::from_slice(b"server may unavailable"); } #[derive(Clone, Default)] @@ -172,18 +176,6 @@ impl Protocol for Kv { let old_op_code = ctx.request().op_code() as u8; - // 如果原始请求是quite_get请求,并且not found,则不回写。 - // if let Some(rsp) = response { - // log::debug!( - // "+++ sent to client for req:{:?}, rsp:{:?}", - // ctx.request(), - // rsp - // ); - // // mysql 请求到正确的数据,才会转换并write - // self.write_mc_packet(ctx.request(), Some(rsp), w)?; - // return Ok(()); - // } - // 先进行metrics统计 //self.metrics(ctx.request(), None, ctx); log::debug!("+++ send to client rsp, req:{:?}", ctx.request(),); @@ -436,19 +428,23 @@ impl Kv { OP_GET | OP_GETQ => (None, Some(MARKER_BYTE_ARR)), _ => (None, None), }; - let err_response; - let response = match ctx.ctx().error { + + let response: Option<&RingSlice> = match ctx.ctx().error { ContextStatus::TopInvalid => { assert!(response.is_none()); - err_response = Some(RingSlice::from_slice(b"invalid request: year out of index")); - err_response.as_ref() + Some(&TOP_INVALID_RSP) } ContextStatus::ReqInvalid => { assert!(response.is_none()); - err_response = Some(RingSlice::from_slice(b"invalid request")); - err_response.as_ref() + Some(&REQ_INVALID_RSP) + } + ContextStatus::Ok => { + if status == RespStatus::NoError || response.is_some() { + response.map(|r| r.deref().deref()) + } else { + Some(&UNKNOWN_RSP) + } } - ContextStatus::Ok => response.map(|r| r.deref().deref()), }; if status != RespStatus::NoError && status != RespStatus::NotFound { log::error!( From 6b5879d3dbfc70b28b7ec9975f2c7074bd57bd24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E6=B3=A2?= Date: Mon, 22 Apr 2024 18:33:52 +0800 Subject: [PATCH 24/24] fix conflicts --- stream/src/handler.rs | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/stream/src/handler.rs b/stream/src/handler.rs index 3db5ac445..4c6df3e75 100644 --- a/stream/src/handler.rs +++ b/stream/src/handler.rs @@ -176,23 +176,6 @@ where } } - // 发送request. 读空所有的request,并且发送。直到pending或者error - #[inline] - fn poll_request(&mut self, cx: &mut Context) -> Poll> { - self.s.cache(self.data.has_multi()); - while let Some(req) = ready!(self.data.poll_recv(cx)) { - self.num.tx(); - - self.s.write_r(0, &**req)?; - - match req.on_sent() { - Some(r) => self.pending.push_back((r, Instant::now())), - None => self.num.rx(), - } - } - Poll::Ready(Err(Error::ChanReadClosed)) - } - #[inline(always)] fn poll_response(&mut self, cx: &mut Context) -> Poll> { while self.pending.len() > 0 {