2013年3月

[gallery ids="339,341,344,340,342,345,343,338"]

朋友鸟蛋来长沙会老婆,顺便买了个小东西,无事,湖南省植物园一游,其间花未大开,有樱花几朵,翠竹几支,无物,无趣,唯摄取几处小景以为留念,待时日正,重游之……

PHP加速器是一个为了提高PHP执行效率,从而缓存起PHP的操作码,这样PHP后面执行就不用解析转换了,可以直接调用PHP操作码,这样速度上就提高了不少。而如果想要执行通过ZendGuard加密的PHP代码,从PHP5.3以后就需要安装ZendGuardLoader。本文将介绍如何安装ZendGuardLoader及eAccelerator,后者为PHP加速器。如果你还没有一个可用的PHP环境,请阅读《使用 Ubuntu 包管理工具安装与配置Nginx + MySQL/PostgreSQL/SQLite + PHP/Perl/Python 服务器环境》 这篇文章。

安装 ZendGuardLoader

从PHP5.3开始,Zend Optimizer已经不再被支持,而Zend推出了PHP5.3的专用版本,改名为Zend Guard Loader,它的下载地址为:

如果你不想进入它们的主页也可以使用下面这个命令直接下载:

wget http://downloads.zend.com/guard/5.5.0/ZendGuardLoader-php-5.3-linux-glibc23-i386.tar.gz

如果你和我一样使用的是64位版本,则使用下面这行命令:

wget http://downloads.zend.com/guard/5.5.0/ZendGuardLoader-php-5.3-linux-glibc23-x86_64.tar.gz

下载完成之后,解压得到的文件,你会得到一个名为:ZendGuardLoader.so 的文件。

创建一个新的目录,并把 ZendGuardLoader.so 移动到该目录下:

mkdir /usr/zend
mv ZendGuardLoader.so /usr/zend

移动完成之后,修改PHP的配置文件,如果你是按《使用 Ubuntu 包管理工具安装与配置Nginx + MySQL/PostgreSQL/SQLite + PHP/Perl/Python 服务器环境》 这篇文章进行的PHP环境配置,那么修改下面这个文件:

/etc/php5/fpm/php.ini

在该文件的最末端添加下面这些配置:

zend_extension=/usr/zend/ZendGuardLoader.so
zend_loader.enable=1
zend_loader.disable_licensing=0
zend_loader.obfuscation_level_support=3
zend_loader.license_path=

保存该文件之后,重新启动 php-fpm

/etc/init.d/php5-fpm restart

安装 eAccelerator

最流行的三种PHP加速器有APC、eAccelerator、XCache,XCache是国人的产品,eAccelerator则好像在全球的范围内使用的人数多一些,APC则是PHP PECL中的一个扩展,好像Facebook在使用它,我使用的是eAccelerator,而本文也只介绍如何安装该加速器。

首先下载eAccelerator,它的官方地址为:

或者使用下面这个命令:

wget https://github.com/eaccelerator/eaccelerator/tarball/master
mv master eaccelerator.tar.gz

解压该文件,进入解压得到的目录中:

tar zxvf eaccelerator.tar.gz
cd eaccelerator-eaccelerator-42067ac/

首先我们复制 control.php 文件到默认虚拟主机的目录下:

cp control.php /srv/www/default/public/eaccelerator-control.php

注意,你解压后得到的目录名称可能与我的不一样,请以你自己的目录名称为准。之后执行下面这些命令:

phpize
./configure
make
make install
make clean

这会在 /usr/lib/php5/20090626/ 目录中生成一个名为 eaccelerator.s 的文件。

现在再一次修改 php.ini 文件,继续在文件的最末端加下面这些配置:

zend_extension="/usr/lib/php5/20090626/eaccelerator.so"
eaccelerator.shm_size="16"
eaccelerator.cache_dir="/tmp/eaccelerator"
eaccelerator.enable="1"
eaccelerator.optimizer="1"
eaccelerator.check_mtime="1"
eaccelerator.debug="0"
eaccelerator.filter=""
eaccelerator.shm_max="0"
eaccelerator.shm_ttl="0"
eaccelerator.shm_prune_period="0"
eaccelerator.shm_only="0"
eaccelerator.compress="1"
eaccelerator.compress_level="9"
eaccelerator.allowed_admin_path="/srv/www/default/public/eaccelerator-control.php"

