use std::future::Future;
use bytes::Bytes;
use linkedbytes::LinkedBytes;
use pilota::thrift::ThriftException;
use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, Interest};
use volo::{net::ext::AsyncExt, util::buf_reader::BufReader};
use self::{framed::MakeFramedCodec, thrift::MakeThriftCodec, ttheader::MakeTTHeaderCodec};
use super::{Decoder, Encoder, MakeCodec};
use crate::{EntryMessage, ThriftMessage, context::ThriftContext};
pub mod framed;
pub mod thrift;
pub mod ttheader;
pub trait ZeroCopyEncoder: Send + Sync + 'static {
fn encode<Msg: Send + EntryMessage, Cx: ThriftContext>(
&mut self,
cx: &mut Cx,
linked_bytes: &mut LinkedBytes,
msg: ThriftMessage<Msg>,
) -> Result<(), ThriftException>;
fn size<Msg: Send + EntryMessage, Cx: ThriftContext>(
&mut self,
cx: &mut Cx,
msg: &ThriftMessage<Msg>,
) -> Result<(usize, usize), ThriftException>;
}
pub trait ZeroCopyDecoder: Send + Sync + 'static {
fn decode<Msg: Send + EntryMessage, Cx: ThriftContext>(
&mut self,
cx: &mut Cx,
bytes: &mut Bytes,
) -> Result<Option<ThriftMessage<Msg>>, ThriftException>;
fn decode_async<
Msg: Send + EntryMessage,
Cx: ThriftContext,
R: AsyncRead + Unpin + Send + Sync,
>(
&mut self,
cx: &mut Cx,
reader: &mut BufReader<R>,
) -> impl Future<Output = Result<Option<ThriftMessage<Msg>>, ThriftException>> + Send;
}
pub trait MakeZeroCopyCodec: Clone + Send + 'static {
type Encoder: ZeroCopyEncoder;
type Decoder: ZeroCopyDecoder;
fn make_codec(&self) -> (Self::Encoder, Self::Decoder);
}
pub struct DefaultEncoder<E, W> {
encoder: E,
writer: W,
linked_bytes: LinkedBytes,
}
impl<E: ZeroCopyEncoder, W: AsyncWrite + AsyncExt + Unpin + Send + Sync + 'static> Encoder
for DefaultEncoder<E, W>
{
#[inline]
async fn encode<Req: Send + EntryMessage, Cx: ThriftContext>(
&mut self,
cx: &mut Cx,
msg: ThriftMessage<Req>,
) -> Result<(), ThriftException> {
cx.stats_mut().record_encode_start_at();
let (real_size, malloc_size) = self.encoder.size(cx, &msg)?;
tracing::trace!(
"[VOLO] codec encode message real size: {}, malloc size: {}",
real_size,
malloc_size
);
cx.stats_mut().set_write_size(real_size);
self.linked_bytes.reset();
self.linked_bytes.reserve(malloc_size);
let mut write_result: Result<(), ThriftException> = self
.encoder
.encode(cx, &mut self.linked_bytes, msg)
.inspect_err(|_| {
cx.stats_mut().record_encode_end_at();
});
if write_result.is_ok() {
cx.stats_mut().record_encode_end_at();
cx.stats_mut().record_write_start_at();
write_result = self
.linked_bytes
.write_all_vectored(&mut self.writer)
.await
.map_err(Into::into);
}
if write_result.is_ok() {
write_result = self.writer.flush().await.map_err(Into::into);
}
cx.stats_mut().record_write_end_at();
match write_result {
Ok(()) => Ok(()),
Err(mut e) => {
let msg = format!(
", cx: {:?}, encode real size: {}, malloc size: {}",
cx.rpc_info(),
real_size,
malloc_size
);
e.append_msg(&msg);
tracing::warn!("[VOLO] thrift codec encode message error: {}", e);
Err(e)
}
}
}
async fn is_closed(&self) -> bool {
match self
.writer
.ready(Interest::READABLE | Interest::WRITABLE)
.await
{
Ok(ready) => ready.is_read_closed() || ready.is_write_closed(),
Err(e) => {
tracing::debug!("[VOLO] thrift codec write half ready error: {}", e);
true
}
}
}
#[cfg(feature = "shmipc")]
fn shmipc_helper(&self) -> volo::net::shmipc::ShmipcHelper {
self.writer.shmipc_helper()
}
}
pub struct DefaultDecoder<D, R> {
decoder: D,
reader: BufReader<R>,
}
impl<D: ZeroCopyDecoder, R: AsyncRead + AsyncExt + Unpin + Send + Sync + 'static> Decoder
for DefaultDecoder<D, R>
{
#[inline]
async fn decode<Msg: Send + EntryMessage, Cx: ThriftContext>(
&mut self,
cx: &mut Cx,
) -> Result<Option<ThriftMessage<Msg>>, ThriftException> {
if self.reader.fill_buf().await?.is_empty() {
tracing::trace!(
"[VOLO] thrift codec decode message EOF, rpcinfo: {:?}",
cx.rpc_info()
);
return Ok(None);
}
let start = std::time::Instant::now();
cx.stats_mut().record_decode_start_at();
cx.stats_mut().record_read_start_at();
tracing::trace!(
"[VOLO] codec decode message received: {:?}",
self.reader.buffer()
);
let res = self.decoder.decode_async(cx, &mut self.reader).await;
let end = std::time::Instant::now();
cx.stats_mut().record_decode_end_at();
tracing::trace!("[VOLO] thrift codec decode message cost: {:?}", end - start);
res
}
#[cfg(feature = "shmipc")]
fn shmipc_helper(&self) -> volo::net::shmipc::ShmipcHelper {
self.reader.shmipc_helper()
}
}
#[derive(Clone)]
pub struct DefaultMakeCodec<MkZC: MakeZeroCopyCodec> {
make_zero_copy_codec: MkZC,
}
impl DefaultMakeCodec<MakeFramedCodec<MakeThriftCodec>> {
pub fn framed() -> Self {
DefaultMakeCodec::new(framed::MakeFramedCodec::new(
thrift::MakeThriftCodec::default(),
))
}
}
impl DefaultMakeCodec<MakeTTHeaderCodec<MakeFramedCodec<MakeThriftCodec>>> {
pub fn ttheader_framed() -> Self {
DefaultMakeCodec::new(ttheader::MakeTTHeaderCodec::new(
framed::MakeFramedCodec::new(thrift::MakeThriftCodec::default()),
))
}
}
impl DefaultMakeCodec<MakeThriftCodec> {
pub fn buffered() -> Self {
DefaultMakeCodec::new(thrift::MakeThriftCodec::default())
}
}
impl<MkZC: MakeZeroCopyCodec> DefaultMakeCodec<MkZC> {
pub fn new(make_zero_copy_codec: MkZC) -> Self {
Self {
make_zero_copy_codec,
}
}
}
impl Default for DefaultMakeCodec<MakeTTHeaderCodec<MakeFramedCodec<MakeThriftCodec>>> {
fn default() -> Self {
Self::new(ttheader::MakeTTHeaderCodec::new(
framed::MakeFramedCodec::new(thrift::MakeThriftCodec::default()),
))
}
}
impl<MkZC, R, W> MakeCodec<R, W> for DefaultMakeCodec<MkZC>
where
MkZC: MakeZeroCopyCodec,
R: AsyncRead + AsyncExt + Unpin + Send + Sync + 'static,
W: AsyncWrite + AsyncExt + Unpin + Send + Sync + 'static,
{
type Encoder = DefaultEncoder<MkZC::Encoder, W>;
type Decoder = DefaultDecoder<MkZC::Decoder, R>;
#[inline]
fn make_codec(&self, reader: R, writer: W) -> (Self::Encoder, Self::Decoder) {
let (encoder, decoder) = self.make_zero_copy_codec.make_codec();
(
DefaultEncoder {
encoder,
writer,
linked_bytes: LinkedBytes::new(),
},
DefaultDecoder {
decoder,
reader: BufReader::new(reader),
},
)
}
}
#[cfg(test)]
mod tests {
use super::DefaultMakeCodec;
#[test]
fn test_mk_codec() {
let _framed = DefaultMakeCodec::framed();
let _ttheader_framed = DefaultMakeCodec::ttheader_framed();
let _buffered = DefaultMakeCodec::buffered();
}
}