暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

linux之提升大文件下载速度

4189


写这篇的缘起是,我要在腾讯云上一台配置为2c4g的云服务器上下载一个比较大的文件,走内网下载。

开始的时候,我使用的工具是wget,wget显示的网速是25MB/s左右。

但是2c4g的机器官方标示的内网带宽是1.5Gbps,按说我的内网网速理应可以达到1.5*1024/8=192MB/s的。

扩展:
1.Gbps和MB/s的关系。
Gbps是gigabit per second的缩写,即千兆比特每秒。是一种传输速率的单位,指每秒传输的位(比特数量),也叫带宽。除此之外,还有Mbps(兆比特每秒)和Kbps(kbps)。
三者之间的换算公式是:1Gbps=1024Mbps, 1Mbps=1024Kbps。

2.Mb/s和MB/s也是表示传输速率的单位。它们和Mbps有什么区别呢?
首先是Mbps,其全称是megabits per second, 表示兆比特每秒。这里的bit是表示数字信号数据的最小单位。Mb/s的含义与其相同,均表示兆比特每秒,指每秒传输的位数量。而对于MB/s,其中的B,是Byte,字节。MB/s的含义是兆字节每秒,指每秒传输的字节数量。我们知道1Byte=8bit。综上,就有了1MB/s=8Mbps=8Mb/s。

内网下载的速率远没有达到预期,我就猜想是不是wget工具的问题,在网上搜索了一番,就找到一个替代工具axel

axel

github地址:https://github.com/axel-download-accelerator/axel/releases

axel是一个多线程下载工具,支持http、https、ftp和ftps协议。

centos安装

采用源码安装, 在网站下载release包, axel-2.4.tar.bz2。

yum install bzip2 gcc-c++ openssl-devel intltool -y

bzip2 -d axel-2.4.tar.bz2

tar -xvf axel-2.4.tar.gz

cd axel-2.4

./configure && make && make install

axel的用法

axel -a -n [Num_of_Thread] url -o [output_file]

其中-n [Num_of_Thread],表示并发线程数。


测试情况

通过不断调整num_of_thread的数量,下载速度得到了明显的提升,达到了174MB/s(n=12), 接近于官网的内网宽带大小。


🐂🐂🐂




python多线程下载大文件

上面我们也看到axel用了多线程,下载速度得到了大幅提升。如果我们用python实现一个多线程下载器应该怎么做?

我们使用python下载文件,一般使用单线程下载。为防止文件过大,直接载入内存从而导致内存不足,一般采用requests的stream模式。比如如下的代码:

import requests
from tqdm import tqdm

def download_file(url, file_path, chunk_size):
    with requests.get(url, stream=True) as r:
        r.raise_for_status()
        file_size = r.headers['Content-length']
        # pbar = tqdm(total=int(file_size), unit='B', unit_scale=True, desc=file_path)
        with tqdm(total=int(file_size), unit='B', unit_scale=True, desc=file_path) as pbar:
            with open(file_path, 'wb') as f:
                for chunk in r.iter_content(chunk_size=chunk_size):
                    f.write(chunk)
                    pbar.update(chunk_size)


写一个多线程的版本,其实就是根据总大小,对数据进行分片下载,每个下载交由一个线程异步下载。指定range请求。Range 请求头的语法:Range: bytes=start-end。注:为防止出现数据损坏或不一致的问题,请对文件进行有效性验证。代码做测试用。

class MultiThreadDownload:
    def __init__(self, url, total, file_name, num=10):
        self.num = num              # 线程数,默认10
        self.url = url               # url
        self.total = total             # 文件大小
        self.file_name = file_name         # 文件名

    def calc_divisional_range(self):
        step = self.total // self.num
        arr = list(range(0, self.total, step))
        result = []
        for i in range(len(arr) - 1):
            s_pos, e_pos = arr[i], arr[i + 1] - 1
            result.append([s_pos, e_pos])
        result[-1][-1] = self.total - 1
        return result

    # 下载方法
    def range_download(self, s_pos, e_pos):
        headers = {"Range": f"bytes={s_pos}-{e_pos}"}
        res = requests.get(self.url, headers=headers, stream=True)
        with open(self.file_name, "rb+") as f:
            f.seek(s_pos)
            for chunk in res.iter_content(chunk_size=64 * 1024):
                if chunk:
                    f.write(chunk)

    def download(self):
        divisional_ranges = self.calc_divisional_range()
        # 先创建空文件
        with open(self.file_name, "wb") as f:
            pass
        with ThreadPoolExecutor() as p:
            futures = []
            for s_pos, e_pos in divisional_ranges:
                futures.append(p.submit(self.range_download, s_pos, e_pos))
            # 等待所有任务执行完毕
            as_completed(futures)






参考:

https://blog.csdn.net/qq_44291044/article/details/98798442

https://blog.csdn.net/as604049322/article/details/119847193


文章转载自PostgreSQL运维技术,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论