use std::ops::Deref;
use std::sync::Arc;
use std::time::Duration;
use cheetah_string::CheetahString;
use rocketmq_runtime::ChildServiceContext;
use rocketmq_runtime::ResourcePermit;
use rocketmq_runtime::ShutdownDeadline;
use rocketmq_runtime::ShutdownReport;
use serde::Serialize;
use crate::base::connection_net_event::ConnectionNetEvent;
use crate::base::pending_request_table::PendingRequestUsage;
use crate::clients::nameserver_endpoint::ConnectTarget;
use crate::clients::nameserver_endpoint::NameServerEndpoint;
use crate::codec::remoting_command_codec::FrameLimits;
use crate::deadline::RequestDeadline;
use crate::error::TransportError;
use crate::request_outcome::OutboundRequestOutcome;
use crate::request_processor::default_request_processor::DefaultRequestProcessor;
use crate::runtime::config::client_config::TransportClientConfig;
use crate::runtime::processor::RequestProcessor;
use crate::runtime::RPCHook;
use crate::security::TransportSecurity;
use crate::telemetry::TransportTelemetry;
use crate::tls::TlsConfig;
use rocketmq_protocol::protocol::remoting_command::RemotingCommand;
use super::CachedConnectionState;
use super::TransportClient;
pub struct TransportClientBuilder<PR> {
config: Arc<TransportClientConfig>,
processor: PR,
service_context: ChildServiceContext,
connection_events: Option<tokio::sync::broadcast::Sender<ConnectionNetEvent>>,
transport_security: Option<Arc<TransportSecurity>>,
telemetry: TransportTelemetry,
frame_limits: FrameLimits,
}
impl<PR> TransportClientBuilder<PR>
where
PR: RequestProcessor + Sync + Clone + 'static,
{
pub fn connection_events(mut self, events: tokio::sync::broadcast::Sender<ConnectionNetEvent>) -> Self {
self.connection_events = Some(events);
self
}
pub fn transport_security(mut self, transport_security: Arc<TransportSecurity>) -> Self {
self.transport_security = Some(transport_security);
self
}
pub fn telemetry(mut self, telemetry: TransportTelemetry) -> Self {
self.telemetry = telemetry;
self
}
pub fn frame_limits(mut self, frame_limits: FrameLimits) -> Result<Self, rocketmq_error::SharedError> {
frame_limits.validate()?;
self.frame_limits = frame_limits;
Ok(self)
}
pub fn build(self) -> Result<TransportClient<PR>, rocketmq_error::SharedError> {
let mut client = TransportClient::build_inner(
self.config,
self.processor,
self.connection_events,
self.service_context,
self.telemetry,
self.frame_limits,
)?;
if let Some(transport_security) = self.transport_security {
client = client.with_transport_security(transport_security);
}
Ok(client)
}
}
#[derive(Clone)]
pub struct RemotingClient<PR = DefaultRequestProcessor> {
transport: Arc<TransportClient<PR>>,
}
impl<PR> RemotingClient<PR>
where
PR: RequestProcessor + Sync + Clone + 'static,
{
pub fn builder(
config: Arc<TransportClientConfig>,
processor: PR,
service_context: ChildServiceContext,
) -> RemotingClientBuilder<PR> {
RemotingClientBuilder {
transport: TransportClient::builder(config, processor, service_context),
}
}
}
impl<PR> RemotingClient<PR>
where
PR: Send + Sync + Clone + 'static,
{
pub fn transport_client(&self) -> Arc<TransportClient<PR>> {
Arc::clone(&self.transport)
}
pub async fn start(self: &Arc<Self>) -> Result<ClientStartReport, rocketmq_error::SharedError> {
self.transport.start().await
}
pub async fn shutdown_until(
&self,
deadline: ShutdownDeadline,
) -> Result<ClientShutdownReport, rocketmq_error::SharedError> {
Ok(self.transport.shutdown_graceful(deadline).await)
}
}
impl<PR> Deref for RemotingClient<PR> {
type Target = TransportClient<PR>;
fn deref(&self) -> &Self::Target {
self.transport.as_ref()
}
}
pub struct RemotingClientBuilder<PR> {
transport: TransportClientBuilder<PR>,
}
impl<PR> RemotingClientBuilder<PR>
where
PR: RequestProcessor + Sync + Clone + 'static,
{
pub fn connection_events(mut self, events: tokio::sync::broadcast::Sender<ConnectionNetEvent>) -> Self {
self.transport = self.transport.connection_events(events);
self
}
pub fn transport_security(mut self, transport_security: Arc<TransportSecurity>) -> Self {
self.transport = self.transport.transport_security(transport_security);
self
}
pub fn telemetry(mut self, telemetry: TransportTelemetry) -> Self {
self.transport = self.transport.telemetry(telemetry);
self
}
pub fn frame_limits(mut self, frame_limits: FrameLimits) -> Result<Self, rocketmq_error::SharedError> {
self.transport = self.transport.frame_limits(frame_limits)?;
Ok(self)
}
pub fn build(self) -> Result<RemotingClient<PR>, rocketmq_error::SharedError> {
Ok(RemotingClient {
transport: Arc::new(self.transport.build()?),
})
}
}
#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Serialize)]
pub struct ClientStartReport {
pub background_tasks_started: usize,
pub already_running: bool,
}
#[derive(Debug, Clone, Serialize)]
pub struct ConnectionShutdownReport {
pub addr: CheetahString,
pub report: ShutdownReport,
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct ClientShutdownReport {
pub background: Option<ShutdownReport>,
pub workers: Option<ShutdownReport>,
pub connections: Vec<ConnectionShutdownReport>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub enum RequestTarget {
Endpoint(CheetahString),
NameServer,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct SendReceipt {
pub endpoint: CheetahString,
pub written_at_millis: u64,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize)]
pub struct PendingUsage {
pub count: usize,
pub retained_bytes: usize,
pub rejected_count: usize,
pub rejected_bytes: usize,
}
impl From<PendingRequestUsage> for PendingUsage {
fn from(usage: PendingRequestUsage) -> Self {
Self {
count: usage.count,
retained_bytes: usage.bytes,
rejected_count: usage.rejected_count,
rejected_bytes: usage.rejected_bytes,
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
pub struct ClientSnapshot {
pub connection_count: usize,
pub connect_flight_count: usize,
pub configured_name_server_count: usize,
pub available_name_server_count: usize,
pub healthy_name_server_count: usize,
pub probing_name_server_count: usize,
pub draining_name_server_count: usize,
pub circuit_open_name_server_count: usize,
pub pending: PendingUsage,
}
impl ClientShutdownReport {
pub fn is_healthy(&self) -> bool {
self.background.as_ref().is_none_or(ShutdownReport::is_healthy)
&& self.workers.as_ref().is_none_or(ShutdownReport::is_healthy)
&& self.connections.iter().all(|connection| connection.report.is_healthy())
}
}
impl<PR: RequestProcessor + Sync + Clone + 'static> TransportClient<PR> {
pub fn builder(
tokio_client_config: Arc<TransportClientConfig>,
processor: PR,
service_context: ChildServiceContext,
) -> TransportClientBuilder<PR> {
TransportClientBuilder {
config: tokio_client_config,
processor,
service_context,
connection_events: None,
transport_security: None,
telemetry: TransportTelemetry::noop(),
frame_limits: FrameLimits::java_compatibility(),
}
}
}
impl<PR: Send + Sync + Clone + 'static> TransportClient<PR> {
pub fn with_transport_security(mut self, transport_security: Arc<TransportSecurity>) -> Self {
self.transport_security = Some(transport_security);
self
}
#[inline]
pub fn is_use_tls(&self) -> bool {
self.tokio_client_config.tls.enable
}
#[inline]
pub fn tls_config(&self) -> &TlsConfig {
&self.tokio_client_config.tls
}
#[must_use]
pub fn snapshot(&self) -> ClientSnapshot {
self.snapshot_inner()
}
pub fn update_name_server_address_list_sync(&self, addrs: Vec<CheetahString>) {
self.update_name_server_address_list_sync_inner(addrs);
}
pub fn update_name_server_connect_targets_sync(&self, targets: Vec<ConnectTarget>, drain_timeout: Duration) {
self.update_name_server_connect_targets_sync_inner(targets, drain_timeout);
}
pub fn apply_name_server_endpoint_snapshot_sync(
&self,
endpoints: Vec<NameServerEndpoint>,
drain_timeout: Duration,
) {
self.apply_name_server_endpoint_snapshot_sync_inner(endpoints, drain_timeout);
}
pub fn register_rpc_hook(&self, hook: Arc<dyn RPCHook>) {
self.cmd_handler.register_rpc_hook(hook);
}
pub fn clear_rpc_hook(&self) {
self.cmd_handler.clear_rpc_hook();
}
pub async fn update_name_server_address_list(&self, addrs: Vec<CheetahString>) {
self.update_name_server_address_list_sync_inner(addrs);
}
pub fn get_name_server_address_list(&self) -> Vec<CheetahString> {
self.endpoint_state
.load()
.endpoints()
.iter()
.map(NameServerEndpoint::compatibility_address)
.collect()
}
pub fn get_available_name_srv_list(&self) -> Vec<CheetahString> {
self.endpoint_state.load().available().iter().cloned().collect()
}
pub async fn request(
&self,
target: RequestTarget,
request: RemotingCommand,
deadline: RequestDeadline,
) -> Result<OutboundRequestOutcome, TransportError> {
self.request_inner(target, request, deadline).await
}
pub async fn send_oneway(
&self,
target: RequestTarget,
request: RemotingCommand,
deadline: RequestDeadline,
) -> Result<SendReceipt, rocketmq_error::SharedError> {
self.send_oneway_inner(target, request, deadline).await
}
pub async fn invoke_request(
&self,
addr: Option<&CheetahString>,
request: RemotingCommand,
timeout_millis: u64,
) -> Result<OutboundRequestOutcome, TransportError> {
self.invoke_request_with_deadline(addr, request, RequestDeadline::from_timeout_millis(timeout_millis))
.await
}
pub async fn invoke_oneway_with_permit(
&self,
addr: &CheetahString,
request: RemotingCommand,
deadline: RequestDeadline,
permit: ResourcePermit,
) -> Result<(), rocketmq_error::SharedError> {
self.invoke_oneway_until(addr, request, deadline, Some(permit)).await
}
pub async fn invoke_request_oneway_with_deadline(
&self,
addr: &CheetahString,
request: RemotingCommand,
deadline: RequestDeadline,
) -> Result<(), rocketmq_error::SharedError> {
self.invoke_oneway_until(addr, request, deadline, None).await
}
pub async fn invoke_request_oneway(
&self,
addr: &CheetahString,
request: RemotingCommand,
timeout_millis: u64,
) -> Result<(), rocketmq_error::SharedError> {
self.invoke_oneway_until(
addr,
request,
RequestDeadline::from_timeout_millis(timeout_millis),
None,
)
.await
}
pub fn reconcile_cached_connection(&self, addr: &CheetahString) -> CachedConnectionState {
self.reconcile_cached_connection_inner(addr)
}
#[deprecated(
since = "1.0.0",
note = "use TransportClient::reconcile_cached_connection; is_address_reachable is not a network probe"
)]
pub fn is_address_reachable(&self, addr: &CheetahString) {
self.is_address_reachable_inner(addr);
}
pub fn close_clients(&self, addrs: Vec<String>) {
self.close_clients_inner(addrs);
}
}