add some comments to design sticky session

This commit is contained in:
Jun Kurihara 2023-04-28 20:03:50 +09:00
commit 2d79be5577
No known key found for this signature in database
GPG key ID: D992B3E3DE1DED23
5 changed files with 33 additions and 8 deletions

View file

@ -60,8 +60,8 @@ path = '/maps'
# For request path starting with "/maps", # For request path starting with "/maps",
# this configuration results that any path like "/maps/org/any.ext" is mapped to "/replacing/path1/org/any.ext" # this configuration results that any path like "/maps/org/any.ext" is mapped to "/replacing/path1/org/any.ext"
# by replacing "/maps" with "/replacing/path1" for routing to the locations given in upstream array # by replacing "/maps" with "/replacing/path1" for routing to the locations given in upstream array
# Note that unless "path_replaced_with" is specified, the "path" is always preserved. # Note that unless "replace_path" is specified, the "path" is always preserved.
# "path_replaced_with" must be start from "/" (root path) # "replace_path" must be start from "/" (root path)
replace_path = "/replacing/path1" replace_path = "/replacing/path1"
upstream = [ upstream = [
{ location = 'www.bing.com', tls = true }, { location = 'www.bing.com', tls = true },

View file

@ -28,12 +28,15 @@ use x509_parser::prelude::*;
#[derive(Builder)] #[derive(Builder)]
pub struct Backend { pub struct Backend {
#[builder(setter(into))] #[builder(setter(into))]
/// backend application name, e.g., app1
pub app_name: String, pub app_name: String,
#[builder(setter(custom))] #[builder(setter(custom))]
/// server name, e.g., example.com, in String ascii lower case
pub server_name: String, pub server_name: String,
/// struct of reverse proxy serving incoming request
pub reverse_proxy: ReverseProxy, pub reverse_proxy: ReverseProxy,
// tls settings /// tls settings
#[builder(setter(custom), default)] #[builder(setter(custom), default)]
pub tls_cert_path: Option<PathBuf>, pub tls_cert_path: Option<PathBuf>,
#[builder(setter(custom), default)] #[builder(setter(custom), default)]

View file

@ -17,6 +17,7 @@ pub struct ReverseProxy {
} }
impl ReverseProxy { impl ReverseProxy {
/// Get an appropriate upstream destination for given path string.
pub fn get<'a>(&self, path_str: impl Into<Cow<'a, str>>) -> Option<&UpstreamGroup> { pub fn get<'a>(&self, path_str: impl Into<Cow<'a, str>>) -> Option<&UpstreamGroup> {
// trie使ってlongest prefix match させてもいいけどルート記述は少ないと思われるので、 // trie使ってlongest prefix match させてもいいけどルート記述は少ないと思われるので、
// コスト的にこの程度で十分 // コスト的にこの程度で十分
@ -52,9 +53,14 @@ impl ReverseProxy {
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
/// Load Balancing Option
pub enum LoadBalance { pub enum LoadBalance {
/// Simple round robin without session persistance
RoundRobin, RoundRobin,
/// Randomly chose one upstream server
Random, Random,
/// Round robin with session persistance using cookie
StickyRoundRobin,
} }
impl Default for LoadBalance { impl Default for LoadBalance {
fn default() -> Self { fn default() -> Self {
@ -63,22 +69,32 @@ impl Default for LoadBalance {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
/// Upstream struct just containing uri without path
pub struct Upstream { pub struct Upstream {
pub uri: hyper::Uri, // base uri without specific path /// Base uri without specific path
pub uri: hyper::Uri,
} }
#[derive(Debug, Clone, Builder)] #[derive(Debug, Clone, Builder)]
/// Struct serving multiple upstream servers for, e.g., load balancing.
pub struct UpstreamGroup { pub struct UpstreamGroup {
/// Upstream server(s)
pub upstream: Vec<Upstream>, pub upstream: Vec<Upstream>,
#[builder(setter(custom), default)] #[builder(setter(custom), default)]
/// Path like "/path" in [[PathNameBytesExp]] associated with the upstream server(s)
pub path: PathNameBytesExp, pub path: PathNameBytesExp,
#[builder(setter(custom), default)] #[builder(setter(custom), default)]
/// Path in [[PathNameBytesExp]] that will be used to replace the "path" part of incoming url
pub replace_path: Option<PathNameBytesExp>, pub replace_path: Option<PathNameBytesExp>,
#[builder(default)] #[builder(default)]
/// Load balancing option
pub lb: LoadBalance, pub lb: LoadBalance,
#[builder(default)] #[builder(default)]
pub cnt: UpstreamCount, // counter for load balancing /// Counter for load balancing
pub cnt: UpstreamCount,
#[builder(setter(custom), default)] #[builder(setter(custom), default)]
/// Activated upstream options defined in [[UpstreamOption]]
pub opts: HashSet<UpstreamOption>, pub opts: HashSet<UpstreamOption>,
} }
impl UpstreamGroupBuilder { impl UpstreamGroupBuilder {
@ -116,6 +132,7 @@ impl UpstreamGroupBuilder {
pub struct UpstreamCount(Arc<AtomicUsize>); pub struct UpstreamCount(Arc<AtomicUsize>);
impl UpstreamGroup { impl UpstreamGroup {
/// Get an enabled option of load balancing [[LoadBalance]]
pub fn get(&self) -> Option<&Upstream> { pub fn get(&self) -> Option<&Upstream> {
match self.lb { match self.lb {
LoadBalance::RoundRobin => { LoadBalance::RoundRobin => {
@ -127,13 +144,16 @@ impl UpstreamGroup {
let max = self.upstream.len() - 1; let max = self.upstream.len() - 1;
self.upstream.get(rng.gen_range(0..max)) self.upstream.get(rng.gen_range(0..max))
} }
LoadBalance::StickyRoundRobin => todo!(), // TODO: TODO:
} }
} }
/// Get a current count of upstream served
fn current_cnt(&self) -> usize { fn current_cnt(&self) -> usize {
self.cnt.0.load(Ordering::Relaxed) self.cnt.0.load(Ordering::Relaxed)
} }
/// Increment count of upstream served
fn increment_cnt(&self) -> usize { fn increment_cnt(&self) -> usize {
if self.current_cnt() < self.upstream.len() - 1 { if self.current_cnt() < self.upstream.len() - 1 {
self.cnt.0.fetch_add(1, Ordering::Relaxed) self.cnt.0.fetch_add(1, Ordering::Relaxed)

View file

@ -266,6 +266,8 @@ where
}; };
// Fix unique upstream destination since there could be multiple ones. // Fix unique upstream destination since there could be multiple ones.
// TODO: StickyならCookieをここでgetに与える必要
// TODO: Stickyで、Cookieが与えられなかったらset-cookie向けにcookieを返す必要。upstreamオブジェクトに含めるのも手。
let upstream_chosen = upstream_group.get().ok_or_else(|| anyhow!("Failed to get upstream"))?; let upstream_chosen = upstream_group.get().ok_or_else(|| anyhow!("Failed to get upstream"))?;
// apply upstream-specific headers given in upstream_option // apply upstream-specific headers given in upstream_option

View file

@ -1,5 +1,5 @@
/// Server name (hostname or ip address) representation in bytes-based struct /// Server name (hostname or ip address) representation in bytes-based struct
/// For searching hashmap or key list by exact or longest-prefix matching /// for searching hashmap or key list by exact or longest-prefix matching
#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)] #[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct ServerNameBytesExp(pub Vec<u8>); // lowercase ascii bytes pub struct ServerNameBytesExp(pub Vec<u8>); // lowercase ascii bytes
impl From<&[u8]> for ServerNameBytesExp { impl From<&[u8]> for ServerNameBytesExp {
@ -8,8 +8,8 @@ impl From<&[u8]> for ServerNameBytesExp {
} }
} }
/// Server name (hostname or ip address) representation in bytes-based struct /// Path name, like "/path/ok", represented in bytes-based struct
/// For searching hashmap or key list by exact or longest-prefix matching /// for searching hashmap or key list by exact or longest-prefix matching
#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)] #[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct PathNameBytesExp(pub Vec<u8>); // lowercase ascii bytes pub struct PathNameBytesExp(pub Vec<u8>); // lowercase ascii bytes
impl PathNameBytesExp { impl PathNameBytesExp {