保存该文件,再一次重新启动 php-fpm,eAcceleratpr安装成功。

你可以下载 Matrix Stack PHP Prober 探针 来检测是否安装成功。

修改 eAccelerator Control控制文件的用户名与密码

要修改 eAccelerator Control的用户名与密码,只需要打开 control.php(在本文中,将该文件复制为 eaccelerator-control.php文件,所以你应该修改该文件),修改下面这两行即可:

$user = "admin";
$pw = "eAccelerator";

这篇文章的题目是有点儿长,因为我也想不使用什么更简单的话来为本文命名了,所以就使用了最直白的方式。本文将教你如何在一个全新的系统上面安装 Nginx 服务器软件,配置 PHP/Perl/Python 程序的支持以及MySQL/PostgreSQL/SQLite数据库支持,因为服务器的优化是各种各样的,而且一般要去做服务器优化的人应该也不会使用包管理工具来安装这些,所以本文不会对服务器的优化做过多的讨论,仅仅只简单的带过,希望本文对你有帮助。

写在前面的话

你能阅读本文,那肯定是有下面这样的需求中的一个或者多个:

  • 刚刚在 Linode 买了一个VPS,选择了Ubuntu作为服务器操作系统,想使用Wordpress/TextPattern或者Drupal建个博客或者网站;
  • 公司买了个新服务器,也装了Ubuntu服务器,需要安装一个Plone给公司内部使用
  • 像我一样,很喜欢使用Movable Type,我想使用它发布一些静态的网站
  • 我是一个SOHOer,有很多个基于PHP开发的客户网站,但是不想再使用共享虚拟主机,想使用自己的服务器集中管理
  • ......

其实这些需求可以简单的归为下面这几个:

  1. 服务器操作系统选择的是 Ubuntu
  2. 服务器需要能支持 PHP/Python/Perl Web应用
  3. 服务器需要提供MySQL/PostgreSQL/SQLite 数据库支持
  4. 服务器应该可以服务于多个网站
  5. 服务器端的Web服务器软件选择了Nginx而不是Apache

有了需求就只要去努力满足这些需求就成了,本文就是在帮助你知道如何满足这些需求,但是我也不是专业人士,所以,有的时候你可能在别的地方发现有更好的方法,如果你真的找到了更好的方法,请在这里留言告诉我一下,但是请不要在这里来展示你的专业,你就当我这里是放屁就成,但是不要在我这里放屁,否则后果自负。

这篇文章写的时候使用的是 Ubuntu Server 64位 LTS版本,版本号为 12.04,如果你使用的是其它版本,请注意有可能本文里面所使用的命令、参数等需要做一些修改才能适用,最主要的比如那个 /etc/apt/source.list 就肯定是要做修改的,否则肯定会有问题出现。

修改 /etc/apt/source.list 文件并更新与升级系统

我使用的软件源为美国的镜像,它的完整的软件源列表如下:

deb http://us.archive.ubuntu.com/ubuntu/ precise main restricted
deb-src http://us.archive.ubuntu.com/ubuntu/ precise main restricted
deb http://us.archive.ubuntu.com/ubuntu/ precise-updates main restricted
deb-src http://us.archive.ubuntu.com/ubuntu/ precise-updates main restricted
deb http://us.archive.ubuntu.com/ubuntu/ precise universe
deb-src http://us.archive.ubuntu.com/ubuntu/ precise universe
deb http://us.archive.ubuntu.com/ubuntu/ precise-updates universe
deb-src http://us.archive.ubuntu.com/ubuntu/ precise-updates universe
deb http://us.archive.ubuntu.com/ubuntu/ precise multiverse
deb-src http://us.archive.ubuntu.com/ubuntu/ precise multiverse
deb http://us.archive.ubuntu.com/ubuntu/ precise-updates multiverse
deb-src http://us.archive.ubuntu.com/ubuntu/ precise-updates multiverse
deb http://us.archive.ubuntu.com/ubuntu/ precise-backports main restricted universe multiverse
deb-src http://us.archive.ubuntu.com/ubuntu/ precise-backports main restricted universe multiverse
deb http://security.ubuntu.com/ubuntu precise-security main restricted
deb-src http://security.ubuntu.com/ubuntu precise-security main restricted
deb http://security.ubuntu.com/ubuntu precise-security universe
deb-src http://security.ubuntu.com/ubuntu precise-security universe
deb http://security.ubuntu.com/ubuntu precise-security multiverse
deb-src http://security.ubuntu.com/ubuntu precise-security multiverse
deb http://extras.ubuntu.com/ubuntu precise main
deb-src http://extras.ubuntu.com/ubuntu precise main
deb http://ppa.launchpad.net/nginx/stable/ubuntu precise main

