记一次 Laravel MethodNotAllowedHttpException 问题排查

问题描述

线上一个 laravel 4.2 的项目有 Exception 产生,通过查看 log 发现每天会有 2 条左右出现,量也不大,通过日志能看出是 MethodNotAllowedHttpException 异常,下面记录一下解决过程。

[2019-05-17 06:04:38] Oregon.ERROR: Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException in /******/endpoint/bootstrap/compiled.php:5753
Stack trace:
#0 /******/endpoint/bootstrap/compiled.php(5749): Illuminate\Routing\RouteCollection->methodNotAllowed(Array)
#1 /******/endpoint/bootstrap/compiled.php(5727): Illuminate\Routing\RouteCollection->getOtherMethodsRoute(Object(Illuminate\Http\Request), Array)
#2 /******/endpoint/bootstrap/compiled.php(5051): Illuminate\Routing\RouteCollection->match(Object(Illuminate\Http\Request))
#3 /******/endpoint/bootstrap/compiled.php(5039): Illuminate\Routing\Router->findRoute(Object(Illuminate\Http\Request))
#4 /******/endpoint/bootstrap/compiled.php(5031): Illuminate\Routing\Router->dispatchToRoute(Object(Illuminate\Http\Request))
#5 /******/endpoint/bootstrap/compiled.php(720): Illuminate\Routing\Router->dispatch(Object(Illuminate\Http\Request))
#6 /******/endpoint/bootstrap/compiled.php(696): Illuminate\Foundation\Application->dispatch(Object(Illuminate\Http\Request))
#7 /******/endpoint/bootstrap/compiled.php(7803): Illuminate\Foundation\Application->handle(Object(Illuminate\Http\Request), 1, true)
#8 /******/endpoint/bootstrap/compiled.php(8410): Illuminate\Session\Middleware->handle(Object(Illuminate\Http\Request), 1, true)
#9 /******/endpoint/bootstrap/compiled.php(8357): Illuminate\Cookie\Queue->handle(Object(Illuminate\Http\Request), 1, true)
#10 /******/endpoint/bootstrap/compiled.php(11154): Illuminate\Cookie\Guard->handle(Object(Illuminate\Http\Request), 1, true)
#11 /******/endpoint/bootstrap/compiled.php(657): Stack\StackedHttpKernel->handle(Object(Illuminate\Http\Request))
#12 /******/endpoint/public/index.php(48): Illuminate\Foundation\Application->run()
#13 {main} [] []

排查过程

  通过查找 nginx access log 找到对应的信息,是 post 请求 index.php,我用 postMan 来进行模拟并没有复现这个 exception,我拿到的只是一个 200 的 Response,并不是 500,这应该是扫描器批量扫描的,或者是 spider 爬的,并没有走域名,而是从 ip 直接访问。

**.93.2.** **.71.95.** 182.61.178.228 - "17/May/2019:06:04:39 +0800" "POST /index.php HTTP/1.1" 453 4534 0.680 500 unix:/tmp/php-cgi-72.sock - 0.004 "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:28.0) Gecko/20100101 Firefox/28.0" 

查找 nginx error log 没有对应的 log,问题到这里就僵住了,返回去看 exception log 找到抛出异常之前最后一个执行的方法

Illuminate\Routing\RouteCollection->getOtherMethodsRoute

/* 代码路径 */
/vendor/laravel/framework/src/Illuminate/Routing/RouteCollection.php  :186
    /**
     * Get a route (if necessary) that responds when other available methods are present.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  array  $others
     * @return \Illuminate\Routing\Route
     *
     * @throws \Symfony\Component\Routing\Exception\MethodNotAllowedHttpException
     */
    protected function getOtherMethodsRoute($request, array $others)
    {
        if ($request->method() == 'OPTIONS')
        {
            return (new Route('OPTIONS', $request->path(), function() use ($others)
            {
                return new Response('', 200, array('Allow' => implode(',', $others)));

            }))->bind($request);
        }

        $this->methodNotAllowed($others);
    }

  看到这就大体明白了,如果 method 是 OPTIONS 会有 200 的正常 Response,否则就抛出错误,猜测 Http method 肯定是一个比较冷门的,导致框架报错,从 postMan 找一个比较冷门的 method UNLOCK 试了一下,果然状态 500,成功复现。

  这里有一段小插曲:为什么状态是 500 呢,应该是 405 才对,查看代码发现处理 error 的逻辑里面调用了一个已经优化掉的日志类,去掉那一行调用,一切正常。

