🔥 使用 Rust 重构 #5
@ -2,7 +2,10 @@
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="Go" enabled="true" />
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
|
16
Cargo.lock
generated
16
Cargo.lock
generated
@ -133,9 +133,9 @@ checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
@ -1452,7 +1452,7 @@ version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"bitflags 1.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1601,7 +1601,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88073939a61e5b7680558e6be56b419e208420c2adb92be54921fa6b72283f1a"
|
||||
dependencies = [
|
||||
"base64 0.13.1",
|
||||
"bitflags 1.3.2",
|
||||
"bitflags 1.2.1",
|
||||
"serde",
|
||||
]
|
||||
|
||||
@ -1666,11 +1666,11 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.9.2"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de"
|
||||
checksum = "23a2ac85147a3a11d77ecf1bc7166ec0b92febfa4461c37944e180f319ece467"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"bitflags 1.2.1",
|
||||
"core-foundation",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
@ -1878,7 +1878,7 @@ version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"bitflags 1.2.1",
|
||||
"core-foundation",
|
||||
"system-configuration-sys",
|
||||
]
|
||||
|
@ -7,3 +7,8 @@ paths = ["/"]
|
||||
[[locations.destinations]]
|
||||
id = "static"
|
||||
uri = "files://regions?index=index.html"
|
||||
|
||||
[[applications]]
|
||||
id = "script"
|
||||
exe = "./script.sh"
|
||||
workdir = "regions"
|
||||
|
3
regions/script.sh
Executable file
3
regions/script.sh
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "Good morning!"
|
25
src/main.rs
25
src/main.rs
@ -2,8 +2,7 @@ pub mod auth;
|
||||
mod config;
|
||||
mod proxies;
|
||||
mod sideload;
|
||||
|
||||
use std::collections::VecDeque;
|
||||
pub mod warden;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use poem::{listener::TcpListener, EndpointExt, Route, Server};
|
||||
@ -12,18 +11,10 @@ use proxies::RoadInstance;
|
||||
use tokio::sync::Mutex;
|
||||
use tracing::{error, info, Level};
|
||||
|
||||
use crate::proxies::{metrics::RoadMetrics, route};
|
||||
use crate::proxies::route;
|
||||
|
||||
lazy_static! {
|
||||
static ref ROAD: Mutex<RoadInstance> = Mutex::new(RoadInstance {
|
||||
regions: vec![],
|
||||
metrics: RoadMetrics {
|
||||
requests_count: 0,
|
||||
failures_count: 0,
|
||||
recent_successes: VecDeque::new(),
|
||||
recent_errors: VecDeque::new(),
|
||||
}
|
||||
});
|
||||
static ref ROAD: Mutex<RoadInstance> = Mutex::new(RoadInstance::new());
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
@ -84,6 +75,16 @@ async fn main() -> Result<(), std::io::Error> {
|
||||
}),
|
||||
);
|
||||
|
||||
// Process manager
|
||||
{
|
||||
let mut app = ROAD.lock().await;
|
||||
{
|
||||
let reg = app.regions.clone();
|
||||
app.warden.scan(reg);
|
||||
}
|
||||
app.warden.start().await;
|
||||
}
|
||||
|
||||
tokio::try_join!(proxies_server, sideload_server)?;
|
||||
|
||||
Ok(())
|
||||
|
@ -5,12 +5,15 @@ use queryst::parse;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::warden::Application;
|
||||
|
||||
use super::responder::StaticResponderConfig;
|
||||
|
||||
#[derive(Debug, Object, Clone, Serialize, Deserialize)]
|
||||
pub struct Region {
|
||||
pub id: String,
|
||||
pub locations: Vec<Location>,
|
||||
pub applications: Vec<Application>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Object, Clone, Serialize, Deserialize)]
|
||||
|
@ -50,6 +50,15 @@ pub struct RoadMetrics {
|
||||
const MAX_TRACE_COUNT: usize = 10;
|
||||
|
||||
impl RoadMetrics {
|
||||
pub fn new() -> RoadMetrics {
|
||||
RoadMetrics {
|
||||
requests_count: 0,
|
||||
failures_count: 0,
|
||||
recent_successes: VecDeque::new(),
|
||||
recent_errors: VecDeque::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_success_rate(&self) -> f64 {
|
||||
if self.requests_count > 0 {
|
||||
(self.requests_count - self.failures_count) as f64 / self.requests_count as f64
|
||||
|
@ -3,6 +3,8 @@ use poem::http::{HeaderMap, Uri};
|
||||
use regex::Regex;
|
||||
use wildmatch::WildMatch;
|
||||
|
||||
use crate::warden::WardenInstance;
|
||||
|
||||
use self::{
|
||||
config::{Location, Region},
|
||||
metrics::RoadMetrics,
|
||||
@ -19,9 +21,20 @@ pub mod route;
|
||||
pub struct RoadInstance {
|
||||
pub regions: Vec<Region>,
|
||||
pub metrics: RoadMetrics,
|
||||
pub warden: WardenInstance,
|
||||
}
|
||||
|
||||
impl RoadInstance {
|
||||
pub fn new() -> RoadInstance {
|
||||
RoadInstance {
|
||||
regions: vec![],
|
||||
warden: WardenInstance {
|
||||
applications: vec![],
|
||||
},
|
||||
metrics: RoadMetrics::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn filter(
|
||||
&self,
|
||||
uri: &Uri,
|
||||
|
73
src/warden/mod.rs
Normal file
73
src/warden/mod.rs
Normal file
@ -0,0 +1,73 @@
|
||||
pub mod runner;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use futures_util::lock::Mutex;
|
||||
use lazy_static::lazy_static;
|
||||
use poem_openapi::Object;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::{debug, warn};
|
||||
|
||||
use crate::proxies::config::Region;
|
||||
|
||||
use self::runner::AppInstance;
|
||||
|
||||
lazy_static! {
|
||||
static ref INSTANCES: Mutex<HashMap<String, AppInstance>> = Mutex::new(HashMap::new());
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct WardenInstance {
|
||||
pub applications: Vec<Application>,
|
||||
}
|
||||
|
||||
impl WardenInstance {
|
||||
pub fn new() -> WardenInstance {
|
||||
WardenInstance {
|
||||
applications: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scan(&mut self, regions: Vec<Region>) {
|
||||
self.applications = regions
|
||||
.iter()
|
||||
.flat_map(|item| item.applications.clone())
|
||||
.collect::<Vec<Application>>();
|
||||
debug!(
|
||||
applications = format!("{:?}", self.applications),
|
||||
"Warden scan accomplished."
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn start(&self) {
|
||||
for item in self.applications.iter() {
|
||||
let mut instance = AppInstance::new();
|
||||
match instance.start(item.clone()).await {
|
||||
Ok(_) => {
|
||||
debug!(id = item.id, "Warden successfully created instance for");
|
||||
INSTANCES.lock().await.insert(item.clone().id, instance);
|
||||
}
|
||||
Err(err) => warn!(
|
||||
id = item.id,
|
||||
err = format!("{:?}", err),
|
||||
"Warden failed to create an instance for"
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for WardenInstance {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Object, Clone, Serialize, Deserialize)]
|
||||
pub struct Application {
|
||||
pub id: String,
|
||||
pub exe: String,
|
||||
pub args: Option<Vec<String>>,
|
||||
pub env: Option<HashMap<String, String>>,
|
||||
pub workdir: String,
|
||||
}
|
104
src/warden/runner.rs
Normal file
104
src/warden/runner.rs
Normal file
@ -0,0 +1,104 @@
|
||||
use std::{borrow::BorrowMut, collections::HashMap, io};
|
||||
|
||||
use super::Application;
|
||||
use futures_util::lock::Mutex;
|
||||
use lazy_static::lazy_static;
|
||||
use tokio::{
|
||||
io::{AsyncBufReadExt, BufReader},
|
||||
process::{Child, Command},
|
||||
};
|
||||
|
||||
lazy_static! {
|
||||
static ref STDOUT: Mutex<HashMap<String, String>> = Mutex::new(HashMap::new());
|
||||
static ref STDERR: Mutex<HashMap<String, String>> = Mutex::new(HashMap::new());
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AppInstance {
|
||||
pub app: Option<Application>,
|
||||
pub program: Option<Child>,
|
||||
}
|
||||
|
||||
impl AppInstance {
|
||||
pub fn new() -> AppInstance {
|
||||
AppInstance {
|
||||
app: None,
|
||||
program: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn start(&mut self, app: Application) -> io::Result<()> {
|
||||
return match Command::new(app.exe.clone())
|
||||
.args(app.args.clone().unwrap_or_default())
|
||||
.envs(app.env.clone().unwrap_or_default())
|
||||
.current_dir(app.workdir.clone())
|
||||
.stdout(std::process::Stdio::piped())
|
||||
.stderr(std::process::Stdio::piped())
|
||||
.spawn()
|
||||
{
|
||||
Ok(mut child) => {
|
||||
let stderr_reader = BufReader::new(child.stderr.take().unwrap());
|
||||
let stdout_reader = BufReader::new(child.stdout.take().unwrap());
|
||||
|
||||
tokio::spawn(read_stream_and_capture(stderr_reader, app.id.clone(), true));
|
||||
tokio::spawn(read_stream_and_capture(
|
||||
stdout_reader,
|
||||
app.id.clone(),
|
||||
false,
|
||||
));
|
||||
|
||||
self.app = Some(app.clone());
|
||||
self.program = Some(child);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
};
|
||||
}
|
||||
|
||||
pub async fn stop(&mut self) -> Result<(), io::Error> {
|
||||
if let Some(child) = self.program.borrow_mut() {
|
||||
return child.kill().await;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_stdout(&self) -> Option<String> {
|
||||
if let Some(app) = self.app.clone() {
|
||||
STDOUT.lock().await.get(&app.id).cloned()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_stderr(&self) -> Option<String> {
|
||||
if let Some(app) = self.app.clone() {
|
||||
STDERR.lock().await.get(&app.id).cloned()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AppInstance {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
async fn read_stream_and_capture<R>(reader: R, id: String, is_err: bool) -> io::Result<()>
|
||||
where
|
||||
R: tokio::io::AsyncBufRead + Unpin,
|
||||
{
|
||||
let mut lines = reader.lines();
|
||||
while let Some(line) = lines.next_line().await? {
|
||||
if !is_err {
|
||||
if let Some(out) = STDOUT.lock().await.get_mut(&id) {
|
||||
out.push_str(&line);
|
||||
}
|
||||
} else if let Some(out) = STDERR.lock().await.get_mut(&id) {
|
||||
out.push_str(&line);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in New Issue
Block a user