最后一项为一个 PPA ,我们需要先更新一个密钥,使用下面这行命令:

apt-key adv --keyserver keyserver.Ubuntu.com --recv-keys C300EE8C

这里有一个小地方需要注意一下下,有可能,我是说有可能,你需要更新的Key并不是 C300EE8C 这个结尾的,那么可以先运行 apt-get update ,然后看终端中提示的是什么Key,再根据这个提示运行上面这行命令,把最后的那个参数修改成为提示信息里面的 Key 的最后8位即可。

更新完成Key之后,更新并升级一下:

sudo apt-get update
sudo apt-get upgrade

安装 Nginx

安装Nginx 很简单,使用下面这行命令即可:

sudo apt-get install nginx

这会将标准的 Nginx 包安装至服务器,但是使用下面这个命令你会看到其实Ubuntu提供了很多个Nginx版本:

aptitude search nginx

有nginx, nginx-common, nginx-extras, nginx-full, nginx-light, nginx-maxsi, nginx-passenger 等等,如果你有一些特殊的需求,而正好这里面某个版本又提供了你的所有需求,那就选择那个满足你需求的版本吧,否则,就直接安装 nginx 即可,这会安装绝大多数人都用得到的东西到你的系统里面去,不会多也不会少。

安装完成之后,可以使用下面这行命令检测你所安装的Nginx版本:

nginx -v

我安装到的版本是:nginx version: nginx/1.2.4,你的可能与我的不一样,但是如要不一样的话,那应该就是比我更新的版本,没有哪个开发者会把版本号越用越小,你也不会吧,之后使用下面这行命令启动Nginx:

/etc/init.d/nginx start

在上面这个命令中, /etc/init.d/nginx 这个文件就是Nginx的管理脚本,可用的参数有:

  • start : 启动Nginx服务器
  • stop : 停止Nginx服务器
  • restart : 重新启动Nginx服务器
  • reload : 重新加载配置文件
  • force-reload : 强制重新加载配置文件
  • status : 当前Nginx服务器的状态
  • configtest : 测试配置文件的正确性(如果你修改了配置文件,重新加载这些配置文件之前,应该先测试一下这些配置文件的正确性,以免因为配置文件的错误而造成服务器的停止,不过像我这种人是从来不这么干的,因为网站停止服务对我来说没啥太大的影响,但是,想想,如果你的网站域名是 taobao.com ,那停止一秒钟的损失有多大?)

除上面说的这个文件还有几个文件与目录你可能需要注意:

  • /etc/nginx/sites-available/ :这个目录里面存放的是所有可用的虚拟主机的配置文件
  • /etc/nginx/sites-enabled :这个目录里面存放的是指向可用虚拟主机配置文件的软件链接,只有在这里面存放对应的软链接的配置文件才会被Nginx服务器加载
  • /etc/nginx/nginx.conf : Nginx 服务器的主配置文件
  • /etc/nginx/sites-available/default :默认网站的配置文件,这个是安装的过程中生成的,在本文中,我将修改它。

修改 Nginx 服务器的默认配置文件,创建默认虚拟主机

/etc/nginx/sites-available/default 文件(以下简称“default文件”)是在安装的过程中自动生成的一个配置文件,它定义的一个虚拟主机,但是在我们的使用中,这个虚拟订同没有太大的作用,所有我们会重新写默认虚拟主机的默认文件,它是这样的一个虚拟主机:

  • 我们可以通过服务器的IP地址访问到该虚拟主机(默认使用80端口)
  • 当我们使用域名访问服务器时,如果该域名没有与某个虚拟主机绑定,那么就表示该的就是这个默认的虚拟主机
  • 这个默认的虚拟主机需要支持PHP程序(因为我会把phpMyAdmin这种数据库管理软件放在默认虚拟主机里面),当然这不是必须的,甚至你可以不定义这个虚拟主机都没有关系。

下面我们来满足这些需求;