- 阅读全文 -

从 0 开始编译安装 swoole 示例

官方文档:https://wiki.swoole.com/wiki/page/6.html

Swoole扩展是按照PHP标准扩展构建的。使用phpize来生成编译检测脚本,./configure来做编译配置检测,make进行编译,make install进行安装。
上面所说都是编译 php 扩展的通用过程。

# 下载 release 版本 #
# 注意:在国内下载 github 的资源,有时候不稳定,可以用迅雷等下载工具在本机下载好,再通过 SFTP 传到服务器 #
cd /usr/local/src
wget https://github.com/swoole/swoole-src/archive/master.tar.gz

# 解压、进入目录 #
tar xzvf swoole-src-master.tar.gz
cd swoole-src-master

# 生成编译检测脚本、配置检查、编译安装 #
# phpize、php-config 请使用你自己的路径 #
/www/server/php/72/bin/phpize && \
./configure --with-php-config=/www/server/php/72/bin/php-config \
--enable-coroutine --enable-openssl  \
--enable-http2  \
--enable-async-redis \
--enable-sockets \
--enable-mysqlnd && \
make clean && make && sudo make install

精简默认安装

/www/server/php/72/bin/phpize && \
./configure --with-php-config=/www/server/php/72/bin/php-config && \
make clean && make && sudo make install

如果上述步骤顺利的话,就会有 Build complete 输出:

Build complete.
Don't forget to run 'make test'.

Installing shared extensions:     /www/server/php/72/lib/php/extensions/no-debug-non-zts-20170718/
Installing header files:          /www/server/php/72/include/php/

cd /www/server/php/72/lib/php/extensions/no-debug-non-zts-20170718/ 就能找到 swoole.so 文件了

修改 php.ini 文件,将 swoole.so 加载进去

[swoole]
extension = /www/server/php/72/lib/php/extensions/no-debug-non-zts-20170718/swoole.so

通过 php -m | grep swoole 来查看是否成功加载了swoole

相关错误

编译安装swoole报错 Enable http2 support, require nghttp2 library.

swoole安装异步redis –enable-async-redis出错 hiredis/hiredis.h: No such file or directory 解决方法

swoole安装异步redis –enable-async-redis出错 hiredis/hiredis.h: No such file or directory 解决方法

在安装 swoole 异步redis,会带上这个选项

--enable-async-redis

编译过程中会有下列报错产生:

hiredis/hiredis.h: No such file or directory

这个原因是没有安装 hiredis 导致。

解决办法 安装 hiredis:

cd /usr/local/src

# 安装之前看一下github,如果有新版本,就安装新的 #
wget https://github.com/redis/hiredis/archive/v0.14.0.tar.gz
mv v0.14.0.tar.gz hiredis-0.14.0.tar.gz
tar xzvf hiredis-0.14.0.tar.gz
cd hiredis-0.14.0
make && make install

# 在底部添加这行环境变量 #
vim /etc/profile
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib

source /etc/profile

OK,到这里就安装完成了,重新编译 swoole 即可。

编译安装swoole报错 "Enable http2 support, require nghttp2 library."

安装swoole时, 因为现在搭建环境的时候不会涉及到http2.0, 所以不会安装对应的依赖包, 会报出类似以下错误:

