内网穿透之仿神卓互联的延迟带宽技术实现

写作背景:

由于公司之前开发的某市公安巡警系统用的是web架构,没上线之前小组一直在测试,由于疫情的关系有好几个同事是在家办公的,这就需要使用内网穿透技术来讲内网的项目映射到公网上,公司里安装的是神卓互联内网穿透客户端,但是公司开通的是2M带宽的版本,我担心带宽不够会让产品经理觉得页面加载慢是因为我没有做界面优化,但是实际用的时候并没有发现页面访问慢,反而很流畅,引起了我的好奇。

神卓互联初始速度为什么很快

我第一个想到的就是带宽带宽肯定是大于2M的,于是就下载一个大文件测试一下下载速度,发现最初下载速度最快,然后速度慢慢趋向于稳定,最终的下载速度确实是2M,还真的是不多不少刚好2M标准带宽。
 

延迟带宽技术

经过不断的测试,最后发现一种叫做延迟带宽的技术,就是刚开始的时候设置一个最高的带宽,慢慢变小至指定的带宽,比如限制用户的带宽是1M,那么用户使用1M的带宽速度肯定是不快的,既要让用户体验好,速度快,又不能提升带宽,那么延迟带宽技术就是最好的方案,我们在家里看视频,往往是最开始的时候加载最耗带宽,加载完成后其实对带宽要求不是很大,如果使用延迟带宽技术,在最开始加载的时候适当调大带宽,加载完成后再流控至最终的带宽。

如何实现延迟带宽

怀着打破砂锅问到底的态度,我决定亲自试一下把这个功能给做出来

在C和java之间选型,我决定先用java尝试一下,因为毕竟C语言的开发效率要明显低于java。

 

查阅了相关资料

看到网上网友的做法是这样的:

1.假设下载或者上传速度上限是m(KB/s),那么发送一个固定的字节数据(假设是n字节)的时间花费是:n/m;
2.假设现在要发送n字节的数据,那么理论所需的时间应该是n/m,而在实际情况下,发送n字节的数据只花费了t秒,那么发送该发送线程就应该睡眠n/m-t秒,这样就基本实现了速度的控制。

 

但我个人认为这样的方法误差比较大,无法做到精准的流量控制,于是继续查阅相关资料。

总结如下:

JAVA中限制接口流量可以通过Guava的RateLimiter类或者JDK自带的Semaphore类来实现,两者有点类似,但是也有区别,要根据实际情况使用。

RateLimiter类是控制以一定的速率访问接口。

Semaphore类是控制允许同时并发访问接口的数量。

流量限制大多使用的令牌桶算法,当有线程拿到令牌时才可以放行,可以通过设置每秒生成的令牌数,来控制通过的速率。一个简单的demo:

public class TestRateLimiter implements Runnable {

    public static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 允许每秒最多500个任务

    public static final RateLimiter limiter = RateLimiter.create(500); 
    public static void main(String[] arg) {
        for (int i = 0; i < 50; i++) {
            limiter.acquire(); 
            Thread t = new Thread(new TestRateLimiter());
            t.start();
        }
    }

    public void run() {
        System.out.println(sdf.format(new Date()) + " 任务结束");
    }
}

还有一种是使用信号量,过设置信号量总数,当线程拿到信号量,才可以执行,当实行完毕再释放信号量。从而控制接口的并发数量。一个简单的demo:

初始化中的第二个参数true代表以公平的方式获取信号量,即先进先出的原则。

public class TestSemaphore implements Runnable {

    public static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

// 允许并发的任务量限制为20个

    public static final Semaphore semaphore = new Semaphore(20, true); 
    public static void main(String[] arg) {
        for (int i = 0; i < 60; i++) {
             Thread t = new Thread(new TestSemaphore());
             t.start();
        }
    }

    public void run() {
        try {
// 获取信号量,不足会阻塞
            semaphore.acquire(); 
            System.out.println(sdf.format(new Date()) + " 任务开始");
            Thread.sleep(5000);
            System.out.println(sdf.format(new Date()) + " 任务结束");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
// 释放信号量
            semaphore.release(); 
        }
    }
}

以上是使用令牌桶算法和信号量实现流控的两种思路,完成版的还在继续完善中。

 

刚才看到有人说为什么要进行流量控制,不控制不是更好吗,它不香吗,其实流控是必须要做的一件事。

为什么需要流控设置

1.流量控制概念

接收端处理数据的速度是有限的,如果发送方的速度太快,就会把缓冲区打满。这个时候如果继续发送数据,就会导致丢包等一系列连锁反应。

所以 TCP 支持根据接收端能力来决定发送端的发送速度。这个机制叫做流控制。

2.流控制

它的具体操作是,接收端主机向发送端主机通知自己可以接收数据的大小,于是发送端会发送不超过这个限度的数据。该大小限度就被称作窗口大小。窗口大小的值就是由接收端主机决定的。

3.窗口大小

在 TCP 报文头部,有一个 16 比特的窗口字段,用来表示接受方的缓冲区大小,接收主机将自己可以接收的缓冲区大小放入这个字段中通知给发送端。这个字段的值越大,说明网络的吞吐量越高。不过,接收端的这个缓冲区一旦面临数据溢出时,窗口大小的值也会随之被设置为一个更小的值通知给发送端,从而控制数据发送量。也就是说,发送端主机会根据接收端主机的指示,对发送数据的量进行控制。

TCP 的流量控制由连接的每一端通过声明的窗口大小来提供 。窗口大小为字节数 ,起始于确认序号字段指明的值,这个值是接收端期望接收的字节。窗口大小是一个 16 bit 字段,因而窗口大小最大为 65535 字节。

4.窗口扩大因子 M

TCP 基于通告窗口大小的机制,运行发送方在停止并等待确认前可以连续发送多个分组。由于发送方不必每发一个分组就停下来等待确认,因此该协议可以加速数据的传输。TCP 传输速率和窗口大小成正相关,在某些情况下,提高窗口大小能够提高传输速率。

但 TCP 窗口大小只有 16bit,最大表示 65535 字节,对当前千兆接口已经是标配,在数据中心对服务器上开始部署 10G 接口的现实情况下,65535 字节的窗口显然是不够的。

常见对 TCP 选项有 7 种,其中 kind=3 是窗口扩大因子选项。TCP 连接初始化时,通信双方使用该选项来协商接收通过的窗口扩大因子。假设 TCP 头部中的通告窗口大小为 N,窗口扩大因子(位移数)是 M,那么 TCP 报文段的实际接收通告窗口大小为:N * (2 *M)。M 的取值范围为 0 ~ 14。这样的话,通告窗口最大约为 1GB,能够满足大部分应用的需求。
 

主机 A 发送 1000bit 的数据给主机 B,这时候主机 B 告诉主机 A,我的窗口大小为 3000,你可以多发点过来,

当主机 B 收到从 3001 号开始的数据段后其缓冲区挤满。

不得不告诉主机 A 暂时停止发送数据,之后窗口收到更新通知后才得以继续进行。

如果这个通知在途中丢失了,可能导致无法继续通信。

所以发送方会时不时发送一个窗口探测的数据段。

此数据端仅含一个字节来获取最新的窗口大小。

当主机 B 处理完之后又会告诉主机 A,我还剩 2000 的空间。你可以继续发送了。
 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值