首先,使用下面这几行命令配置系统自动生成的 default 文件(你可以删除它,但是这个文件也不占用太多的地方,就留着吧,它里面还有很多示例可供你学习呢):

cd /etc/nginx/sites-available/
cp default default.backup

然后清空 default文件,并使用一个你喜欢的编辑器编辑它:

cat /dev/null > default
vi default

/etc/nginx/sites-available/default 文件的内容为:

server {
    listen 80;
    server_name _;
    root /srv/www/default/public;
    error_log /srv/www/default/logs/error.log;
    access_log /srv/www/default/logs/access.log;
}

保存该文件,然后创建上面这些配置文件中指定的目录:

mkdir -p /srv/www/default/public
mkdir /srv/www/default/logs
chown www-data:www-data -R /srv/www/

创建完成之后可以使用下面这行命令来测试一下配置文件是否正确:

/etc/init.d/nginx configtest

如果没有报错,那么使用下面这行命令重新加载配置文件:

/etc/init.d/nginx reload

然后在浏览器中打开 http://your.server.ip.address/ 这个地址即可访问到你的默认虚拟主机,比如我现在是在自己的电脑上面,那么可以使用 http://127.0.0.1 或者 http://localhost/ 访问到该虚拟主机,我在电脑在局域网中的IP地址为 192.168.1.4,所以,使用 http://192.168.1.4 也可以访问到该虚拟主机(局域网中的其它计算机也可以使用这个地址访问你的虚拟主机)。但是你应该发现了,访问这些地址得到的都是同一个结果:

403 Forbidden

这是因为该虚拟主机的根目录中没有索引文件,而且也不允许Nginx自动对目录进行索引,可以使用下面这两种办法来检测一下你的配置是否正确:

创建一个默认的索引文件 index.html

这是推荐的方法,它不会让Nginx自动索引目录中的文件(有些文件你可能并不想让所有访问者都这么轻易的知道),在 /srv/www/default/public/ 目录下创建一个名为 index.html 的文件,输入下面这些内容:

<!DOCTYPE html><html lang="zh-CN">
<head><meta charset="utf-8" /><title>Matrix Stack Index</title></head>
<body><h1><a href="http://matrixstack.com/node/139">Matrix Stack</a></h1>
</body></html>

保存该文件,再访问上面这些地址,你将看到默认浏览器就会访问到这个文件了。

开启目录的自动索引功能:autoindex

这个文法来得更简单一些,只需要在配置文件里面加上下面这一行即可:

autoindex on;

重新加载Nginx配置文件之后再访问你的服务器地址,即可看到一个页面,它的左上角有一个标题,内容为: Index of * ,这个就是Nginx的Autoindex功能,如果你没有看到这样的一个页面,而是前面上一种方法所创建的那个 *index.html 文件的内容,那是因为当一个目录中存在索引文件时,Nginx会优先使用该索引文件而不是自动对目录进行索引,你只需要删除 index.html 这个文件即可。

关于虚拟主机根目录的设置

虚拟主机的根目录你可以自己选择,我一般并不使用上面这个路径,而是使用 /home/pantao/websites/default/public 这个作为默认,规则是这样的:

  • 所有的虚拟主机根目录都为 /home/USERNAME/websites/DOMAIN.NAME/public 目录,其中 USERNAME 表示该虚拟主机所属用户的用户名,DOMAIN.NAME 为该虚拟主机的主域名,它与 /etc/nginx/sites-available/ 中该虚拟主机的配置文件名是一样的。
  • 将所有的具有网站的用户的主用户组都设置为 www-data ,Nginx与PHP都是以该身份运行的,所以设置为这个,对于用户来说,文件管理的权限方面有很大的便捷性。

那么按照这种规则的话,如果我的服务器上面有一个名为 huwenxiao 的用户,它的网站主域名为 huwenxiao.com ,那么该网站的路径即为:

/home/huwenxiao/websites/huwenxiao.com/public

同样的,这个网站如果雇用了日志功能,这些日志就保存在

/home/huwenxiao/websites/huwenxiao.com/logs

目录里,public为所有公开访问的文件目录,logs为日志目录,同样的,私有文件目录则可以使用 private(这比如我使用Drupal系统的私有文件系统的话,我一般就会设置私有文件目录为 private 目录),对于像 Elgg 这样的系统的数据文件目录则可以设置为 data等。不过所有这些都只是我个人的喜好,你完全不用去在乎这些,使用你自己感觉最好的就是最好的方法。

