文章存档 » 十二月 2012

Shell脚本: 自动检测最快的Ubuntu软件源

每次装好Ubuntu,对于大多数用户来说,首先要做的事就是手动修改/etc/apt/sources.list文件,将里面的官方软件源地址更换为自己学校或者公司的软件源。当我们更换一个工作环境后,可能伴随着又要替换旧的软件源地址。

笔者觉得这样每次手动更改软件源是一件及其麻烦重复的劳动,于是编写了一个自动更新最快软件源的脚本,从此一劳永逸。

原理

最直观的想法就是:对各个软件源进行测速,选出最快的那个,之后将其替换为新的软件源。

那么如何对各个软件源测速呢?有两种方法:

一、用ping命令 测量其平均响应时间 选出响应时间最短的那个 二、用wget命令 测量下载一个文件的总时间 选出耗时最少的那个

那么这两种方法有什么区别呢?我们该用哪个呢?

前者选出的是响应时间最优的,后者选出的是下载速度最快的。我们都知道软件源的作用是供客户端下载更新软件,所以当然是后者的方法更为准确,但笔者最终选择了前者作为测速方案,因为前者的用户体验更好且代码简单易懂。设想,如果我们采用后者,那么需要从每个软件源下载一个文件,并且这个文件不能太小,否则无法区分他们的速度,那么一个显而易见的情况是脚本需要运行较长的时间。

虽然存在某些软件源可能响应时间很短,而下载速度却很慢的情况,但经过笔者的多次实验,发现这样的情况并不常见。

实现

首先测试用户网络状态

利用

local speed=`ping -W1 -c1 www.baidu.com 2> /dev/null | grep "^rtt" |  cut -d '/' -f5`

取出其平均响应时间 如果speed == “” 则说明网络不通,提示用户,且退出程序。 否则,说明网络正常,继续执行。

检测软件源列表文件是否存在

test -f $SOURCES_MIRRORS_FILE

若不存在,提示用户,且退出程序。

对每个软件源地址进行测速

在测速之前清空上次运行的测速结果文件,之后将每个软件源的测速结果(源地址 平均响应时间)写入测速结果文件

对测速结果进行排序

sort -k 2 -n -o $MIRRORS_SPEED_FILE $MIRRORS_SPEED_FILE

对每行记录 按照平均响应时间升序排列

选出最快的软件源

