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

  • 测试条件: I3 380M + 4G + Windows7 + iis7 + PHP5.4(fastcgi) + Zend2.3
  • 网站设置: 使用默认的模块和控制器,配置config和module缓存. 不连接任何服务和数据库.为避免带宽不足清空view
  • 测试软件: Jmeter 20并发用户.测试机和被测试机是不同的电脑,它们之间用100M5类线直连

20-zend2-jmeter

在测试结果中,平均响应时间需要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做少量改动.

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?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

2
3
4
5
[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的类库,这里应该可以引用,如果还是不行就请下载本文后面提供的完整代码.

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<?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.');
}
2
3
4
5
    "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相关模块).

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<?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官方手册,本文后面也有示例代码下载.

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<?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相同,结果如下:
20-yaf-zend2-jmeter
现在测试数据好看多了,平均响应时间提高到81毫秒, 每秒响应请求数提高到241个. 其实我还测试过没有集成zf2服务管理器的yaf性能,每秒请求数可以达到1200(这是11年的I3移动版CPU的老笔记本电脑,每秒1200已经很强了),但是不提供服务的项目性能再好都没有意义.

这是一个完整的yaf集成zf2的项目示例.下载后请把把其中的dist配置文件复制并去掉.dist扩展名.yaf-sample-d771c9a.tar

Leave a Reply

Your email address will not be published. Required fields are marked *

Time limit is exhausted. Please reload the CAPTCHA.

Proudly powered by WordPress   Premium Style Theme by www.gopiplus.com