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

Jus*_*tin 11 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
Run Code Online (Sandbox Code Playgroud)

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

'database'  => env('DB_DATABASE', 'forge'),
'key' => env('APP_KEY', 'somekey'),
Run Code Online (Sandbox Code Playgroud)

丢失的文件很少发生,可能每月一次左右,并且总是发生在调试会话之后.我总是不得不重新启动Apache以使其再次运行.

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

setInterval(function(){
    $scope.getGridPagedDataAsync();
}, 500);
Run Code Online (Sandbox Code Playgroud)

这只是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));
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

Dotenv::$cached[$key]
Run Code Online (Sandbox Code Playgroud)

总是很好,即使$ _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/

Jus*_*tin 5

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

这是我的 Dotenv 1.1.0 和 Laravel 5.0.27 补丁,用于修复 .env 问题: 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,
Run Code Online (Sandbox Code Playgroud)

到您的 config/session.php 以使此补丁生效。

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

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

请注意:“它可以在我的机器上运行”。我真的希望 Laravel 和 Dotenv 开发人员能想出更好的东西,但同时我也可以接受这些修复。


N.B*_*.B. 4

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

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

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

为了避免将一个程序集成到另一个程序中的棘手问题,我们可以完全避免这种情况,并且可以.php通过 FastCGI 协议提供服务。这意味着 Web 服务器(Apache 或 Nginx)将接受 HTTP 请求并将其传递到另一个“Web”服务器。在我们的例子中,这将是 PHP FastCGI Process Manager 或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 文件的方式,但它们将存储在映射驱动器上
  • php-fpm在 VM 上启动 nginx 或 Apache 来为您的项目提供服务

现在,您通过此过程获得的好处是:您不会使用侦听端口 80 / 443 的程序污染您的 Windows 计算机,当您完成工作时,您可以直接关闭虚拟机而不会丢失工作,您也可以轻松地模拟您的网站在实际生产计算机上的行为方式,您不会感到惊讶,例如“它在我的开发计算机上运行,​​但在我的生产计算机上不起作用”,因为您拥有用于这两个目的的相同软件。

这些是我的观点,它们并不都是冷酷的事实,我在这里写的内容应该持保留态度。如果您认为我写的内容可能对您有帮助,那么请尝试以这种方式解决问题。如果没有,也没有什么不好的感觉,祝您项目顺利。