[分享]奉献给各位小白:使用多线程升级 -- 提升下载速度 [整理、增强]

系统安装、升级讨论
版面规则
我们都知道新人的确很菜,也喜欢抱怨,并且带有浓厚的Windows习惯,但既然在这里询问,我们就应该有责任帮助他们解决问题,而不是直接泼冷水、简单的否定或发表对解决问题没有任何帮助的帖子。乐于分享,以人为本,这正是Ubuntu的精神所在。
头像
outersky
帖子: 188
注册时间: 2007-04-25 10:07
来自: 上海

[分享]奉献给各位小白:使用多线程升级 -- 提升下载速度 [整理、增强]

#1

帖子 outersky » 2008-03-13 21:17

[之前这里写了很多废话,统统删除]
网速10K以上的,就不要进来参和了,减少对服务器的连接数[做小白要厚道]。

第一部分: 多线程 update ,下载软件列表
首先需要把服务器那边的包的列表拉回来,系统才知道哪个文件升级了。
我的思路是:
1:用工具将服务器那边的文件统统取到本地[多线程]。
2:在本地建立一个最小的镜像站点。
3:将源指向本地,update,速度当然就快了。

下面开始一步一步说明:
1: 我写了个简单的脚本如下。 注:我现在只开了一个官方的源[保证软件最新最鲜],而且只去二进制文件,不取源代码。所以我的sources.list 只有一行:

代码: 全选

deb http://archive.ubuntu.com/ubuntu/ hardy main restricted universe mulitiverse 
脚本如下[请取下保存为 sync_repo.sh ]:

代码: 全选


#/bin/sh

#服务器的根地址,与 sources.list 里面那个地址的前半段一致。
DIST_URL=http://archive.ubuntu.com/
#CPU架构
DIST_ARCH=i386
#需要安装的包的类型,四个都写上吧
DIST_REPO="main multiverse universe restricted"
#服务器的目录
DIST_DIR=ubuntu/dists/hardy/
#本地保存的位置:放在这里是为了给apache2假设站点用。需要注意有合适的权限。
LOCAL_DIR=/var/www/

#进入本地目录
cd ${LOCAL_DIR}

#函数:取得某个子目录下面的文件, 带一个目录参数,比如  get_sub_dir main 
get_sub_dir(){
    echo handling $1 ...
    TMP_DIR=$1/binary-${DIST_ARCH}/
#将之前存在的目录统统删除,免得 axel 发现已经存在相同文件名后,取下来的文件会带 .0 .1 之类讨厌的后缀。
    rm -rf ${TMP_DIR}
    mkdir -p ${TMP_DIR}
    cd ${TMP_DIR}
#多线程去拉软件列表, 10 可以改小一点。
    axel -n 10 ${DIST_URL}${DIST_DIR}${TMP_DIR}Packages.bz2
}

#函数:刷新软件列表,其实就是循环调用上面的 get_sub_dir,分别取得  main multiverse restricted universe 四个目录的内容
refresh_package_list(){
  for repo in ${DIST_REPO} ; do
    cd ${LOCAL_DIR}${DIST_DIR}
    get_sub_dir ${repo}
  done
}

#如果之前已经有 ${DIST_DIR} 目录了,提醒一下
if [ -d ${DIST_DIR} ]; then
    echo dir: ${LOCAL_DIR}${DIST_DIR} already exists.
fi

#如果还没有,那么提醒并自动创建
if [ ! -d ${DIST_DIR} ] ; then
    echo "dir: ${LOCAL_DIR}${DIST_DIR} not exists, will create it."
    mkdir -p ${DIST_DIR} 
fi

#进入目录,相当于  cd /var/www/ubuntu/dists/hardy
cd ${DIST_DIR}

#函数:wget 单线程取一个文件,如果之前存在,删除之。
wget_file(){
  if [ -f $2 ]; then
    rm $2
  fi
  wget $1$2
}

#获取 Release 文件,里面有本Release的时间。这个文件很小,所以单线程。
#这里会先给你看看本地原来的Release[如果有的话]里面的时间,让你知道你现在的这个是什么时间更新的
#如果觉得没有必要,那么就不进行后面的更新了。
if [ -f Release ]; then
    echo "Release file exists, and Date is :"    
    grep Date: Release
    echo 
    echo -n "Are you going to continue ?[y/n/Y/N/yes/no]"
    read answer
    case ${answer} in
    n|N|no)
        exit 0
        ;;
    esac

