Webtatic 安装PHP5.4及相关扩展

PHP 5.4.25 has been released on PHP.net on 6th February 2014, and is also available for CentOS/RHEL 5.10 and 6.5 at Webtatic via Yum.
Update 2013-07-21 – A new package “php54w-mysqlnd” has been added as an alternative to “php54w-mysql”. This will instead provide mysql, mysqli, and pdo_mysql built against the PHP MySql native driver rather than the system default libmysqlclient. It will replace “php54w-mysql55″, as it will work with MySQL 5.0/5.1/5.5 server)
Update 2013-06-20 – Webtatic now has released PHP 5.5.0 for CentOS/RHEL 5 and 6
Update 2013-05-26 – CentOS/RHEL 5.x now supported.
Update 2013-05-18 – A new package “php54w-pecl-zendopcache” has been added, Zend Optimizer Plus opcode cache.
Update 2012-08-26 – APC is stable enough now and so the extension has been added
Update 2012-07-22 – memcache and xdebug extensions have been added
Update 2012-04-29 – mcrypt, tidy, mssql, interbase have been added back in to the repository.
PHP 5.4.0 adds new features such as:
  • Traits
  • Built-in web server
  • Array short notation
  • Array return value de-referencing
  • Finally killing off magic-quotes and safe-mode
To see what else has been added, check out the changelog.
To install, first you must add the Webtatic EL yum repository information corresponding to your CentOS/RHEL version to yum:
CentOS/RHEL 6.x:
rpm -Uvh http://mirror.webtatic.com/yum/el6/latest.rpm
CentOS/RHEL 5.x:
rpm -Uvh http://mirror.webtatic.com/yum/el5/latest.rpm
Now you can install php by doing:
yum install php54w
If you would like to upgrade php to this version it is recommended that you check that your system will support the upgrade, e.g. making sure any CPanel-like software can run after the upgrade.
Unless you know what you are doing, it is risky upgrading an existing system. It’s much safer to do this by provisioning a separate server to perform the upgrade as a fresh install instead.
If you know what you are doing, you can upgrade PHP by:
yum install yum-plugin-replace
yum replace php-common --replace-with=php54w-common
It will likely give you a message “WARNING: Unable to resolve all providers …”. This is normal, and you can continue by tying “y“. You will be given a chance to see what packages will be installed and removed before again being given a chance to confirm.

Packages

Package Provides
php54w mod_php, php54w-zts
php54w-bcmath
php54w-cli php-cgi, php-pcntl, php-readline
php54w-common php-api, php-bz2, php-calendar, php-ctype, php-curl, php-date, php-exif, php-fileinfo, php-ftp, php-gettext, php-gmp, php-hash, php-iconv, php-json, php-libxml, php-openssl, php-pcre, php-pecl-Fileinfo, php-pecl-phar, php-pecl-zip, php-reflection, php-session, php-shmop, php-simplexml, php-sockets, php-spl, php-tokenizer, php-zend-abi, php-zip, php-zlib
php54w-dba
php54w-devel
php54w-embedded php-embedded-devel
php54w-enchant
php54w-fpm
php54w-gd
php54w-imap
php54w-interbase php_database, php-firebird
php54w-intl
php54w-ldap
php54w-mbstring
php54w-mcrypt
php54w-mssql
php54w-mysql php-mysqli, php_database
php54w-mysqlnd php-mysqli, php_database
php54w-odbc php-pdo_odbc, php_database
php54w-pdo
php54w-pecl-apc
php54w-pecl-memcache
php54w-pecl-zendopcache
php54w-pecl-xdebug
php54w-pgsql php-pdo_pgsql, php_database
php54w-process php-posix, php-sysvmsg, php-sysvsem, php-sysvshm
php54w-pspell
php54w-recode
php54w-snmp
php54w-soap
php54w-tidy
php54w-xml php-dom, php-domxml, php-wddx, php-xsl
php54w-xmlrpc

Opcode Caches

A precompiled PHP APC package is available as an opcode cache, which is recommended for performance reasons. It can be installed via:
yum install php54w-pecl-apc
Zend have now released Zend Optimizer Plus opcode cache as open source, and is now known as Zend OPcache. As it’s more actively maintained than APC, it has been added as a package to the Webtatic EL6 repository. It can be installed via:
yum install php54w-pecl-zendopcache

error_reporting E_ALL now includes E_STRICT

You may get a lot more errors coming out of your error logs if by default your error_reporting is set to E_ALL now without explicitly turning off E_STRICT. The default php.ini that comes with the PHP package turns this off by default, but if you are upgrading from an existing installation, your php.ini may not be updated, meaning this will likely be turned on.

zend studio 9.0 zend studio 10.0 无限期试用的方法

zend studio 9.0 zend studio 10.0 无限期试用的方法

zend studio是根据两个文件来判断试用时间的,他们分别是:

在mac和linux下的路径大概是你的home目录中~/.zs,和~/.ZendStudio/9_0

Windows下则是位于你的用户目录中,路径是类似的,比如我的windows 7下,路径是C:\Users\Administrator\.zs,和C:\Users\Administrator\9_0.

无限试用的方法就是在文件过期前将这两个文件删除,每次试用有30天,也算方便。

Mac os 10.8 一键安装PHP、Mysql、Nginx

基于HomeBrew安装,如果没有安装Brew,请先执行安装命令。

ruby -e "$(curl -fsSL https://raw.github.com/Homebrew/homebrew/go/install)"
# Install nginx
brew install nginx

# Create a link to autostart nginx when the system starts
mkdir -p ~/Library/LaunchAgents
cp /usr/local/Cellar/nginx/1.*/*.nginx.plist ~/Library/LaunchAgents/
launchctl load -w ~/Library/LaunchAgents/*.nginx.plist

# Cancel the self-starting
launchctl unload -w ~/Library/LaunchAgents/org.nginx.nginx.plist

# Install MySQL
brew install mysql
cp /usr/local/Cellar/mysql/5.*/*.mysql.plist ~/Library/LaunchAgents/
launchctl load -w ~/Library/LaunchAgents/*.mysql.plist

#Install Percona
brew install percona-server brew link percona-server unset TMPDIR mysql_install_db --verbose --user=`whoami` --basedir="$(brew --prefix percona-server)" --datadir=/usr/local/var/percona --tmpdir=/tmp
mkdir -p ~/Library/LaunchAgents
cp /usr/local/opt/percona-server/homebrew.mxcl.percona-server.plist ~/Library/LaunchAgents/
launchctl load -w ~/Library/LaunchAgents/homebrew.mxcl.percona-server.plist

#Install PHP-fpm
brew tap josegonzalez/php
brew update
brew install --without-apache --with-fpm --with-mysql php54
sudo cp `brew --prefix php54`/homebrew-php.josegonzalez.php54.plist /Library/LaunchAgents/
sudo launchctl load -w /Library/LaunchAgents/homebrew-php.josegonzalez.php54.plist
php-fpm -v
sudo mv /usr/sbin/php-fpm /usr/sbin/php-fpm.bak
sudo ln -s /usr/local/Cellar/php54/5.4.11/sbin/php-fpm /usr/sbin/php-fpm
php-fpm -v
php -v
sudo mv /usr/bin/php /usr/bin/php.bak
sudo ln -s /usr/local/bin/php /usr/bin/php
php -v echo 'export PATH=$PATH:/usr/local/sbin' >> ~/.zshrc
# or ~/.bash_profile

levi提供了一个更详细的方案,就不重复造车了。
http://levi.cg.am/?p=3129

帮朋友招几个phper,金立通信,10K-16K,两年,北京,请大家推荐下

金立
10K-16K
2年以上
Linux+yaf
可选北京(嘉华大厦)或深圳总部

1)2年以上php开发工作经验,具有成功的大型网站项目开发经验;
2)精通PHP,精通MVC开发模式;熟悉PHP性能优化和安全开发方法; 精通MYSQL数据结构设计和优化
3)熟练linux的基本管理,有良好的编程习惯和代码风格,有Linux系统下开发经验更佳;
4)熟悉HTML/XHTML、CSS、Javascript、AJAX等Web页面技术;
5)良好的学习能力和沟通能力,工作积极主动,具有强烈的责任心和团队合作精神;
6)工作地点:北京或深圳
7)公司名称:深圳市金立通信设备有限公司
8)年底双薪,正常情况下2到4个月年终奖,餐补+交通补助+通话补助,旅游费用,加班按照国家规定有加班工资

简历直投:guohb#gionee.com

PHP的PSR-0命名标准

PSR是Proposing a Standards Recommendation(提出标准建议)的缩写,是由PHP Framework Interoperability Group(PHP通用性框架小组,简称PHP-FIG)发起的,通过他们命名就可以看出,这是个主要是针对框架通用性而做努力的开放性小组,他们的在Github上有自己的仓库地址,目前只有一个被接受的标准,那就是PSR-0标准,标准定义了PHP自动加载的命名规范和文件路径规范。 针对PSR-0标准主要提到了以下几点:

要求

  • 一个完全合格的命名空间和类名必须有以下的结构“\<提供者名称>\(<命名空间>\)*<类名>”
  • 每个命名空间必须有顶级的命名空间(“提供者”)
  • 每个命名空间可以有任意多个子命名空间
  • 每个命名空间在被从文件系统加载时必须被转换为“操作系统路径分隔符”(DIRECTORY_SEPARATOR )
  • 每个“_”字符在“类名”中被转换为DIRECTORY_SEPARATOR 。“_”符号在命名空间中没有这个含义
  • 符合命名标准的命名空间和类名必须以“.php”结尾来加载文件
  • 提供商名称,命名空间,类名可以由大小写字母组成,其中命名空间和类名是大小写敏感的以保证多系统兼容性
  • 如果文件不存在需要返回false

例子

\Doctrine\Common\IsolatedClassLoader => /path/to/project/lib/vendor/Doctrine/Common/IsolatedClassLoader.php
\Symfony\Core\Request => /path/to/project/lib/vendor/Symfony/Core/Request.php
\Zend\Acl => /path/to/project/lib/vendor/Zend/Acl.php
\Zend\Mail\Message => /path/to/project/lib/vendor/Zend/Mail/Message.php

下划线在命名空间和类名中的使用

\namespace\package\Class_Name => /path/to/project/lib/vendor/namespace/package/Class/Name.php
\namespace\package_name\Class_Name => /path/to/project/lib/vendor/namespace/package_name/Class/Name.php

设置这个标准是为了保证最基本的共同点。你可以通过实现5.3的SplClassLoader来测试这个标准。

扩展例子

提供一个函数来展示如何使用上述标准。

<?php
function autoload($className)
{
    $className = ltrim($className, '\\');
    $fileName  = '';
    $namespace = '';
    if ($lastNsPos = strripos($className, '\\')) {
        $namespace = substr($className, 0, $lastNsPos);
        $className = substr($className, $lastNsPos + 1);
        $fileName  = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
    }
    $fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';

    require $fileName;
}

SplClassloader的实现

接下来这个gist实现了SplClassLoader可以加载你按照上面标准来实现的通用类库,这是5.3里面推荐的加载方式。

http://gist.github.com/221634

扩展实现

因为这个标准提到了如果文件不存在的时候应该范围false,但是在上面函数的例子中并没有实现该机制,所有有人实现了优化的SplClassLoader。

<?php

class ClassLoader
{
    /**
     * @var array Contains namespace/class prefix as key and sub path as value
     */
    protected $paths;

    /**
     * Construct a loader instance
     *
     * @param array $paths Containing class/namespace prefix as key and sub path as value
     */
    public function __construct( array $paths )
    {
        $this->paths = $paths;
    }

    /**
     * Load classes/interfaces following PSR-0 naming
     *
     * @param string $className
     * @return null|boolean Null if no match is found, bool if match and found/not found.
     */
    public function load( $className )
    {
        if ( $className[0] === '\\' )
            $className = substr( $className, 1 );

        foreach ( $this->paths as $prefix => $subPath )
        {
            if ( strpos( $className, $prefix ) !== 0 )
                continue;

            $lastNsPos = strripos( $className, '\\' );
            $prefixLen = strlen( $prefix ) + 1;
            $fileName = $subPath . DIRECTORY_SEPARATOR;

            if ( $lastNsPos > $prefixLen )
            {
                // Replacing '\' to '/' in namespace part
                $fileName .= str_replace(
                    '\\',
                    DIRECTORY_SEPARATOR,
                    substr( $className, $prefixLen, $lastNsPos - $prefixLen )
                ) . DIRECTORY_SEPARATOR;
            }

            // Replacing '_' to '/' in className part and append '.php'
            $fileName .= str_replace( '_', DIRECTORY_SEPARATOR, substr( $className, $lastNsPos + 1 ) ) . '.php';

            if ( ( $fileName = stream_resolve_include_path( $fileName ) ) === false )
                return false;

            require $fileName;
            return true;
        }
    }
}

