ubuntu 安装 Apache

ubuntu 安装 Apache

1:安装apache

输入命令:sudo apt-get install apache2

安装完后,打开浏览器,在地址栏输入:localhost或者http://127.0.0.1

看到It works,表示安装成功!

默认根目录:/var/www/html

2:安装mysql:

sudo apt-get install mysql-server-5.0

安装完后,会要你新设置mysql root密码,输入你自己的密码后enter键,再确认密码。

3:安装PHP 5 and Apache PHP 5 module:

sudo apt-get install php5 libapache2-mod-php5

sudo /etc/init.d/apache2 restart(重启apache)
命令:gksudo gedit /var/www/text.php 【在apache默认根目录/var/www/html 下创建一text.php文件】

在text.php文件中输入以下代码检验是否安装成功:

<?php
echo “helloworld”;
?>

在浏览器地址栏输入:http://localhost/text.php 如果页面显示helloworld则表示成功
4:让apache和php支持php :

sudo apt-get install libapache2-mod-auth-mysql
sudo apt-get install php5-mysql
sudo /etc/init.d/apache2 restart(重启apache)

至此环境已搭建完!

/var/www/htnl (apache2网页文件放在这里)

/var/lib/mysql(mysql文件放在这里)

phpexcel导出数据0000

phpexcel导出数据0000

最近做一个php项目的时候,遇到一个问题。

功能:使用phpexcel导出数据。

问题描述:导出身份证后四位是0000

这是因为在excel中如果在一个默认的格中输入或复制超长数字字符串,它会显示为科学计算法。

方法一:设置单元格为文本

$objPHPExcel = new PHPExcel();
$objPHPExcel->setActiveSheetIndex(0);
$objPHPExcel->getActiveSheet()->setTitle('Simple');
//设置A3单元格为文本
$objPHPExcel->getActiveSheet()->getStyle('A3')->getNumberFormat()
->setFormatCode(PHPExcel_Style_NumberFormat::FORMAT_TEXT);
//也可以设置整行或整列的style
/*
//E 列为文本
$objPHPExcel->getActiveSheet()->getStyle('E')->getNumberFormat()
->setFormatCode(PHPExcel_Style_NumberFormat::FORMAT_TEXT);
//第三行为文本
$objPHPExcel->getActiveSheet()->getStyle('3')->getNumberFormat()
->setFormatCode(PHPExcel_Style_NumberFormat::FORMAT_TEXT);
*/

更多的格式可以在PHPExcel/Style/NumberFormat.php中找到。

注意:上述的设置对长数字字符串还是以文本方式来显示科学计数法的结果。

方法二:在设置值的时候显示的指定数据类型

$objPHPExcel = new PHPExcel();
$objPHPExcel->setActiveSheetIndex(0);
$objPHPExcel->getActiveSheet()->setTitle('Simple');
$objPHPExcel->getActiveSheet()->setCellValueExplicit('D1',123456789033, PHPExcel_Cell_DataType::TYPE_STRING);

方法三:在数字字符串前加一个空格使之成为字符串

$objPHPExcel = new PHPExcel();
$objPHPExcel->setActiveSheetIndex(0);
$objPHPExcel->getActiveSheet()->setTitle('Simple');
$objPHPExcel->getActiveSheet()->setCellValue('D1', ' ' . 123456789033);

推荐使用第二、三种,第一种没有根本解决问题。

attr获取checkbox属性出错

attr获取checkbox属性出错

前几天,有人给 Multiple Select 插件 提了问题:

setSelects doesn't work in Firefox when using jquery 1.9.0

一直都在用 jQuery 1.8.3 的版本,没有尝试过 jQuery 1.9.0 的版本。

于是,开始调试代码,在 1.9.0 的版本中:

<input type="checkbox" />
<script>
$(function() {
$('input').click(function() {
$(this).attr('checked');
});
});
</script>

点击 checkbox,结果都是 undefined

而在 1.8.3 的版本中,结果是 checked 和 undefined

到这里,问题答案找到了,就是使用 attr() 方法的问题,于是查看官方文档, 才知道从 jQuery 1.6 开始新增了一个方法 prop(),但是一直都没有使用过。

