3 releases

0.1.3 Nov 18, 2024
0.1.2 Oct 21, 2024
0.1.1 Oct 19, 2024

#4 in #factory

Download history 125/week @ 2024-10-14 200/week @ 2024-10-21 146/week @ 2024-11-18

152 downloads per month

Custom license

27KB
536 lines

reqwest-lb

Crates.io Apache licensed Build Status

A crate for reqwest support load balancer, use the reqwest-middleware.

Overview

This crate provide a middleware LoadBalancerMiddleware, it implement reqwest-middleware::Middleware, then use the lb:// instead http:// or https://

  • dependencies

    [dependencies]
    reqwest = "0.12"
    reqwest-middleware = "0.3"
    reqwest-lb = "0.1"
    
  • example

    use reqwest::{Client, Url};
    use reqwest_lb::LoadBalancerMiddleware;
    use reqwest_lb::SimpleLoadBalancer;
    use reqwest_lb::{LoadBalancerFactory, LoadBalancerPolicy};
    use reqwest_middleware::{ClientBuilder, ClientWithMiddleware};
    
    pub fn create<const N: usize>(
        ports: [u16; N],
        policy: LoadBalancerPolicy<Url>,
    ) -> ClientWithMiddleware {
        // create load balancer factory
        let mut factory = LoadBalancerFactory::default();
    
        // create url list
        let mut urls = Vec::with_capacity(N);
        for port in ports {
            urls.push(Url::parse(format!("http://127.0.0.1:{}", port).as_str()).unwrap());
        }
    
        // create load balancer
        let load_balancer = SimpleLoadBalancer::new(urls, policy);
    
        // register load balancer for the host: example-server
        factory.add("example-server", load_balancer);
    
        // create reqwest client
        let middleware = LoadBalancerMiddleware::new(factory);
        ClientBuilder::new(Client::builder().no_proxy().build().unwrap())
            .with(middleware)
            .build()
    }
    
    pub async fn run() {
        // use round robin policy
        let client = create([3001, 3002], LoadBalancerPolicy::RoundRobin);
        // http://127.0.0.1:3001
        let response = client.get("lb://example-server/").send().await.unwrap();
        // http://127.0.0.1:3002
        let response = client.get("lb://example-server/").send().await.unwrap();
    }
    
    
  • load balancer policy

    • RoundRobin (default)
    • Random
    • First
    • Last
    • Weight
  • discovery

    use discovery dynamic control the supplier elements, can send the elements change event insert,remove or initialized to the stream.

    
    use http::Extensions;
    use reqwest::Url;
    use reqwest_lb::discovery::Change;
    use reqwest_lb::supplier::discovery::DiscoverySupplier;
    use reqwest_lb::{LoadBalancer, LoadBalancerPolicy, SimpleLoadBalancer};
    
    #[tokio::test]
    async fn laod_balancer_discovery() {
        let ports = (0..10).map(|offset| 3000 + offset).collect::<Vec<_>>();
        let mut events = ports
            .iter()
            .map(|port| {
                Url::parse(&format!("http://127.0.0.1:{}", port)).map(|url| Change::Insert(*port, url))
            })
            .collect::<Vec<_>>();
        events.push(Ok(Change::Initialized));
    
        let discovery = futures::stream::iter(events);
        let supplier = DiscoverySupplier::new(discovery);
        let load_balancer = SimpleLoadBalancer::new(supplier, LoadBalancerPolicy::RoundRobin);
        let mut extensions = Extensions::new();
        for port in ports {
            let selected = load_balancer.choose(&mut extensions).await;
            assert_eq!(
                selected,
                Ok(Some(
                    Url::parse(&format!("http://127.0.0.1:{}", port)).unwrap()
                ))
            );
        }
    }
    
    
  • with retry

    the load balancer policy use RoundRobin

    
    use reqwest::{Client, Url};
    use reqwest_lb::{
        LoadBalancerFactory, LoadBalancerMiddleware, LoadBalancerPolicy, SimpleLoadBalancer,
    };
    use reqwest_middleware::ClientBuilder;
    use reqwest_retry::policies::ExponentialBackoff;
    use reqwest_retry::RetryTransientMiddleware;
    
    #[tokio::test]
    async fn with_retry() {
        // load balancer middleware
        let mut factory = LoadBalancerFactory::default();
        let urls = vec![
            // error
            Url::parse("https://www.rust-lang-error.org").unwrap(),
            // ok
            Url::parse("https://www.rust-lang.org").unwrap(),
        ];
        let load_balancer = SimpleLoadBalancer::new(urls, LoadBalancerPolicy::RoundRobin);
        factory.add("rust-server", load_balancer);
        let load_balancer_middleware = LoadBalancerMiddleware::new(factory);
    
        // retry middleware
        let retry_middleware = RetryTransientMiddleware::new_with_policy(
            ExponentialBackoff::builder().build_with_max_retries(1),
        );
    
        // reqwest client
        // https://www.rust-lang-error.org => retry error (choose next) => https://www.rust-lang.org
        let client = ClientBuilder::new(Client::default())
            .with(retry_middleware)
            .with(load_balancer_middleware)
            .build();
    
        let response = client.get("lb://rust-server").send().await;
        assert!(response.is_ok());
    }
    
    
    

License

This project is licensed under the Apache 2.0

Dependencies

~7–18MB
~228K SLoC