安装数据库程序:MySQL/PostgreSQL/SQLite

使用下面这行命令安装这三个数据库系统:

apt-get install mysql-client mysql-server postgresql sqlite3

安装完成之后,对于 MySQL ,我们可以运行一下下面这个命令以提高MySQL数据库的安全性:

mysql_secure_installation

除了修改MySQL root 帐户密码那个没必须修改之外,其它的选项直接一路回车到底即可,也就是一路Yes,对于 PostgreSQL 数据库,我没有使用过,所以也不知道该如何进行下一步的设置,如果你是一个 PostgreSQL 用户,欢迎告诉我安装 PostgreSQL 之后还需要进行哪些操作。

增加 Nginx 对PHP 的支持

现在运行的网站,其程序绝大多数都是基于PHP语言开发的,Nginx支持以fastcgi 的方式对PHP进行支持,首先,使用下面这行命令安装PHP:

apt-get install php5-common php5-dev php5-cgi php5-fpm php-apc php5-mysql php5-pgsql php5-sqlite php5-curl php5-gd php5-idn php-pear php5-mcrypt php5-memcache php5-ming php5-recode php5-tidy php5-xmlrpc php5-xsl

上面这行命令会安装一些最常用的PHP库,包括 MySQL/PostgreSQL/SQLite 的支持,等待安装程序完成之后,可以使用下面这行命令检查安装的PHP版本:

php -v

还需要使用下面这行命令重新启动 php-fpm:

/etc/init.d/php5-fpm restart

为默认虚拟主机提供PHP支持,并且安装phpMyAdmin数据库管理软件

要为某一个Nginx虚拟主机提供PHP支持,需要修改该虚拟主机的配置文件,打开 /etc/nginx/sites-available/default 文件,在它的 server {} 块中添加下面这些代码:

index index.php index.html index.htm;
location ~ .php$ {
    fastcgi_split_path_info ^(.+.php)(/.+)$;
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;
}

保存该文件之后还需要重新加载一次该配置文件,使用下面这行命令(以后如果再遇到修改Nginx配置文件的情况,请自行加载,我这里不再重复提示):

/etc/init.d/nginx reload

重新加载之后,可以使用 phpinfo() 函数来查看当前的PHP环境了,在 /srv/www/default/public/ 目录下创建一个名为 phpinfo() 的文件,你可以使用下面这行命令:

echo "<?php phpinfo(); ?>" > /srv/www/default/public/phpinfo.php

这行命令会创建该文件之后,并在该文件中插入 <?php phpinfo(); ?> 这一行代码,现在你可以访问该文件了,如果你在前面的Nginx默认虚拟主机配置中,开启了 autoindex,那么刷新你的浏览器,就会看到,现在网页里面已经列出了该文件,点击该文件即可,如果你没有开启 autoindex,那么请访问 http://your.server.ip.address/phpinfo.php 即可。如果你不想看 phpinfo 信息,那还可以使用PHP服务器探针,这样查看起来可能更简单,本文最下方提供了一个PHP探针的下载地址,你可以将该探针下载并解压至你的虚拟主机目录中,然后访问http://your.server.ip.address/prober.php 即可。

安装 phpMyAdmin MySQL数据库管理工具