从中文意思看,两者分别是获取/设置 attributes 和 properties 的方法,那么为什么还要增加 prop() 方法呢?

因为在 jQuery 1.6 之前,使用 attr() 有时候会出现不一致的行为。

那么,什么时候使用attr(),什么时候使用prop()?

根据官方的建议:具有 true 和 false 两个属性的属性,如 checked, selected 或者 disabled 使用prop(),其他的使用 attr()

到此,将 attr(‘checked’) 改成 prop(‘checked’) 即可修复提的 issues 了。

等等,貌似问题还没真正解决,为什么开头例子中 jQuery 1.8.3 和 1.9.0 使用 attr() 会有所区别呢?

想知道他们的区别,最好的办法还是看他们的源代码:

1.8.3 attr():

attr: function( elem, name, value, pass ) {
var ret, hooks, notxml,
nType = elem.nodeType;
// don't get/set attributes on text, comment and attribute nodes
if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
return;
}
if ( pass && jQuery.isFunction( jQuery.fn[ name ] ) ) {
return jQuery( elem )[ name ]( value );
}
// Fallback to prop when attributes are not supported
if ( typeof elem.getAttribute === "undefined" ) {
return jQuery.prop( elem, name, value );
}
notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
// All attributes are lowercase
// Grab necessary hook if one is defined
if ( notxml ) {
name = name.toLowerCase();
hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook );
}
if ( value !== undefined ) {
if ( value === null ) {
jQuery.removeAttr( elem, name );
return;
} else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) {
return ret;
} else {
elem.setAttribute( name, value + "" );
return value;
}
} else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) {
return ret;
} else {
ret = elem.getAttribute( name );
// Non-existent attributes return null, we normalize to undefined
return ret === null ?
undefined :
ret;
}
}

1.9.0 attr():

attr: function( elem, name, value ) {
var ret, hooks, notxml,
nType = elem.nodeType;
// don't get/set attributes on text, comment and attribute nodes
if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
return;
}
// Fallback to prop when attributes are not supported
if ( typeof elem.getAttribute === "undefined" ) {
return jQuery.prop( elem, name, value );
}
notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
// All attributes are lowercase
// Grab necessary hook if one is defined
if ( notxml ) {
name = name.toLowerCase();
hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook );
}
if ( value !== undefined ) {
if ( value === null ) {
jQuery.removeAttr( elem, name );
} else if ( hooks && notxml && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
return ret;
} else {
elem.setAttribute( name, value + "" );
return value;
}
} else if ( hooks && notxml && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
return ret;
} else {
// In IE9+, Flash objects don't have .getAttribute (#12945)
// Support: IE9+
if ( typeof elem.getAttribute !== "undefined" ) {
ret = elem.getAttribute( name );
}
// Non-existent attributes return null, we normalize to undefined
return ret == null ?
undefined :
ret;
}
}

1.8.3 和 1.9.0 的 prop() 是一样的:

prop: function( elem, name, value ) {
var ret, hooks, notxml,
nType = elem.nodeType;
// don't get/set properties on text, comment and attribute nodes
if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
return;
}
notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
if ( notxml ) {
// Fix name and attach hooks
name = jQuery.propFix[ name ] || name;
hooks = jQuery.propHooks[ name ];
}
if ( value !== undefined ) {
if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
return ret;
} else {
return ( elem[ name ] = value );
}
} else {
if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
return ret;
} else {
return elem[ name ];
}
}
}

首先,我们看下 attr() 和 prop() 的区别:

attr() 里面,最关键的两行代码

elem.setAttribute( name, value + "" );
ret = elem.getAttribute( name );

很明显的看出来,使用的 DOM 的 API setAttribute() 和 getAttribute() 方法操作的属性元素节点。

prop() 里面,最关键的两行代码

return ( elem[ name ] = value );
return elem[ name ];

可以理解为 document.getElementById(el)[name] = value,这是转化成 element 的一个属性。

对比调试 1.9.0 和 1.8.3 的 attr() 方法,发现两者的区别在于

hooks.get( elem, name ))

返回的值不一样,具体的实现:

1.8.3 中

boolHook = {
get: function( elem, name ) {
// Align boolean attributes with corresponding properties
// Fall back to attribute presence where some booleans are not supported
var attrNode,
property = jQuery.prop( elem, name );
return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ?
name.toLowerCase() :
undefined;
}
}

1.9.0 中

boolHook = {
get: function( elem, name ) {
var
// Use .prop to determine if this attribute is understood as boolean
prop = jQuery.prop( elem, name ),
// Fetch it accordingly
attr = typeof prop === "boolean" && elem.getAttribute( name ),
detail = typeof prop === "boolean" ?
getSetInput && getSetAttribute ?
attr != null :
// oldIE fabricates an empty string for missing boolean attributes
// and conflates checked/selected into attroperties
ruseDefault.test( name ) ?
elem[ jQuery.camelCase( "default-" + name ) ] :
!!attr :
// fetch an attribute node for properties not recognized as boolean
elem.getAttributeNode( name );
return detail && detail.value !== false ?
name.toLowerCase() :
undefined;
}
}

由此可见,1.9.0 开始不建议使用 attr() 来对具有 true 和 false 两个属性的属性进行操作了。

那么我们的结论是:

具有 true 和 false 两个属性的属性,如 checked, selected 或者 disabled 使用prop(),其他的使用 attr()。

nusoap:Attempt to modify property of non-object

nusoap报错:Attempt to modify property of non-object

最近做一个PHP项目,需要通过webservice调用第三方接口。

ok。很好实现,通过PHP的soap很容易就能实现。

扔到生产库上运行,500错误。

排查之后发现,生产环境php.ini中的soap没有打开。

开启扩展很容易,但是生产环境上跑着很多个项目,所以这种解决方案pass。

转用nusoap类库。

开开心心改完了程序,运行报错:

A PHP Error was encountered
Severity: Warning
Message: Attempt to modify property of non-object
Filename: nusoap/nusoap.php
Line Number: 4694

解决方案如下:

原代码:

$this->schemas[$ns]->imports[$ns2][$ii]['loaded'] = true;

改成:

$this->schemas[$ns][$ns2]->imports[$ns2][$ii]['loaded'] = true;

如果运行期间还有错误,用@抑制一下:

if (! @$list2[$ii]['loaded']) {
$this->schemas[$ns][$ns2]->imports[$ns2][$ii]['loaded'] = true;
$url = @$list2[$ii]['location']

PHP正则匹配中文

PHP正则匹配中文

javascript中,要判断字符串是中文是很简单的。

比如:

var str = "php编程";
if (/^[\u4e00-\u9fa5]+$/.test(str)) {
alert("该字符串全部是中文");
} else {
alert("该字符串不全部是中文");
}

想当然的,在php中来判断字符串是否为中文,就会沿袭这个思路:

<?php
$str = "php编程";
if (preg_match("/^[\u4e00-\u9fa5]+$/",$str)) {
print("该字符串全部是中文");
}else {
print("该字符串不全部是中文");
} ?>

不过,很快就会发现,php并不支持这样的表达,报错:

Warning: preg_match() [function.preg-match]: Compilation failed: PCRE does not support \L, \l, \N, \U, or \u at offset 3 in test.php on line 3

刚开始从google上查了很多次,想从php正则表达式对于十六进制数据的

表达方式上进行突破,发现在php中,是用\x表示十六进制数据的。于是,

变换成如下的代码:

$str = "php编程";
if (preg_match("/^[\x4e00-\x9fa5]+$/",$str)) {
print("该字符串全部是中文");
} else {
print("该字符串不全部是中文");
}

貌似不报错了,判断的结果也正确,不过把$str换成“编程”两字,结果却

还是显示“该字符串不全部是中文”,看来这样的判断还是不够准确。
模式修正符: u

可能还跟编码有关系,

因此需要了解一下模式修正符的相关知识——于是继续搜索百度。

在一篇《模式修正符》的文章中了解到:

u (PCRE_UTF8)

此修正符启用了一个 PCRE 中与 Perl 不兼容的额外功能。模式字符串被当成 UTF-8。

本修正符在 Unix 下自 PHP 4.1.0 起可用,在 win32 下自 PHP 4.2.3 起可用。

例子:

preg_match(‘/[\x{2460}-\x{2468}]/u’, $str); 匹配 内码汉字

按照他提供的方式进行测试,代码如下:

$str = "php编程";
if (preg_match("/^[\x{2460}-\x{2468}]+$/u",$str)) {
print("该字符串全部是中文");
} else {
print("该字符串不全部是中文");
}

发现这次依然对是否为中文判断失常。不过,既然\x表示的十六进制数据,

为什么和js里边提供的范围\x4e00-\x9fa5不一样呢?

于是我就换成了下边的代码:

$str = "php编程";
if (preg_match("/^[\x4e00-\x9fa5]+$/u",$str)) {
print("该字符串全部是中文");
} else {
print("该字符串不全部是中文");
}

本来以为铁定成功了的事情,没想到,warning又一次产生了:

Warning: preg_match() [function.preg-match]: Compilation failed: invalid UTF-8 string at offset 6 in test.php on line 3

看来又有错误的表达方式了,于是对照了一下那篇文章的表达方式,

给”4e00″和”9fa5″两边分别用”{“和”}”包起来,跑了一遍,发现真的准确了:

$str = "php编程";
if (preg_match("/^[\x{4e00}-\x{9fa5}]+$/u",$str)) {
print("该字符串全部是中文");
} else {
print("该字符串不全部是中文");
}

知道了php中utf-8编码下用正则表达式匹配汉字的最终正确表达式——/^[\x{4e00}-\x{9fa5}]+$/u,

于是我又用这个表达式去百度搜索,发现竟然还真有别人得出过这样正确的结论,只不过通过常规的方式很难找到而已,而且仅仅搜到有一篇——《用正则删除汉字》,看来互联网上对于

信息的正确性的筛选还是亟待加强的。

<?php
$action = trim($_GET['action']);
if($action == "sub")
{
$str = $_POST['dir'];
//if(!preg_match("/^[".chr(0xa1)."-".chr(0xff)."A-Za-z0-9_]+$/",$str)) //GB2312汉字字母数字下划线正则表达式
if(!preg_match("/^[\x{4e00}-\x{9fa5}A-Za-z0-9_]+$/u",$str)) //UTF-8汉字字母数字下划线正则表达式
{
echo"<span style="color: red;">您输入的[".$str."]含有违法字符</span>";
}
else
{
echo "<span style="color: green;">您输入的[".$str."]完全合法,通过!</span>";
}
}
?>

多进程和多线程

多进程和多线程

我们介绍了多进程和多线程,这是实现多任务最常用的两种方式。现在,我们来讨论一下这两种方式的优缺点。

首先,要实现多任务,通常我们会设计Master-Worker模式,Master负责分配任务,Worker负责执行任务,因此,多任务环境下,通常是一个Master,多个Worker。

如果用多进程实现Master-Worker,主进程就是Master,其他进程就是Worker。

如果用多线程实现Master-Worker,主线程就是Master,其他线程就是Worker。

多进程模式最大的优点就是稳定性高,因为一个子进程崩溃了,不会影响主进程和其他子进程。(当然主进程挂了所有进程就全挂了,但是Master进程只负责分配任务,挂掉的概率低)著名的Apache最早就是采用多进程模式。

多进程模式的缺点是创建进程的代价大,在Unix/Linux系统下,用fork调用还行,在Windows下创建进程开销巨大。另外,操作系统能同时运行的进程数也是有限的,在内存和CPU的限制下,如果有几千个进程同时运行,操作系统连调度都会成问题。

多线程模式通常比多进程快一点,但是也快不到哪去,而且,多线程模式致命的缺点就是任何一个线程挂掉都可能直接造成整个进程崩溃,因为所有线程共享进程的内存。在Windows上,如果一个线程执行的代码出了问题,你经常可以看到这样的提示:“该程序执行了非法操作,即将关闭”,其实往往是某个线程出了问题,但是操作系统会强制结束整个进程。

在Windows下,多线程的效率比多进程要高,所以微软的IIS服务器默认采用多线程模式。由于多线程存在稳定性的问题,IIS的稳定性就不如Apache。为了缓解这个问题,IIS和Apache现在又有多进程+多线程的混合模式,真是把问题越搞越复杂。

线程切换

无论是多进程还是多线程,只要数量一多,效率肯定上不去,为什么呢?

我们打个比方,假设你不幸正在准备中考,每天晚上需要做语文、数学、英语、物理、化学这5科的作业,每项作业耗时1小时。

如果你先花1小时做语文作业,做完了,再花1小时做数学作业,这样,依次全部做完,一共花5小时,这种方式称为单任务模型,或者批处理任务模型。

假设你打算切换到多任务模型,可以先做1分钟语文,再切换到数学作业,做1分钟,再切换到英语,以此类推,只要切换速度足够快,这种方式就和单核CPU执行多任务是一样的了,以幼儿园小朋友的眼光来看,你就正在同时写5科作业。

但是,切换作业是有代价的,比如从语文切到数学,要先收拾桌子上的语文书本、钢笔(这叫保存现场),然后,打开数学课本、找出圆规直尺(这叫准备新环境),才能开始做数学作业。操作系统在切换进程或者线程时也是一样的,它需要先保存当前执行的现场环境(CPU寄存器状态、内存页等),然后,把新任务的执行环境准备好(恢复上次的寄存器状态,切换内存页等),才能开始执行。这个切换过程虽然很快,但是也需要耗费时间。如果有几千个任务同时进行,操作系统可能就主要忙着切换任务,根本没有多少时间去执行任务了,这种情况最常见的就是硬盘狂响,点窗口无反应,系统处于假死状态。

所以,多任务一旦多到一个限度,就会消耗掉系统所有的资源,结果效率急剧下降,所有任务都做不好。

计算密集型 vs. IO密集型

是否采用多任务的第二个考虑是任务的类型。我们可以把任务分为计算密集型和IO密集型。

计算密集型任务的特点是要进行大量的计算,消耗CPU资源,比如计算圆周率、对视频进行高清解码等等,全靠CPU的运算能力。这种计算密集型任务虽然也可以用多任务完成,但是任务越多,花在任务切换的时间就越多,CPU执行任务的效率就越低,所以,要最高效地利用CPU,计算密集型任务同时进行的数量应当等于CPU的核心数。

计算密集型任务由于主要消耗CPU资源,因此,代码运行效率至关重要。Python这样的脚本语言运行效率很低,完全不适合计算密集型任务。对于计算密集型任务,最好用C语言编写。

第二种任务的类型是IO密集型,涉及到网络、磁盘IO的任务都是IO密集型任务,这类任务的特点是CPU消耗很少,任务的大部分时间都在等待IO操作完成(因为IO的速度远远低于CPU和内存的速度)。对于IO密集型任务,任务越多,CPU效率越高,但也有一个限度。常见的大部分任务都是IO密集型任务,比如Web应用。

IO密集型任务执行期间,99%的时间都花在IO上,花在CPU上的时间很少,因此,用运行速度极快的C语言替换用Python这样运行速度极低的脚本语言,完全无法提升运行效率。对于IO密集型任务,最合适的语言就是开发效率最高(代码量最少)的语言,脚本语言是首选,C语言最差。

异步IO

考虑到CPU和IO之间巨大的速度差异,一个任务在执行的过程中大部分时间都在等待IO操作,单进程单线程模型会导致别的任务无法并行执行,因此,我们才需要多进程模型或者多线程模型来支持多任务并发执行。

现代操作系统对IO操作已经做了巨大的改进,最大的特点就是支持异步IO。如果充分利用操作系统提供的异步IO支持,就可以用单进程单线程模型来执行多任务,这种全新的模型称为事件驱动模型,Nginx就是支持异步IO的Web服务器,它在单核CPU上采用单进程模型就可以高效地支持多任务。在多核CPU上,可以运行多个进程(数量与CPU核心数相同),充分利用多核CPU。由于系统总的进程数量十分有限,因此操作系统调度非常高效。用异步IO编程模型来实现多任务是一个主要的趋势。

对应到Python语言,单进程的异步编程模型称为协程,有了协程的支持,就可以基于事件驱动编写高效的多任务程序。我们会在后面讨论如何编写协程。

PHP 使用buffer

PHP 使用buffer

php与mysql的连接有三种方式,mysql,mysqli,pdo。

不管使用哪种方式进行连接,都有使用buffer和不使用buffer的区别。

什么叫使用buffer和不使用buffer呢?

客户端与mysql服务端进行查询操作,查询操作的时候如果获取的数据量比较大,那个这个查询结果放在哪里呢?

有两个地方可以放:客户端的缓冲区和服务端的缓冲区。

我们这里说的buffer指的是客户端的缓冲区,如果查询结果已经从服务端获取回来了,放置在了客户端的缓冲区,我们就称之为使用buffer。

如果还是存放在服务端的缓冲区的话,我们就说没有使用buffer(unbuffer)。

使用buffer和不使用buffer有什么区别?

主要在内存方面,使用buffer会增加客户端的内存压力,当返回的数据结果特别大的时候可能会占用调用客户端(实际就是一个php进程)比较大的进程。不使用buffer自然对服务端(这里说的是提供mysql服务的服务器)压力更大。

php中三种模式是如何设置是否使用buffer的?

  • mysql默认的query是使用buffer的,而不使用buffer就需要使用mysql_unbuffer_query
  • mysqli默认的query是不使用buffer的,要使用buffer就需要设置mysqli_store_result
  • pdo默认的quey是不使用buffer的,要使用buffer就需要设置mysql_attr_use_buffered_query

大致相关代码如下:

<?php
$dbconfig = array(
'host' => '',
'port' => '3306',
'user' => '',
'pass' => '',
'db' => '',
);
$sql = 'select * from so_topic_app';
//---------mysql//
$db = mysql_connect($dbconfig['host'], $dbconfig['user'], $dbconfig['pass']);
mysql_select_db($dbconfig['db'], $db);
mysql_set_charset('utf8', $db);
// mysql使用buffer
$res = mysql_query($sql, $db);
$data = array;
while($row = mysql_fetch_row($res)) {
$data = $row;
}
// mysql不使用buffer
$res = mysql_unbuffered_query($sql, $db);
$data = array;
$data = $row;
}
mysql_close($db);
//---------mysqli//
$db = mysqli_connect($dbconfig['host'], $dbconfig['user'], $dbconfig['pass'], $dbconfig['db']);
// mysqli不使用buffer
$result = mysqli_query($db, $sql);
$data = array;
while($row = $result->fetch_array) {
$data = $row;
}
// mysqli使用buffer
$result = mysqli_query($db, $sql, mysqli_store_result);
$data = array;
$data = $row;
}
mysqli_free_result($result);
mysqli_close($db);
//---------pdo//
$dsn = mysql:dbname={$dbconfig['db']};host={$dbconfig['host']};
$pdo = new pdo($dsn, $dbconfig['user'], $dbconfig['pass']);
// pdo不使用buffer
$stmt = $pdo->prepare($sql);
$stmt->execute;
$data = array;
$data = $stmt->fetchall;
// pdo使用buffer
$pdo->setattribute(pdo::mysql_attr_use_buffered_query, true);
$stmt->execute;
$data = array;
?>

当然如果数据量非常大的话,大部分人还是会考虑使用分批次来提取和处理数据。所以实际上需要我们关注和使用mysql是使用buffer还是不使用buffer的场景非常少。

分布式系统中异步处理的优化

分布式系统中异步处理的优化

单机时代的数据请求

十五年前写软件是很简单的,一个Client对应一个DB Server,或者多个Client对应一个DB Server,每一个Client执行各自的服务。当时的讨论很多是说,这个东西要写在Client端还是写在DB Server端,流行的思路有两种:

把DB Server写得很复杂,比如Oracle数据库,而Client端则写得很简单,只有调用返回
DB很简单,只有简单的表,而Client写得复杂。很多创业公司会这样做,因为他们对SQL不是很熟悉,但是很熟悉PHP。早期Facebook就是典型的代表

大数据时代的数据请求

单机时代随着两个趋势而逐渐成为历史。一个趋势是随着互联网的流行,越来越多的人开始上网使用Web服务,而且很多时候用户增长速度是非常快的,结果造成一台DB Server无法储存下所有用户的数据。第二个趋势是计算机能力越来越强,网络服务针对每一个用户要做的事情也变多了,比如Facebook不仅要保存一个用户的个人信息,还有他的关系链信息,他的使用习惯、点击习惯等,就造成一个用户的数据量也大大增加,仅仅访问一个DB Server就准备好一个页面变成了不可能的事情。

这就带来了一个问题:针对多个DB Server的程序应该怎么写?

针对这个问题也有两个思路:

串行同步。先query DB1,返回res1,再使用res1做另一个DB的query,返回res2。这是在第二个Query依赖第一个Query结果的情况
并行同步。针对DB1的query跟针对DB2的query同步进行。这是两个Query之间没有依赖关系的情况。Facebook早期专门写了一个并行处理的函数,用法是 ExecParallelQuery(conn1,Query1,conn2,Query2)
这个时候的代码就比以前的代码更加复杂了,不过还是能实现需要实现的需求。但这时候带来了一个新的问题,就是等待。一个页面的加载可能需要调用不同的函数,而不同的函数可能是由不同的团队写的。比如获取朋友关系的函数getFriends把自己需要的数据用同步的方式获取了,但如果一个第三方开发者过来,则不仅要调用这个函数,还需要调用其他函数,这样其他函数的执行就需要等待前面这个getFriends函数返回了结果之后才能开始执行,就很慢了。

要如何做到并行处理在代码层面很直观,在机器上的执行效率又好呢?

异步的处理思路就是这么来的。

所谓异步就是,我这个函数知道这里需要访问哪几个DB Server,但我先不着急去访问,而是先记录一下,等等看其他函数是不是也要访问这个DB,如果有的话,待会儿再一起去访问。异步处理的指令比如说是 conn.asyncExec(Query) ,这个可以立刻返回一个Future对象,意思就是“待会儿再去执行”。如果每个函数都返回这种Future对象,那么就可以根据这些Future对象来判断哪些请求没有依赖可以并行处理,哪些请求有依赖需要串行处理了。如此,不同的团队写出来的函数就不用一个等一个,而是可以在更高层面上互相合作。

然而这又带来了一个问题,那就是异步处理的写法是具有传染性的。如果一个服务中有的函数写的异步,有的函数没写异步,就会造成有的函数返回了Future Object,有的函数返回了数值,导致无法执行。要实现异步,需要关联的所有函数都用异步的写法返回Future Object才可以。

所以Facebook在转向异步处理的过程是非常痛苦的,一开始做了局部修改,再修改调用了局部修改过的函数的函数,所有调用的调用都要修改,最后全部改成了异步,只要有调用远程服务IO的操作都要改。每一个DB Query都拆分成两步,一个set request,一个receive response。这里的工作量很大,所以如果创业团队的话,最好是第一天就用正确的写法,就不会这么痛苦。

所有函数改写后,每一个函数执行都会返回Future Object。那么异步处理的第一步,就是将这些Future Object形成一棵依赖树的结构,好像这样:

分布式系统中异步处理的优化
分布式系统中异步处理的优化

这里每个节点都是一个Future对象,每一个Future对象有两种状态,一个是等待执行,一个是完成执行。同级的节点是没有依赖关系的,可以并行执行;上下节点是有依赖关系的,需要串行执行,先执行下层再执行上层。

树结构形成后,从下到上执行,直到最上面的top parent节点被执行进入完成执行的状态,就是完成,比如一个页面加载完毕。

所以异步处理之后有一个很有意思的情况,那就是PHP这个语言已经跟以前不同了,不再是一上来就是执行,而是一上来先lazy一下,看清楚所有的Query之后再执行。

异步处理还需要解决的问题

到目前为止,这样做异步处理似乎已经是足够好的优化,但实际上还有问题。看看下面这个例子。

比如我们现在有两个查询需求。一个是查询你在淘宝上买过东西的朋友,另一个是查询你在淘宝上买过保时捷的朋友。常理来说,我们会先想到查询你在淘宝上的朋友,再进行另一个条件的查询,比如这样:

IdList friends = waitFor(getFriends(myId));
yield return getTaoBaoBuyers(friends);

但是对于保时捷这个查询而言,这是不对的,因为淘宝上买保时捷的人是很少的,可能就一两个,而淘宝上的好友数可能有上百。因此保时捷的查询应该是这个次序比较优化:

IdList buyers = waitFor(getPorscheBuyer());
yield return getFriends(buyers);

这个次序应该如何决定?实际上不应该在写程序的时候决定,因为写程序的时候是无法避免有先后顺序的——编辑器只能一行一行的写代码,但是机器执行却无需管这个。所以更好的方法应该是在执行代码之前再加入一个phase。

其实传统数据库的cardinality(基数)功能已经解决了这个问题。你在DB query里面使用 INNER JOIN 这个指令,其实DB已经能够预判哪一个表给出的row会比较少,从而以更优化的次序去执行。但现在我们用的编程语言,无论是PHP,Java,Python还是C/C++,并没有考虑这个问题。有人会开很多线程来解决这个问题,但这不是最佳方案,因为在Linux系统里,你的线程数要是上了200-300,就会有很大的overhead。

代码执行的次序,这是一个。另外最近几年还有一个流行的优化思路,就是上memcache。我们有时候会看到程序员把他自己的函数放进了memcache,相当于是依赖树的中间的一个节点,我就问他为什么要把他这个Class放入memcache,他可能会说,他觉得这个节点和这个节点的child被调用的次数多。我觉得这可能不是特别理想的。你今天觉得这个Class被调用的多,可以放进memcache,但明天是不是会有更重要的Class会更值得放进memcache,于是你又要把memcache的资源让给这个新的Class?如果你放入memcache的Class并不是最重要的,这就相当于真正优化的可能性被拿走了。

如何让异步执行的更好?

哪个query先执行,哪个query后执行,不应该是在编码阶段来做的。哪个Class该进memcache,哪个Class该出memcache,也不应该在编码阶段来做。应该有一个中间的阶段,专门进行这种调度工作,然而到目前为止,还没有公司能够做到,因为没有合适的语言。

异步处理在分布式系统中怎样做有更好的优化作用,我们需要更多的思考。希望大家能够把计算机当作科学去思考,而不仅仅是工程应用。我们现在看十几年前,对单机是非常了解了,那么未来过了五年十年再回来看,可能对分布式系统也会了解的比现在更多很多,可能给分布式系统写程序也会变得跟给单机写程序一样简单。当然这就需要更合适的工具语言去给大家提供这种异步的便利。是不是会有Haskell那样lazy的方式从系统层面解决这个问题?希望跟大家一起思考探讨。

php标准注释

php标准注释

文件头部模板

/**
*这是一个什么文件
*
*此文件程序用来做什么的(详细说明,可选。)。
* @author
* @version
* @since
*/

函数头部注释

/**
* some_func
* 函数的含义说明
*
* @access public
* @param mixed $arg1 参数一的说明
* @param mixed $arg2 参数二的说明
* @since 1.0
* @return array
*/

类的注释

/**
* 类的介绍
*
* 类的详细介绍(可选。)。
* @author richard<e421083458@163.com>
* @since 1.0
*/
class Test
{
}

程序代码注释
1. 注释的原则是将问题解释清楚,并不是越多越好。
2. 若干语句作为一个逻辑代码块,这个块的注释可以使用/* */方式。
3. 具体到某一个语句的注释,可以使用行尾注释://。

/* 生成配置文件、数据文件。*/
$this->setConfig();
$this->createConfigFile(); //创建配置文件
$this->clearCache(); // 清除缓存文件
$this->createDataFiles(); // 生成数据文件
$this->prepareProxys();
$this->restart();