測試最新的ZF2.3架構,發現zf2性能比預想的低太多, 在筆記本上每秒竟然隻能響應30個請求.
- 測試條件: I3 380M + 4G + Windows7 + iis7 + PHP5.4(fastcgi) + Zend2.3
- 網站設定: 使用預設的子產品和控制器,配置config和module緩存. 不連接配接任何服務和資料庫.為避免帶寬不足清空view
- 測試軟體: Jmeter 20并發使用者.測試機和被測試機是不同的電腦,它們之間用100M5類線直連
在測試結果中,平均響應時間需要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預設檔案主要的差別:
- 要增加chdir(dirname(__DIR__)). 目的是讓zf2能正常讀取配置檔案
- require 'init_autoloader.php'; 這個檔案是在ZendSkeletonApplication.zip中複制出來的,目的是引用composer, zf2類庫就要通過這個檔案加載進來.
- $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相同,結果如下:
現在測試資料好看多了,平均響應時間提高到81毫秒, 每秒響應請求數提高到241個. 其實還測試過沒有內建zf2服務管理器的yaf性能,每秒請求數可以達到1200(這是11年的I3移動版CPU的老筆記本電腦,每秒1200已經很強了),但是不提供服務的項目性能再好都沒有意義.
這是一個完整的yaf內建zf2的項目示例.下載下傳後請把把其中的dist配置檔案複制并去掉.dist擴充名.yaf-sample-d771c9a.tar