Laravel 5在AJAX密集型应用程序中丢失会话和.env配置值

时间:2015-07-08 14:05:43

标签: php ajax session laravel

我正在使用Laravel 5(具体而言,“laravel / framework”版本为“v5.0.27”),其中session driver ='file'。

我正在使用Windows 7 64位计算机进行开发。

我注意到有时候(每周一次左右)我会意外地随机退出。有时甚至在我登录后立即发生这种情况。我已将日志消息添加到我的auth逻辑代码中,但未触发日志代码。 Laravel表现得好像已经完全丢失了会话文件。

另一个更严重的问题是,有时在调试会话之后(使用xdebug和Netbeans),Laravel开始丢失其他文件 - .env设置,一些调试条JS文件等。错误日志包含以下消息:

[2015-07-08 13:05:31] local.ERROR: exception 'ErrorException' with message 'mcrypt_encrypt(): Key of size 7 not supported by this algorithm. Only keys of sizes 16, 24 or 32 supported' in D:\myproject\vendor\laravel\framework\src\Illuminate\Encryption\Encrypter.php:81
[2015-07-08 13:05:31] local.ERROR: exception 'PDOException' with message 'SQLSTATE[HY000] [1044] Access denied for user ''@'localhost' to database 'forge'' in D:\myproject\vendor\laravel\framework\src\Illuminate\Database\Connectors\Connector.php:47

这清楚地表明Laravel没有读取.env文件,所以它使用默认设置:

'database'  => env('DB_DATABASE', 'forge'),
'key' => env('APP_KEY', 'somekey'),

丢失的文件很少发生,也许一个月左右发生一次,并且总是发生在调试会话之后。我总是不得不重启Apache以使其再次运行。

为了对系统进行压力测试并可靠地重现问题,我在Angular控制器中使用了快速黑客:

setInterval(function(){
    $scope.getGridPagedDataAsync();
}, 500);

这只是从Angular到Laravel的基本数据请求。

就是这样 - 现在我可以在3分钟或更短的时间内重现会话失败和.env失败。

我之前在相同的PC上使用相同的Apache + PHP开发了AJAX密集型Web应用程序,但没有Laravel,没有.env,我之前没有注意到这些问题。

在通过代码进行调试时,我发现Laravel根本没有使用PHP内置会话,而是实现了自己的基于文件的会话。显然,它没有提供与默认PHP会话相同的可靠性,我不确定原因。

当然,在现实生活中,我的应用程序不会是AJAX密集型的,但根据我的经验,只有两个同时发生的AJAX请求失去会话就足够了。

我在Laravel上看到了一些关于各种会话问题的相关错误报告。然而,我还没有看到关于dot-env的任何内容,但似乎也遇到了同样的问题。

我的猜测是Laravel不使用文件锁并等待,因此如果由于某种原因无法读取文件(可能被某些并行进程或Apache锁定),那么Laravel只会放弃并返回它可以的任何内容。

这有什么好的解决方案吗?也许它特定于Windows,问题会在Linux机器上消失?

好奇,为什么Laravel(或Symfony)开发人员还没有修复他们的会话文件驱动程序。我知道锁定/等待会减慢速度,但至少有一些选项可以打开“可靠的会话”。

与此同时,我将尝试逐步完成Laravel代码,看看我是否可以发明一些“快速和肮脏”的解决方案,但是拥有一些可靠的“最佳实践”解决方案会好得多。

关于.env

的更新

问题变成与锁定文件无关。我找到了.env问题的Laravel错误报告,它引导我找到Dotenv项目的链接报告,反过来说,这是一个核心的PHP问题。令我不安的是,Dotenv开发者说Dotenv从未打算用于制作,但Laravel似乎依赖Dotenv。

https://github.com/laravel/framework/pull/8187中,似乎有一个解决方案应该朝着一个方向发挥作用,但是一些评论者表示,在他们的情况下问题恰恰相反。名为 crynobone 的人提供了一个聪明的代码段来尝试:

$value = array_get($_ENV, $key, getenv($key));

在Dotenv和Laravel Githubs上出现了另一个使用“makeMutable()”的建议,但是评论者报告说这可能会破坏测试。

所以我尝试了 crynobone 的代码,但它对我不起作用。在调试时,我发现在我的情况下,当并发请求的事情发生故障时,无法在getenv()中找到$ key,也无法在$ _ENV中找到$ key,甚至在$ _SERVER中也找不到。 唯一有用的(快速和肮脏的experminet)是添加:

static :: $ cached [$ name] = $ value;

到Dotenv类,然后在helpers.php env()方法中我看到:

Dotenv::$cached[$key]

总是很好,即使$ _ENV和getenv都什么也没有。

虽然Dotenv不适合制作,但我不想改变我们的部署和配置工作流程。

接下来,我将不得不调查会话问题......

附录

相关的Laravel错误报告(有些甚至来自版本4.看起来似乎并未修复): https://github.com/laravel/framework/issues/4576

https://github.com/laravel/framework/issues/5416

https://github.com/laravel/framework/issues/8172

和一篇旧文章揭示了正在发生的事情(至少在会议问题上): http://thwartedefforts.org/2006/11/11/race-conditions-with-ajax-and-php-sessions/

