Google App Engine PHP55 随机服务器崩溃(500 次),错误代码为 204

Posted

技术标签:

【中文标题】Google App Engine PHP55 随机服务器崩溃(500 次),错误代码为 204【英文标题】:Google App Engine PHP55 random server crashes (500´s) with error code 204 【发布时间】:2018-02-16 13:49:03 【问题描述】:

我们的团队一直在开发这个 RESTful API,使用 Slim php 作为路由器,使用 mysql Propel ORM,并使用这个 app.yaml 配置将其部署为 Google App Engine 中的服务

service: api
runtime: php55
api_version: 1
threadsafe: yes

instance_class: F1
automatic_scaling:
  min_idle_instances: automatic
  max_idle_instances: automatic
  min_pending_latency: automatic
  max_pending_latency: automatic

skip_files:
- ^vendor/(.*/)+[Tt]ests/.*$
- ^\.(.*)

handlers:
- url: .*
script: app.php

被 Ember.js Web 应用程序使用,通过我们收到的奇怪无模式服务器崩溃 500 秒的所有开发,更准确地说:

500 Server Error Error: Server Error 服务器遇到错误 并且无法完成您的请求。请在 30 秒后重试。

使用 App Engine 日志。

处理此请求的进程遇到问题, 导致它退出。这很可能会导致使用新的进程 为您的应用程序的下一个请求。 (错误代码 204)

在随机端点中,否则 99% 的时间都可以正常工作,我们当然不想在这些随机崩溃的情况下投入生产。

我们尝试过的:

    检查是否已达到 MySQL max_connections,因为我们每次请求都会打开和关闭一个连接。 将我们的实例从 F1 升级到 F4_1G 以解决内存不足的问题。 在 localhost 中使用 dev_appserver.py 进行压力测试(我们在这里没有遇到任何崩溃) 尝试捕获整个 Slim 应用程序进行调试(它实际上从未捕获到异常,因此我们认为它确实与 Google App Engine 有关)

这是正常请求流程的一些代码。

app.php

/*
 * Create SLIM application
 */
$app = new \Slim\App([
    "settings"  => [
        "determineRouteBeforeAppMiddleware" => true,
    ]
]);

//Custom Authentication middleware
    $app->add(new \OAuth2Server\SlimAuthenticationMiddleware());

//CORS and Headers Middleware
    $app->add(function($request, $response, $next) 

        $response = $next($request, $response);

        $response = $response->withHeader("Access-Control-Allow-Origin", "*");
        $response = $response->withHeader("Access-Control-Allow-Headers", "Content-Type, authorization");
        $response = $response->withHeader("Access-Control-Allow-Methods", "POST, GET, PATCH, DELETE, OPTIONS");
        $response = $response->withHeader("content-type", "application/json; charset=utf8");

        return $response;

    );


    require_once("router.php");

    $app->run();

路由器.php

$app->get($apiVersionPath.'/visits/id','\Controllers\Visits:get')
    ->add(new \OAuth2Server\ValidateRequestUser(array("doctor", "nurse","superuser","admin")));

访问控制器 GET/ID 相关代码。

 /**
     * @param Request $request
     * @param Response $response
     * @param []$args
     * @return Response
     */
    public function get($request, $response, $args) 

        $id = $request->getAttribute("route")->getArgument("id");

        $serializer = new Serializer();

        if(!is_numeric($id) || $id == 0)
                    throw new InvalidArgumentException("Invalid Argument");
        

         $visit = \VisitQuery::create()
                  ->useUserQuery()
                   ->filterByClientId($request->getAttribute("user")->getClientId())
                  ->endUse();

         $visit = $visit->findPk($id);

         if(!isset($visit) || !($visit instanceof \Visit))
                    throw new EntityNotFoundException("Visit not found");
         

        $resource = $visit->toResource(false);

        $serializer->addResource($resource);

        $body = $response->getBody();
        $body->write($serializer->serialize());
        return $response;


【问题讨论】:

根据this issue 204s 通常表示内存问题。由于碰撞实例类型没有帮助并且没有观察到任何模式,我只能建议添加您的应用程序代码,以防有人注意到可疑或可能的解释。 谢谢@DanCornilescu,我刚刚添加了一些请求流程的代码 【参考方案1】:

我们在 PHP Flex 引擎上运行 API 服务,并在使用自动缩放时注意到了类似的问题。为了解决这个问题,我们必须提高实例类(尝试转到 F2),并且通过将 min_idle_instances 设置为 2,始终至少运行两个实例。

在使用任务队列和基本扩展时,我们在标准版 App Engine 上也遇到了同样的问题。看起来您还没有这样做,但如果是这样,我们找到的唯一解决方案是在 queue.yaml 中启用重试,并在将任务添加到推送队列时设置“快速失败”标头:

$pushTask = new PushTask($handler,
  array('message_data' => json_encode($message)),
  array('header'=> 'X-AppEngine-FailFast:true'));

否则,任务组将因 204 错误而失败。

我对您的问题感兴趣的一点是,您似乎正在尝试发出 HTTP 请求。在我们所有的测试中,我们能够重现错误的几种方法之一是将数百个任务放到一个队列中,并让每个任务运行以下代码:

$memCache = new Memcache;
$memCache->set($_SERVER['HTTP_X_APPENGINE_TASKNAME'] . '_1', 'Test 1');
ch = curl_init();
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_POSTFIELDS, 'hi');
curl_setopt($ch, CURLOPT_URL, 'https://www.google.com');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
set($_SERVER['HTTP_X_APPENGINE_TASKNAME'] . '_' . $index, $message);
$response = curl_exec($ch);
$memCache->set($_SERVER['HTTP_X_APPENGINE_TASKNAME'] . '_2', 'Test 2');

每当我们遇到错误时,我们都能通过它的键 TASK_NAME_1 在 Memcache 中找到第一条消息,但我们永远找不到第二条条目 TASK_NAME_2。就像你说的,没有异常被捕获,因为整个脚本都死了。

这种行为让我们相信 Google 的 Curl 实施可能存在问题,因为我们使用的是完整版本:

extension = php_curl.dll

但我们没有明确的答案。对我们来说唯一的解决方案是增加我们的实例计数并依靠重试来完成我们的代码。

希望上述解决方案之一对您有用,如果有机会,您能否向我们展示您的 PHP.ini 文件中的内容?

【讨论】:

老实说,我们只用这个 apc.cache_by_default = "0" 尝试了 php.ini,(我们在谷歌搜索时发现了这种方法)其余与默认 php 相同.ini 来自 GAE 中的 php55。你有关于 queue.yaml 的参考吗?【参考方案2】:

这是由threadsafe: yes引起的,请将其设置为no/false。

【讨论】:

以上是关于Google App Engine PHP55 随机服务器崩溃(500 次),错误代码为 204的主要内容,如果未能解决你的问题,请参考以下文章

Google Cloud App Engine app.yaml php72 路由问题

将参数传递给 app.yaml Google App Engine 中的 php

PHP Google App Engine YAML 找不到目录

如何在 Google Cloud App Engine 上运行 PHP 服务

Google App Engine PHP灵活环境中的URL处理程序

Google App Engine 环境设置