phpMyAdmin 是我一直使用的 MySQL 数据库管理工具,它基于 Web ,不像其它一些桌面客户端,它被安装在服务器上,这使得我们可以在任何时候都对 MySQL 进行管理,安装它很简单,只需要从其 官方网站(http://www.phpmyadmin.net/) 下载最新版本的程序包,将其解压至你的虚拟主机目录即可,以本文中的默认虚拟主机为例:

cd /srv/www/default/public
wget http://nchc.dl.sourceforge.net/project/phpmyadmin/phpMyAdmin/3.5.5/phpMyAdmin-3.5.5-all-languages.tar.gz
tar phpMyAdmin-3.5.5-all-languages.tar.gz
mv phpMyAdmin-3.5.5-all-languages phpmyadmin

安装完成之后访问 http://your.server.ip.address/phpmyadmin/ 即可访问 phpMyAdmin ,用户名请使用安装MySQL时设置的 root 帐户及其密码即可。

配置Drupal/WordPress/TextPattern 等常见基于PHP的内容管理系统(CMS)的网址重写

Nginx 有十分强大的网址重写能力,虽然 WordPress 或者 Drupal 等系统都是在Apache上面开发的,官方并没有提供 Nginx 的解决办法,但是我们可以根据 Apache 的网址重写规则很容易的写出 Nginx 版本,在这里,以 Drupal 的网址重写规则为例。

Drupal 的所有请求都是交由 index.php 文件负责的,它接受一个名为 q 的参数,该参数的值为一个 Drupal 路径,比如本文的URL地址为 http://www.matrixstack.com/node/139 ,在该URL地址中, node/139 就是一个 Drupal 路径,它的真实路径应该为: http://www.matrixstack.com/index.php?q=node/139 ,同样的,像本站的介绍页面的 Drupal 路径为about,它在真实路径应该是:http://www.matrixstack.com/index.php?q=about,所以,要让Nginx实现网址重写功能,很简单,只需要把类似 node/139 这样的重写为 index.php?q=node/139 即可,在 default 文件里面添加下面这段配置代码:

location / {
    rewrite ^/(.*)$ /index.php?q=$1;
}

似乎这样就可以了,但是这样是不行的,还有很多静态文件,它们的路径并不是一个Drupal 路径,而且也不需要Drupal去处理;还有一种情况,比如Drupal 7 的 Image Style功能以及 Drupal 6 中的 Imagecache 等,它们的需求更复杂,当静态文件存在时,直接返回静态文件,不存在是,则由Drupal生成相应的静态文件再返回,下一次请求时直接返回静态文件即可,需求似乎很复杂,但是其实让Nginx来实现这些复杂的需求所需要的网址重写是很简单的一件事情,删除上面的这三行代码,而加入下面这些代码:

location / {
    try_files $uri $uri/ @drupal;
}

location @drupal {
    rewrite ^/(.*)$ /index.php?q=$1;
}

上面这两块代码, location / 部分中 ,try_files 表示,先将请求的URI地址当作文件处理($uri),如果该文件不存在,则再将其当作一个目录处理($uri/),如果该目录也不存在,则当作Drupal路径处理(@drupal),然后在下面的 location @drupal 块中,对 @drupal 路径的进行了定义,即网址重写定义。

很多时候我们都只知道什么样儿的情况下代码会正确的执行,但是却总是记不住怎么样代码会出错,这里列出一些在 Go 开发中最经常看到的一些错误,这让我们在Go报错之前就能知道不去犯这个错,这或许能节省你很多时间。

不必须的导入 (Unncessary Imports)

创建文件 goerror1.go ,并加入下面代码:

package main

import "fmt"
import "os"

func main() {
    fmt.Println("Hello World!")
}

输出:

# command-line-arguments
./goerror1.go:4: imported and not used: "os"

错误提示为:导入了但是没有使用的包os,为什么?在Go中,你需要使用什么,就导入什么,而导入了什么,就一定要去使用它,否则,就不要导入它,在上面的代码中,你导入了 os 包,但是却没有使用它,所以,它的存在没有任何价值,没有任何价值的东西就不应该存在于你的代码中。

确切的名称——大小写敏感

package main

import "fmt"

func main() {
    fmt.println("Hello World!")
}

输出:

./goerror2.go:6: cannot refer to unexported name fmt.println
./goerror2.go:6: undefined: fmt.println

你定义的时候使用的是什么名称,那么在使用的使用就必须使用一模一样的名称, p 与 P 是两人个完全不同的东西,看看上面的代码,我们没有定义过 fmt.println,所以,我们不能去调用它,同样的,像下面这样的写法都是错误的:

Package main
Import "fmt"
import "FMT"
Func main() {}
Fmt.Println
fmt.println

用分号分开两行代码

如果你是一个有C、C++、Java或者Perl背景的开发者,你可能会注意到了, Go并不要求你在每一行代码结束时插入一个分号,这是因为它会自动的为我们添加(请看[Effective Go]9/articles/effective-go.html)这篇文章的 分号说明部分),但是,有的时候我们却开始烂用这种特性,比如下面这段代码:

package main

import "fmt"

func main() {
    fmt.Println("Hello") fmt.Println("World!")
}

输出:

./goerror3.go:6: syntax error: unexpected name, expecting semicolon or newline or }