fi

#获取两个文件
wget_file ${DIST_URL}${DIST_DIR} Release.gpg
wget_file ${DIST_URL}${DIST_DIR} Release

#给你看看新的Release的时间
grep Date: Release

#如果比上面一个原来的时间新,那就意味着需要更新了
#如果觉得没有必要,也可以自己放弃
echo -n "Will you refresh package list ?[y/n/Y/N/yes/no]"
read answer
case ${answer} in
Y|y|yes)
#如果要去更新,那么就去取软件列表回来,多线程
    refresh_package_list
    ;;
esac


弄好了就运行这个脚本:

代码: 全选

 bash sync_repo.sh 
2:现在软件列表已经取回来了,打开apache服务器,我这里是默认打开的。如果没有开,那么请下载 apache2 软件包,不再赘述。

代码: 全选

                  apt-get install apache2
                  sudo /etc/init.d/apache2 start
             
3:将源指向本地。
这里我是直接编辑 /etc/hosts的,指向 127.0.0.1 如:

代码: 全选

                       # more /etc/hosts
127.0.0.1	localhost
127.0.0.1	archive.ubuntu.com
                
然后运行正常的update。

代码: 全选

 apt-get update 
呵呵,飞吧。

第二部分:多线程 apt-get dist-upgrade , 下载软件包
说明:这里使用了网络上的 apt-axel 这个脚本,大家可以去搜索一下,并感谢一下原作者,至少在心里默念3编。

1:上面,我们修改了/etc/hosts,现在要改回去。 直接把那一行注释掉,不要删除,因为下次还要用。修改为:

代码: 全选

                       # more /etc/hosts
127.0.0.1	localhost
#127.0.0.1	archive.ubuntu.com
                
2:保存下面的脚本为 apt-axel ,放到某个能找到的目录下,方便使用,我存放在了 /usr/local/sbin 目录下面。
我仅仅做了略微的修改[原来的里面支持8个源],并且补充了一些注释,让新生代小白容易看一点。

apt-axel :

代码: 全选

    #!/bin/bash

###########################################################################
#
# Authors: Jesús Espino García & Lucas García
# Email: jespino@imap.cc
# Date: 31/05/2004
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
###########################################################################

# If the config file not exist, load the default parameters

VERSION="0.1"

#可以在本地存在一个配置文件,没有也没有关系,我现在就没有建,脚本会自动判断的。
CONFIG_FILE="/etc/apt-axel.conf";

#注意,如下命令统统在tmp目录下面执行。
cd /tmp

#如果在本地有了配置,就加载,我现在没有这个配置,当然不加载
if [ -f $CONFIG_FILE ]; then
  . $CONFIG_FILE 
else
#配置都下载下面几个语句里面了。
  # Verbose TRUE or FALSE
  VERBOSE="FALSE"

  # Conections
#默认多线程的连接数
  CONNECTIONS=8

  # Servers
#服务器路径,与第一部分里面那个基本一致,但是注意这里后面是有 ubuntu 这个子目录的。
   server1="http://archive.ubuntu.com/ubuntu/"
fi

# Set the package variable to be global
package="";

# Show the help
#函数:显示一个帮助
mostrar_ayuda() {
  echo "Usage: apt-axel <option> <package>";
  echo "";
  echo "Options:";
  echo "  install - Install new packages (paquete es libc6 y no libc6.deb)";
  echo "  upgrade - Do a software upgrade";
  echo "  dist-upgrade - Do a distribution upgrade, see apt-get(8)";
  echo "  --version - Print the current version of apt-axel";
  echo "  --help - Show this help";
  echo "";
}

