天天看點

在Yaf中使用Zf2的配置和服務管理器

測試最新的ZF2.3架構,發現zf2性能比預想的低太多, 在筆記本上每秒竟然隻能響應30個請求.

  • 測試條件: I3 380M + 4G + Windows7 + iis7 + PHP5.4(fastcgi) + Zend2.3
  • 網站設定: 使用預設的子產品和控制器,配置config和module緩存. 不連接配接任何服務和資料庫.為避免帶寬不足清空view
  • 測試軟體: Jmeter 20并發使用者.測試機和被測試機是不同的電腦,它們之間用100M5類線直連
在Yaf中使用Zf2的配置和服務管理器

在測試結果中,平均響應時間需要0.637秒,每秒處理30個請求.這個資料比zf1低一半. 同一台機zf1可以達到每秒60多個請求

看到這個測試結果後有點想放棄zf2,可是在開發靈活和便捷性上zf2确實要比zf1要好得多. 分析發現,在zf2中路由,派遣和服務注冊,子產品注冊拖累了整個速度. 以前做項目用到過yaf,發現yaf性能挺好的,于是計劃用yaf代替zf2的mvc子產品.

一. 在yaf項目中加載zf2類庫

首先你得有一個能正常運作的yaf項目.在這個基礎上改動以增加zf2支援.可以參考yaf官方教程做一個yaf的基本架構站點. 這裡假設你已按照官方教程架設好yaf站點

1. 在yaf項目中使用composer

yaf官網示範中預設沒有zf2的類加載器,考慮到今後還要加載其它的類庫代碼,這裡使用composer加載zf2依賴. 要在yaf項目中引用composer需要對yaf項目的/public/index.php和/conf/application.ini做少量改動.

<?php

/**
 * This makes our life easier when dealing with paths. Everything is relative
 * to the application root now.
 */
chdir(dirname(__DIR__));

// Decline static file requests back to the PHP built-in webserver
if (php_sapi_name() === 'cli-server' && is_file(__DIR__ . parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH))) {
    return false;
}

// Setup autoloading
require 'init_autoloader.php';

// Define path to application directory
define("APP_PATH", dirname(__DIR__));

// Create application, bootstrap, and run
$app = new Yaf_Application(APP_PATH . "/conf/application.ini");
$app->bootstrap()
        ->run();
           

改動後的檔案和yaf預設檔案主要的差別:

  1. 要增加chdir(dirname(__DIR__)). 目的是讓zf2能正常讀取配置檔案
  2. require 'init_autoloader.php'; 這個檔案是在ZendSkeletonApplication.zip中複制出來的,目的是引用composer, zf2類庫就要通過這個檔案加載進來.
  3. $app->bootstrap() 必須要有引導器,因為後面要在bootstrap裡面加載zf2配置和服務

因為yaf預設會禁用spl加載器,這會造成無法加載非yaf字首命名的類.是以我們還需要更改yaf運作配置.在/conf/application.ini中設定 application.system.use_spl_autoload = true

[product]
application.directory = APP_PATH "/application/"
application.dispatcher.catchException = true
application.system.use_spl_autoload = true
      

總結一下,你需要更改 index.php,application.ini增加init_autoloader.php檔案,哦!别忘記了編輯composer.json在其中增加zf2依賴配置. 我這裡用的是zf2.3. 然後更新composer依賴. 等待composer依賴更新完成後測試項目,看看在yaf項目中是否可以用zf2的類庫,這裡應該可以引用,如果還是不行就請下載下傳本文後面提供的完整代碼.

<?php

/**
 * Zend Framework (http://framework.zend.com/)
 *
 * @link      http://github.com/zendframework/ZendSkeletonApplication for the canonical source repository
 * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
 * @license   http://framework.zend.com/license/new-bsd New BSD License
 */