In file included from /root/build/swoole-src-master/swoole.c:16:0:
/root/build/swoole-src-master/php_swoole.h:143:2: error: #error "Enable http2 support, require nghttp2 library."
 #error "Enable http2 support, require nghttp2 library."

解决方法 安装 nghttp2:

# 如果 github 上有新版本的就下载安装新版的 #
wget https://github.com/nghttp2/nghttp2/releases/download/v1.34.0/nghttp2-1.34.0.tar.gz
tar zxvf nghttp2-1.34.0.tar.gz
cd nghttp2-1.34.0
./configure
make && make install

安装好 nghttp2,再次编译安装 swoole 即可。

翻译资源管理系统

翻译资源管理系统

Author Scott Yu
E-mail yusureyes@163.com

项目介绍

  这是一个用于管理多语言资源的译文管理系统,由于用户分布于全球各地,公司的 App 需要显示多语言,Android 和 iOS 有大量的译文需要管理,手工维护极其麻烦,于是这个系统诞生了,方便 translator 在平台翻译,翻译完成之后,开发者将一键导出代码,直接放置在项目中。

  • 注意 translator 需要自己找人翻译,本系统只是维护译文资源,并不会自动翻译。

    公司 App 下载方式:软件商店搜索 Yeelight

软件架构

Tools Version
PHP 7.1+
Mysql 5.6+
框架 Laravel 5.3

安装教程

  1. git clone 项目至本地目录
  2. composer install
  3. cp .env.example .env 修改配置信息
  4. php artisan key:generate 生成 APP_KEY
  5. database/sql 找到 SQL 文件导入数据库
  6. 配置 Apache / Nginx 站点,浏览访问

如果 storage 不可写,请赋权限:
chmod -R 777 storage

演示地址

http://translate.demo.yusure.cn
管理员账号密码: admin 123456
Translator账号密码: translator 123456

使用说明

如何录入源语言(中文)
  1. 创建应用(可以后期创建):可以将多个 Project 分配到一个应用下,因为项目迭代会出现多个 Project,为方便管理,增加应用管理。
  2. 创建项目:点击 Project List,勾选需要翻译的语言,右上角添加项目
  3. 回到 Project List,点击 “录入” 按钮,一个小键盘的图标,录入 key(程序用的) 和 源语言(中文)
如何配置待翻译语言:

修改配置文件config/languages.php

return [
    /* 英语 */
    'en'    => 'English',
    /* 韩语 */
    'ko'    => 'Korean',
    /* 法语 */
    'fr'    => 'French',
];
原文录入完成之后,如何邀请 translator 帮忙翻译:
  1. 首先帮 translator 创建好账号,并发送给他。
  2. 点击查看 Project,在语言管理页面,点击红色的小手图标邀请按钮,将其账号勾选提交。

  3. 在邀请的图标后面是锁定功能,锁定之后,translator 不能修改译文,在 translator 完成翻译之后,该语言的译文自动锁定,如果需要修改,管理员可以随时解锁。
  4. 最后面是给 translator 发送邮件提醒,邮箱是帮 translator 创建账号时添加的,发信配置在 .env 文件。
如何配置对照语言:

例如翻译英文需要参考中文,翻译法语需要英文作为参考,那么就需要修改这个配置文件
config/translator.php

如何导出译文:

当译文都 ready 的时候,需要导出译文,导出译文有两种方式:第一种基于语言去导出,第二种针对整个应用(可以合并多个 Project)可以导出压缩包。
目前可以导出三种格式 Android xml、iOS strings、RN js。

translator 视角
  1. 支持对译文资源进行评论
  2. 支持标记有问题的译文资源,方便后续定位。(注意必须要处理掉所有标记才能完成翻译)

项目截图

本项目在公司内部运行半年有余,经过很多细节优化,为 Android、iOS 工程师提供了便利,现在将其开源出来,为开源事业添砖加瓦!
本项目为开源项目,允许把它用于任何地方,不受任何约束,欢迎 star、 fork 项目。