최신 PHP에 대응하는 eAccelerator 빌드를 구하기 어려운 경우가 있는데, 더군다나 Win32 빌드가 버그가 있어서 캐시가 제대로 저장되지 않고 error_log에 오류를 뿌려대는 버그가 있습니다.

...
EACCELERATOR: Open for write failed for "C:/tmp/eaccelerator/0/3/2//eaccelerator-322dca16f42e18e9c9905165d150e901": No such file or directory
...

이를 수정하기 위해서 win32 빌드를 직접 해 보았습니다.

빌드하는 방식은 다음 사이트를 참고하여 정확히 따라하면 완벽하게 빌드됩니다.

 How to build APC or eAccelerator for PHP on windows

(블로그를 검색해보면 한글로 설명해놓은 곳도 많으니 참고하시기 바랍니다.)

캐시 디렉토리가 잘 못 지정되는 버그에 대한 패치는 다음과 같습니다.

--- a/eaccelerator.c
+++ b/eaccelerator.c
@@ -396,7 +396,11 @@ int eaccelerator_md5(char* s, const char* prefix, const char* key TSRMLS_DC)
     PHP_MD5Update(&context, (unsigned char*)key, strlen(key));
     PHP_MD5Final(digest, &context);
     make_digest(md5str, digest);
+#ifndef ZEND_WIN32
     snprintf(s, MAXPATHLEN-1, "%s/%d/", EAG(cache_dir), ea_mm_instance->cache_dir_uid);
