我在我的一个网站遇到问题,当网页加载时会执行两个ajax请求。我正在将jQuery与基于zend框架的PHP应用程序结合使用。
相关的HTML(简化)如下所示:
<select id="first">
<option value="1">First Option</option>
<option value="2">Second Option</option>
</select>
<div class="first_block"></div>
<select id="second">
<option value="1">First Option</option>
<option value="2">Second Option</option>
</select>
<div class="second_block"></div>
这是我的jQuery的样子:
$(document).ready(function(){
// function to update the first block
var updateFirstBlock = function(){
var param = $(this).val();
$.ajax('module/controller/action/param/' + param, {
'success': function(data){
$('.first_block').html(data);
}
});
};
// bind and trigger the first update function
$('select#first').bind('change', updateFirstBlock );
$('select#first').trigger('change');
// function to update the second block
var updateSecondBlock = function(){
var param= $(this).val();
$.ajax('module/controller/another-action/param/' + param, {
'success': function(data){
$('.second_block').html(data);
}
});
};
// bind and trigger the second update function
$('select#second').bind('change', updateSecondBlock );
$('select#second').trigger('change');
});
PHP应用程序只返回一些内容,这取决于分发的值。
现在加载页面时会发生什么,在十个案例中的九个案例中,两个请求中的一个没有得到答案。另一个获得200 OK
,失败一次。没有规律性,请求失败。
Web服务器(Apache 2.2)配置中是否有可能出现问题,因此两个同时触发的请求相互约束?
修改
如果我将这两个请求都设置为async: false
,则它们始终正确执行。所以我认为必定会发生碰撞。
编辑2
这种行为的一个可能原因可能是php的会话锁定。我会进一步研究这个问题。
答案 0 :(得分:1)
看来你肯定在PHP会话锁定的正确机架上:
默认的PHP会话模型会锁定会话,直到页面加载完毕。因此,如果您有两个或三个加载的帧,并且每个帧都使用会话,则它们将一次加载一个。这样,任何时候只有一个PHP执行上下文具有对会话的写访问权。
有些人在完成向session_write_close()
写入任何数据后立即致电$_SESSION
来解决此问题 - 他们甚至可以在调用数据后继续读取数据。 session_write_close()
的不利之处在于,您的代码仍会锁定在任何会话页面上第一次调用session_start()
,并且您必须使用session_write_close()
你尽可能在任何地方使用会话。这仍然是一个非常好的方法,但如果您的会话访问遵循某些特定模式,您可能有另一种方法需要较少的代码修改。
我们的想法是,如果您的会话代码主要从会话中读取,并且很少写入会话,那么您可以允许并发访问。为了防止会话数据完全损坏,我们将在写入会话时锁定会话的后备存储(通常是tmp文件)。这意味着会话仅在我们写入后备存储的短暂时刻被锁定。但是,这意味着如果您同时加载两个页面,并且都修改了会话,则 Last One Wins 。无论哪一个首先加载,其数据都会被加载第二个的数据覆盖。如果你没问题,你可以继续 - 否则,使用上面的session_write_close方法。
如果你有一些复杂的代码依赖于会话中的某些状态,以及数据库或文本文件中的某些状态,或者别的东西 - 再次,你可能不想使用这种方法。当你有两个同时运行的页面时,你可能会发现一个页面运行中途,修改你的文本文件,然后第二个页面一直运行,进一步修改你的文本文件,然后第一个完成 - 你的数据可能是被毁坏或完全丧失。
因此,如果您准备调试可能非常非常恶劣的竞争条件,并且您的会话的访问模式主要是读取并且很少写入(而不是写得非常繁琐),那么您可以尝试以下系统。
将示例从session_set_save_handler()复制到您开始会话的上面的包含文件中。修改会话write()方法:
function write($id, $sess_data)
{
global $sess_save_path, $sess_session_name;
$sess_file = "$sess_save_path/sess_$id";
if ($fp = @fopen($sess_file, "w")) {
flock($fp,LOCK_EX);
$results=fwrite($fp, $sess_data);
flock($fp,LOCK_UN);
return($results);
} else {
return(false);
}
}
您可能还想为会话添加GC(垃圾收集)方法。
当然,请接受这个建议 - 我们目前在我们的测试服务器上运行它,它似乎在那里工作正常,但是人们报告了共享内存会话处理程序的可怕问题,并且这个方法可能不安全。
您还可以考虑为代码中可怕的并发敏感位实现自己的锁。
参考:http://ch2.php.net/manual/en/ref.session.php#64525
实现数据库会话处理程序可能是值得的。如果使用了良好的数据库结构,使用数据库可以消除这个问题,并且实际上可以略微提高性能。