# Get a package from the ftp server
#函数:这个就是多线程去取软件包的。
get_package() {

#函数中的函数
  descargar() {
    echo "===========BEGIN DOWNLOAD ${server1}${url} ==to = /tmp/${archivo}======"
    # Check the $VERBOSE variable and the filesize, if the filesize is lower than 200K will use only 4 conections
    # And if the $VERBOSE variable is TRUE, then print the axel output
      if [ $size -gt 200000 ]; then
        echo "axel -n $CONNECTIONS $server1$url "
        axel -n $CONNECTIONS $server1$url 
      else
        echo "axel -n 4 $server1$url "
        axel -n 4 $server1$url 
      fi
    echo "===========END DOWNLOAD ${server1}${url}========"
    
  }

  # Getting data
#用apt-cache找到某个指定的软件的具体的url位置.
  url=$(apt-cache show $package | grep ^Filename: | sed s/^Filename:\ //)
  echo "URL:[${url}]"

#存下来的文件名称
  archivo=$(echo "$url" | sed s/^.*$package/$package/)
  echo "archivo:[${archivo}]"
  pkgstatus=$(dpkg -s $package 2> /dev/null | grep ^Status: | grep -v "not-installed")
  echo "pkgstatus:[${pkgstatus}]"

#MD5校验值,取下来以后,当然要看看包有没有损坏啦
  md5sum=$(apt-cache show $package | grep ^MD5sum: | sed s/^MD5sum:\ //)
  echo "md5sum:[${md5sum}]"
  size=$(apt-cache show $package | grep ^Size: | sed s/^Size:\ //)
  echo "size:[${size}]"

  # Downloading the package
#看看之前是否已经下载过了,即本地的cache里面是否已经存在
  if [ -f /var/cache/apt/archives/$archivo ]; then
    echo -n "Package already downloaded. Checking md5sum of $package package: "
#如果md5不匹配,还是要删除再重新去下载
    while [ $md5sum != $(md5sum /var/cache/apt/archives/$archivo | sed s/\ .*$//) ]; do 
      echo "incorrect"
      echo "Downloading again $package package."
      rm -f /var/cache/apt/archives/$archivo
      descargar
    done
    echo "correct"

#如果本地没有cache,去下载吧,没什么可犹豫的
  else 
      descargar
  fi

  # Move the file to /var/cache/apt/archives
#如果下载成功了,把文件移到cache目录下面去。
  if [ -f /tmp/${archivo} ]; then
    if [ $md5sum == $(md5sum /tmp/$archivo | sed s/\ .*$//) ]; then
      mv -f /tmp/$archivo /var/cache/apt/archives/
    fi
  fi
}

#下面几个函数不说,大家一看函数名字也知道干什么的了
pkg_install() {
  echo "want to install [$1]"
  for package in $(apt-get -s install $1 | grep ^Inst\ | sed s/^Inst\ // | sed s/\ .*$//); do
    echo "package:[${package}]"
    get_package
  done
  apt-get -y install $1
}

upgrade() {
  for package in $(apt-get -s upgrade | grep ^Inst\ | sed s/^Inst\ // | sed s/\ .*$//); do
    get_package
  done
  apt-get -y upgrade
}

dist_upgrade() {
  for package in $(apt-get -s dist-upgrade | grep ^Inst\ | sed s/^Inst\ // | sed s/\ .*$//); do
    get_package
  done
  apt-get -y dist-upgrade
}


#
# Main program
#
if [ `id -u` != 0 ]; then
  echo "You must be root to run this command."
else
  case $1 in
    install) pkg_install $2;;
    upgrade) upgrade;;
    dist-upgrade) dist_upgrade;;
    (--version) echo "Current Version: $VERSION";;
    (--help | -h) mostrar_ayuda;;
    *) mostrar_ayuda;;
  esac
fi
运行新的脚本来升级:

代码: 全选

    apt-axel dist-upgrade 
如果要安装单个软件包:

代码: 全选

    apt-axel install   your_package_name

完毕!
上次由 outersky 在 2008-03-16 12:44,总共编辑 2 次。
头像
eexpress
帖子: 58428
注册时间: 2005-08-14 21:55
来自: 长沙

#2

帖子 eexpress » 2008-03-13 22:21

其实,脚本,只是需要有大量的测试,以便在特殊的情况下,有容错能力。
● 鸣学
头像
cnshzj007
帖子: 685
注册时间: 2006-05-24 21:38
来自: 上海
联系:

#3

帖子 cnshzj007 » 2008-03-13 22:37

好想法,顶,我期待着用。
我只是一个ARCH的FANS,来自RH和UBUNTU的滋润!
头像
INUYASHA
帖子: 366
注册时间: 2007-03-16 15:42

#4

帖子 INUYASHA » 2008-03-16 1:07

建议将update的功能也加入好了(直接调用apt-get update)
这样功能齐了
头像
SecretZero
帖子: 1670
注册时间: 2007-08-30 5:39

#5

帖子 SecretZero » 2008-03-16 1:19

楼主,不会用代码啊。。

能不能具体点?
深度,值得深入!
http://bbs.deepin.org/?u=348684

最新的综艺魔法相册
http://www.77studio.net/?fromuid=344
头像
outersky
帖子: 188
注册时间: 2007-04-25 10:07
来自: 上海

#6

帖子 outersky » 2008-03-16 11:45

我在另外一个帖子里面把 apt-get update 的功能也改成多线程的了,不过需要独立操作一下,
可以到 系统安装与升级里面去看看。我在这里再贴一遍:
outersky 写了:我现在在作8.04的小白,所以几乎天天有更新,有时甚至一天就更新好几次,与楼主的情况类似,但是又不尽相同。
我能上网,但是网速比较慢,因为同时用的人太多了,所以如果不抢的话,带宽一般在3k左右,相比现在每天动辄100M的更新,不来点狠的还真不行。
通过 apt-axel 脚本,现在可以实现下载deb包的时候实现多线程,一般在30k左右,比之前的好多了,已经知足了。

但是 apt-get update 更新软件列表的时候,速度还是比较慢,下载一个Packages.bz2就要半个多小时,简直要吐血,所以我就想了个办法,用axel先下载回来,但是有了Packages.bz2以后,不知道怎么让apt-get去分析这个里面的内容,于是乎。。。
我想到了在我本机假设一个最小的镜像,只镜像apt-get update需要下载的文件,于是写了一个 sync_repo.sh 的脚本,以保持正确的目录结构,然后,修改 /etc/hosts 把127.0.0.1设定为 archive.ubuntu.com ,也就是我现在源的地址,大家一定明白了,下一步就是启动apache了,因为我之前直接把包下载到 /var/www下面了,所以apache启动完毕以后,一切OK.

这个时候再运行 apt-get update ,速度飞快,哈哈, 如果最后看到真的有软件更新了,那么我就把 /etc/hosts 里面那一行去掉, 开始从真正的站点去下载deb,当然,是使用apt-axel多线程下载。

用apache的好处就是,如果真的需要联网的时候,切换起来比较方便,不需要配置deb / deb-src。

我现在使用的脚本如下,仅供参考:

代码: 全选


#/bin/sh

DIST_URL=http://archive.ubuntu.com/
DIST_ARCH=i386
DIST_REPO="main multiverse universe restricted"
DIST_DIR=ubuntu/dists/hardy/
LOCAL_DIR=/var/www/

cd ${LOCAL_DIR}

get_sub_dir(){
    echo handling $1 ...
    TMP_DIR=$1/binary-${DIST_ARCH}/
    rm -rf ${TMP_DIR}
    mkdir -p ${TMP_DIR}
    cd ${TMP_DIR}
    axel -n 10 ${DIST_URL}${DIST_DIR}${TMP_DIR}Packages.bz2
}

refresh_package_list(){
  for repo in ${DIST_REPO} ; do
    cd ${LOCAL_DIR}${DIST_DIR}
    get_sub_dir ${repo}
  done
}

if [ -d ${DIST_DIR} ]; then
    echo dir: ${LOCAL_DIR}${DIST_DIR} already exists.
fi

if [ ! -d ${DIST_DIR} ] ; then
    echo "dir: ${LOCAL_DIR}${DIST_DIR} not exists, will create it."
    mkdir -p ${DIST_DIR} 
fi

cd ${DIST_DIR}

wget_file(){
  if [ -f $2 ]; then
    rm $2
  fi
  wget $1$2
}

if [ -f Release ]; then
    echo "Release file exists, and Date is :"    
    grep Date: Release
    echo 
    echo -n "Are you going to continue ?[y/n/Y/N/yes/no]"
    read answer
    case ${answer} in
    n|N|no)
        exit 0
        ;;
    esac

fi

wget_file ${DIST_URL}${DIST_DIR} Release.gpg
wget_file ${DIST_URL}${DIST_DIR} Release

grep Date: Release

echo -n "Will you refresh package list ?[y/n/Y/N/yes/no]"
read answer
case ${answer} in
Y|y|yes)
    refresh_package_list
    ;;
esac


INUYASHA 写了:建议将update的功能也加入好了(直接调用apt-get update)
这样功能齐了
头像
outersky
帖子: 188
注册时间: 2007-04-25 10:07
来自: 上海

#7

帖子 outersky » 2008-03-16 12:43

我脚本写的不多,问题在所难免[很想课本上的话],欢迎大家提意见或者报告问题。我将继续努力,为各位小白做一点力所能及的事。
头像
linlee
帖子: 1132
注册时间: 2007-10-20 11:30

#8

帖子 linlee » 2008-03-16 12:56

谢谢分享 :P
头像
wavydoom
帖子: 1081
注册时间: 2008-02-15 0:08

#9

帖子 wavydoom » 2008-03-16 13:15

cow,这么难?
头像
outersky
帖子: 188
注册时间: 2007-04-25 10:07
来自: 上海

#10

帖子 outersky » 2008-03-16 13:29

wavydoom 写了:cow,这么难?
我会继续努力,争取把所有的事情,都拿脚本来搞定,基本实现四个自动化。
头像
sevk
帖子: 2060
注册时间: 2007-05-08 16:26
系统: arch
来自: 火星内核某分子内某原子核内
联系:

#11

帖子 sevk » 2008-03-16 13:36

好文!
如果apt支持p2p,并且支持upnp和内网UDP穿透就爽了.
特别是apt在后台更新时,p2p慢慢变快的特点很强大.

都怪电信,今年个人用户只有2M了,我从CN99下载最快也只有250K了.
明年换网通玩玩.
笔记本 :
F208S : gentoo
A460P i3G D6 : UBUNTU + WIN7
UN43D1 : UBUNTU + WIN7
1000人超级QQ群 LINUX + WIN : 31465544 或 18210387
头像
outersky
帖子: 188
注册时间: 2007-04-25 10:07
来自: 上海

#12

帖子 outersky » 2008-03-16 13:40

诚心气我们是不是,我更新一次真的不容易啊,尤其是看到 apt-get update的时候,单位竟然是 b/s ,当时那叫一个心凉,可是,谁叫咱是小白呢。
sevk 写了:好文!
如果apt支持p2p,并且支持upnp和内网UDP穿透就爽了.
特别是apt在后台更新时,p2p慢慢变快的特点很强大.

都怪电信,今年个人用户只有2M了,我从CN99下载最快也只有250K了.
明年换网通玩玩.
头像
wavydoom
帖子: 1081
注册时间: 2008-02-15 0:08

#13

帖子 wavydoom » 2008-03-16 14:07

outersky 写了:诚心气我们是不是,我更新一次真的不容易啊,尤其是看到 apt-get update的时候,单位竟然是 b/s ,当时那叫一个心凉,可是,谁叫咱是小白呢。
sevk 写了:好文!
如果apt支持p2p,并且支持upnp和内网UDP穿透就爽了.
特别是apt在后台更新时,p2p慢慢变快的特点很强大.

都怪电信,今年个人用户只有2M了,我从CN99下载最快也只有250K了.
明年换网通玩玩.
带宽大户~拖出去砍了 :lol:
头像
SecretZero
帖子: 1670
注册时间: 2007-08-30 5:39

#14

帖子 SecretZero » 2008-03-17 1:52

厉害~!强~!
立刻试试。。
深度,值得深入!
http://bbs.deepin.org/?u=348684

最新的综艺魔法相册
http://www.77studio.net/?fromuid=344
头像
SecretZero
帖子: 1670
注册时间: 2007-08-30 5:39

#15

帖子 SecretZero » 2008-03-17 2:12

更改hosts后无法bash啊。。
运行脚本提示不能连接。

换了这个好像不是太行哦

代码: 全选

# more /etc/hosts
127.0.0.1   localhost
127.0.0.1   archive.ubuntu.com
sudo apt-get update不了
上次由 SecretZero 在 2008-03-17 2:36,总共编辑 3 次。
深度,值得深入!
http://bbs.deepin.org/?u=348684

最新的综艺魔法相册
http://www.77studio.net/?fromuid=344
回复