但是下面这样的代码看上去和上面的一模一样,但是却能正确的运行:

package main

import "fmt"

func main() {
    fmt.Println("Hello")
    fmt.Println("World!")
}

输出:

Hello
World!

为什么?因为Go不允许你将两行代码写进一行,除非你显示的使用了一个分号来区分这两人行代码,比如我们把上面的代码改成下面这种形式的也是可以正确运行的:

package main

import "fmt"

func main() {
    fmt.Println("Hello"); fmt.Println("World!")
}

看了我前面翻译的那篇 [Effective Go]9/articles/effective-go.html) 这篇文章的朋友应该也会知道了,为什么上面这段代码能将两行写作一行。

不必要的分号

创建 goerror4.go 文件,写入下面这段代码,再运行它:

package main

import "fmt";;

func main() {
    fmt.Print("Hello World!")
}

输出:

/goerror4.go:3: empty top-level declaration

在Go中,任何一个分号的出现都表示一个声明的结束,那么,在上面的代码中, import "fmt";; 中的第一个分号是可以接受的,它表示那一个声明的结束,但是在第一个与第二个分号之间不在有任何的声明出现,所以,第二个声明是多余的。

其它一些常见的语法错误

下面这些错误也是很常见的:

package 'main' // 错误,不允许使用引号包裹包名
package "main" // 同上
package main.x // 包名只允许是一个最简单的词汇,不允许使用 "."
package main/x // 同上
import 'fmt'   // 需要双引号
import fmt     // 需要双引号
func main {}   // 需要括号
func main() [] // 需要大括号
func main() { fmt.Println('Hello World!') }
               // 错误,需要使用双引号

更多关于 Golang 的文章可以访问:

Python中有一个函数 os.walk 很好用,不过感觉Go里面同样功能的函数也十分不错,它也叫 Walk ,属于 path/filepath 包里面的函数,它与Python的不同之处在于,Python会是直接遍历所有目录,而Go的版本则是使用一种 “探索者”的模式,该函数本身并不返回什么内容,而是允许你传入自己的函数用来处理每一次遇到文件时的动作。

type WalkFunc func(path string, info os.FileInfo, err error) error

上面这个就是上面所说的“探索者”的函数类型,我们像下面这么使用它:

package main

import (
    "fmt"
    "os"
    "path/filepath"
)
func explorer(path string, info os.FileInfo, err error) error {
    if err != nil {
        fmt.Printf("ERROR: %v", err)
        return err
    }
    if info.IsDir() {
        fmt.Printf("找到目录: %sn", path)
    } else {
        fmt.Printf("找到文件: %sn", path)
    }
    return nil
}

func main() {
    filepath.Walk("/home/cox/websites/codinuts.csmumu.net/", explorer)
}

我们首先定义一下 filepath.WalkFunc 的具体实现,我上面的示例中,我首先判断是不是目录,如果是目录就打印:“找到目录:『目录路径」”,找到文件就打印:“找到文件:「文件路径」“,有错误的话,打印出错误,然后返回错误,如果没有错误,则返回 nil。之后将该函数传送给 filepath.Walk 之后,它将会在每一次遍历到文件时调用该函数,到现在为止,似乎该函数只能让我们打印出找到的内容而已,没有什么实际用处,其实不然,再看下面这个示例:

package main

import (
    "os"
    "fmt"
    "path/filepath"
)

type Walker struct {
    directories []string
    files       []string
}

func main() {
    walker := new (Walker)
    path := "/home/cox/go/src/github.com/pantao"
    filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
            if err != nil { return err }
            if info.IsDir() {
                walker.directories = append(walker.directories, path)
            } else {
                walker.files = append(walker.files, path)
            }
            return nil
        })
    fmt.Printf("找到 %d 个目录以及 %d 个文件n", len(walker.directories), len(walker.files))
}

在上面的示例中,我创建了一个新的类型 Walker ,它有两人个字段 directories []string 与 files []string 分别用来保存找到的目录和文件,然后让 WalkFunc 去掉用它,不再直接打印出找到的文件,而是将找到的目录和文件都保存到 walker 的字段里面去,以备以后使用。

这个在我的 Amazon S3 Cograndient 工具里面就使用到了,我使用它来获取本地目录的所有文件。

