use futures::Future;
use lapin::options::BasicQosOptions;
use lapin::Channel;
use log::debug;
use log::info;
use log::warn;
use serde::de::DeserializeOwned;
use serde_json;
use super::rmq_primitive::constant::*;
use super::rmq_primitive::create_channel;
use super::rmq_primitive::unreliable_ack_or_reject;
use super::rmq_primitive::AckType;
use super::rmq_primitive::Responsibility;
use crate::Result;
pub async fn consume_forever<InputMsg, HandlerState, HandlerResult>(
rmq_uri: &str,
input_queue: &'static str,
handler: fn(HandlerState, Channel, InputMsg) -> HandlerResult,
handler_state: HandlerState,
prefetch_count: u16,
) where
InputMsg: DeserializeOwned + Send + 'static,
HandlerState: Clone + Send + 'static,
HandlerResult: Future<Output = Result<Responsibility>> + Send + 'static,
{
loop {
match consume_queue(
rmq_uri,
&input_queue,
handler,
handler_state.clone(),
prefetch_count,
)
.await
{
Ok(()) => (),
Err(e) => {
warn!(
"error happened when consuming queue {}, will retry: {}",
&input_queue, e
);
}
}
let duration = std::time::Duration::from_millis(2000);
info!(
"sleep for {} seconds before reconnecting to queue {}",
&duration.as_secs(),
&input_queue
);
tokio::time::sleep(duration).await
}
}
async fn consume_queue<InputMsg, HandlerState, HandlerResult>(
rmq_uri: &str,
input_queue: &'static str,
handler: fn(HandlerState, Channel, InputMsg) -> HandlerResult,
handler_state: HandlerState,
prefetch_count: u16,
) -> Result<()>
where
InputMsg: DeserializeOwned + Send + 'static,
HandlerState: Clone + Send + 'static,
HandlerResult: Future<Output = Result<Responsibility>> + Send + 'static,
{
info!("creating channel for consuming queue {}", input_queue);
let channel = create_channel(rmq_uri).await?;
info!("setting prefetch to be {}", prefetch_count);
channel
.basic_qos(prefetch_count, BasicQosOptions { global: false })
.await?;
let consumer_tag = format!("dgec-{}", input_queue);
info!("creating consumer {}", &consumer_tag);
let consumer = channel
.basic_consume(
input_queue,
&consumer_tag,
RMQ_BASIC_CONSUME_OPTIONS,
lapin::types::FieldTable::default(),
)
.await?;
info!("entering consuming loop for queue {}", input_queue);
for delivery in consumer {
let (channel, msg) = delivery?;
tokio::spawn(handle_one_delivery(
channel,
msg,
handler,
handler_state.clone(),
));
}
Ok(())
}
async fn handle_one_delivery<InputMsg, HandlerState, HandlerResult>(
channel: Channel,
delivery: lapin::message::Delivery,
handle: fn(HandlerState, Channel, InputMsg) -> HandlerResult,
handler_state: HandlerState,
) where
InputMsg: DeserializeOwned + Send + 'static,
HandlerState: Clone + Send + 'static,
HandlerResult: Future<Output = Result<Responsibility>> + Send + 'static,
{
debug!("processing message of tag: {}", delivery.delivery_tag);
match serde_json::from_slice::<InputMsg>(&delivery.data) {
Err(e) => {
warn!(
"failed to parse json when processing delivery: {}, msg will be dropped, error is: {}, data is: {:?}",
&delivery.delivery_tag, e, &delivery.data
);
unreliable_ack_or_reject(channel, AckType::Ack, delivery.delivery_tag).await
}
Ok(msg) => match handle(handler_state, channel.clone(), msg).await {
Err(e) => {
warn!(
"an error occurred while handling message {}, will requeue it, error is: {}",
&delivery.delivery_tag, e
);
unreliable_ack_or_reject(channel, AckType::Reject, delivery.delivery_tag).await
}
Ok(Responsibility::Reject) => {
debug!("explicitly rejecting message {}", &delivery.delivery_tag);
unreliable_ack_or_reject(channel, AckType::Reject, delivery.delivery_tag).await
}
Ok(Responsibility::Accept) => {
debug!("accepting message {}", &delivery.delivery_tag);
unreliable_ack_or_reject(channel, AckType::Ack, delivery.delivery_tag).await
}
},
}
}