/**
 * This autoloading setup is really more complicated than it needs to be for most
 * applications. The added complexity is simply to reduce the time it takes for
 * new developers to be productive with a fresh skeleton. It allows autoloading
 * to be correctly configured, regardless of the installation method and keeps
 * the use of composer completely optional. This setup should work fine for
 * most users, however, feel free to configure autoloading however you'd like.
 */
// Composer autoloading
if (file_exists('vendor/autoload.php')) {
    $loader = include 'vendor/autoload.php';
}

$zf2Path = false;

if (is_dir('vendor/ZF2/library')) {
    $zf2Path = 'vendor/ZF2/library';
} elseif (getenv('ZF2_PATH')) {      // Support for ZF2_PATH environment variable or git submodule
    $zf2Path = getenv('ZF2_PATH');
} elseif (get_cfg_var('zf2_path')) { // Support for zf2_path directive value
    $zf2Path = get_cfg_var('zf2_path');
}

if ($zf2Path) {
    if (isset($loader)) {
        $loader->add('Zend', $zf2Path);
    } else {
        include $zf2Path . '/Zend/Loader/AutoloaderFactory.php';
        Zend\Loader\AutoloaderFactory::factory(array(
            'Zend\Loader\StandardAutoloader' => array(
                'autoregister_zf' => true
            )
        ));
    }
}

if (!class_exists('Zend\Loader\AutoloaderFactory')) {
    throw new RuntimeException('Unable to load ZF2. Run `php composer.phar install` or define a ZF2_PATH environment variable.');
}
           
"require": {
        "php": ">=5.3.3",
        "zendframework/zendframework": "2.3.*"
    }
      

2. 讓Yaf項目中的zf2配置檔案起作用

上面的改動完成後在yaf中可以使用zf2的類庫了,這還不夠,我們還要在項目中使用zf2的架構配置和服務管理器. 在改動前需要先了解yaf的限制.

yaf是c語言寫的mvc架構,配置檔案預設是applicatin.ini. yaf并不像zf2那樣包含各種功能子產品,特别是yaf不能直接使用zf2的配置檔案.也就是說zf2的配置在yaf上是沒有效果的.為了讓zf2配置起作用,我們需要在yaf中調用zf2的服務管理子產品以讀取配置, 然後把配置好的服務子產品注冊到yaf_register中.

zf2如果除開mvc子產品(這裡我們用yaf代替了zf2-mvc),最核心的就是servermanager。所有的配置項,所有的服務類,包括事件等都是由servermanager管理,如需在yaf中使用zf2配置,就要把servermanager移植到yaf中. 在zf2中servermanager是最先被執行的,并且把執行個體作為參數由各種後續對象傳遞,在yaf中我們使用yaf_regisger傳遞servermanager執行個體. 由于我們用yaf代替了zf2的mvc子產品,是以在我們移植後的項目中zf2配置中的mvc部分是沒有作用的

我們先把zf2的配置檔案複制到/conf中,注意zf2配置檔案預設在/config目錄,而yaf配置是在/conf中,為了讓配置檔案能正常工作,請把zf2應用配置檔案application.config.php中的config_glob_paths配置檔案路徑由/config改為/conf. 除此以外其它的配置項不需要做任何更改.

接下來需要改動yaf的Bootstrap.php檔案,還記得index.php中的bootstrap()麼?,那就是為了運作這個檔案準備的. 在這個檔案中所有_init開頭的方法都會被自動執行,我們把注冊servermanager和讀取zf2配置的方法寫在這裡,這樣yaf項目啟動的時候配置檔案就起作用了. 然後在這裡把通過配置生成的servermanager對象注冊給yaf_regisger.這樣在整個yaf中就可以像zf2那樣使用服務管理器調用各種功能(不能調用mvc相關子產品).

<?php

use Zend\ServiceManager\ServiceManager;
use Zend\Mvc\Service\ServiceManagerConfig;
use Zend\ModuleManager\Listener\ConfigListener;
use Zend\ModuleManager\Listener\ListenerOptions;
use Zend\ModuleManager\ModuleEvent;

class Bootstrap extends Yaf_Bootstrap_Abstract {