2 个答案:

答案 0 :(得分:4)

我个人认为使用.env配置Laravel是一个糟糕的决定。拥有包含key:value样式配置的.php文件要好得多。

但是,您遇到的问题不是PHP的错,也不是Apache的错误 - 它很可能是 Windows问题。

其他一些事情:Apache包含一个模块,允许将PHP二进制文件集成到Apache的进程或线程中,称为mod_php - 问题在于PHP不仅速度慢,而且还要将另一个二进制文件集成到现有的一个是超级棘手的,事情可能会被遗漏。在这种情况下,PHP也必须使用线程安全构建。如果不是,那么奇怪的错误就会发生(并且会发生)。

为了避免将一个程序巧妙地集成到另一个程序中的问题,我们可以完全避免这种情况,并且我们可以通过FastCGI协议提供.php。这意味着Web服务器(Apache或Nginx)将接受HTTP请求并将其传递给另一个“Web”服务器。在我们的例子中,这将是PHP FastCGI流程管理器或PHP-FPM

PHP-FPM是提供.php页面的首选方式 - 不仅因为它更快(比通过mod_php集成更快,更快),而且您可以轻松扩展您的HTTP前端并拥有多台计算机提供.php个页面,使您可以轻松地水平扩展HTTP前端。

但是,PHP-FPM被称为主管流程,它依赖于流程控制。据我所知,Windows不支持* nix的进程控制,因此php-fpm不适用于Windows(如果我在这里错了,请纠正我)。

这对你来说意味着什么?这意味着您应该使用旨在与您想要做的很好地配合的软件。 这是应该遵循的逻辑:

  • Web服务器接受HTTP请求(Apache或Nginx)
  • Web服务器验证请求,解析原始HTTP请求,确定请求是否过大,如果一切顺利,则将请求代理到php-fpm
  • php-fpm处理请求(在您的情况下,它启动Laravel)并返回Web服务器向用户显示的HTML

现在,这个过程虽然很棒,但有一些问题,这里有一个很大的问题就是PHP如何处理会话。默认的PHP会话是存储在服务器某处的文件。这意味着,如果您有2台物理机为php-fpm提供服务,那么您将遇到会话问题。这是Laravel做得很棒的地方 - 它允许您使用基于加密cookie的会话。它有一些限制(您无法在这些会话中存储资源并且您有大小限制),但正确构建的应用程序首先不会在会话中存储太多信息。当然,有多种方式来处理会话,但在我看来,加密的cookie是超级的,使用起来非常简单,功能强大。当使用这样的cookie时,它是携带会话信息的客户端,并且任何包含解密密钥的计算机都可以读取此会话,这意味着您可以轻松地将您的设置扩展到多个服务器 - 他们只需要访问相同的服务器解密密钥(它是APP_KEY中的.env)。基本上,您需要将相同的Laravel安装复制到您希望为项目提供服务的计算机上。

我处理开发过程中遇到的问题的方法如下:

  • 使用虚拟机(假设是Oracle Virtualbox)
  • 安装Ubuntu 14.04
  • 将网络驱动器映射到Windows主机(使用Samba)
  • 使用您首选的编辑PHP文件的方式,但它们将存储在映射的驱动器上
  • 在VM上引导nginx或Apache以及php-fpm来为您的项目提供服务

现在您通过此过程获得的是:您不会使用侦听端口80/443的程序污染您的Windows计算机,当您完成工作后,您可以关闭VM而不会丢失工作,也可以轻松模拟您的网站在实际生产机器上的行为,并且您不会有惊喜,例如“它适用于我的开发机器,但它在我的生产机器上不起作用”因为您有相同的软件两个目的。

这些是我的意见,它们并非都是冷酷的事实,我在这里所写的内容应该带有一点点。如果您认为我写的内容可能会对您有所帮助,那么请尝试以这种方式解决问题。如果没有,那么,没有难过的感觉,祝你的项目好运。

答案 1 :(得分:4)

经过两天的密集调试后,我有一些可能对其他人有用的解决方法:

以下是修补.env问题的Dotenv 1.1.0和Laravel 5.0.27补丁: https://gist.github.com/progmars/db5b8e2331e8723dd637

这是我的解决方法补丁,可以减少会话问题的频率(或完全修复它们,如果你不在每次请求时自己写会话): https://gist.github.com/progmars/960b848170ff4ecd580a

我用Laravel 5.0.27和Dotenv 1.1.0测试了它们。

最近还为Laravel 5.1.1和Dotenv 1.1.1重新创建了补丁: https://gist.github.com/progmars/e750f46c8c12e21a10ea https://gist.github.com/progmars/76598c982179bc335ebb

确保添加

'metadata_update_threshold' => 1,

到你的config / session.php,让这个补丁生效。

每次重新创建时,所有补丁都应该应用于vendor文件夹。

此外,您可能希望将会话修补程序分开,因为您只需更新一次session.php,但每次在部署之前重新创建补丁时,应将补丁的其他部分应用于vendor文件夹。 / p>

警告:"它适用于我的机器"。我真的希望Laravel和Dotenv开发人员能够提出更好的东西,但同时我可以接受这些修复。