引用地址:https://github.com/andrerom/fig-standards/blob/psr2/proposed/PSR-2.md

写在后面

标准对于开发者来说是一个好事,如今已经越来越多的开源项目加入了这个标准Pear2、PHPBB、ComposerPackagist、Joomla、Drupal、SymfonyCakePHPDoctrine2等等,我常用的一个框架MicroMVC也在很早的时候就采用了这个标准。采用同样标准的项目可以无缝的接入,做为开发者最好要尝试并接收一个好的标准。

扩展阅读

PHP官方关于SplClassLoader的RFC:https://wiki.php.net/rfc/splclassloader PHP标准化组织论坛:https://groups.google.com/forum/?fromgroups#!forum/php-standards

REPL for thinkphp

REPL是什么?
http://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop

简单来说,REPL是一个shell版本的交互运行模式。输入代码即可直接运行,并查看运行结果。与web运行相比,不需要web环境,不需要将代码写到php文件中,再去访问。在测试代码、线上调试、线上运行数据统计方面,可以说是非常方便。

目前来看,几乎所有语言都有自己的shell版本,不管是脚本语言还是需要编译的语言(http://www.4wei.cn/archives/1002052),PHP也有自己的Cli模式。

PHP's command-line interface has an interactive mode, which you can run by doing php -a (assuming php is your PHP-CLI executable, not PHP-CGI). There is also phpsh and wigwam, which have tab completion and other features, and boris, which claims to be more robust than PHP's native interactive mode.

我觉得ruby on rails的shell是最为强大的。在开发和线上调试时,获取数据、获取日志非常方便。当然,从今天起,thinkphp也有了。
一句话理解 REPL for thinkphp,便是通过php cli模式,加载thinkphp中定义的常量、模型、控制器,可以直接在命令行中直接获取运行命令后的结果。
比如,要获取最新注册的一个用户,按常规的办法,肯定是添加一个控制器,添加一个方法,调用M("User")->find(),并把代码写到文件中,上传到服务器,然后访问这个方法的url。

而repl模式,则只需要在shell敲入M("User")->find(); 就可以看到结果了。

REPL for thinkphp的安装说明:

A. 支持平台(Windows不支持):
Linux Centos 5.5
Ubuntu 13.04
Linux Deepin 12.12.1
Mac OS X 10.7.5

B. 依赖条件
1. PHP 5.3.0 +
2. Readline moudle(php5-readline)
3. pcntl_signal(PHP for OS X http://php-osx.liip.ch/)

C. ThinkPHP版本要求
1. 3.1、3.2核心:https://github.com/liu21st/thinkphp
2. 最新版Extend扩展包:https://github.com/liu21st/extend

D. 使用方法:
1. 按正常流程开发Thinkphp项目,并将Extend扩展包下载到ThinkPHP/Extend目录下
2. 复制入口文件index.php为cli.php,并在cli.php添加两行配置define('MODE_NAME', 'cli');define('MODE_REPL', true);
3. 使用命令行访问cli.php,如 /usr/bin/php /www/cli.php
4. shell中可以使用php的语法,运算方法,Thinkphp的A、D、M、U等所有语法和常量,并能直接输出运行结果
5. Ctrp+C或者输入 exit 即可退出shell

部份截图:

百万级访问网站前期的技术准备

转载请注明出自 http://zhiyi.us
对互联网有了解的人都有自己的想法,有人就把想法付诸实现,做个网站然后开始运营。其实从纯网站技术上来说,因为开源模式的发展,现在建一个小网站已经很简单也很便宜。当访问量到达一定数量级的时候成本就开始飙升了,问题也开始显现了。因为带宽的增加、硬件的扩展、人员的扩张所带来的成本提高是显而易见的,而还有相当大的一部分成本是因为代码重构、架构重构,甚至底层开发语言更换引起的,最惨的就是数据丢失,辛辛苦苦好几年,一夜回到创业前。

减少成本就是增加利润。很多事情,我们在一开始就可以避免,先打好基础,往后可以省很多精力,少操很多心。

假设你是一个参与创业的技术人员,当前一穷二白,什么都要自己做,自己出钱,初期几十万的资金,做一个应用不是特别复杂的网站,那么就要注意以下几点:

一、开发语言

一般来说,技术人员(程序员)创业都是根据自己技术背景选择自己最熟悉的语言,不过考虑到不可能永远是您一个人写程序,这点还得仔细想想。无论用什么语言,最终代码质量是看管理,所以我们还是从纯语言层面来说实际一点。现在流行的java、php、.net、python、ruby都有自己的优劣,python和ruby,现在人员还是相对难招一些,性能优化也会费些力气,.net平台买不起windows server。java、php用的还是最多。对于初期,应用几乎都是靠前端支撑的网站来说,php的优势稍大一些,入门简单、设计模式简单、写起来快、性能足够等,不过不注重设计模式也是它的劣势,容易变得松散,隐藏bug稍多、难以维护。java的优势在于整套管理流程已经有很多成熟工具来辅助,强类型也能避免一些弱智BUG,大多数JAVA程序员比较注重设计模式,别管实不实际,代码格式看起来还是不错的。这也是个劣势,初学者可能太注重模式而很难解决实际需求。

前端不只是html、css这类。整个负责跟用户交互的部分都是前端,包括处理程序。这类程序还是建议用php,主要原因就是开发迅速、从业人员广泛。至于后端例如行为分析、银行接口、异步消息处理等,随便用什么程序,那个只能是根据不同业务需求来选择不同语言了。

二、代码版本管理

如果开发人员之间的网络速度差不多,就SVN;比较分散例如跨国,就hg。大多数人还是svn的.

假设选了svn,那么有几点考虑。一是采用什么树结构。初期可能只有一条主干,往后就需要建立分支,例如一条开发分支,一条上线分支,再往后,可能要每个小组一个分支。建议一开始人少时选择两条分支,开发和线上,每个功能本地测试无误后提交到开发分支,最后统一测试,可以上线时合并到上线分支。如果喜欢把svn当做移动硬盘用,写一点就commit一次也无所谓,就是合并的时候头大一些,这些人可以自己建个分支甚至建立个本地代码仓库,随便往自己的分支提交,测试完毕后再提交到开发分支上。

部署,可以手工部署也可以自动部署。手工部署相对简单,一般是直接在服务器上svn update,或者找个新目录svn checkout,再把web root给ln -s过去。应用越复杂,部署越复杂,没有什么统一标准,只要别再用ftp上传那种形式就好,一是上传时文件引用不一致错误率增加,二是很容易出现开发人员的版本跟线上版本不一致,导致本来想改个错字结果变成回滚的杯具。如果有多台服务器还是建议自动部署,更换代码的机器从当前服务池中临时撤出,更新完毕后再重新加入。

不管项目多小,养成使用版本管理的好习惯,最起码还可以当做你的备份,我的 http://zhiyi.us 虽然就是一个wordpress,可还是svn了,只改动一两句css那也是劳动成果。

三、服务器硬件

别羡慕大客户和有钱人,看看机房散户区,一台服务器孤独的支撑的网站数不清。如果资金稍微充足,建议至少三台的标准配置,分别用作web处理、数据库、备份。web服务器至少要8G内存,双sata raid1,如果经济稍微宽松,或静态文件或图片多,则15k sas raid1+0。数据库至少16G内存,15k sas raid 1+0。备份服务器最好跟数据库服务器同等配置。硬件可以自己买品牌的底板,也就是机箱配主板和硬盘盒,CPU内存硬盘都自己配,也可以上整套品牌,也可以兼容机。三台机器,市场行情6、7万也就配齐了。

web服务器可以既跑程序又当内存缓存,数据库服务器则只跑主数据库(假如是MySQL的话),备份服务器干的活就相对多一些,web配置、缓存配置、数据库配置都要跟前两台一致,这样WEB和数据库任意一台出问题,把备份服务器换个ip就切换上去了。备份策略,可以drbd,可以rsync,或者其他的很多很多的开源备份方案可选择。rsync最简单,放cron里自己跑就行。备份和切换,建议多做测试,选最安全最适合业务的,并且尽可能异地备份。
四、机房

三种机房尽量不要选:联通访问特别慢的电信机房、电信访问特别慢的联通机房、电信联通访问特别慢的移动或铁通机房。那网通机房呢?亲,网通联通N久以前合并改叫联通了。多多寻找,实地参观,多多测试,多方打探,北京、上海、广州等各个主节点城市,还是有很多优质机房的,找个网络质量好,管理严格的机房,特别是管理要严格,千万别网站无法访问了,打个电话过去才知道别人维护时把你网线碰掉了,这比DOS都头疼。自己扯了几根光纤就称为机房的,看您抗风险程度和心理素质了。机房可以说是非常重要,直接关系到网站访问速度,网站访问速度直接关系到用户体验,我可以翻墙看风景,但买个网游vpn才能打开你这个还不怎么知名的网站就有难度了。或许您网站的ajax很出色,可是document怎么也不ready,一些代码永远绝缘于用户。

五、架构

初期架构一般比较简单,web负载均衡+数据库主从+缓存+分布式存储+队列。大方向上也确实就这几样东西,细节上也无数文章都重复过了,按照将来会有N多WEB,N多主从关系,N多缓存,N多xxx设计就行,基本方案都是现成的,只是您比其他人厉害之处就在于设计上考虑到缓存失效时的雪崩效应、主从同步的数据一致性和时间差、队列的稳定性和失败后的重试策略、文件存储的效率和备份方式等等意外情况。缓存总有一天会失效,数据库复制总有一天会断掉,队列总有一天会写不进去,电源总有一天会烧坏。根据墨菲定律,如果不考虑这些,网站早晚会成为茶几。

六、服务器软件

Linux、nginx、php、mysql,几乎是标配,我们除了看名字,还得选版本。Linux发行版众多,只要没特殊要求,就选个用的人最多的,社区最活跃的,配置最方便的,软件包最全最新的,例如debian、ubuntu。至于RHEL之类的嘛,你用只能在RHEL上才能运行的软件么?剩下的nginx、php、mysql、activemq、其他的等等,除非你改过这些软件或你的程序真的不兼容新版本,否则尽量版本越新越好,版本新,意味着新特性增多、BUG减少、性能增加。总有些道听途说的人跟你说老的版本稳定。所谓稳定,是相对于特殊业务来说的,而就一个php写的网站,大多数人都没改过任何服务器软件源代码,绝大多数情况是能平稳的升级到新版本的。类似于jdk5到 jdk6,python2到python3这类变动比较大的升级还是比较少见的。看看ChangeLog,看看升级说明,结合自己情况评估一下,越早升级越好,别人家都用php6写程序了这边还php4的逛游呢。优秀的开源程序升级还是很负责任的,看好文档,别怕。

以上这六点准备完毕,现在我们有了运行环境,有了基本架构骨架,有了备份和切换方案,应该开始着手设计开发方面的事情了。开发方面的事情无数,下一篇会先说一些重点。

七、数据库

几乎所有操作最后都要落到数据库身上,它又最难扩展(存储也挺难)。对于mysql,什么样的表用myisam,什么样的表用innodb,在开发之前要确定。复制策略、分片策略,也要确定。表引擎方面,一般,更新不多、不需要事务的表可以用myisam,需要行锁定、事务支持的,用innodb。myisam的锁表不一定是性能低下的根源,innodb也不一定全是行锁,具体细节要多看相关的文档,熟悉了引擎特性才能用的更好。现代WEB应用越来越复杂了,我们设计表结构时常常设计很多冗余,虽然不符合传统范式,但为了速度考虑还是值得的,要求高的情况下甚至要杜绝联合查询。编程时得多注意数据一致性。

复制策略方面,多主多从结构也最好一开始就设计好,代码直接按照多主多从来编写,用一些小技巧来避免复制延时问题,并且还要解决多数据库数据是否一致,可以自己写或者找现成的运维工具。
分片策略。总会有那么几个表数据量超大,这时分片必不可免。分片有很多策略,从简单的分区到根据热度自动调整,依照具体业务选择一个适合自己的。避免自增ID作为主键,不利于分片。
用存储过程是比较难扩展的,这种情形多发生于传统C/S,特别是OA系统转换过来的开发人员。低成本网站不是一两台小型机跑一个数据库处理所有业务的模式,是机海作战。方便水平扩展比那点预分析时间和网络传输流量要重要的多的多。
NoSQL。这只是一个概念。实际应用中,网站有着越来越多的密集写操作、上亿的简单关系数据读取、热备等,这都不是传统关系数据库所擅长的,于是就产生了很多非关系型数据库,比如Redis/TC&TT/MongoDB/Memcachedb等,在测试中,这些几乎都达到了每秒至少一万次的写操作,内存型的甚至5万以上。例如MongoDB,几句配置就可以组建一个复制+自动分片+failover的环境,文档化的存储也简化了传统设计库结构再开发的模式。很多业务是可以用这类数据库来替代mysql的。

八、缓存

数据库很脆弱,一定要有缓存在前面挡着,其实我们优化速度,几乎就是优化缓存,能用缓存的地方,就不要再跑到后端数据库那折腾。缓存有持久化缓存、内存缓存,生成静态页面是最容易理解的持久化缓存了,还有很多比如varnish的分块缓存、前面提到的memcachedb等,内存缓存,memcached首当其冲。缓存更新可用被动更新和主动更新。被动更新的好处是设计简单,缓存空了就自动去数据库取数据再把缓存填上,但容易引发雪崩效应,一旦缓存大面积失效,数据库的压力直线上升很可能挂掉。主动缓存可避免这点但是可能引发程序取不到数据的问题。这两者之间如何配合,程序设计要多动脑筋。

九、队列

用户一个操作很可能引发一系列资源和功能的调动,这些调动如果同时发生,压力无法控制,用户体验也不好,可以把这样一些操作放入队列,由另几个模块去异步执行,例如发送邮件,发送手机短信。开源队列服务器很多,性能要求不高用数据库当做队列也可以,只要保证程序读写队列的接口不变,底层队列服务可随时更换就可以,类似Zend Framework里的Zend_Queue类,java.util.Queue接口等。

十、文件存储

除了结构化数据,我们经常要存放其他的数据,像图片之类的。这类数据数量繁多、访问量大。典型的就是图片,从用户头像到用户上传的照片,还要生成不同的缩略图尺寸。存储的分布几乎跟数据库扩展一样艰难。不使用专业存储的情况下,基本都是靠自己的NAS。这就涉及到结构。拿图片存储举例,图片是非常容易产生热点的,有些图片上传后就不再有人看,有些可能每天被访问数十万次,而且大量小文件的异步备份也很耗费时间。
为了将来图片走cdn做准备,一开始最好就将图片的域名分开,且不用主域名。很多网站都将cookie设置到了.domain.ltd,如果图片也在这个域名下,很可能因为cookie而造成缓存失效,并且占多余流量,还可能因为浏览器并发线程限制造成访问缓慢。
如果用普通的文件系统存储图片,有一个简单的方法。计算文件的hash值,比如md5,以结果第一位作为第一级目录,这样第一级有16个目录。从0到F,可以把这个字母作为域名,0.yourimg.com到f.yourimg.com(客户端dns压力会增大),还可以扩展到最多16个NAS集群上。第二级可用年月例如,201011,第三级用日,第四级可选,根据上传量,比如am/pm,甚至小时。最终的目录结构可能会是 e/201008/25/am/e43ae391c839d82801920cf.jpg。rsync备份时可以用脚本只同步某年某日某时的文件,避免计算大量文件带来的开销。当然最好是能用专门的分布式文件系统或更专业点的存储解决方案。

 

这一系列的最后一篇写给普通编程人员,如果不感兴趣可直接看本文最后几段。

开始设计代码结构之前,先回顾一下之前准备过的事情:我们有负载均衡的WEB服务器,有主从DB服务器并可能分片,有缓存,有可扩展的存储。在组织代码的各个方面,跟这些准备息息相关,我一二三的列出来分别说,并且每一条都以“前面讲到”这个经典句式开头,为了方便对照。

别着急看经典句式,我思维跳跃了,插一段。实际开发中,我们总会在性能和代码优雅性上作折中。对于当今的计算机和语言解释器,多几层少几层对象调用、声明变量为Map还是HashMap这种问题是最后才需要考虑的问题,永远要考虑系统最慢的部分,从最慢的部分解决。例如看看你用的ORM是不是做了很多你用不到的事情,是不是有重复的数据调用。我们做的是web应用开发,不是底层框架API,代码易读易懂是保证质量很重要的一方面,你的程序是为了什么而设计,有不同的方法……算了,这个话题另起一篇文章来说,扯远了,想交流可关注我的微博 http://t.sina.com.cn/liuzhiyi,咱继续……

前面讲到,WEB 服务器是要做负载均衡的,图片服务器是要分开的。对于这点,代码在处理客户端状态时,不要把状态放到单机上,举例,不要用文件session,嗯,常识。如果有可能,最好在一开始就做好用户单点认证的统一接口,包括跨域如何判断状态、静态页面如何判断状态,需要登录时的跳转和返回参数定义,底层给好接口,应用层直接就用(可参考GAE的 user服务)。登录方面的设计要考虑移动设备的特性,比如电脑可以用浮动层窗口,但NOKIA自带的浏览器或UCWEB就无法处理这种表现形式,程序一定既能处理AJAX请求又能直接通过URL来处理请求。图片服务器分开,资源文件最好也布局到图片服务器,也就是WEB服务器只服务动态程序。虽然开发测试时稍微复杂(因为需要绝对URI才能访问),但将来页面前端优化上会轻松许多,并且你的WEB服务器IO优化也轻松许多。程序引用资源文件时,要有一个统一的处理方法,在方法内部可以自动完成很多事情,例如将css/js根据组合,拼成一个文件,或者自动在生成的URI后面加上QUERYSTRING,如果将来前端用了缓存服务,那生成QUERYSTRING是最简单的刷新服务端缓存和客户端缓存的办法。

前面讲到,数据库会有复制,可能会多主多从,可能会分片。我们程序在处理数据的过程中,最好能抽象出来单独放做一层。拿现在流行的MVC模式来说,就是在M层下方再放一个数据层,这个数据层不是通常所说的JDBC/PDO/ActiveRecord等,而是你自己的存取数据层,仅对外暴露方法,隐藏数据存取细节。这个数据层内部不要怕写的难看,但一定要提供所有的数据存储功能,其他任何层次不要看到跟数据库打交道的字眼。之所以这样做,是因为在单关系数据库的情况下,可能会SELECT…JOIN…或直接INSERT…INTO…,可你可能会将一些表放到key-value数据库里存储,或者分片,这么做之后原来的语句和方式要全部改变,如果过于分散,则移植时会耗费很大精力,或得到一个很大的Model。在数据层面的设计上,尽量避免JOIN查询,我们可以多做冗余,多做缓存,每种数据尽量只需要一次查询,然后在你的程序里面进行组合。对于比较复杂的数据组合,在实时性要求不高的情况下,可采用异步处理,用户访问时只取处理后的结果。在对于主键的处理上,避免使用自增ID,可以用一定规则生成的唯一值当做主键,这种主键是最简单的分片分布策略。即使用自增ID,也最好用一个自增ID发生器,否则从数据库不小心被写了一下,那主键很容易冲突。

前面讲到,咱数据库前面还有某些缓存挡着。别把mysql的query cache当缓存,应用稍复杂的时候QUERY CACHE反而会成为累赘。缓存跟数据库和业务结合的很紧密,正因为跟业务关系紧密,所以这点没有放之四海而皆准的方法。但我们还是有一些规则可参照。规则一:越接近前端,缓存的颗粒度越大。例如在WEB最前端缓存整个页面,再往后一层缓存部分页面区域,再往后缓存区域内的单条记录。因为越靠近后端,我们的可操作性越灵活,并且变化最多的前端代码也比较方便编写。在实践中,因为产品需求变化速度非常快,迭代周期越来越短,有时很难将Controller和 Model分的那么清楚,Controller层面处理部分缓存必不可免,但要保证如果出现这种情况,Controller所操作的缓存一定不要影响其他数据需求方,也就是要保证这个缓存数据只有这一个Controller在用。规则二:没有缓存时程序不能出错。在不考虑缓存失效引发的雪崩效应时,你的程序要有缓存跟没缓存一个样,不能像新浪微博一样,缓存一失效,粉丝微博全空,整个应用都乱套了。在缓存必不可少的情况下,给用户出错信息都比给一个让人误解的信息强。规则三,缓存更新要保证原子性或称作线程安全,特别是采用被动缓存的方式时,很可能两个用户访问时导致同一个缓存被更新,通常情况这不是大问题,可缓存失效后重建时很可能是引发连锁反应的原因之一。规则四:缓存也是有成本的。不只是技术成本,还有人工时间成本。如果一个功能使用缓存和不使用,在可预见的访问量情况下区别微小,但使用缓存会使复杂度增加,那就不用,我们可以加个TODO标注,在下次迭代的时候加上缓存处理。

前面讲到,文件存储是独立的,那么所有的文件操作就都是远程调用。可以在文件服务器上提供一个很简单的RESTful接口,也可以提供xmlrpc 或json serveice,WEB服务器端所生成和处理的文件,全部通过接口通知文件服务器去处理,WEB服务器本身不要提供任何文件存储。你会发现很多大网站的上传图片跟保存文章是分两步完成的,就是基于这个原因。

以上几条“前面讲到”,其实无数人都讲过,我也只是结合前几篇文章用自己的话重复了一遍,真正分析起来精髓很简单——除了良好的功能逻辑分层,我们还要为数据库存储、缓存、队列、文件服务等程序外层资源调用单独设计接口,你可以把你的程序想象成是运行在 Amazon EC2 上并用他的所有web service服务,你的数据库就是它的SimpleDB,你的队列就是他的SQS,你的存储就是他的S3,唯一不同是amazon的接口是远程调用,你的是内部调用。

将支撑服务接口化,意味着将MySQL更换到PostgreSQL不需要更改业务处理程序,移植团队甚至不需要跟业务开发团队过多沟通;意味着业务开发团队是对接口编程而不是对数据库编程;意味着不会因为某个业务开发人员的失误而拖垮性能。

对程序扫盲不感兴趣的直接看这里——

产品设计完了,程序框架搭完了,可能有矛盾在这个节骨眼儿产生了。不断有产品设计抱怨说他的创意没实现到预期效果,有程序员抱怨说产品设计不切实际。这种抱怨多缘于产品人员不懂技术,技术人员不理解产品。从广义上来讲,产品包含市场策略、营销手段、功能设计,产品和技术在争论时往往把焦点放在功能上,而实际重点是,实现这个功能所消耗的成本跟能这个功能带来的利益能否换算,能否取其轻重。若可以,争议解决。若不能,则抛硬币看运气。因为一个功能的加强而引发指标井喷,或因项目拖延而导致贻误战机的例子比比皆是。激进的决策者注重利益,保守的决策者注重损失,聪明的决策者会考虑这个问题是否真的那么严重。

关系到未来的事情谁都说不准,要不怎么说创业一半靠运气呢。不过总有能说的准的事情,那就得靠数据说话。

没有100%也有99.9%的网站安装了访问统计代码,连我的 http://zhiyi.us 也不例外,新闻联播也总说科学决策科学发展的。有了统计,能确定的事情就很多了。例如,可以根据来源-目标转化率来分析哪类渠道的人均获取成本低,根据来源-内容访问猜测用户跳出率原因,根据用户点击行为判断链接位置是否合理等。将数据以不同方式组合起来,找到内在联系,分析内因外因,制定对应策略,减少拍脑门决策。靠数据支撑运营是个非常专业的事情,虽然不懂深奥的数学模型不会复杂的公式计算,渐渐学会因为A所以B,因为A和B所以C还是相对简单的。

全系列完毕。老话,大半夜连抽烟带码字的挺伤身,转载请注明出处 http://zhiyi.us/internet/thinking-twice-before-building-your-site-final.html

从100PV到1亿级PV网站架构演变

一个网站就像一个人,存在一个从小到大的过程。养一个网站和养一个人一样,不同时期需要不同的方法,不同的方法下有共同的原则。本文结合我自已14年网站人的经历记录一些架构演变中的体会。

1:积累是必不可少的

架构师不是一天练成的。

1999年,我作了一个个人主页,在学校内的虚拟空间,参加了一次主页大赛,几个DREAMWEAVER的页面,几个TABLE作布局,一个DB连接,几行PHP的代码嵌入在HTML中,再用FTP传到服务器上就可以给别人展示一个网站。
2000年,个人主页已经不能满足好奇,在当时的网管中心管起几台机器,作起网线水晶头,用ALL PEOPLE SEEMS TO NEED DATA PROCESS的理论开始认识了7层网络模块(面试技术员工时,经常会问这些网络基础知识的理解)。有了基础理论的武装,我也开始配置各种服务来玩LINUX,AIX和FREEBSD这些系统。面对各种原理不懂的系统,目的只是想尽办法去解决网站需要的各种基础服务。当时搭建了REALSERVER流媒体服务,各种开源FTP下载服务,BATTLENET游戏网关,APACHE(keepalive等配置,http报头相关的知识 也是面试的老客户),DNS,QMAIL等服务给学校的学生使用;
网站有近10万PV的时候,开始考虑如何作扩展拆分,MYSQL的MASTER SLAVE作读写分离,MYSQL的索引优化是当时唯一会使用的DB性能优化方法。这个阶段基本上能解决需求,同时也遇到瓶颈,不知道访问量再大一个数量级,怎么办?明显感到技术能力不够。当时受限于网站的量一直没有新的数量级的突破,导致了几年内技术工作以维护为主,体验着网站日常运维的各种纠结,体力活。而这时期网站的2层架构方法也一再被重复应用到后续的N个网站的搭建过程中,2001年开始的JSP与PHP之争好像对我没什么感觉,因为我还是只会用那种很土的2层架构作着网站:页面中嵌入JSP,连接后端的MYSQL进行数据的CRUD,没有事务,没有考虑数据库读写错误,没有考虑两层系统不可用,因为有问题只需要重启,有报错只需要改JSP后刷新页面。作网站确实是体力活。
2005年,在道富参与了FXCNG项目,用MULE ESB,接触到MULE的前几个月还没有意识到这个系统在架构中的位置。直到半年后,在与第三方系统集成的应用中,才发现,这样的一个系统就是传说中的中间件,他可以专业的解决一部份系统的职能,比如当时的消息和远程调用代理。这时候我逐步有了一些架构观点,一个大型的应用系统中,是需要隔离一部份技术职能,让系统责任单一,方便技术维护和团队扩展,专人办专事。
2006年,阿里软件,一个全新的开始。进入阿里软件的前三个月在马总的老家,淘宝的发源地:湖畔花园小区,2楼的4室两厅里进行了封闭式开发,很累,但很兴奋。这段时间,我参与了一个全新外贸ERP系统的搭建。虽然只作为一名普通的JAVA开发工程师,仅负责一个很小的模块开发,但还是正式体会到一个大型系统的初创过程。这段时间对MVC的架构理论有了深刻的理解。MVC三层架构比2层架构带来的更好的技术扩展与团队扩展能力让我映像深刻。M层负责DB逻辑的CRUD,架构师们开发出了大量的中间层JAR包,完成了DAO层,DO的封装,M层也完成了SERVICE层,完成了BO的封装,接口包的封装,系统使用了最常用的工厂、单例、FACACE等模式(直到现在,我面试人还是常常只会问这些基本的模式)。V层的模板隔离,前端开发可以在这一层和后端开发一起修改界面(之后,更大规模的团队连这一层都可以再分离)。C层完成输入输出的基本校验和检查然后调用后面的服务层功能或作转发。 简洁朴素的结构生命力是比较持久的。直到现在,MVC还是具有很强的生命力,在更种大型网站中承担重要架构骨架。
07年,我对应用层,服务中心层,持久层三层架构并没有多少的实践应用和理解。因为MVC对于初创系统或中小型网站,20人左右的团队规模来讲,已经适用。换个角度看,当时在3个月内作出一个完整的ERP系统也只用使用MVC,再选进的架构也需要考虑网站发展的阶段。用户一边使用,工程师一边迭代改进架构,这个方式是作网站,不是传统应用软件开发。ERP本身源自传统应用软件领域,我们在用互联网的方式作管理软件,最大的挑战应该是这种边作边改进架构的理念。07年,这个理念之争逐步在团队内达成了一致,架构师们小心的平衡着业务和架构,这个网站高峰时,也支撑到了日访问量近千万的级别,没有闪架。
08年,阿里软件开放平台,又是一个新系统。全新的系统架构总是可以得到足够的时间来考虑末来1到3年的增涨。实际上,互联网系统,我们感觉只能考虑到一年的架构。这一年,我参与架构设计,开始理解了三层架构的价值与扩展能理,服务中心开始搭建,因为这个系统将接受的几百万在线的旺旺用户访问,所以部份系统开始考虑服务中心,想把业务逻辑聚合到服务层,由统一的团队进行扩展维护。另外,随着两年下来小的业务系统越来越多,小机上的oracle连接数也吃紧了,服务中心的需求越来越大。首先进行的是用户中心,想法是集成整个集团队账号体系,基于『旺号』体系。这个用户中心项目有个响亮的名字:UDB,项目过程可以写一本书。这里不再展开。
《SAAS架构设计》这本书,针对的是软件平台的早期ISV用户。现在的眼光来看,这书写的过于简单,正如十年后再来看今天我的写的这个笔记一样。
09年,加盟了刚刚成立的aliexpress部门。经历了两三个应用的小系统到亿级PV的在线交易系统。遇到了很多问题,体会到一个小系统与大网站不同阶段架构的演变过程有不同的难处。

下文从不同维度分享亿级PV网站架构下我的体会和观点。

2:知识结构
网站架构师有很多,有科班出身的,有美术专业的,有生物专业的,有学物理的,有派出所警察出身的,我觉得都是OK的。我也接触到了这些架构师,非常有特点,在很多技术领域有自已专深的。英雄不问出路,好汉不提当年勇。架构师知识背景可以不同,个人看法是不同领域的人作网站架构可以带入很多交叉的思路。就像种树的人再去种花,其实也是可以看到一些共性的总结抽象。

网站架构师需要有编程的经验,从基本的算法,常用的设计模式,多线程开发,远程调用,不同类型数据源使用,这些是面试的时候看得基本功。我认为一个资深的测试专家一定是开发高手,一个架构师必须也是有长期的开发经验,很多性能优化是要从一行行代码优化起的。试想在一个被调用1000万次次每天的页面,一行代码如果每次都走到,每次少运算1ns,也可以节省不少的电力。我为环保作贡献,我骄傲。

网站架构师需要对网络环境有很好的知识理解。架构问题是需要考虑网络部署。比如系统因不可用而发生切换的时候,从一个机房切到另一个机房,要考虑网站的服务对用户访问速度上会有多大影响。这时候的技术方案可能是切DNS,也可能是切前端的跳转机,或是底层部份服务调用到另一个机房。对于这类切换的方案,架构师需要计算网络时间的开销带来的QPS影响,和用户体验上的延迟,每个请求估算需要精确到ms级。如果是全球范围内DNS切换,需要知道DNS刷新的时间经验周期,比如:全球更新在1小时左右,而80%的地区用户会在20分钟内刷新,这样系统带来的业务影响会有多大。

网站架构师需要对网络协议有深入的理解。HTTP协议是最基础的,无论是SESSION还是COOKIE在HTTP协议基础上怎么应用,COOKIE的大小,数量,浏览器是怎么处理HTTP协议的。这些基础有关键时候会影响业务的进行。比如,SAFRI浏览器对第三方COOKIE是禁用的,某功能跨域写COOKIE的时候每次都会重新生成COOKIE,直接导致系统统计用户UV的时候,数量增大,影响各种转化率的计算。HTTP协议还需要考虑本身的连接管理池大小和连接是否KEEPALIVE,这些细节很多时候成为架构上扩展能力的瓶颈。一个静态页面服务的HTTP MAXCLIENT设置 为2500,机器只有10台,很可能在一次中小型活动中连接数到顶,用户部份请求无法满足。

架构师需要考虑数据格式带来的性能影响。很多远程系统调用走的是HTTP协议为基础,数据格式为纯文本或JSON,或XML等,这类调用需要考虑数据的序列化和反序列化,这个工作是CPU开销型的,对性能优化上需要有针对性。QPS高的系统RT一定会短,但RT短的系统不一定比RT高的系统能表现更好的QPS。

架构师需要有很好的数学能力,计算一个QPS里系统从网络请求发出,到网络的IO时间,DB的磁盘读写时间,CPU运算时间,再到数据库连接数,数据分库分表容量规划,都需要有精确的计算。因为容量计算不正确带来问题也是非常多的。比如一台小机上ORACLE的连接数开了2000个,而应用系统由于不断的扩展,小业务系统不断加入,大型促销活动前,临时机器的不断上线,很快就把DB连接数用完,引起业务部份不可用。架构师需要去合理估算每种应用的服务能力,以及他对DB等资源的合理连接数。

加构师对JVM的内存分区及管理策略要有深入的了解,GC的频率可以发现很多系统容量的问题。一个OLD区不断加大的系统,伴随YGC高频发生,加上TCP机器连接数很可能高,可能是要是机器了。一个业务功能不断叠加的系统,很可能PERM区会需要加大设置,否则容易OUT OF MEMORY。

加构师需要精读《数据库系统概念》这类书,对不同DB的索引原理和库表存储结构有了解,我们可以不是ORACLE ACE,但一定要听得懂ACE的DB架构和性能优化方面的建议。并且在原则上,前端用户系统架构上不要出现直连DB的设计,这是亿级PV架构的基础设计保障,特别是一些营销类功能系统,短时并发大的页面不能有DB直连,一些小应用可例外对待。

架构师需要很好的学习能力,技术是不断变化的,昨天用DUBBO,明天可能要换HSF;今天MEMCACHE,明天可能REDIS;今天刚刚把应用拆分,明天可能就要合并。公司外的技术社区还不断有一些好的开源中间件和框架出来,需要不断学习,关注。大网站的架构模式不一定合适小网站,新中间件和框架实施需要考虑运维成本和学习推广成本,架构上要选合适当前阶段的。架构师需要和不同类型的专业人才沟通,所以要能快速理解并学习不同专业的知识去补充自身的知识结构不足。

架构师需要理解业务,在一些业务系统型的网站,业务架构师也显得异常关键,比如像交易型系统,支付型系统。业务架构师需要解决业务层次结构,业务边界划分,业务优先级与技术优先级的平衡。传统软件的系统分析师不知道是否也干这角色?但互联网的业务架构师要求更高,应该是建立在系统架构师的基础上再看高一层,通过业务和技术的综合影响力去帮助网站取得合理的架构,更好得拿到业务结果。

网站架构师的知识结构是宽又深的。

3:设计理念

每个架构师都会有一些自已原设计理念和原则。我的基本思路是:架构要作到至少1年的预见性(半年不叫预见性,因为方案实施要半年)。设计的目标是尽量让系统可以水平扩展,并利于。当然,有些业务处在生存的边缘,可能架构方案只有几个月的生命力。但一些成本不高收益稳定的架构理念,不管什么时候都是值得优先考虑的。以下是架构设计的一些常用手段。

1>:异步换同步:系统中的很多调用是可以异步化的,包括WEB界面上的AJAX异步,还有服务端的消息型异步;AJAX调用的应用要注意把这种类型的应用集中到一个隔离的服务系统中,以方便在必要的时候进行服务降级。
AJAX调用一般都是界面上非同步非强依赖的功能点;服务端异步的系统可以让服务端的请求RT变短,提升服务器QPS,同时减少应用强依赖。
一个小型系统(峰值万级消息per second)的服务端异步消息可以借助RMDB的表实现,当网站规模变大时(峰值百万级消息每秒),消息系统需要有一个中间件,负责消息持久化及数据CRUD管理;再大点的时候,消息中间件的分布式与可用性会有更高要求,需要综合使用多种架构设计理念;

同步换异步对软件工程上的好处是,可以把一个子系统的不同模块分别由不同的人开发维护,调试期间,两个模块也不会有很强的依赖。提高开发并发性。

2>: 集中变分布:

一个网站小的时候,很多业务都会在一两个应用系统中实现。比如一个电子商务网站,从登录,到首页,到搜索,到产品DETAIL,到购物车,下单支付,风控,订单管理,用户中心到售后用户纠纷流程。网站小的时候,这种一体化的业务架构模式在网站规模小的时候,无论是研发团队规模还是硬件成本都是比较低的。这个时期的扩展性一般只需要作到LB后面挂一片集群。服务器资源利用率这时候也是比较高的。

随着业务规模扩大,需要把系统独立分拆出来,基本原则是:不同维护策略和服务等级的页面和服务 不要放在一个应用容器中,最好不要放在一个虚拟机或物理机上。发生过很多次紧急事件。因为大流量页面上带着一个小的AJAX请求,把提供AJAX服务的WEB应用压死。而这种AJAX应用平时又是比较容易在容量评估的时候被忽略的。也比较难以管理AJAX,因为一个前端开发工程师很可能因为一次小的运营活动加上一个调用。服务器端不同服务类型的功能也需要分拆到不同服务中,服务的聚合一定要有一定的原则,并不断的调整治理聚合服务内容。如果把一个文件生成类的业务功能(比如用户批量导入导单)和一个下单的服务放在一起,很容易让下单这类核心主干逻辑功能受批量导出功能影响,当架构师需要作服务降级时,不得不侵入代码层作服务功能的隔离。

架构上的基础设施也需要有隔离策略。比如一个功能先后需要完成读数据,再生成文件,再发消息,再写数据库,写CACHE,再把数据同步到另一个机房。这一串逻辑中,除了异步化策略之外,还需要考虑一些基础职能的隔离,比如把生成文件的功能封装成一个服务,文件存储也需要从集中式变成分布式。T级可以考虑NAS类的集中式存储方案,P级和Z级的文件容量一般是需要考虑分布式文件系统方案,开源的也比较多。数据库与从集中式变分布式是现在流行的方案,之前我们小网站的时候常用MASTER SLAVE,然后再大点搞双MASTER写,多SLAVE读;再大点流量或者应用系统过多时,数据库的连接数也会受到考验,这时候分布式的分库分表方案是必须的。当然对架构师来讲,如果能用上一种云方案,不需要业务架构师考虑分库分表方案,那会更有幸福感。同步系统也需要考虑集中变分布的策略,两个机房或同一机房两个系统进行数据镜像同步,需要考虑多通道,分表,分字段,分库进行同步,有时候还需加入一些商业逻辑作为同步数据的判断。非镜像同步的时候,同步系统还需要考虑业务逻辑之间的事务特性。

3>: 架构层次化:

早期网站一般是两层架构,应用层+数据库层;现在大型网站经常采用三层架构,应用+服务中心+持久层,这三层分别在不断的增强可用性和可扩展性;理论上增强后的三层可以称为saas+ paas +iaas。
我把saas层看作现在淘宝开放平台上的第三方ISV应用,独立发展,互不影响,SAAS层数据隔离,运维隔离。SAAS层还可以自建分布式CACHE,集中式CACHE或简单的本机CACHE。电子商务网站本身的系统也可以把这个当成架构设计的目标之一,把自已的应用层作成像第三方APP一样的存在,这样发展效率和扩展性都会高很好。

paas层是我理解中的服务中心,具有应用逻辑的一个业务层服务中心,比如UIC用户中心,IC商品中心,TC交易中心等等 ,一般这样的一个服务中心会被多个上层SAAS应用所调用依赖。对一只被一个SAAS应用依赖的服务中心是否值得建立,这个要看投入产出比,一般小网站可以直接让应用连着DB,而中型网站也可以考虑在一个应用内部分为两层,先从JAR包层面隔离,PHP的话可以用代码目录结构上来隔离。网站更大规模的时候,1:1的依赖也是值得建服务中心的,因为这样可以隔离下面的持久层和上面的应用层,并且可以在PAAS层隔离考虑缓存等职能,可以考虑在这一层实现流控,隔离对DB连接数量的依赖。PAAS层要尽量实现自已的水平扩展,服务无状态。

iaas层负责实现持久层,一般数据源都在这一层,常见网站的数据源不外呼这四种:RMDB(这个玩转了近20年了),KV(最近10年比较热,KV可以分为内存型或持久型,对于持久型的KV,可以把数据挂到各类存储中),inverted index or file(倒排索引类),FILE SYSTEM(各类传统文件存储或自已实施的小文件中间件,普通文件中间件)。

这三次之间是1:1:1的关系建立,还是N:1:1,或是N:N:N,都是需综合考虑的。曾经有一次,我在设计一个系统的时候,为应用层界面设计了一个用户列表的头像显示功能就引发了一个调用比例考虑不全的重大问题。当时,用户有个旺旺的第三方游戏插件,插件主界面上有个好友列表,每个好友都有个头像读取的请求。假设用户每天9点左右登录旺旺的人中会有10%的人马上去玩这个游戏,9点左右在线按100万人算,每个人的好友有平均50个,则每天9点左右用户头像URL的HTTP请求量会有50*10万,产生近500万个突发的HTTP请求。虽然有CDN,依然存在很大的头像请求容量的不足,并且服务端获取用户好友列表信息的接口调用并发量也会很大,如果没有提前对第三方应用进行接口调用限制和设计上的规范化,调用比例很可能带来极大的系统伤害。
应用层与服务层之间的调用与依赖会随着网站规模变得越来越复杂,当网站小的时候,这两层直接的固定协议调用是可以接受的,调用方知道服务端的IP LIST,也知道调用的SOCKET,还有调用的协议;规模更大的时候,调用变成N:N的方式,随然有层次,但已经成网状结构,这时候需要服务治理与服务依赖的监控,流控等基础设施。对于服务治理,引入服务中间件,比如阿里的DUBBO和HSF是比较成熟的可以处理每天亿级的服务调用量并作好配置维护,调用统计,分布式,名称服务,流控,路由等基础职责,业界开源的也有很多;服务层还需要处理异步消息调用与消息通知的机制,这时候需还要配全一些消息中间件。

4>: 功能分解化

网站的应用级功能在网站小的时候一般都在一个物理机上,但在网站发展过程中,有些模块经常因业务原因发生变化和升级,有些模块流量和调用量比较大,有些模块处理的及时性和异步性要求不同,有些模块与外部调用特别多;有些模块经常报异常,有些模块IO多,有些模块偏CPU计算型。不同的模块需要随网站规模发展进展不断的分解。

架构师之道在于庖丁解牛一般的理解业务系统的复杂度和结构关系,进行合适的分解和聚合,这是我理解业务架构的核心贡献之一。一个业务架构师首先是一个技术架构师,没有技术背景无法理解系统内的技术边界,没有业务能理无法预见架构变化的趋势,也无法预见业务系统的流量变化。

5>:服务中心化

服务化有很多方式,三层网站架构下,亿级PV的网站最好能把同一业务逻辑被多方使用,边界清楚的功能隔离出来作为服务。服务中心可以封装对持久层的访问,形成带有业务逻辑的一种原子性服务,加上一些事务性控制的多个原子服务。服务中心不要有界面,管理好服务的粒度,可用性,高并发下的性能,以及服务路由,监控为主要任务。

6>:结点监控化

亿级PV网站的监控是非常关键的,很多系统问题,服务问题,流量问题,性能问题,业务异动都需要通过监控来发现。监控可以分为几类,一类是快照型的,像搞活动的时候特别需要一个大盘监控。可以看全局的流量,交易量,访客分布,来源分布,系统LOAD,DB连接数,CPU和网卡口子的状态;一类是基线型,可以看到每小时,分天同一个指标的变化历史。看到一个页面响应速度,服务器RT时间的变化;一类是关键业务逻辑结点的按需统计,比如需要看一下某页面改动后某个页面点击量和原来的差别。

监控会带来系统的性能损失,特别是在线打点,不管你是在容器层面作的,还是在业务逻辑侵入方式实现的;另一种是通过日志分析,可能实时性差一些,比如有3分钟延迟;还有一类是基于RMDB直连的分析,一般会在备库上把数据导出来作分析,实时性好一些,但对备库或主库DB有压力。还有一类是基于消息的分析来实现监控。让一些关键结点有动作时,发现异步消息到消息队列上,然后监控系统的抓取模块和正常 业务逻辑一样去订阅消费这些消息。这种方式需要监控团队与业务逻辑有协同,这对长期运维有挑战。

4:基础架构

亿级网站的基础架构是较多时间投入的一个工作,小网站一般没有中间件的概念,基础架构投入精力不多,但一样可以运行的很好。对于小网站,DB也像是一个中间件。一个亿级PV的网站,要看PV,也要看UV。这两个数字的规模对系统的技术架构挑战点是不同的。PV流过的系统和UV经过的系统路径不同,比例可能也有所不同。

架构师需要分析这个路径,好比庖丁解牛般的分析。在合适的节点引入中间件。比如一个亿级商品量的系统,需要从商品的POST服务性能,图片存储空间,图片缩图处理服务,多语言商品信息翻译,商品信息与图片在不同系统之间同步的服务,图片CDN服务,商品信息更新的通知和提醒服务,商品搜索服务,商品统计类信息服务等不同阶段和信息模块的CRUD中引入中间件,让系统可扩展,可承受高并发。

在合适的时间点引入中间件提升架构水平扩展能力,只是关心可扩展是不够的。基础架构不只是要关心系统的可扩展能力,还需要关心可用性。系统达到亿级PV后,每停机1分钟损失的流量都都是很大的。系统架构师预见并规划好系统容量。对于预料之外的超过容量的PV进行服务降级,限流,针对系统不可用时提供组织保障机制,用提前制定的紧急响应流程让不可用时间尽可能变短,这也是很重要的架构师职责。异地机房容灾或是同一机房的系统切换也应该有定期不定期的演习。对于不同国家之间的机房灾备,系统必须考虑机房之间的调用延迟,国内同步系统一般在10MS之内的延迟是可以接受的,对于非同步系统,延迟可适当放大,这种延迟的时间需要根据业务特性进行评估。对于中美之间的200ms级别的延迟,系统需要有合理的评估,尽可能不要有中美服务同步调用。这个200ms的延迟来自网络物理传输,来自路由器路由算法的延迟,也有来自机房本地的信息号交换过程,是刚性的,很多大型电商网站都面临这一问题的挑战。EBAY, AMAZON,alibaba和GOOGLE这类的网站架构设计时,一定会有很多系统不得不讨论这一延迟带来的系统方案区别。有时候网站会因业务原因考虑建完全独立分站,有时候会灰这种架构问题的影响考虑作单写还是双写。如果是全球机房,则这一问题会变得更复杂。数据同步和分发会是一个关键的中间件和可用性设施。

性能是大规模网站的重要基础架构问题。网站应用层,我们关心系统的关键页面的QPS值,比如在100并发下,系统某页面能接受每秒几次正常调用;综合页面的QPS也是需要关注的,特别是当一个前台应用内的界面比较多的时候。WEB应用的QPS可以通过服务端日志中的COOKIE来回放,进行线上线下的压测来取得一个有信心的数字。前台的WEB应用原则上不要有直接的DB层访问,小规模网站者需要平衡投入产出比,有时候作一些TRADE OFF也是值得的。对于服务层的应用,一般关心TPS,因为调用都来自WEB应用系统,所以通过COOKIE回放这种调用是不可能。持久层的TPS和上两层的QPS,TPS量之间存在一个比例。多个数据库的TPS可能对应一个服务层的一个TPS。这对于系统的容量和机器的扩容估主也非常关键,需要维护这么一个状态的快照。架构师才能让这个状态时刻保持胸有成竹。发现关键资源瓶颈对于分析QPS和TPS是非常 关键的。

服务治理除了作抽应用层服务中心的工作和JAR包之间的依赖管理之外,服务强弱依赖也是需要有一个系统来监控和管理的。随时知道一个新上的系统在依赖哪个服务,或被哪个应用依赖,这是架构师工作的必要工具。架构师从输出经验,到提供工具平台,是一个必然的过程。小网站需要一个架构师的经验快速搭建,大规模网站则不可能靠一个人的经验来进行判断,需要更多的数据采集和分析生成规则。监控系统是一个网站健康状态的指示仪。

部署架构是网站进入10亿级规划,99.99%可用性要求下必然关注的问题。无论是EBAY还是AMAZON都在部署上有很多投入。单一的机房由于电力,机柜等问题,经常出现部署上的硬件约束;容灾与不同地区访问体验要求异地机房能提供在线同时的服务。部署上需要考虑是全机房的对称部署,或是应用不同分级的分区部署。比如持久层统一,服务层与应用层多机房对称部署;或是持久层与应用层服务层完全对称,但数据分区;这种分区需要考虑买家维度、卖家维度,或是IP区域分区,不同区生成的数据通过同步系统实现各区的最终一致。以订单为例,分区是可以让美国买家创新的订单写在美国分区数据持久层,然后异步消息生成同步任务,数据同步到卖家所在的分区。

基础架构的工作还有很多,架构师责无庞待。if not me, who?

5:软件工程

架构师除了作经验,工具和代码输出之外,还需要关注工作机制的建立和人员的传帮带。发布流程,可重复使用的灰度发布ABtest方案,代码管理规范,代码开发规范,人员梯队,业务优先级判断,中间件和平台化工作推进都是每一天的日常工作。有时候帮测式工程师去搭好并维护一套测试环境,也算是本职工作。

有些架构师被称为PM型架构师,也有被感觉像RA型的,偏咨询师型的架构,偏业务型的,偏算法型的,偏性能调优的,偏中间件和服务治理的,偏基础架构型的,这个是看网站发展阶段的需要,缺什么,作什么。关键是看架构在软件工程过程中对产品,对团队的输出是否能解决问题,拿到结果!eat what, what strong。

6:不同类型业务系统技术架构的差异化

每个网站架构都有不同,完全复制是不科学的。哪怕现在想再作一个淘宝网,光靠把淘宝全部几万台机器搬去是不行的,搭不出一个淘宝网。完全复制淘宝网的建设过程也不是靠谱的。可以复制或参考的是架构的原则和经验教训。不同类型的业务系统有不同的业务发展过程,业务架构发展演变过程不同;技术架构发展过程也不同,技术解决问题的重点不同,有些网站一开始需要解决的问题是如何从一个老网站中改版和分拆,有些则是全新的搭建。有些网站自建物流系统,有些则是与多家物流第三方对接系统。比如:有些网站交易模式简单,有些则需要去支持各种不同交易模式,像多次付款,预售,批发,团批,阶梯价格。。有些网站只需要解决支付 宝对接,有些则自建网银与支付系统,风控系统。

架构师要小心经验的惯性。大网站的方法不一定合适小网站。小网站的格局也不可能适用大规模。时代在变,地点在变,因时制宜,因地制宜。

7:小趋势的生命力

开放平台是胸怀: 06年,我们都谈开放平以。其实这个理念最初考验的是网站拥有者的胸怀。你是否愿意让其它人进来操作你的数据,是否愿意看到别人作出比你理好的应用层系统?甚至是一些服务层的系统?
FB与微博是社会化:07年,我们都讲SNS。SNS无处不在,因为他本质上是一个社会化的思路下的技术系统表示。愿意接受UGC,能否以社会化的方式让系统内的数据产生和管理发生。原意为这些社会化的小数据作系统,才可以最终生成大数据的拥有者。
电商团购是心理:09年,GROUPON火了,大家都团购。团购本身是没有什么技术创新的。有人说O2O是他的模式创新,可是,难道在原来的C2C网上不能实现吗?就像超市里把促销的商品放在货架边上的花车上,和放在货架里有本质区别吗?区别在于心理,用户体验上的区别。有时候这也会是一种竟争力,是一种常态化的经营思路,但不会主流。
移动PC平板是体验:10年,平板热。这种比手机屏大,比笔记本屏小的东西,满足了某些场景的方便性需求,体验创新很有机会。
Pinterest电商导购是基尼:11年,导购网站火了。瀑布流热了,国内的蘑菇街,美丽说火了。从根本上来看,导购是解决 了网站商品与用户流量之间的基尼关系,把基尼指数变得更小一些。否则80%的流量一直放在20%的热门商品和大卖家的店里,市场规模会有影响。作生态圈好一些,有活路的人多了,市场 才稳定。
外贸电商是库存:12年,外贸电商预热了,GOOGLE TRENDS里显示,才作两年的ALIEXPRESS的指数超过了DHGATE这个作了五六年跨境电商B2B网站,也越来越接近ALIBABA。COM这个老牌SOURCING网站。外贸从批发变小单是什么背景?我想本质上他的销售链变了。MIC基本还没变,没有变成快速反应能力的供应商,但出品商变成了拥有小单外贸客服能力的80后;进口商变成了国外的RETAILER,国外的超市变成了最终消费者。
体感外设是物联:13年,各类体感设备越来越丰富。什么手势,什么随身拍,什么位置设备,拍照设备。好玩。按马斯少理论来讲,工作是生存需求,买房子是安全需求,买车和大房子是社交需求,体现在的单位和职位是尊重需求,买体感设备,那是自我实现。

BARABASI预见了末来,小趋势改变末来的本质是一种叫幂律的无形之手,像我们所熟知的长尾。据说人类行为90%是可以预测的,人类的90%的形为是可以采集的。架构师从不同观察者的角度理解他们的观点有时候会有更多的预见性。

8:LAST BUT NOT THE LEAST

作网站如作人。架构的是人的骨架,人还需要配一个好的心态:心胸+态度。心胸是装进不同声音采集到信息的基础,态度是推广服务他人的手段。一个新架构方案下去,一定会有反对的声音。如何去说服别人现在就启动架构升级或转型方案,是需要带着心态去的。毕竟一个大的架构方案是需要很多人一起努力才能拿到结果,不是一两个英雄人物能造就的。架构师的工作方式是主动的,而不是问题驱动的。能解决问题的架构师是牛B的,能预见问题或提前准备的架构师是称职的,这才是技术促进业务。

没图片写贴子就是比较快?不过读起来是会费点眼力。

从0到千万级访问量网站架构演变史

read it later.

架构演变第一步:物理分离webserver和数据库
最 开始,由于某些想法,于是在互联网上搭建了一个网站,这个时候甚至有可能主机都是租借的,但由于这篇文章我们只关注架构的演变历程,因此就假设这个时候已 经是托管了一台主机,并且有一定的带宽了,这个时候由于网站具备了一定的特色,吸引了部分人访问,逐渐你发现系统的压力越来越高,响应速度越来越慢,而这 个时候比较明显的是数据库和应用互相影响,应用出问题了,数据库也很容易出现问题,而数据库出问题的时候,应用也容易出问题,于是进入了第一步演变阶段: 将应用和数据库从物理上分离,变成了两台机器,这个时候技术上没有什么新的要求,但你发现确实起到效果了,系统又恢复到以前的响应速度了,并且支撑住了更 高的流量,并且不会因为数据库和应用形成互相的影响。
看看这一步完成后系统的图示:

from_0_to_architecture_1

这一步涉及到了这些知识体系:
这一步架构演变对技术上的知识体系基本没有要求。
架 构演变第二步:增加页面缓存
好景不长,随着访问的人越来越多,你发现响应速度又开始变慢了,查找原因,发现是访问数据库的操作 太多,导致数据连接竞争激烈,所以响应变慢,但数据库连接又不能开太多,否则数据库机器压力会很高,因此考虑采用缓存机制来减少数据库连接资源的竞争和对 数据库读的压力,这个时候首先也许会选择采用squid 等类似的机制来将系统中相对静态的页面(例如一两天才会有更新的页面)进行缓存(当然,也可以采用将页面静态化的方案),这样程序上可以不做修改,就能够 很好的减少对webserver的压力以及减少数据库连接资源的竞争,OK,于是开始采用squid来做相对静态的页面的缓存。
看看这一步完成后 系统的图示:

from_0_to_architecture_2

这一步涉及到了这些知识体系:
前端页面缓存技术,例如squid,如想用好的话还得 深入掌握下squid的实现方式以及缓存的失效算法等。
架构演变第三步:增加页面片段缓存
增加了 squid做缓存后,整体系统的速度确实是提升了,webserver的压力也开始下降了,但随着访问量的增加,发现系统又开始变的有些慢了,在尝 到了squid之类的动态缓存带来的好处后,开始想能不能让现在那些动态页面里相对静态的部分也缓存起来呢,因此考虑采用类似ESI之类的页面片段缓存策 略,OK,于是开始采用ESI来做动态页面中相对静态的片段部分的缓存。看看这一步完成后系统的图示:
from_0_to_architecture_3

这一步涉及到了这些知识体系:
页面片段缓存技术,例如ESI等,想用好的话同样需要 掌握ESI的实现方式等;
架构演变第四步:数据缓存: 
在采用ESI之类的技术再次提高了系统的缓存 效果后,系统的压力确实进一步降低了,但同样,随着访问量的增加,系统还是开始变慢,经过查找,可能会发现系统中存在一些重复获取数据信息的地方,像获取 用户信息等,这个时候开始考虑是不是可以将这些数据信息也缓存起来呢,于是将这些数据缓存到本地内存,改变完毕后,完全符合预期,系统的响应速度又恢复 了,数据库的压力也再度降低了不少。

from_0_to_architecture_4

这一步涉及到了这些知识体系:
缓存技术,包括像Map数据结构、缓存算法、所选用的 框架本身的实现机制等。

架构演变第五步: 增加webserver 
好景不长,发现随着系统访问量的再度增加,webserver机器的压力在高峰期会上升到比较高,这个时候开始考虑增加一台webserver,这也是为 了同时解决可用性的问题,避免单台的webserver down机的话就没法使用了,在做了这些考虑后,决定增加一台webserver,增加一台webserver时,会碰到一些问题,典型的有:
1、 如何让访问分配到这两台机器上,这个时候通常会考虑的方案是Apache自带的负载均衡方案,或LVS这类的软件负载均衡方案;
2、如何保持状态 信息的同步,例如用户session等,这个时候会考虑的方案有写入数据库、写入存储、cookie或同步session信息等机制等;
3、如何 保持数据缓存信息的同步,例如之前缓存的用户数据等,这个时候通常会考虑的机制有缓存同步或分布式缓存;
如何让上传文件这些类似的功能继续正常, 这个时候通常会考虑的机制是使用共享文件系统或存储等;
在解决了这些问题后,终于是把webserver增加为了两台,系统终于是又恢复到了以往 的速度。

这一步涉及到了这些知识体系:
负载均衡技术(包括但不限于硬件负载均衡、软件负载均衡、负载算法、linux转发协议、 所选用的技术的实现细节等)、主备技术(包括但不限于ARP欺骗、linuxheart-beat等)、状态信息或缓存同步技术(包括但不限于 Cookie技术、UDP协议、状态信息广播、所选用的缓存同步技术的实现细节等)、共享文件技术(包括但不限于NFS等)、存储技术(包括但不限于存储 设备等)。
架构演变第六步:分库
享受了一段时间的系统访问量高速增长的幸福后,发现系统又开始变慢 了,这次又是什么状况呢,经过查找,发现数据库写入、更新的这些操作的部分数据库连接的资源竞争非常激烈,导致了系统变慢,这下怎么办呢,此时可选的方案 有数据库集群和分库策略,集群方面像有些数据库支持的并不是很好,因此分库会成为比较普遍的策略,分库也就意味着要对原有程序进行修改,一通修改实现分库 后,不错,目标达到了,系统恢复甚至速度比以前还快了。
看看这一步完成后系统的图示:

from_0_to_architecture_5

这一步涉及到了这些知识体系:
这一步更多的是需要从业务上做合理的划分,以实现分 库,具体技术细节上没有其他的要求;
但同时随着数据量的增大和分库的进行,在数据库的设计、调优以及维护上需要做的更好,因此对这些方面的技术还 是提出了很高的要求的。
架构演变第七步:分表、DAL和分布式缓存
随着系统的不断运行,数据量开始大幅度增长,这个时候发现分库后查询仍然会有些慢,于是按照分库的思想开始做分表的工作,当然,这不可避免的会需要对程序 进行一些修改,也许在这个时候就会发现应用自己要关心分库分表的规则等,还是有些复杂的,于是萌生能否增加一个通用的框架来实现分库分表的数据访问,这个 在ebay的架构中对应的就是DAL,这个演变的过程相对而言需要花费较长的时间,当然,也有可能这个通用的框架会等到分表做完后才开始做,同时,在这个 阶段可能会发现之前的缓存同步方案出现问题,因为数据量太大,导致现在不太可能将缓存存在本地,然后同步的方式,需要采用分布式缓存方案了,于是,又是一 通考察和折磨,终于是将大量的数据缓存转移到分布式缓存上了。
看看这一步完成后系统的图示:
from_0_to_architecture_6这一步涉及到了这些知识体系:
分表更多的同样是业务上的划分,技术上涉及到的会有动 态hash算法、consistenthash算法等
DAL涉及到比较多的复杂技术,例如数据库连接的管理(超时、异常)、数据库操作的控制(超 时、异常)、分库分表规则的封装等;
架构演变第八步:增加更多的webserver
在做完分库分表这些工作后,数据库上的压力已经降到比较低了,又开始过着每天看着访问量暴增的幸福生活了,突然有一天,发现系统的访问又开始有变慢的趋势 了,这个时候首先查看数据库,压力一切正常,之后查看webserver,发现apache阻塞了很多的请求,而应用服务器对每个请求也是比较快的,看来 是请求数太高导致需要排队等待,响应速度变慢,这还好办,一般来说,这个时候也会有些钱了,于是添加一些webserver服务器,在这个添加 webserver服务器的过程,有可能会出现几种挑战:
Apache的软负载或LVS软负载等无法承担巨大的web访问量(请求连接数、网络流 量等)的调度了,这个时候如果经费允许的话,会采取的方案是购 买硬件负载,例如F5、Netsclar、Athelon之类的,如经费不允许的话,会采取的方案是将应用从逻辑上做一定的分类,然后分散到不同的软负载 集群中;
原有的一些状态信息同步、文件共享等方案可能会出现瓶颈,需要进行改进,也许这个时候会根据情况编写符合网站业务需求的分布式文件系统 等;
在做完这些工作后,开始进入一个看似完美的无限伸缩的时代,当网站流量增加时,应对的解决方案就是不断的添加webserver。看看这一步 完成后系统的图示:

from_0_to_architecture_7

这一步涉及到了这些知识体系:
到了这一步,随着机器数的不断增长、 数据量的不断增长和对系统可用性的要求越来越高,这个时候要求对所采用的技术都要有更为深入的理解,并需要根据网站的需求来做更加定制性质的产品。

架 构演变第九步:数据读写分离和廉价存储方案
突然有一天,发现这个完美的时代也要结束了,数据库的噩梦又一次出现在眼前了,由于 添加的webserver太多了,导致数据库连接的资源还是不够用,而这个时候又已经分库分表了,开始分析数据库的压力状况,可能会发现数据库的读写比很 高,这个时候通常会想到数据读写分离的方案,当然,这个方案要实现并不容易,另外,可能会发现一些数据存储在数据库上有些浪费,或者说过于占用数据库资 源,因此在这个阶段可能会形成的架构演变是实现数据读写分离,同时编写一些更为廉价的存储方案,例如BigTable这种。
看看这一步完成后系统 的图示:
from_0_to_architecture_8

这一步涉及到了这些知识体系:
数据读写分离要求对数据库的复制、 standby等策略有深入的掌握和理解,同时会要求具备自行实现的技术;
廉价存储方案要求对OS的文件存储有深入的掌握和理解,同时要求对采用 的语言在文件这块的实现有深入的掌握。

架构演变第十步:进入大型分布式应用时代和廉价服务器群梦想时代
经 过上面这个漫长而痛苦的过程,终于是再度迎来了完美的时代,不断的增加webserver就可以支撑越来越高的访问量了,对于大型网站而言,人气的重要毋 庸置疑,随着人气的越来越高,各种各样的功能需求也开始爆发性的增长,这个时候突然发现,原来部署在webserver上的那个web应用已经非常庞大 了,当多个团队都开始对其进行改动时,可真是相当的不方便,复用性也相当糟糕,基本是每个团队都做了或多或少重复的事情,而且部署和维护也是相当的麻烦, 因为庞大的应用包在N台机器上复制、启动都需要耗费不少的时间,出问题的时候也不是很好查,另外一个更糟糕的状况是很有可能会出现某个应用上的bug就导 致了全站都不可用,还有其他的像调优不好操作(因为机器上部署的应用什么都要做,根本就无法进行针对性的调优)等因素,根据这样的分析,开始痛下决心,将 系统根据职责进行拆分,于是一个大型的分布式应用就诞生了,通常,这个步骤需要耗费相当长的时间,因为会碰到很多的挑战:
1、拆成分布式后需要提 供一个高性能、稳定的通信框架,并且需要支持多种不同的通信和远程调用方式;
将一个庞大的应用拆分需要耗费很长的时间,需要进行业务的整理和系统 依赖关系的控制等;
3、如何运维(依赖管理、运行状况管理、错误追踪、调优、监控和报警等)好这个庞大的分布式应用。
经过这一步,差不多 系统的架构进入相对稳定的阶段,同时也能开始采用大量的廉价机器来支撑着巨大的访问量和数据量,结合这套架构以及这么多次演变过程吸取的经验来采用其他各 种各样的方法来支撑着越来越高的访问量。
看看这一步完成后系统的图示:
from_0_to_architecture_9
这一步涉及到了这些知识体系:
这一步涉及的知识体系非常的多,要求对通信、远程调 用、消息机制等有深入的理解和掌握,要求的都是从理论、硬件级、操作系统级以及所采用的语言的实现都有清楚的理解。
运维这块涉及的知识体系也非常 的多,多数情况下需要掌握分布式并行计算、报表、监控技术以及规则策略等等。
说起来确实不怎么费力,整个网站架构的经典演变过程都和上面比较的类 似,当然,每步采取的方案,演变的步骤有可能有不同,另外,由于网站的业务不同,会有不同的专业技术的需求,这篇blog更多的是从架构的角度来讲解演变 的过程,当然,其中还有很多的技术也未在此提及,像数据库集群、数据挖掘、搜索等,但在真实的演变过程中还会借助像提升硬件配置、网络环境、改造操作系 统、CDN镜像等来支撑更大的流量,因此在真实的发展过程中还会有很多的不同,另外一个大型网站要做到的远远不仅仅上面这些,还有像安全、运维、运营、服 务、存储等,要做好一个大型的网站真的很不容易,写这篇文章更多的是希望能够引出更多大型网站架构演变的介绍,:)。

我比较喜欢的一些PHP编码规范

在项目开发中,往往要遵循一到两个编码规范,大到PSR-0,PSR-1,PSR-2和PSR-3,小到一个常用项目的规范,我都收集了一下。

http://blog.csdn.net/colzer/article/details/9079325
http://dev.discuz.org/wiki/index.php?title=%E7%BC%96%E7%A0%81%E8%A7%84%E8%8C%83
https://github.com/yiisoft/yii2/wiki/Core-framework-code-style
http://docs.typecho.org/phpcoding

看到有几个非常有意思的要求:
There should be a newline at the end of file.
根据UNIX的C语言编码规范,必须留出最后一个空行,我在SOF上看到大神们的讨论,原因是说是大约在使用cat命令查看内容时方便,或者说在对比差异时方便,我自己不太理解。

我觉得最后留一行的话,其实是方便开发,可以一下定位到最后一行开头,立即就可以写代码咯。

YII要求Tab缩进,不要用空格,我深以为然,不然tab键用来搞毛啊,PSR的要求就太无理了,得变通一下,用在Python强制缩进那不得被空格拍累死。
Code MUST use tabs for indenting, not spaces.
当然,这点大家有颇多争议,习惯就好。最讨厌在vim里打开像狗啃以后对不齐的代码,愁。

PHP的坑之二:explode函数分割符为空

explode() 函数把字符串分割为数组,是平时用得最多的一个函数了。

本函数返回由字符串组成的数组,其中的每个元素都是由 separator 作为边界点分割出来的子字符串。
separator 参数不能是空字符串。如果 separator 为空字符串(""),explode() 将返回 FALSE。如果 separator 所包含的值在 string 中找不到,那么 explode() 将返回包含 string 中单个元素的数组。
如果设置了 limit 参数,则返回的数组包含最多 limit 个元素,而最后那个元素将包含 string 的剩余部分。
如果 limit 参数是负数,则返回除了最后的 -limit 个元素外的所有元素。此特性是 PHP 5.1.0 中新增的。

平时在切割的时候,都是自己指定分割符,可以保证程序在预料之中运行,如果是用户输入,则需要对分割符进行严格的校验,以期达到预想结果。

PHP的坑之二:array_combine的长度校验

array_combine我以前没有用过,有次看手册看到这个函数,今天测试了一下,发现一坑。

先来看看手册的说明:
array_combine() 函数通过合并两个数组来创建一个新数组,其中的一个数组是键名,另一个数组的值为键值。
如果其中一个数组为空,或者两个数组的元素个数不同,则该函数返回 false。

error_reporting(0);
$a1 = array(1=>1, 2=>3);
$a2 = array(3);
$b = @array_combine($a1, $a2);
var_dump($b);//bool(true)

如果屏蔽错误以后,最后拿到的数据是完全在意料之外的,这里一定要校验一下combine的两个数组长度

PHP的坑之一:array_merge与函数运算符加号的区别

面试中应该会经常遇到,自己平时只在一维数组中使用,今天特意重新查了+和array_merge区别

1. array_merge在参考手册中的说明如下:
array_merge()将两个或多个数组的单元合并起来,一个数组中的值附加在前一个数组的后面。返回作为结果的数组。
如果输入的数组中有相同的字符串键名,则该键名后面的值将覆盖前一个值。然而,如果数组包含数字键名,后面的值将不会覆盖原来的值,而是附加到后面。

2. 对于使用“+”合并数组:
如果数组中有相同的key,则会把最先出现的值作为最终结果返回,而把后面的数组拥有相同键名的那些值“抛弃”掉(注意:不是覆盖而是保留最先出现的那个值)。

<?php
$a1 = array(1 => "first", "2" => "second", 3 => "third");
$a2 = array(2 => "second value", 3 => "3rd");
print_r($a1 + $a2);
print_r(array_merge($a1, $a2));

猜猜结果是啥

Array
(
    [1] => first
    [2] => second
    [3] => third
)
Array
(
    [0] => first
    [1] => second
    [2] => third
    [3] => second value
    [4] => 3rd
)

Tips and Tricks: PHP Shell Scripts

发现在php Interactive shell下,无法使用标准输入和标准输出,只能放在php文件中通过cli模式执行,查了一下资料。

Overview
One of the many features of PHP that is over looked is utilizing PHP as a shell language (just like you can use perl). Many times I find that developers will create cron jobs that utilize wget or lynx to fetch the script and process it. This is not only a bad idea as the script resides in a publicly accessible location but it is more so a bad idea as it doesn’t utilize some of the great features things that PHP will automatically do for you in your configuration files. These things are automatically done by the PHP CLI, if not enabled you can configure PHP with the –enable-cli flag.

Features
There are many features that you can utilize by using this as a shell script as I am outlining below. Some of these are simply php.ini changes that will happen by default and others are simply ways to retrieve information. Essentially what you can do using this tool is unlimited. There is even an Ncurses extension with documentation.

Default Configuration Changes
html_errors is changed to false. So you do not see html error messages on the command line and instead get nicely printed regular error messages (this does not mean if you put in html for error messages that it will not show up)!

implicit_flush is changed to true. Since we are on the command line, chances are that you do not want to buffer the content. If you have a need to buffer the content, this is still possible utilizing output buffering.

max_execution_time is changed to an unlimited time frame. Since you are likely not using this for a web site the data doesn’t need to time out for quick serving as it is most likely some form of utility script that was written.

register_argc_argv is changed to true. Typically one of the frequently asked questions is how to get input when calling a script from the command line. Well argc and argv will give you the solution you need. $argc (integer) will hold the number of arguments that were passed in and $argv (array) will hold the values. The keys of $argv are numeric.

Creating an Executable Script
To create an executable script the permissions on the script must have +x for executable and also have the path of php in the interpreter line (1st line of the file). For example:
Permissions: chmod +x script_name.php.
Interpreter Line: #!/path/to/php.
Executing: ./script_name in the current directory.

STDIN/STDOUT/STDERR
In the PHP CLI, STDIN, STDOUT and STDERR are already initialized, therefore it is rather simple to fetch and write data mindlessly. Each of these are defined by constants that can be utilized with the stream functions (fwrite, fgets, fseek, fscanf, etc). Here are a few examples:

Retrieve and Output Information:

#!/path/to/php
< ?php
fwrite(STDOUT,  "Please enter in some input: ");
$line = trim(fgets(STDIN));
fwrite(STDOUT, $line . "\r\n");
?>

Retrieve Information, Check for Errors and Output:

#!/path/to/php
< ?php
fwrite(STDOUT,  "Please enter in an alphanumeric string: ");
$line = trim(fgets(STDIN));
if (!ctype_alpha($line)) {
    fwrite(STDERR, "The information you entered contained more information that alphanumeric characters.\r\n");
} else {
    fwrite(STDOUT, $line . "\r\n");
}
?>

Arguments using argc and argv
Sometimes, you might have the need to create a shell script that will allow you to pass in the values at runtime. This simply happens using $argv and $argc. The count is contained in $argc. Note that there will always be 1 for the name of the script executed. Each argument is delimited by a space unless escaped or enclosed in quotes. So here is a simple example showing the input in a very simplistic way:

Retrieve Count and Print Out Variables

#!/path/to/php
< ?php
if ($argc > 0) {
    fwrite(STDOUT, '# of arguments: ' . $argc . "\r\n");
    foreach ($argv as $k => $v) {
        fwrite(STDOUT, $k+1 . ': ' . $v . "\r\n");
    }
}
?>

Additional Resources
Nothing that was covered here was outside of the scope of the PHP manual. Look at the command line features in the PHP manual for items that I did not cover or for additional examples. In short, it is really easy to create PHP shell scripts that have many benefits and can be a great tool when used in the right context. Remember, use the best tool for the job. If you can do it, it doesn’t mean that you should.

form:http://blog.digitalstruct.com/2007/04/24/tips-and-tricks-php-shell-scripts/