    public function _initConfig() {
        $config = Yaf_Application::app()->getConfig();
        Yaf_Registry::set("config", $config);
    }

    public function _initServiceManager() {
        $configuration = require APP_PATH . '/conf/application.config.php';
        $smConfig = isset($configuration['service_manager']) ? $configuration['service_manager'] : array();
        $serviceManager = new ServiceManager(new ServiceManagerConfig($smConfig));
        $serviceManager->setService('ApplicationConfig', $configuration);

        $configListener = new ConfigListener(new ListenerOptions($configuration['module_listener_options']));

        // If not found cache, merge config
        if (!$configListener->getMergedConfig(false)) $configListener->onMergeConfig(new ModuleEvent);

        // If enabled, update the config cache
        if ($configListener->getOptions()->getConfigCacheEnabled() &&
                !file_exists($configListener->getOptions()->getConfigCacheFile())) {
            $configFile = $configListener->getOptions()->getConfigCacheFile();
            $content = "<?php\nreturn " . var_export($configListener->getMergedConfig(false), 1) . ';';
            file_put_contents($configFile, $content);
        }

        $serviceManager->setService('config', $configListener->getMergedConfig(false));


        Yaf_Registry::set('ServiceManager', $serviceManager);
    }

    public function _initSessionManager() {
        Yaf_Registry::get('ServiceManager')->get('Zend\Session\SessionManager');
    }

    public function _initPlugin(Yaf_Dispatcher $dispatcher) {
        $user = new UserPlugin();
        $dispatcher->registerPlugin($user);
    }

}
           

二. 在yaf項目使用zf2服務管理器調用資料庫,cache,日志

調用各種zf2的服務功能時,需要先確定你的zf2配置中有對應的設定,具體設定參考zf2官方手冊,本文後面也有示例代碼下載下傳.

<?php

use Zend\Session\Container as SessionContainer;
use Zend\Db\TableGateway\TableGateway;

class IndexController extends Yaf_Controller_Abstract {

    public function indexAction() {
        if (!isset($session->n)) {
            $session->n = 0;
        }
       $session->n++;
        echo $session->n;
        $adapter = $this->getDbAdapter();
        $table = new TableGateway('admin', $adapter);
        $entities = $table->select();
        foreach ($entities as $entity) {
            var_dump($entity->username);
        }
        $cache = $this->getStorage();
        $cache->setItem('asdfas', 'asdfasdf');
        $this->getLogger()->alert('asdfasd');
        $this->getView()->assign("content", "Hello World");
    }

    /**
     * db adapter
     * @return \Zend\Db\Adapter\Adapter
     */
    public function getDbAdapter() {
        return Yaf_Registry::get('ServiceManager')->get('Zend\Db\Adapter\Adapter');
    }

    /**
     * storage
     * @return \Zend\Cache\Storage\StorageInterface
     */
    protected function getStorage() {
        return Yaf_Registry::get('ServiceManager')->get('Zend\Cache\Storage\StorageInterface');
    }

    /**
     * logger
     * @return \Zend\Log\Zend\Log\Logger
     */
    protected function getLogger() {
        return Yaf_Registry::get('ServiceManager')->get('Zend\Log\Logger');
    }

}
           

三. 測試yaf內建zf2後的性能

我們使用jmeter測試改動後的yaf項目性能,測試條件和參數與上次測試zf2相同,結果如下:

在Yaf中使用Zf2的配置和服務管理器

現在測試資料好看多了,平均響應時間提高到81毫秒, 每秒響應請求數提高到241個. 其實還測試過沒有內建zf2服務管理器的yaf性能,每秒請求數可以達到1200(這是11年的I3移動版CPU的老筆記本電腦,每秒1200已經很強了),但是不提供服務的項目性能再好都沒有意義.

這是一個完整的yaf內建zf2的項目示例.下載下傳後請把把其中的dist配置檔案複制并去掉.dist擴充名.yaf-sample-d771c9a.tar