从线上宕机引起的问题谈开去

背景

  公司的外网线上项目连续宕机将近2周时间,后来通过查找日志,发现是某度蜘蛛大量爬取造成。因为公司花钱进行了SEO推广,所以也不能直接禁掉蜘蛛,所以一度造成网站打不开的情况。期间增加带宽,扩容cpu,增强搜索引擎集群等方案,都得不到好的效果。

分析

  开始优化代码,记录慢日志,在云平台看慢查询,渐渐优化慢查询,但是问题依旧。慢慢通过分析慢日志注意到如下细节:

这里我摘取了两个日志片段,其实类似日志片段还有非常多。可以看到将近相同的时间不同的许多进程都进行了GC,而这里的所谓GC是什么?
在StartSession.php上看到了如下代码:

/**
     * Remove the garbage from the session if necessary.
     *
     * @param  \Illuminate\Session\SessionInterface  $session
     * @return void
     */
    protected function collectGarbage(SessionInterface $session)
    {
        $config = $this->manager->getSessionConfig();

        // Here we will see if this request hits the garbage collection lottery by hitting
        // the odds needed to perform garbage collection on any given request. If we do
        // hit it, we'll call this handler to let it delete all the expired sessions.
        if ($this->configHitsLottery($config)) {
            $session->getHandler()->gc($this->getSessionLifetimeInSeconds());
        }
    }

这里注释的意思是有一定概率进行GC,并不是每次都进行GC,先来看下GC做了什么事情。
因为配置的是文件session,所以我们找到文件Session的实现文件FileSessionHandler.php,摘取GC函数代码如下:

/**
     * {@inheritdoc}
     */
    public function gc($lifetime)
    {
        $files = Finder::create()
                    ->in($this->path)
                    ->files()
                    ->ignoreDotFiles(true)
                    ->date('<= now - '.$lifetime.' seconds');

        foreach ($files as $file) {
            $this->files->delete($file->getRealPath());
        }
    }

代码的意思很简单,就是通过搜索文件,获取到过期的session文件然后进行删除,这里的过期时间在session.php的配置里定义:

 /*
    |--------------------------------------------------------------------------
    | Session Lifetime
    |--------------------------------------------------------------------------
    |
    | Here you may specify the number of minutes that you wish the session
    | to be allowed to remain idle before it expires. If you want them
    | to immediately expire on the browser closing, set that option.
    |
    */

    'lifetime' => 120,

    'expire_on_close' => true,

可以看到默认是2个小时,下面一个参数的意义是是否要在浏览器关闭的时候立即过期!刚才说到了,有一定概率执行这个GC,那这个概率多大呢?

/**
     * Determine if the configuration odds hit the lottery.
     *
     * @param  array  $config
     * @return bool
     */
    protected function configHitsLottery(array $config)
    {
        return random_int(1, 100) <= 2;
    }

上面代码是我把默认配置替换后的结果,也就是会有2%的可能会触发一次GC!而一次GC我们要回收两个小时之前的过期session文件。如果在并发量稍微有点大的情况下,要删除的文件其实是非常可观的,删除这个IO操作就是直接导致了cpu负荷直接超载的罪魁祸首。

解决方案

  方法很简单,就是换driver,比如集中式的redis session方案。

总结

  虽然这次项目的主要负责人不是我,不过通过这次抓取日志,跟踪代码,分析代码学到了很多优化的一手资料。Laravel其实是一个相当灵活的框架,文件session方式只能在测试环境进行应用,而在线上环境必须上集中式session方式,不然流量起来的时候,就会吃苦头。Laravel如不经过优化的情况下,他的性能是不高的,好在他也提供了很多可以优化的扩展接口,算的上是目前PHP里相当成熟的框架了。

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!