head -n 1 $MIRRORS_SPEED_FILE | cut -d ' ' -f1 `

通过取已排序列表中的第一条,选出最快的软件源

询问用户是否要使用该软件源

用户确认后,先对用户之前的软件源进行备份,然后再替换。

脚本源代码

最新版本:https://github.com/KJlmfe/soEasyUbuntu/tree/master/getfastmirror

#!/bin/bash

#Program:
#    This program gets the fastest ubuntu software sources from SOURCES_MIRRORS_FILE
#    and backup && update /etc/apt/sources.list

#Author:  KJlmfe    www.freepanda.me

#History:
#    2012/12/6    KJlmfe    First release


VERSION="precise"  # precise is code of Ubuntu 12.04 if your ubuntu is not 12.04 please change
TEST_NETCONNECT_HOST="www.baidu.com"
SOURCES_MIRRORS_FILE="sources_mirrors.list"    
MIRRORS_SPEED_FILE="mirrors_speed.list"

function get_ping_speed()    #return average ping $1 time
{
    local speed=`ping -W1 -c1 $1 2> /dev/null | grep "^rtt" |  cut -d '/' -f5`
    echo $speed
}

function test_mirror_speed()    #
{
    rm $MIRRORS_SPEED_FILE 2> /dev/null; touch $MIRRORS_SPEED_FILE
    
     cat $SOURCES_MIRRORS_FILE | while read mirror
    do
        if [ "$mirror" != "" ]; then
            echo -e "Ping $mirror c"
            local mirror_host=`echo $mirror | cut -d '/' -f3`    #change mirror_url to host
    
            local speed=$(get_ping_speed $mirror_host)
    
            if [ "$speed" != "" ]; then
                echo "Time is $speed"
                echo "$mirror $speed" >> $MIRRORS_SPEED_FILE
            else
                echo "Connected failed."
            fi
        fi
    done
}

function get_fast_mirror()
{
    sort -k 2 -n -o $MIRRORS_SPEED_FILE $MIRRORS_SPEED_FILE
    local fast_mirror=`head -n 1 $MIRRORS_SPEED_FILE | cut -d ' ' -f1`
    echo $fast_mirror
}

function backup_sources()
{
    echo -e "Backup your sources.list.n"
    sudo mv /etc/apt/sources.list /etc/apt/sources.list.`date +%F-%R:%S`
}

function update_sources()
{
    local COMP="main restricted universe multiverse"
    local mirror="$1"
    local tmp=$(mktemp) 

    echo "deb $mirror $VERSION $COMP" >> $tmp
    echo "deb $mirror $VERSION-updates $COMP" >> $tmp
    echo "deb $mirror $VERSION-backports $COMP" >> $tmp 
    echo "deb $mirror $VERSION-security $COMP" >> $tmp
    echo "deb $mirror $VERSION-proposed $COMP" >> $tmp

    echo "deb-src $mirror $VERSION $COMP" >> $tmp 
    echo "deb-src $mirror $VERSION-updates $COMP" >> $tmp 
    echo "deb-src $mirror $VERSION-backports $COMP" >> $tmp 
    echo "deb-src $mirror $VERSION-security $COMP" >> $tmp 
    echo "deb-src $mirror $VERSION-proposed $COMP" >> $tmp

    sudo mv "$tmp" /etc/apt/sources.list
    echo -e "Your sources has been updated, and maybe you want to run "sudo apt-get update" now.n";
}

echo -e "nTesting the network connection.nPlease wait...   c"

if [ "$(get_ping_speed $TEST_NETCONNECT_HOST)" == "" ]; then
    echo -e "Network is bad.nPlease check your network."; exit 1
else
    echo -e "Network is good.n"
    test -f $SOURCES_MIRRORS_FILE

    if [ "$?" != "0" ]; then  
        echo -e "$SOURCES_MIRRORS_FILE is not exist.n"; exit 2
    else
        test_mirror_speed
        fast_mirror=$(get_fast_mirror)

        if [ "$fast_mirror" == "" ]; then
            echo -e "Can't find the fastest software sources. Please check your $SOURCES_MIRRORS_FILEn"
            exit 0
        fi

        echo -e "n$fast_mirror is the fastest software sources. Do you want to use it? [y/n] c"    
        read choice

        if [ "$choice" != "y" ]; then
            exit 0
        fi

        backup_sources
        update_sources $fast_mirror
    fi
fi

exit 0 

图片来自: DigiDreamGrafix.com

PHP实现文件下载断点续传

如果我们的网站提供文件下载的服务,那么通常我们都希望下载可以断点续传(Resumable Download),也就是说用户可以暂停下载,并在未来的某个时间从暂停处继续下载,而不必重新下载整个文件。

通常情况下,Web服务器(如Apache)会默认开启对断点续传的支持。因此,如果直接通过Web服务器来提供文件的下载,可以不必做特别的配置,即可享受到断点续传的好处。由于这些文件直接通过Web服务器来提供下载,后端脚本无法对这个下载过程进行控制。这对于仅提供公开、静态文件的网站来说不是问题,但对于需要提供私有、动态文件的网站来说,直接通过Web服务器来提供下载就无法满足需求了。这时,就需要在编写后台脚本程序时,加入对断点续传的支持。

本文将以PHP为例,简要介绍实现文件下载断点续传的方法。

原理

断点续传的原理还是比较直观的。

HTTP协议规定了如何传输某个资源的一部分,而不是全部。比如,有一个文件的大小是1000字节,浏览器可以只请求该文件的前300个字节,或者只请求第500到第1000个字节。通过这种方式,就可以不必在一次请求中传输某个资源的全部内容,而是发起多次请求,每次仅请求其中的一部分内容。等所有这些请求都返回之后,再把得到的内容一块一块的拼接起来得到完整的资源。

实现断点续传就是要利用HTTP协议的上述特性。当用户暂停下载的时候,浏览器会记录已经下载到什么位置,当用户在未来某一时间恢复下载时,就可以从上次暂停的位置继续下载,而不必从头开始。

实现

由于部分传输不是强制的,服务器可以支持也可以不支持,所以,我们需要在程序中告诉浏览器,它请求的资源是否支持部分传输。这可以通过设置HTTP的 Accept-Ranges 响应头信息来实现。PHP代码如下:

header('Accept-Ranges: bytes');

Accept-Ranges: bytes 告诉浏览器,该资源支持以字节为单位的部分传输。这个响应头需要附加在支持部分传输的所有资源上。

当接受到一个请求时,我们需要从浏览器的请求中提取浏览器具体是在请求资源的哪一个部分。这个信息是通过 Range 请求头来传递的。在PHP中,它被存储在$_SERVER['HTTP_RANGE']中。我们需要检查这个变量是否定义了,如果定义了,则使用该值,否则,就将range设为整个资源。

$range = "0-". ($content_length-1);
if(isset($_SERVER['HTTP_RANGE'])){
    $range = $_SERVER['HTTP_RANGE'];
}

接下来,就需要分析 $range 的值,来决定返回资源的哪一部分内容。可能的取值示例:

100-200 // 第100到第200字节
500-    // 第500字节到文件末尾
-1000   // 最后的1000个字节

这里需要注意,得到一个Range之后,你需要对它的取值进行检验,包括:

  1. 开始位置非负
  2. 结束位置需要大于开始位置
  3. 开始位置需要小于文件长度减一 (因为这里的位置索引是从0开始的)
  4. 若结束位置大于文件长度减一,则需要把它的值设置为文件长度减一

如果Range的取值不合法,则需要终止程序并告知浏览器:

header('HTTP/1.1 416 Requested Range Not Satisfiable');

为了保持文章简洁,具体的校验代码这里就不提供了。下面假定你已经校验了Range的取值,并得到了 $start$end 两个变量,分别表示开始位置和结束位置。

接下来要做的就是把文件的对应部分的内容发送给浏览器。不过要注意的是,这里涉及到需要发送多个HTTP响应头信息,具体如下:

header('HTTP/1.1 206 Partial Content');
header('Accept-Ranges: bytes');
header("Content-Range: bytes $start-$end/$filesize");
$length = $end - $start + 1;
header("Content-Length: $length");

/* 输出文件的指定部分 */

这里的$length需要注意一下,它的取值是本次传输的内容的长度,而不是整个文件的长度。另外需要注意的一点是,这里的HTTP状态码是206,不是200。

总结

文件下载的断点续传实际上是利用了HTTP协议中对传输部分文件的支持。而HTTP协议的这一特性不仅可以用于实现断点续传,客户端程序也可以利用它来实现多线程下载。

在实现断点续传的过程中,需要注意正确设置各种HTTP头信息。错误的头信息将导致用户下载到的文件损坏,无法使用。

参考资料

提高代码质量之代码审查

写代码是一种创造性的劳动,是现在社会中少数的纯手工的工作之一。程序员就像手工艺人,代码就像手工艺品。手工艺品有自己独特的魅力,但是也缺乏流水线产品的严谨和一致性。所以代码审查(Code Review)就像是把玩鉴赏手工艺品一样,通过审查代码来体会编码者的思维逻辑,同时相互学习取长补短。代码审查是提高个人和团队的代码质量的一个很有用的方法。

个人对自己的代码可以进行代码审查,因为今天的你已经不是昨天的你,你可以站在不同的角度和不同的层次来审查自己过去的代码。子曰:温故而知新。所以对于自己的代码应该定期做Code Review。可以用好日程安排工具,比如Google Calendar之类。在自己做到觉得需要日后来审查一下的地方,就可以在日程安排上记录一笔。到时候就可以按照记录去审查了。作为一个想不断进取和自我提高的程序员来说,这是很高效的方法。

自己的代码审查很好进行,只要你有毅力。相比之下团队的代码审查就不是那么容易实施了。但是代码审查在团队中能体现出更大的促进作用。除了可以让团队成员之间相互学习进步、激发思考、统一编程风格之外,代码审查还能发现一些系统的潜在问题和QA测试不到的问题,从而提高代码质量。

但是在团队内进行代码审核的时候,也要注意方式方法,才能发挥其积极作用。否则还有可能产生负面的影响。

1. 引导团队成员对代码审核的正确认识

代码审核并非是给某人挑错,也不是瞻仰膜拜牛人的奇技淫巧,而是大家泡一杯咖啡端一杯茶,一起来鉴赏品玩代码而已。目的是为了促进团队成员的成长和提高。

如果在代码审查的时候发现bug,不要过于责问,而是应该从技术层面加以分析、建议和讨论。如果你是Manager, 一定要注意不要炫耀自己,低调一点,重点在做好组织工作。初期一定要引导好团队成员对代码审核的认识,不要变成个人代码秀或者挑错大会和批斗大会了。

2. 要让团队成员看到代码审查的好处

在组织Code Review的初期,一定要用心去挖掘一些可以让大家学到东西的代码段。让大家体会到,可以从别人严谨的逻辑和优雅的编码学习经验技术,也可以从自己和别人的脏代码以及疏忽大意来吸取教训。尝到了甜头,这么有好处有意思的事情,大家肯定乐意再来一次了。

3. 平等轻松的纯技术讨论

在做代码审查的时候,只能有组织者,不能有CTO、Manager、权威、叫兽砖家。这是一个平等的轻松的技术讨论会,可以佐以饮品和零食哈哈~要激励大家勇敢地说出自己的看法,置疑其他人的看法。这样才能激发出大家的参与热情和不断地思考。这种讨论其实就是一种头脑风暴。

4. 针对项目的热点和难点进行代码审查

针对一次代码审查进行选题的时候,可以多从项目的热点和难点入手,比如框架是怎么工作的?数据库是怎么封装的?缓存是怎么处理的?内存占用高的地方是怎么优化的?一些复杂的算法是怎么实现的?某个bug为什么会反复出现?————走进科学将带你走进代码审查的世界~~哈哈回到正题,代码审查的选题一定要对事不对人。记住!要避免针对某个程序员的代码审核。。。除非你想炒了他。

5. 少而精

代码审查是一项很激烈的脑力运动,而且在多人参与的时候尤其如此。和头脑风暴的本质差不多。不但自己要理清别人的代码,还要去审视和判断,还要说出自己和看法,还要听懂别人说出的看法。所以每次代码审查的内容和时间要少而精,否则大家疲劳之后,讨论就会变得冗余而无趣。

6. 发现问题由编码者自己去修正或者重写

当我们在审查时发现了问题、讨论了修正和改进的方案,我们肯定需要一个人来讲这些方案实现出来。这个时候就应该由造成问题的程序员来做了。因为只有真实去改动代码的时候,才会将问题最真实的原因暴露出来。这个程序员才能去彻底修复这个bug。同时在他也会从自己的错误中得到提高,在之后的职业生涯中应该不会再犯同样的错误。

7. 做好讨论记录和问题跟踪

在大家的思想相互撞击的时候肯定会有很多灵光闪呀闪,如果不记录下来就太可惜了,丧失了讨论的意义。所以每次代码审查一定要有专人来负责做记录。讨论之后将记录整理并email给所有人。

PM和Manager之类的就可以根据讨论记录来分配改进任务。每个团队都应该有自己的项目管理系统,或者说是bug追踪系统吧。如果没有,赶紧去搞一个。推荐开源的Redmine (Ruby on Rails)Mantis (PHP/MySQL),以及提供在线服务的Lighthouse

特别要说一下Redmine,支持中文,功能强大且简洁,跨平台使用很舒服,而且也支持多种数据库(MySQL, PostgreSQL or SQLite),同时可以和git集成使用(在git push时自动更新ticket状态)。

8. 使用适合自己团队的代码审查工具

现在很多团队都转向了使用GIT,那么就可以利用Github来作为代码审查的工具。在Github里面可以针对某个branch的某个commit提交评论,然后Github会给项目的参与者发送一封邮件。通过这种在线的评论来进行代码审查,让大家可以利用零散时间去看一些小的代码段,就像在论坛发帖回帖一样,不用一定要坐在一起,不会打断正常的工作,同时也完整地保留了讨论的过程。如果是个人或者小团队,不想开源自己的代码的话,也可以利用Bitbucket来建立私有库托管代码和进行代码审查。

总之

XXXX是一把双刃剑,代码审查也是如此。 进行代码审查对于组织者的能力要求比较高,要多思考和调整,激发大家一起来做好代码审查,发挥其最有益的效用。