在本文中,我们将看看如何使用一个来自Go以外的API创建应用,我们将使用Google提供的URL缩短服务API来创建URL地址的缩短版本,你可以在http://goo.gl/体验一把,比如你现在可以通过http://goo.gl/FE6F2 这个地址来访问本文,goo.gl 还会生成一个你的网址的二维码,比如本文的二维码是:

在Go网络编程中使用外部API——基于 Google API 创建URL地址缩短服务

这样的服务是很有用的,比如本文的网址,你应该也看到了,不管是让你记下来或者是让你写下来都太难了,但是缩短之后不管怎么样都简单得多,这么好的服务,Google以API的形式给大家免费使用,你可以在https://developers.google.com/url-shortener/ 了解更多有关该API的信息。

我们要写的程序十分的很简单,同时,我还想在写了这个小程序之后再将它发布到 Google的AppEngine上面去,不过这都是后话了,整个程序需要用到的知识就下面这么几个:

第一步 确定已经正确的创建了开发环境

很简单:

cox@Cox:~/websites$ mkdir urlshortener
cox@Cox:~/websites$ cd urlshortener/
cox@Cox:~/websites/urlshortener$ export GOPATH=$(pwd)
cox@Cox:~/websites/urlshortener$ export PATH=$GOPATH/bin:$PATH
cox@Cox:~/websites/urlshortener$ mkdir pkg bin src

第二步 安装APIs

执行下面命令:

go get code.google.com/p/google-api-go-client/urlshortener/v1

第三步 在我们的代码中导入该API

上面安装的API可以通过下面这样导入到我们的项目中来了:

import "code.google.com/p/google-api-go-client/urlshortener/v1"

完成其它的代码

我的整个代码如下:

package main

import (
        "fmt"
        "net/http"
        "text/template"
        "code.google.com/p/google-api-go-client/urlshortener/v1"
)

// 本项目的网页模板
var tmpl = template.Must(template.New("URL Shortener Template").Parse(`
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" /><title>Goo.gl网址缩短服务</title>
<style>
input, button { border: .1em solid #ccc; border-radius: .3em; padding: .5em; }
button { background-color: #f0f0f0; }
button:hover { background-color: #ccc;}
</style>
</head>
<body>
<h1>Goo.gl网址缩短服务</h1>
{{if .}}{{.}}<br /><hr /><br />{{end}}
<form action="/shorten" type="POST">
<label for="long-url">长网址:</label><input type="text" name="url" id="long-url" placeholder="请在这里输入您要缩短的网址" /><button><span>给我短址</span></button>
</form>
<br /><hr /><br />
<form action="/lengthen" type="POST">
<label for="short-url">短网址:</label><input type="text" name="url" id="short-url" placeholder="请在这里输入您要获取原始网址的短网址" /><button><span>给我长址</span></button>
</form></body></html>
`))

func handleRoot(w http.ResponseWriter, r *http.Request) {
        tmpl.Execute(w, nil)
}

func handleShorten(w http.ResponseWriter, r *http.Request) {
        url := r.FormValue("url") // 获取由网页提交的网址
        svc, _ := urlshortener.New(http.DefaultClient) // 使用http中的default client创建一个新的 urlshortener 实例
        shorturl, _ := svc.Url.Insert(&urlshortener.Url { LongUrl: url, }).Do() // 填充长的网址然后呼叫缩短服务
        tmpl.Execute(w, fmt.Sprintf("<h2 class="url"><a href="%s">%s</a></h2><h3 class="url">源始长网址为:<em>%s</em></h3>", shorturl.Id, shorturl.Id, url))
}

func handleLengthen(w http.ResponseWriter, r *http.Request) {
        url := "http://goo.go/" + r.FormValue("url")
        svc, _ := urlshortener.New(http.DefaultClient)
        longurl, err := svc.Url.Get(url).Do()
        if err != nil {
                fmt.Println("error: %v", err)
                return
        }
        tmpl.Execute(w, fmt.Sprintf("<h2 class="url"><a href="%s">%s</a></h2><h3 class="url">短网址为:<em>%s</em></h3>", url, url, longurl))
}

func main() {
        http.HandleFunc("/", handleRoot)
        http.HandleFunc("/shorten", handleShorten)
        http.HandleFunc("/lengthen", handleLengthen)

        http.ListenAndServe(":8001", nil)
}

运行该程序之后您即可以打开浏览器,并访问: