Javascript在页面加载时的执行顺序

原文:http://dancewithnet.com/2007/03/22/order-of-execution-of-javascript-on-web/

一、在HTML中嵌入Javasript的方法

  1. 直接在Javascript代码放在标记对<script>和</script>之间
  2. 由<script />标记的src属性制定外部的js文件
  3. 放在事件处理程序中,比如:<p onclick="alert('我是由onclick事件执行的Javascript')">点击我</p>
  4. 作为URL的主体,这个URL使用特殊的Javascript:协议,比如:<a href="javascript:alert('我是由javascript:协议执行的javascript')">点击我</a>
  5. 利用javascript本身的document.write()方法写入新的javascript代码
  6. 利用Ajax异步获取javascript代码,然后执行

 

第3种和第4种方法写入的Javascript需要触发才能执行,所以除非特别设置,否则页面加载时不会执行。

二、Javascript在页面的执行顺序

  1. 页面上的Javascript代码是HTML文档的一部分,所以Javascript在页面装载时执行的顺序就是其引入标记<script />的出现顺序, <script />标记里面的或者通过src引入的外部JS,都是按照其语句出现的顺序执行,而且执行过程是文档装载的一部分。
  2. 每个脚本定义的全局变量和函数,都可以被后面执行的脚本所调用。
  3. 变量的调用,必须是前面已经声明,否则获取的变量值是undefined。
    <script type="text/javscrpt">//<![CDATA[
    alert(tmp);  //输出 undefined
    var tmp = 1;
    alert(tmp);  //输出 1
    //]]></script>
  4. 同一段脚本,函数定义可以出现在函数调用的后面,但是如果是分别在两段代码,且函数调用在第一段代码中,则会报函数未定义错误。
    <script type="text/javscrpt">//<![CDATA[
    aa();            //浏览器报错
    //]]></script>
    <script type="text/javscrpt">//<![CDATA[
    aa();			//输出 1 
    function aa(){alert(1);}
    //]]></script>
  5. document.write()会把输出写入到脚本文档所在的位置,浏览器解析完documemt.write()所在文档内容后,继续解析document.write()输出的内容,然后在继续解析HTML文档。
    <script type="text/javascript">//<![CDATA[
        document.write('<script type="text/javascript" src="test.js"><\/script>');
        document.write('<script type="text/javascript">');
        document.write('alert(2);')
        document.write('alert("我是" + tmpStr);');
        document.write('<\/script>');
        //]]></script>
      <script type="text/javascript">//<![CDATA[
        alert(3);
        //]]></script>

    test.js的内容是:

    var tmpStr = 1;
        alert(tmpStr);
    • 在Firefox和Opera中的弹出值的顺序是:1、2、我是1、3
    • 在IE中弹出值的顺序是:2、1、3,同时浏览器报错:tmpStr未定义

    原因可能是IE在document.write时,并未等待加载SRC中的Javascript代码完毕后,才执行下一行,所以导致2先弹出,并且执行到document.write(‘document.write(“我是” + tmpStr)’)调用tmpStr时,tmpStr并未定义,从而报错。

    解决这个问题,可以利用HTML解析是解析完一个HTML标签,再执行下一个的原理,把代码拆分来实现:

    <script type="text/javascript">//<![CDATA[
        document.write('<script type="text/javascript" src="test.js"><\/script>');
        //]]></script>
      <script type="text/javascript">//<![CDATA[
        document.write('<script type="text/javascript">');
        document.write('alert(2);')
        document.write('alert("我是" + tmpStr);');
        document.write('<\/script>');
        //]]></script>
      <script type="text/javascript">//<![CDATA[
        alert(3);
        //]]></script>

    这样IE下和其他浏览器输出值的顺序都是一直的了:1、2、我是1、3。

三、如何改变Javascript在页面的执行顺序

  1. 利用onload
    <script type="text/javascript">//<![CDATA[
    window.onload = f;
    function f(){alert(1);}
    alert(2);
    //]]></script>

    输出值顺序是 2、1。

    需要注意的是,如果存在多个winodws.onload的话,只有最有一个生效,解决这个办法是:

    window.onload = function(){f();f1();f2();.....}

    利用2级DOM事件类型

    if(document.addEventListener){
    window.addEventListener('load',f,false);
    window.addEventListener('load',f1,false);
    ...
    }else{
    window.attachEvent('onload',f);
    window.attachEvent('onload',f1);
    ...
    }
  2. IE中可以利用defer,defer作用是把代码加载下来,并不立即执行,等文档装载完毕之后再执行,有点类似window.onload,但是没有window.onload那样的局限性,可以重复使用,但是只在IE中有效,所以上面的例子可以修改成为
    <script type="text/javascript">//<![CDATA[
    document.write('<script type="text/javascript" src="test.js"><\/script>');
    document.write('<script type="text/javascript" defer="defer">');
    document.write('alert(2);')
    document.write('alert("我是" + tmpStr);');
    document.write('<\/script>');
    //]]></script>
    <script type="text/javascript">//<![CDATA[
    alert(3);
    //]]></script>

    这样IE就不报错了,输出值的顺序变成:1、3、2、我是1

    当HTML解析器遇到一个脚本,它必须按常规终止对文档的解析并等待脚本执行。为了解决这个问题HTML4标准定义了defer。通过defer来提示浏览器可以继续解析HTML文档,并延迟执行脚本。这种延迟在脚本从外部文件载入时非常有用,让浏览器不必等待外部文件全部载入之后才继续执行,能有效的提高性能。IE是目前唯一支持defer属性的浏览器,但IE并没有正确的实现了defer属性,因为延迟的脚本总是被延迟,直到文档结束,而不是只延迟到下一个非延迟的脚本。这意味着,IE中延迟的脚本的执行顺序相当混乱,并且不能定义任何后面非延迟脚本并须的函数和变量。在IE中所有的defer的脚本执行时间应该都是HTML文档树建立以后,window.onload之前。

  3. 利用Ajax。
    因为xmlhttpRequest能判断外部文档加载的状态,所以能够改变代码的加载顺序。

Add comment 八月 17th, 2010 Melon``

Nginx服务器的upload上传模块手记

Nginx服务器的upload上传模块

文档:http://www.grid.net.ru/nginx/upload.en.html
参考:
1)http://hi.baidu.com/fangle_life/blog/item/99946f55ce46c4143a2935b4.html
2)http://www.sunnyu.com/?p=145

该模块通过Nginx服务器来接受用户上传的文件,在Nginx接受完文件以后再转给后端的程序做处理。使用ngxin上传模块比php的上传模块更稳定且消耗更少的资源。它自动分析客户端的上传请求,将上传的文件保存到 upload_store 所指向的目录位置. 然后这些文件信息将被从原始的请求中剔除,重新组装好上传参数后转到后端由 upload_pass 指定的位置去处理,这样就可以任意处理上传的文件。
每一个上传的 file 字段值将可以由upload_set_form_field 指定的值替换. 文件的内容可以由$upload_tmp_path 变量读到或简单的移到其他位置. 将文件删除由 upload_cleanup 指定控制。

重新安装nginx

./configure --prefix=/usr/local/web --user=nobody --group=nobody
--with-http_stub_status_module
--without-http-cache
--add-module=/home/nginx_upload_module-2.0.12

配置nginx

 location = /upload {
        # Pass altered request body to this location
        upload_pass /upload.php;

        # Store files to this directory
        # The directory is hashed, subdirectories 0 1 2 3 4 5 6 7 8 9 should exist
        upload_store /tmp;

        # Allow uploaded files to be read only by user
        upload_store_access user:r;

        # Set specified fields in request body
        upload_set_form_field $upload_field_name.name "$upload_file_name";
        upload_set_form_field $upload_field_name.content_type "$upload_content_type";
        upload_set_form_field $upload_field_name.path "$upload_tmp_path";

        # Inform backend about hash and size of a file
        upload_aggregate_form_field "$upload_field_name.md5" "$upload_file_md5";
        upload_aggregate_form_field "$upload_field_name.size" "$upload_file_size";

        upload_pass_form_field "^submit$|^description$";

        upload_cleanup 400 404 499 500-505;

上传页面:

<html>
<head>
<title>Test upload</title>
</head>
<body>
<h2>Select files to upload</h2>
<form name="upload" method="POST" enctype="multipart/form-data" action="/upload">
<input type="file" name="file1"><br>
<input type="file" name="file2"><br>
<input type="file" name="file3"><br>
<input type="file" name="file4"><br>
<input type="file" name="file5"><br>
<input type="file" name="file6"><br>
<input type="submit" name="submit" value="Upload">
<input type="hidden" name="test" value="value">
</form>
</body>
</html>

输出:

Array
(
    [file1_name] => 111.jpg
    [file1_content_type] => image/pjpeg
    [file1_path] => /tmp/0000000004
    [file1_md5] => bb52c3f90b95fbfabcc35142d1c9e1bd
    [file1_size] => 8180
    [submit] => Upload
)

2 comments 八月 9th, 2010 Melon``

IE6同时操作一个DOM的问题

IE6和其它符合标准的浏览器除了解释CSS差异外,对DOM的异步操作还是有比较大的不同。

这里所说到的异步,就例如在页面加载的过程中,我要执行一段JS,把一个script追加到head内(类似于jLoader的操作)。

一般情况下,IE6都还正常。但是当两段JS同时追加script到head内,IE6就会显示“未知错误”。。。好一个未知错误。

先说一说,html页面和js的渲染加载都是单线程的

那为什么会有两个script可以同时操作DOM呢?这是因为页面内部有些异步请求,这个和“单线程”加载是两回事,看看上面的文章就会明白。

既然两个script会同时修改同一元素,一定就要有先后顺序,否则就会弹出红色警告(IE6下)。IE6对这个处理没有做好,或者是基于安全性考虑不允许这么

做;FF都能执行得行云流水。

这种情况,我的惯常做法是延迟执行。

1.可以用回调的办法,待第一段代码执行完,再回调执行第二段代码。(回调办法是常用的,可以很好确保先后顺序)

2.或者简单写个setTimeout晚点做,最近那个广告代码我也是这么处理。(这是应用在迟点执行没有问题的情况)

可能你会认为为什么不onload或者DOM ready之后再执行。其实是一样的,大家都在等onload,onload后一起执行,还不是一样杯具。

IE6这东西我已经习惯了怎么去对付它,欢迎多交流啊。。。

3 comments 七月 29th, 2010 Melon``

substr带来的杯具

问题:Http::Get 在没有超时的情况下竟然返回False???

童鞋在开发擂台活动的时候需要调用搜索接口,调用方式采用fsockopen,主要代码如下:

public static function Get($host,$pos,$port=80,$timeout=3){
	$fp = fsockopen($host, $port, $errno, $errstr, $timeout);
	if (!$fp) {
		return false;		
	}else {
		if ($pos{0} != '/') { $pos = '/'.$pos; }
		$stream = "GET {$pos} HTTP/1.0\r\n";
		$stream .= "Host: $host\r\n";
		if(self::$_cookies) {$stream .= self::$_cookies;}
		$stream .= "Connection: Close\r\n";
		$stream .= "\r\n";
 
		fwrite($fp, $stream);
		usleep(10);
		stream_set_timeout($fp, $timeout);
		$res = stream_get_contents($fp);
	        $info = stream_get_meta_data($fp);
		fclose($fp);
		if ($info['timed_out']) {
			return false;    	
		}else {
			if(self::$_get_response_headers) {
				self::$_response_headers = self::GetResponseHeaders($res, true);
			}
			return  substr(strstr($res, "\r\n\r\n"),4);
		}
	}
}

这代码是老大写的,用了n春秋没有问题,老大走后问题悄悄的来了。

好,和大家一起探讨调试技巧。

可以看到,这个函数只有两个return,我们很容易定位。只要我们dump出$info,$res变量就一目了然了。

可悲的是$info['timed_out']为False,这样重点自然去到else部分。

if(self::$_get_response_headers) {
	self::$_response_headers = self::GetResponseHeaders($res, true);
}
return  substr(strstr($res, "\r\n\r\n"),4);

大家是否觉得if(self::$_get_response_headers) 这里可以忽略不看呢?如果你确定这里是False的话,可以省略里面

的流程;在不确定的情况下还是看看吧。

self::$_response_headers = self::GetResponseHeaders($res, true);这行,没错,很清楚,这行貌似与最后的

return毫无关系,但是一定要谨慎,有经验的你一定要看看self::GetResponseHeaders($res, true)这里的$res调用是

否用了引用?导致影响了$res的结果?好了,多疑的我去看了GetResponseHeaders的定义,没有用引用。。。

好,难道是return substr(strstr($res, “\r\n\r\n”),4);这里返回False???晕,马上查看手册。

strstr
Find first occurrence of a string (PHP 3, PHP 4, PHP 5) 
 
string strstr ( string haystack, string needle )
 
Returns part of haystack string from the first occurrence of needle to the end of haystack. 
If needle is not found, returns FALSE. 
If needle is not a string, it is converted to an integer and applied as the ordinal value of a 
 
character. 
 
substr
Return part of a string (PHP 3, PHP 4, PHP 5) 
 
string substr ( string string, int start [, int length] )
 
substr() returns the portion of string specified by the start and length parameters. 
 
If start is non-negative, the returned string will start at the start'th position in string, counting 
 
from zero. For instance, in the string 'abcdef', the character at position 0 is 'a', the character at 
 
position 2 is 'c', and so forth.

一看!没错啊,肯定是返回string。。如果是多种返回的话会写mixed对吧。。

好了出绝招了。

if($_GET['dd'] == 'dd'){
	var_dump(strstr($res, "\r\n\r\n"));//返回\r\n\r\n
	var_dump(substr(strstr($res, "\r\n\r\n"),4));//返回false
}
return substr(strstr($res, "\r\n\r\n"),4);

还不明白?看这个。

$a = substr("\r\n\r\n\",4); //由于我接口没有返回数据,所以就是\r\n\r\n
var_dump($a);//false
 
$a = substr("",1);
var_dump($a);//false

substr在没有找到的情况下会返回False!

官网上有写的,只能怪自己没有认真研究,附上地址substr函数

OK,解决办法如下。

$body = substr(strstr($res, "\r\n\r\n"),4);
return $body ? $body : '';

暂时到这里。

2 comments 七月 9th, 2010 Melon``

Perl 5 教程

地址:http://www.cbi.pku.edu.cn/chinese/documents/perl/index.htm

Perl 5 教程

by flamephoenix
第一部分 Perl语言

第一章 概述

一、Perl是什么?
二、Perl在哪里?
三、运行
四、注释

第二章 简单变量

一、整型
二、浮点数
三、字符串

第三章 操作符

一、算术操作符
二、整数比较操作符
三、字符串比较操作符
四、逻辑操作符
五、位操作符
六、赋值操作符
七、自增自减操作符
八、字符串联结和重复操作符
九、逗号操作符
十、条件操作符
十一、操作符的次序

第四章 列表和数组变量

一、列表
二、数组–列表的存贮
1、数组的存取
2、字符串中的方括号和变量替换
3、列表范围
4、数组的输出
5、列表/数组的长度
6、子数组
7、有关数组的库函数
(全文…)

Add comment 六月 28th, 2010 Melon``

小鸡进窝

小游戏地址:http://www.4399.com/flash/31809_1.htm?1024

好好玩的啊,咔咔咔。

我的成绩。

Add comment 六月 26th, 2010 Melon``

把FirePHP集成到56框架

简要:

FirePHP(以下简称FP)是基于FireBug插件开发的一款调试工具。它把调试的信息以响应头的形式发送到客户端,从而做到不影响页面输出的同时,又能看到debug数据。

以前的调试方式:

当前我们调试数据的时候,一般会这样做。

if (debug === TRUE){
	echo "<pre>";//<pre>标签的意思是格式化,自动换行。
	print_r($data);
}

这样做的缺点是影响页面的正常输出,会使调试信息和原有的html混排。

有了FP之后,就可以使调试数据和html输出完全分离。

运用FP的最终效果:

可以看到,借助了FP,把输出的数组以层的形式显示的浏览器上,没有影响到html的实体。

FP的原理:

FP的原理十分简单,先看看它怎么调用。

require_once 'FirePHPCore/fb.php';
 
$var = array('test0'=>'val0','test1'=>'val1');//定义一个数组,就是你想调试输出的内容
fb($var,__FILE__ .':'.__LINE__);//以普通的形式输出,标记为__FILE__ .':'.__LINE__的值
fb($var,'An array');//以普通的形式输出,标记为An array
fb($var,FirePHP::WARN);//以警告的形式输出
fb($var,FirePHP::INFO);//以信息的形式输出
fb($var,'Error type',FirePHP::ERROR);//以报错的形式输出,标记为Error type

效果图。

头信息。

从响应头可以看到,窜中包含着josn窜,这就是我们所需要的主要信息,通过插件的格式化,显示于我们眼前。

看看源码:

核心方法是FirePHP.fb,下面简要说明。

public function fb($Object) {
//...
 
if(func_num_args()==1) {
    } else
    if(func_num_args()==2) {//当有两个参数的时候,第二个参数可视为已什么形式显示。
      switch(func_get_arg(1)) {
        case self::LOG:
        case self::INFO:
        case self::WARN:
        case self::ERROR:
        case self::DUMP:
        case self::TRACE:
        case self::EXCEPTION:
        case self::TABLE:
        case self::GROUP_START:
        case self::GROUP_END:
          $Type = func_get_arg(1);
          break;
        default:
          $Label = func_get_arg(1);//默认为标记
          break;
      }
    } else
    if(func_num_args()==3) { //三个参数的时候,第二个为标记,第三个为类型
      $Type = func_get_arg(2);
      $Label = func_get_arg(1);//
    } else
    if(func_num_args()==4) {
      $Type = func_get_arg(2);
      $Label = func_get_arg(1);
      $Options = func_get_arg(3);
    } else {
      throw $this->newException('Wrong number of arguments to fb() function!');
    }
 
//....
   if(!$this->detectClientExtension()) {//通过userAgent判断用户的浏览器是否有装FP,如果想要IE都支持,改一下这里就可以。不过IE没有提供格式化显示的接口,你就只能看json了。。
      return false;
    }
 
//....
 
$msg = '['.$this->jsonEncode($msg_meta).','.$this->jsonEncode(, $skipFinalObjectEncode).']';//josn encode
 
//...
//for ...  循环send header
$this->setHeader('X-Wf-1-'.$structure_index.'-'.'1-'.$this->messageIndex,
                               (($i==0)?strlen($msg):'')
                               . '|' . $part . '|'
                               . (($i<count($parts)-2)?'\\':''));
 
//...
 
}

把FP整合到我们的框架中(框架我几乎每周都会更新的,以后有新开发已这个为准,新同学也看这个,谢谢啦)。

框架SVN:svn://56SVN/php_team/frameworks_56/trunk

为简洁起见,我把FP用到的两个类都整合到class.FirePHP.php中。

在func.Global.php加入。

/**
  * @todo FirePHP 
  * @author Melon`` @ 2010
  */
function fb(){
  if (FirePHP === TRUE){
	  $instance = FirePHP::getInstance(true);
	  $args = func_get_args();
	  return call_user_func_array(array($instance,'fb'),$args);
  }else{
  	  @header('FirePHP-Warning: fp=ml');	
  }
}

在common.inc.php加入。

 if ($_GET['fp'] == 'ml'){//firePhp
 	define("FirePHP", TRUE);
 }else{
 	define("FirePHP", FALSE);
 }

测试使用:
在App.Demo.php中加入。

/**
	  * @todo  
	  * @author Melon`` @ 2010
	  */
	public function TestFP(){
		User::Charset('utf-8');
		$arr = User::GetUsersProfile(array('rebill','hahamelon'),true,1);
		fb($arr,__FUNCTION__.':'.__LINE__);//直接调用这个函数就可以
 
		echo '这里是页面输出';
	}

显示地址:用装了FP的FireFox打开

效果就和最上面的第一张图一样。

安装方法。
这里略了,很简单。

好,暂时到这里,希望大家能用上。

1 comment 六月 26th, 2010 Melon``

javascript 捕获事件作用的对象

打开 商务空间 的此链接 “这里网网址:http://www.ujelly.org”。

在IE下会提示“您当前的链接可能存在安全问题,建议停止访问,点确定继续访问,点取消停止”,但在FF下不会。

JS是这样写的。

function catchClick(e){
    var dde_e = e||event.srcElement;
	while(dde_e && dde_e.tagName != "A"){
		if(dde_e.parentNode)
			dde_e = dde_e.parentNode;
		else return;
	}
    if(dde_e.tagName=="A"){
		newHref = dde_e.href.toLowerCase();
		theHost = getHost(newHref);
		for(i=0;i<bad_word_length;i++){
			if(theHost.indexOf(bad_word_array[i])>-1){
				if(!confirm('您当前的链接可能存在安全问题,建议停止访问,点确定继续访问,点取消停止')){
					return false;
				}
			}
		}
   }
}
document.onclick=catchClick;

这程序主要存在两个问题。
1.绑定的是document对象的onclick事件,而里面又用while的遍历。这样冒泡+遍历,结果不可预估。
2.FF传递event事件是通过最后一个隐含参数传递的,这里没有问题,问题是该event是不能直接获取作用的对象。

我把FF的event属性方法输出如下:

target:http://www.ujelly.org/
type:click
currentTarget:[object HTMLDocument]
eventPhase:3
bubbles:true
cancelable:true
timeStamp:5443953
stopPropagation:function stopPropagation() { [native code] }
preventDefault:function preventDefault() { [native code] }
initEvent:function initEvent() { [native code] }
view:[object Window]
detail:1
initUIEvent:function initUIEvent() { [native code] }
screenX:68
screenY:108
clientX:68
clientY:24
ctrlKey:false
shiftKey:false
altKey:false
metaKey:false
button:0
relatedTarget:null
initMouseEvent:function initMouseEvent() { [native code] }
CAPTURING_PHASE:1
AT_TARGET:2
BUBBLING_PHASE:3
getPreventDefault:function getPreventDefault() { [native code] }
layerX:0
layerY:0
pageX:68
pageY:24
which:1
rangeParent:null
rangeOffset:0
cancelBubble:false
isChar:false
SCROLL_PAGE_UP:-32768
SCROLL_PAGE_DOWN:32768
originalTarget:http://www.ujelly.org/
explicitOriginalTarget:[object Text]
preventBubble:function preventBubble() { [native code] }
preventCapture:function preventCapture() { [native code] }
isTrusted:true
MOUSEDOWN:1
MOUSEUP:2
MOUSEOVER:4
MOUSEOUT:8
MOUSEMOVE:16
MOUSEDRAG:32
CLICK:64
DBLCLICK:128
KEYDOWN:256
KEYUP:512
KEYPRESS:1024
DRAGDROP:2048
FOCUS:4096
BLUR:8192
SELECT:16384
CHANGE:32768
RESET:65536
SUBMIT:131072
SCROLL:262144
LOAD:524288
UNLOAD:1048576
XFER_DONE:2097152
ABORT:4194304
ERROR:8388608
LOCATE:16777216
MOVE:33554432
RESIZE:67108864
FORWARD:134217728
HELP:268435456
BACK:536870912
TEXT:1073741824
ALT_MASK:1
CONTROL_MASK:2
SHIFT_MASK:4
META_MASK:8

很明显,是没有什么tagName等属性的,而这些属性都保存在e.target对象中。

FF下

      alert(e.target.tagName);//输出 A

最终把代码修改如下,解决掉那两个问题。

     function catchClick(e){//Melon`` @ 2010-06-22
	var href = "";
	if(e && e.target && e.target.href){//FF
		href = e.target.href;
	}else if (event.srcElement && event.srcElement.href){//IE
		href = event.srcElement.href;
	}
	href = href.toLowerCase();
	theHost = getHost(href);
	for(i=0;i<bad_word_length;i++){
		if(theHost.indexOf(bad_word_array[i])>-1){
			if(!confirm('您当前的链接可能存在安全问题,建议停止访问,点确定继续访问,点取消停止')){
				return false;
			}
		}
	}
	return true;
}
 
document.onclick=catchClick;

测试地址:ie_ff_event.html

暂时到这里,有好的方法欢迎补充。

Add comment 六月 22nd, 2010 Melon``

自动灌水

最近有一个自动灌水的需求。

设定需求如下:

1、指定视频地址,评论开始时间、结束时间,每天要求评论的条数。

2、建立评论内容库,并对内容进行分类。

3、建立发评论的用户名库。

为了使这个系统工作得更真实,更能模拟用户的真实行为,实现如下:

假设某种子视频,每日需要发评论为1000。每小时发的评论数要与pv曲线关联,按比例分配即可,例如18点发150条,而凌晨3点则只发50条,写个比例的配置即可实现。

假设有N台机器定时来处理自动发评论,间隔都是每5秒执行一次,第一台机第1秒执行,第二台机第2秒执行,间隔开。同时需要一个心跳程序来记录台前有多少台机在执行这个应用,并用一脚本监控(例如每小时去看看某一机器是否在正常工作,这个非常重要,因为多一台机少一台机会影响数据的准确性),机器区分可以用server_addr。

针对一个种子视频,每台机器每次计划任务发的评论数为:

(N * 5 ) / (3600 * C)
 

N为该小时需要发的评论数,C是心跳程序监控到的机器数量。

如18点需要发评论150,机器数为C为5。

则  (150  * 5) / (3600 * 5) = 0.04166666

为什么是小数?因为不是每次计划任务都会发评论的,按概率随机而定。

对php,可以这样写。

IF ( rand(1,1 000 000) < 41666 ){
   执行发评论
}ELSE{
   什么都不做
}

心跳的逻辑,发评论的算法,各自用类封装好。

10-06-17

由于crontab最小的间隔单位是分钟,所以不能用计划任务来跑。春林同学的解决办法如下:

 #!/bin/sh
 
while : ; do
        cd /home/webadm/htdocs/new/newauto/crond && /home/php/bin/php auto_send_com.php > /dev/null
 
        sleep 5;
done

暂时到这里

Add comment 六月 9th, 2010 Melon``

mysql表锁导致更新失败的问题

最近lily发现我们的排行榜出现了图的情况,同时出现了两组一样的关键字。

排行榜

(排行榜截图)

我看了一下库,果然出现了两条记录。

数据库

(数据库截图)

word字段是index索引,我需要把它改成unique才能确保该字段的唯一,从逻辑上也应该是唯一的。

错误做法:

delete from `search_top_daily` where tid in (SELECT tid FROM `search_top_daily` group by word having count(word) > 1)
 MySQL 返回:文档
#1093 - You can't specify target table 'search_top_daily' for update in FROM clause

意思是你不能在同一语句中,先select出表中的某些值,再update或delete这个表。

明显,这是myisam存储引擎的表锁限制。

mysql表锁

(mysql表锁示意图)

正确做法:

#创建临时表,把需要的数据主键导入其中
create table tmp as select min(tid) as tid from `search_top_daily` group by word;
 
#执行删除操作
delete from `search_top_daily`  where tid not in (select tid from tmp); #已删除行数: 42 (查询花费 1.1961 秒)
 
#修改索引
ALTER TABLE `search_top_daily` DROP INDEX `word` ;
ADD UNIQUE `word` ( `word` ) ; #立刻为word加上unique索引
 
#删掉临时表
drop table tmp;

网上虽然有解决方案,但是没有说明错误做法的原因,如有错误,请指正。

Add comment 六月 1st, 2010 Melon``