+#else
+    snprintf(s, MAXPATHLEN-1, "%s/", EAG(cache_dir));
+#endif
     n = strlen(s);
     for (i = 0; i < EACCELERATOR_HASH_LEVEL && n < MAXPATHLEN - 1; i++) {
         s[n++] = md5str[i];


이 문제는 커밋 "Windows reverted to the old way, to hard to get a uid."의 부족분을 약간 더 수정한 것입니다.

다음 첨부파일은 PHP 5.4.19 소스를 바탕으로 컴파일된 결과물이며, TS / NTS 버전을 각각 포함하고 있습니다.

eAccelerator-v1.0-php-5.4.xy.zip

※ 주의: eAccelerator는 PHP 최신의 일부 기능이 제대로 안되는 문제가 있으며, 여기에 첨부된 dll은 이 문제에 관련된 패치가 전혀 적용되어 있지 않으니 주의하셔야 합니다.

by dumpcookie 2013. 8. 28. 19:38

예전부터 하려던 것을 이번 모니위키 1.2.0 릴리스를 내놓으면서 이것 저것 정리하면서 시도해보았습니다.

인터넷에 검색해보면 http://www.xpressengine.com/tip/15921874같은 내용처럼 모니위키를 구버전 XE에서 연동시키는 소스를 볼 수 있지만 위의 예에서 연동시키는 방식으로는 모니위키의 ACL 플러그인을 쓰지 못할 뿐만 아니라, XE의 구조가 조금만 바뀌어도 쓸 수 없게 되어버리기때문에 그리 권장할 만한 구현 방식이 아닙니다.

XE의 회원을 연동하기 위해서는 nFORGE 회원과 모니위키를 연동시킨 방식처럼 "회원이 로그인 된 상태인 경우 강제로 모니위키 회원 정보를 생성하는 방식"으로 접근하면 편리합니다. 이 경우에는 Security 플러그인을 확장하는 방식이 아니라 모니위키의 회원 클래스인 WikiUser 클래스를 확장하는 방식을 사용하게 됩니다.

class User_xe17 extends WikiUser {
    function User_xe17($id = '') {
        global $DBInfo;

        define('__XE__', true);

        $zbxe_root_dir = !empty($DBInfo->zbxe_root_dir) ?
                $DBInfo->zbxe_root_dir : __DIR__.'/../../../xe'; // XE root dir

        require_once($zbxe_root_dir."/config/config.inc.php");

        $context = &Context::getInstance();
        $context->init(); // 여기서 매우 느리다.
        ...

(위의 방식은 제가 직접 찾은 방법이나, 이러한 방식으로 할 수 있다는 내용을 블로그를 검색해보니 찾을 수 있더군요)

그러나 위와 같은 방식으로 하면 모니위키가 너무 너무 느려지기때문에 간소화된 init()을 다음과 같은 식으로 호출하는 것이 낫습니다.

    function xe_context_init($xe) {
        //
        // simplified XE context init method to speed up
        //

        // set context variables in $GLOBALS (to use in display handler)
        $xe->context = &$GLOBALS['__Context__'];
        $xe->context->_COOKIE = $_COOKIE;

        $xe->loadDBInfo();

        // set session handler
        if (Context::isInstalled() && $this->db_info->use_db_session == 'Y') {
            $oSessionModel = getModel('session');
            $oSessionController = getController('session');
            session_set_save_handler(
                    array(&$oSessionController, 'open'),
                    array(&$oSessionController, 'close'),
                    array(&$oSessionModel, 'read'),
                    array(&$oSessionController, 'write'),
                    array(&$oSessionController, 'destroy'),
                    array(&$oSessionController, 'gc')
           );
        }
        session_start();

        if ($sess = $_POST[session_name()]) {
            session_id($sess);
        }
    }

    function User_xe17($id = '') {
        global $DBInfo;

        define('__XE__', true);

        $zbxe_root_dir = !empty($DBInfo->zbxe_root_dir) ?
                $DBInfo->zbxe_root_dir : __DIR__.'/../../../xe'; // XE root dir

        require_once($zbxe_root_dir."/config/config.inc.php");

        $context = &Context::getInstance();
        $this->xe_context_init($context); // simplified init context method $context->init(); 대신에 사용.

        $oMemberModel = &getModel('member');
        $oMemberController = &getController('member');

        if ($oMemberModel->isLogged()) {
            ...

참고적으로 위와같이 간소화된 context 초기화 루틴을 사용하는 경우 기존의 context->init()을 사용하는 것보다 거의 20배가 빨랐습니다. (아파치 벤치 ab -n 500 -c 5로 로컬에서 측정한 경우. XE 회원 연동을 아예 하지 않는 경우가 그렇지 않은 경우보다 2.5배 빠름)

이를 좀 더 개선하여 Varnish cache를 사용할 수 있도록 고쳤으며, 최초 접속하는 경우에만 사용자 정보를 DB를 통해 가져오는 절차를 거치고 그 이후에는 세션을 통해서 인증을 하게 되므로 속도 저하가 없어졌습니다.

여기서 사용한 방식을 응용하면 XE1.7의 회원 정보를 보다 빠르게 타 PHP에서 연동시킬 수 있을 것으로 생각됩니다.

완전한 소스는 http://moniwiki.kldp.net/wiki.php/XeUserPlugin 에서 받으실 수 있습니다.

특징 및 기타 변경사항

- 버전 1.5부터는 모니위키 1.2.5에 기본 내장 예정 (2015/04/30)
- XE 1.7, XE 1.8 모두 지원함
- Varnish cache 지원 가능. XE 사용자 모듈을 사용하는데에 따르는 속도 저하가 없음 (2015/4/30 수정)

by dumpcookie 2013. 7. 5. 05:48

드디어 모니위키 속도가 300RPS의 벽을 깼다.

불필요한 초기화 부분 한 줄 제거했는데 이렇게 나옴;; https://github.com/wkpark/moniwiki/commit/bc662afdc2268e86154da8a6abfe61c0a93432c8 참조

아래는 nginx + php-fpm + eaccelerator를 설정하고 모니위키 1.2.0 개발판 테스트해본 결과.

아파치를 쓰더라도 거의 비슷하지만 nginx + php-fpm의 경우가 좀 더 빠른 듯.


아래는 몇 번을 반복했을 때 가장 높은 측정값이 나온 경우.

$ ab -c 5 -n 300 http://localhost:8800/moniwiki/wiki.php?FrontPage
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Finished 300 requests


Server Software:        nginx/0.8.54
Server Hostname:        localhost
Server Port:            8800

Document Path:          /moniwiki/wiki.php?FrontPage
Document Length:        30134 bytes

Concurrency Level:      5
Time taken for tests:   0.778 seconds
Complete requests:      300
Failed requests:        0
Write errors:           0
Total transferred:      9088800 bytes
HTML transferred:       9040200 bytes
Requests per second:    385.65 [#/sec] (mean)
Time per request:       12.965 [ms] (mean)
Time per request:       2.593 [ms] (mean, across all concurrent requests)
Transfer rate:          11409.94 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   1.2      0      16
Processing:     5   13   4.7     12      43
Waiting:        0   12   4.7     11      43
Total:          5   13   4.9     12      44

Percentage of the requests served within a certain time (ms)
  50%     12
  66%     13
  75%     14
  80%     15
  90%     18
  95%     22
  98%     27
  99%     29
 100%     44 (longest request)

config.php의 설정값

$trail=0; // trail 끔
$cachetime=60*60*24; // 캐시 설정

by dumpcookie 2013. 5. 21. 08:56