23. 从零用Rust编写正反向代理,流控小姐姐的温柔一刀!

原创
2023/12/19 08:31
阅读数 5.9K

wmproxy

wmproxy已用Rust实现http/https代理, socks5代理, 反向代理, 静态文件服务器,四层TCP/UDP转发,内网穿透,后续将实现websocket代理等,会将实现过程分享出来,感兴趣的可以一起造个轮子

项目地址

国内: https://gitee.com/tickbh/wmproxy

github: https://github.com/tickbh/wmproxy

温柔的小姐姐

  我的名字叫 <font color=red>流控</font>,至于我的工作你们看到我的名字也想必很清楚,我被创造出来为了的这世界更美好,期望这世界永远不会堵车,所以他们称我为温柔的小姐姐。

与数据不得不说的故事

  数据是个急性子的家伙,每次看他总是充充忙忙的带着一大推的大部分,想在我这里横冲直撞的。这就不得不说上一次他那急忙的模样了。

  还记得上次他带着一大帮的兄弟,成千上万个的,知道的人知道他们在护送重要资料,不知道的还以为他们去打仗呢!

  我守护的是一个广场,还有广场后面的一条路。

  那一次他带着兄弟来的太快,就跟我说:“流控姐姐,快点通往广场的门打开,我这边有兄弟们来了”。

  我就把门打开,让他带着他的兄弟们过来快速的进入广场。

  不一会儿广场慢慢的有点挤了起来,他就跟我说:“你快点把广场前面的路开大点,后面的兄弟来的太快,这条路又不能快速的通过,会把你的广场挤满的”。

  我就说:“不行哦,我接到上级的指令,通知说现在这条路只能开这么大,每秒通过100人最多了(限速1M/S),前面那条大路现在的人太多了,你这边要是太多的人会全部堵在一起的。”

  他就急忙的问:“那怎么办,我现在数据很重要,也很急,你看旁边那些无关的也一直在占用着路,你能不能向你的上级反馈一下?”

  我就说:“我现在就帮你反馈一下,但是要等下上级的调度哦。暂时你还是只能通过这么多!”

  我转身向上级说:“这边有大量的重要数据拥堵在广场这,他们请求调高优先级,提高缓冲区及通行速度。”

  上级答:“我现在去协调一下,把其它的数据叫他们先缓缓,你叫他先耐心等待下”

  眼看着上级的指定还没有下来,但是广场上已经快挤满了人了,我赶紧去那个广场的入口处理,我就把那个广场前的那个闸机打开,不让新的人进来。要不然等下广场出事故了。

  数据兄弟看着我,我也表示我暂时也无能为力,我说:“让你后面的兄弟缓缓,我这广场暂时容纳不下那么多人了(数据缓冲区已满,不再接收新的数据,也不会读出socket上的数据,反向的压着流量的传输)。”

  此时正在我们焦急等待的时候,上级传来了指令,说其它位置已经暂缓处理了,你当下可以把广场外围开起来,并把广场前面的路两边都放开(优先级调高,优先传输重要数据)。

  我就立马启动了扩容的按钮,只见广场外围的大圈全部打开,可以容纳10倍的人,并把广场前的出口路打开,可以通行10000人/s限速100M/S)。

  数据说:“快快快,已经可以通行了,快把闸机打开,我要赶紧带着兄弟们把资料送到。”

  只看到广场出去的人比进入的人多了很多,广场一下子空了起来。

  很快数据就全部带着他的兄弟走了,去完成任务了,说:“等我完成这任务,我向你来讨教讨教你这边的管理法,怎么能保证高效的完成调度的任务”。

当起了老师

数据完成了他的任务,回来的时候在跟我请教了起来。

  数据说:“我就是想问问,你的那个广场是怎么个情况,怎么一满了就可以自动防止人进入,然后一空了就可以通知人进入的。”

  我跟他解答说:“我这是一个异步处理的一把刀,我可以在空闲的时候完全的不占用任何的资源,在忙碌的时候又可以把全部的CPU用上。完成高效的运转。”

  数据问:“那你用了什么秘密法宝,他这么厉害?”

  我说:“我就是用了一种古代就开始在用的——旗语,也就是PollSemaphore,我这里存一把旗,并存了一把钥匙,当我有钥匙的时候,也就是广场人没满的时间,你来了我就放你进场,这样子我也不用管广场里有没有满。当你进入后,如果 广场满了,他会将我手上的钥匙拿走。没有钥匙的话,新进来的人我就不会让你们进入了。”

  数据问:“那如果广场有位置的话,那你怎么样才能重新得到钥匙?”

  我说:“这就是我高效的时候了,刚刚我钥匙交出去的时候,我已经调用了self.sem.poll_acquire(cx),当广场有人出去的话,他就会通知Waker,然后我就可以主动去找他拿到钥匙了,这样子我就可以重新拥有钥匙了。”

  数据说:“原来你缓冲区是这样子的吖,那你出口的那条路上,怎么限定人流量的?”

  我说:“这个就要有请RateLimitLayer了,他有定义了per每个周期的时间就比如每秒,或者每分钟,或者每小时,nums就是每个周期内可以通行的字节数。下面是详细的定义。”

pub struct RateLimitLayer {
    /// 周期内可以通行的数据
    nums: u64,
    /// 每个周期的时间
    per: Duration,
    /// 当前周期下,还剩下可通行的数据
    left_nums: u64,
    /// 下一个时间重新计算的日期
    util: Instant,
    sleep: Pin<Box<Sleep>>,
}

  数据问:“那如果当前周期耗完的话,是不是还没有到下个周期前就不能继续通行了?”

  我答:“确实是的,你当前周期耗光了可用的额度,那不能通行了哦,我就会向Pin::new(&mut self.sleep).poll(cx).is_pending(),如果他现在不能用,就等会到那个时间,他就会通知我啦。他通知我,我就会重置掉到前的数据,这样子你就可以继续通行了。”

self.left_nums = self.nums;
self.util = Instant::now() + self.per;
self.sleep.as_mut().set(tokio::time::sleep_until(Instant::now() + self.per));

  数据说:“小姐姐你好厉害,还好有你在这里严格的控制着,我才能那么准时的到达”。

  我答:“那是,请叫我温柔的一刀,该严格的时候我就会严格,不严格那只会更麻烦。你说是吧。”

流控在互联网中是很重要的概念,因为基本上大部分的公网出口都不是无限的,就同一个网站,API的接口重要性肯定会比静态文件重要性来的高,所以为了使系统更稳定,感谢流控小姐姐使出这温柔的一刀。

点击 <font color=green>[关注]</font><font color=green>[在看]</font><font color=green>[点赞]</font> 是对作者最大的支持

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
1
分享
返回顶部
顶部