mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-09-01 00:39:24 +00:00
refactor(rpc): Centralize RPC service and unify API (#1427)
This change introduces a major refactoring of the RPC service layer to improve modularity, unify the API, and simplify the overall architecture. Key changes: - Replaced per-network-instance RPC services with a single global RPC server, reducing resource usage and simplifying management. - All clients (CLI, Web UI, etc.) now interact with EasyTier core through a unified RPC entrypoint, enabling consistent authentication and control. - RPC implementation logic has been moved to `easytier/src/rpc_service/` and organized by functionality (e.g., `instance_manage.rs`, `peer_manage.rs`, `config.rs`) for better maintainability. - Standardized Protobuf API definitions under `easytier/src/proto/` with an `api_` prefix (e.g., `cli.proto` → `api_instance.proto`) to provide a consistent interface. - CLI commands now require explicit `--instance-id` or `--instance-name` when multiple network instances are running; the parameter is optional when only one instance exists. BREAKING CHANGE: RPC portal configuration (`rpc_portal` and `rpc_portal_whitelist`) has been removed from per-instance configs and the Web UI. The RPC listen address must now be specified globally via the `--rpc-portal` command-line flag or the `ET_RPC_PORTAL` environment variable, as there is only one RPC service for the entire application.
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
use std::{net::SocketAddr, sync::Arc};
|
||||
|
||||
use anyhow::Context;
|
||||
use cidr::IpCidr;
|
||||
|
||||
use crate::{
|
||||
instance::instance::InstanceRpcServerHook,
|
||||
instance_manager::NetworkInstanceManager,
|
||||
proto::{
|
||||
api::{
|
||||
config::ConfigRpcServer,
|
||||
instance::{
|
||||
AclManageRpcServer, ConnectorManageRpcServer, MappedListenerManageRpcServer,
|
||||
PeerManageRpcServer, PortForwardManageRpcServer, StatsRpcServer, TcpProxyRpcServer,
|
||||
VpnPortalRpcServer,
|
||||
},
|
||||
logger::LoggerRpcServer,
|
||||
manage::WebClientServiceServer,
|
||||
},
|
||||
rpc_impl::{service_registry::ServiceRegistry, standalone::StandAloneServer},
|
||||
rpc_types::error::Error,
|
||||
},
|
||||
rpc_service::{
|
||||
acl_manage::AclManageRpcService, config::ConfigRpcService,
|
||||
connector_manage::ConnectorManageRpcService, instance_manage::InstanceManageRpcService,
|
||||
logger::LoggerRpcService, mapped_listener_manage::MappedListenerManageRpcService,
|
||||
peer_manage::PeerManageRpcService, port_forward_manage::PortForwardManageRpcService,
|
||||
proxy::TcpProxyRpcService, stats::StatsRpcService, vpn_portal::VpnPortalRpcService,
|
||||
},
|
||||
tunnel::tcp::TcpTunnelListener,
|
||||
};
|
||||
|
||||
pub struct ApiRpcServer {
|
||||
rpc_server: StandAloneServer<TcpTunnelListener>,
|
||||
}
|
||||
|
||||
impl ApiRpcServer {
|
||||
pub fn new(
|
||||
rpc_portal: Option<String>,
|
||||
rpc_portal_whitelist: Option<Vec<IpCidr>>,
|
||||
instance_manager: Arc<NetworkInstanceManager>,
|
||||
) -> anyhow::Result<Self> {
|
||||
let mut rpc_server = StandAloneServer::new(TcpTunnelListener::new(
|
||||
format!("tcp://{}", parse_rpc_portal(rpc_portal)?)
|
||||
.parse()
|
||||
.context("failed to parse rpc portal address")?,
|
||||
));
|
||||
rpc_server.set_hook(Arc::new(InstanceRpcServerHook::new(rpc_portal_whitelist)));
|
||||
register_api_rpc_service(&instance_manager, rpc_server.registry());
|
||||
Ok(Self { rpc_server })
|
||||
}
|
||||
|
||||
pub async fn serve(mut self) -> Result<Self, Error> {
|
||||
self.rpc_server.serve().await?;
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ApiRpcServer {
|
||||
fn drop(&mut self) {
|
||||
self.rpc_server.registry().unregister_all();
|
||||
}
|
||||
}
|
||||
|
||||
fn register_api_rpc_service(
|
||||
instance_manager: &Arc<NetworkInstanceManager>,
|
||||
registry: &ServiceRegistry,
|
||||
) {
|
||||
registry.register(
|
||||
PeerManageRpcServer::new(PeerManageRpcService::new(instance_manager.clone())),
|
||||
"",
|
||||
);
|
||||
|
||||
registry.register(
|
||||
ConnectorManageRpcServer::new(ConnectorManageRpcService::new(instance_manager.clone())),
|
||||
"",
|
||||
);
|
||||
|
||||
registry.register(
|
||||
MappedListenerManageRpcServer::new(MappedListenerManageRpcService::new(
|
||||
instance_manager.clone(),
|
||||
)),
|
||||
"",
|
||||
);
|
||||
|
||||
registry.register(
|
||||
VpnPortalRpcServer::new(VpnPortalRpcService::new(instance_manager.clone())),
|
||||
"",
|
||||
);
|
||||
|
||||
for client_type in ["tcp", "kcp_src", "kcp_dst", "quic_src", "quic_dst"] {
|
||||
registry.register(
|
||||
TcpProxyRpcServer::new(TcpProxyRpcService::new(
|
||||
instance_manager.clone(),
|
||||
client_type,
|
||||
)),
|
||||
client_type,
|
||||
);
|
||||
}
|
||||
|
||||
registry.register(
|
||||
AclManageRpcServer::new(AclManageRpcService::new(instance_manager.clone())),
|
||||
"",
|
||||
);
|
||||
|
||||
registry.register(
|
||||
PortForwardManageRpcServer::new(PortForwardManageRpcService::new(instance_manager.clone())),
|
||||
"",
|
||||
);
|
||||
|
||||
registry.register(
|
||||
StatsRpcServer::new(StatsRpcService::new(instance_manager.clone())),
|
||||
"",
|
||||
);
|
||||
|
||||
registry.register(LoggerRpcServer::new(LoggerRpcService), "");
|
||||
|
||||
registry.register(
|
||||
ConfigRpcServer::new(ConfigRpcService::new(instance_manager.clone())),
|
||||
"",
|
||||
);
|
||||
|
||||
registry.register(
|
||||
WebClientServiceServer::new(InstanceManageRpcService::new(instance_manager.clone())),
|
||||
"",
|
||||
);
|
||||
}
|
||||
|
||||
fn parse_rpc_portal(rpc_portal: Option<String>) -> anyhow::Result<SocketAddr> {
|
||||
if let Some(Ok(port)) = rpc_portal.as_ref().map(|s| s.parse::<u16>()) {
|
||||
Ok(SocketAddr::from(([0, 0, 0, 0], port)))
|
||||
} else {
|
||||
let mut rpc_addr = rpc_portal
|
||||
.map(|addr| {
|
||||
addr.parse::<SocketAddr>()
|
||||
.context("failed to parse rpc portal address")
|
||||
})
|
||||
.transpose()?;
|
||||
select_proper_rpc_port(&mut rpc_addr)?;
|
||||
rpc_addr.ok_or_else(|| anyhow::anyhow!("failed to parse rpc portal address"))
|
||||
}
|
||||
}
|
||||
|
||||
fn select_proper_rpc_port(addr: &mut Option<SocketAddr>) -> anyhow::Result<()> {
|
||||
match addr {
|
||||
None => {
|
||||
*addr = Some(SocketAddr::from(([0, 0, 0, 0], 0)));
|
||||
select_proper_rpc_port(addr)?;
|
||||
Ok(())
|
||||
}
|
||||
Some(addr) => {
|
||||
if addr.port() == 0 {
|
||||
let Some(port) = crate::utils::find_free_tcp_port(15888..15900) else {
|
||||
tracing::warn!(
|
||||
"No free port found for RPC portal, skipping setting RPC portal"
|
||||
);
|
||||
return Err(anyhow::anyhow!("No free port found for RPC portal"));
|
||||
};
|
||||
addr.set_port(port);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user