태터데스크 관리자

도움말
닫기
적용하기   첫페이지 만들기

태터데스크 메시지

저장하였습니다.
밤하늘의 실제별, 나도 가질 수 있다?!

[Flex] AMFPHP 속도 테스트

2007/07/18 13:23

 

[공지]이미지나 링크가 깨졌다면 댓글 부탁드립니다.

AMFPHP, OpenAMF, XML,JSON,TEXT에 대한 전체 속도 테스트에 관련된 내용은 http://blog.jidolstar.com/167 를 참고한다.


사용자 삽입 이미지
Flex 와 AMFPHP(http://www.amfphp.org/) 간에 데이터 전송속도를 확인했다.

AMFPHP는 PHP를 이용해 Array형태로 객체형태로 데이터를 전송하기 위해 사용한다. 가볍고 사용하기 편할 뿐만 아니라 무료이기 때문에 실무에도 많이 쓰이고 있다고 들었다.

AMFPHP는 ActionScript Message Format(AMF)의 형태로 데이터를 하나의 객체로 송수신하는 형태를 가진다. LCDS(구, FDS), BlazeDS, OpenAMF를 이용하면 AMF형태로 송수신할 수 있다. 그런데 이들은 Java로 작성되어 있기 때문에 PHP환경에서 하려면 AMFPHP를 이용해야할 것이다.

이미 사용하신 분들이라면 다들 아는 내용이긴 하지만 관련 한글로된 문서도 없고 개인적으로 AMFPHP에 대해서 알 필요가 있어서 직접 테스트 해보았다.

Mike Potter의 "PHP and Flex - JSON, XML or AMFPHP?" 에서는 XML, JSON, AMFPHP에서 Array를 이용하여 건당 Load속도를 체크했다. 나는 이와 더불어 보통 TEXT와 AMFPHP에서 String방식으로 전송했을 때를 추가했다.

지금부터 테스트 환경을 구축하고 실제 결과를 보여주도록 하겠다.

AMF 다운로드 및 설정


먼저 AMFPHP 1.9 Beta 2를 다운로드 받아 웹디렉토리에 위치시킨다.

amfphp/browser/service-config.xml을 아래와 같이 수정한다.

services-config.xml 중... (Language : xml)
<channels>
    <channel-definition id="my-amfphp" class="mx.messaging.channels.AMFChannel">
        <endpoint uri="http://[도메인 또는 IP]/디렉토리/amfphp/gateway.php" class="flex.messaging.endpoints.AMFEndpoint"/>
    </channel-definition>
</channels>

만약 한글을 써야한다면 amfphp/gateway.php 에서 $gateway->setCharsetHandler() 함수를 아래와 같이 수정한다.

gateway.php 중 (Language : php)
$gateway->setCharsetHandler("iconv", "euc-kr", "utf-8");

테스트용 PHP 프로그램

아래 print_xml.php, print_json.php, print_text.php를 웹디렉토리 안에 위치시킨다.
JSON 데이타를 만들기 위해 ZEND 프레임워크에 있는 Json encode()를 사용했다.

print_xml.php (Language : php)
<?
print "<rows>";

for( $i = 0; $i < 500; $i++ )
{
    print "<row><col1>This is row ".$i."</col1><col2>10000000</col2><col3>More text to add to this row.</col3></row>";
}

print "</rows>";
?>

print_json.php (Language : php)
<?
include( "Zend/Json.php" );

for( $i = 0; $i < 500; $i++ )
{
    $Col1 = "This is row ".$i;
    $Col2 = "10000000";
    $Col3 = "More text to add to this row.";
    $ThisRow = array("col1"=>$Col1, "col2"=>$Col2, "col3"=>$Col3 );
    $TotalArray[] = $ThisRow;
    //print "<row><col1>This is row ".$i."</col1><col2>10000000</col2><col3></col3></row>";
}
print Zend_Json::encode($TotalArray);
?>

print_text.php (Language : php)
<?
for( $i = 0; $i < 500; $i++ )
{
    print "This is row ".$i."|10000000|More text to add to this row.\n";
}
?>


아래 sample.php는 amfphp/service/ 폴더에 위치시킨다.

sample.php (Language : php)
<?php
// Create new service for PHP Remoting as Class
class sample
{
    function sample ()
    {
        // Define the methodTable for this class in the constructor
        $this->methodTable = array(
            "getData" => array(
                "description" => "Return a list of data",
                "access" => "remote",
                "returns" => "string"
            )
        );
    }

    function getData( $obj )
    {
        $num = 500;
        if( $obj[0]['bText'] == false )
        {
            for( $i = 0; $i < $num; $i++ )
            {
                $Col1 = "This is row ".$i;
                $Col2 = "10000000";
                $Col3 = "More text to add to this row.";
                $ThisRow = array("col1"=>$Col1, "col2"=>$Col2, "col3"=>$Col3 );
                $TotalArray[] = $ThisRow;
            }
            return $TotalArray;
        }
        else
        {
            $str="";
            for( $i = 0; $i < $num; $i++ )
            {
                $Col1 = "This is row ".$i;
                $Col2 = "10000000";
                $Col3 = "More text to add to this row.";
                $ThisRow = $Col1."|".$Col2."|".$Col3."\n";
                $str .= $ThisRow;
            }
            return( $str );
        }
    }
}
?>


Flex  소스

아래는 Flex 코드이다. AmfPhpSpeedTest 이름으로 프로젝트를 생성후 아래 2개 파일을 프로젝트 폴더에 위치시킨다.

만약 JSON Lib가 없는 경우 http://labs.adobe.com/wiki/index.php/ActionScript_3:resources:apis:libraries#corelib 에서 corelib를 다운로드 받아서 corelib.swc파일을 자신의 프로젝트 라이브러리에 추가해야 한다.
이와 관련된 내용는 http://tong.nate.com/lhs0806/28443586 를 참고한다.

RemotingConnect.as (Language : xml)
package
{
    import flash.net.NetConnection;
    import flash.net.ObjectEncoding;

    public class RemotingConnection extends NetConnection
    {
    public function RemotingConnection( sURL:String )
        {
            objectEncoding = ObjectEncoding.AMF0;
            if (sURL) connect( sURL );
        }
       
        public function AppendToGatewayUrl( s : String ) : void
        {
            //
        }
    }
}

AmfPhpSpeedTest.mxml (Language : xml)
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
    <mx:Script>
        <![CDATA[
            import mx.collections.ArrayCollection;
            import mx.rpc.events.ResultEvent;
            import com.adobe.serialization.json.JSON;
           
            public var gateway : RemotingConnection;
            private var bText:Boolean = false;

            [Bindable]
            public var startTime:Date;
           
            [Bindable]
            public var endTime:Date;
           
            [Bindable]
            public var elapsedTime:Number;
           
            public var applicationData:ArrayCollection;
           
            public function sendXML():void
            {
                startTime = new Date();
                http_service_xml.send();
            }
           
            public function sendJSON():void
            {
                startTime = new Date();
                http_service_json.send();
            }

            public function sendTEXT():void
            {
                startTime = new Date();
                http_service_text.send();
            }
           
            public function sendAMFPHP( bText:Boolean ):void
            {
                this.bText = bText;
                startTime = new Date();
                gateway = new RemotingConnection( "http://192.168.0.200/ZendFlex_PHPPrintXML/amfphp/gateway.php" );
                gateway.call( "sample.getData", new Responder(onResult, onFault), new Array({"bText":bText}));
            }
           
            public function handleXML(event:ResultEvent):void
            {
                datagrid.dataProvider = event.result.rows.row;
                endTime = new Date();
                elapsedTime = endTime.getTime() - startTime.getTime();
            }
           
            public function handleTEXT(event:ResultEvent):void
            {
                var arr:Array = new Array();
                var line:Array = String(event.result).split("\n"); 
                var col1:String;
                var col2:String;
                var col3:String;
                for( var i:int; i < line.length; i++)
                {
                    var temp:Array = line[i].split("|");
                    arr.push( {"col1":temp[0], "col2":temp[1], "col3":temp[2] } )
                }          
                datagrid.dataProvider = new ArrayCollection( arr );  
                endTime = new Date();
                elapsedTime = endTime.getTime() - startTime.getTime();
                       
            }
           
            public function handleJSON(event:ResultEvent):void
            {
                //get the raw JSON data and cast to String
                var rawData:String = String(event.result);
               
                //decode the data to ActionScript using the JSON API
                //in this case, the JSON data is a serialize Array of Objects.
                var arr:Array = (JSON.decode(rawData) as Array);
               
                //create a new ArrayCollection passing the de-serialized Array
                //ArrayCollections work better as DataProviders, as they can
                //be watched for changes.
                var dp:ArrayCollection = new ArrayCollection(arr);
               
                //pass the ArrayCollection to the DataGrid as its dataProvider.
                datagrid.dataProvider = dp;
                endTime = new Date();
                elapsedTime = endTime.getTime() - startTime.getTime();
            }
           
            public function onResult( result:* ) : void
            {
                if( true == bText )
                {
                    var arr:Array = new Array();
                    var line:Array = String(result).split("\n"); 
                    var col1:String;
                    var col2:String;
                    var col3:String;
                    for( var i:int; i < line.length; i++)
                    {
                        var temp:Array = line[i].split("|");
                        arr.push( {"col1":temp[0], "col2":temp[1], "col3":temp[2] } )
                    }          
                    datagrid.dataProvider = new ArrayCollection( arr );
                }
                else
                {
                    datagrid.dataProvider = new ArrayCollection(  result as Array );
                }
                endTime = new Date();
                elapsedTime = endTime.getTime() - startTime.getTime();
            }


            public function onFault( fault : String ) : void
            {
                trace( fault );
            }
        ]]>

    </mx:Script>
    <mx:HTTPService result="handleXML(event)" url="http://192.168.0.200/ZendFlex_PHPPrintXML/print_xml.php" id="http_service_xml">
    </mx:HTTPService>
    <mx:HTTPService result="handleJSON(event)" url="http://192.168.0.200/ZendFlex_PHPPrintXML/print_json.php" id="http_service_json">
    </mx:HTTPService>
    <mx:HTTPService result="handleTEXT(event)" url="http://192.168.0.200/ZendFlex_PHPPrintXML/print_text.php" id="http_service_text">
    </mx:HTTPService>
    <mx:DataGrid id="datagrid" right="10" left="10" top="50" bottom="100">
        <mx:columns>
            <mx:DataGridColumn headerText="Column 1" dataField="col1"/>
            <mx:DataGridColumn headerText="Column 2" dataField="col2"/>
            <mx:DataGridColumn headerText="Column 3" dataField="col3"/>
        </mx:columns>
    </mx:DataGrid>
    <mx:Text text="{startTime}" x="6" bottom="74"/>

    <mx:Text text="{endTime}" x="10" bottom="48"/>
   
    <mx:Text text="{elapsedTime}" x="10" bottom="18"/>
    <mx:Button x="11" y="10" label="Call XML" click="sendXML()"/>
    <mx:Button x="91" y="10" label="Call JSON" click="sendJSON()"/>
    <mx:Button x="178" y="10" label="Call TEXT" click="sendTEXT()"/>
    <mx:Button x="403" y="10" label="Call AMFPHP-Array" click="sendAMFPHP(false)"/>
    <mx:Button x="265" y="10" label="Call AMFPHP-TEXT" click="sendAMFPHP(true)"/>
</mx:Application>
 


위 코드에서 빨간색으로 되어진 부분은 해당 주소로 변경해준다.
만약 php소스가 로컬에 위치하지 않는다면 아래와 같은 crossdomain.xml 를 웹디렉토리에 위치시킨다.

crossdomain.xml (Language : xml)
<?xml version="1.0"?>
<cross-domain-policy>
   <allow-access-from domain="*"/>
</cross-domain-policy>
 

실행결과 (현재 테스트 할 수 없습니다.)



 

분석결과


아래 표에 나온 데이타는 해당 건당 각 방식의 데이타 Load완료 시간이다. 시간은 10번씩 테스트 해서 나온 평균 시간이다. 단위는 ms(1000ms = 1sec)이다.

  XML JSON TEXT AMFPHP-TEXT AMFPHP-ARRAY
30000건 6017 9273 640 697 6649
20000건 3719 6003 418 384 4331
10000건 1890 2965 193 209 2067
5000건 915 1469 103 103 1036
1000건 177 359 40 53 175
500건 99 184 40 47 131

보는바와 같이 AMFPHP가 Array 방식으로 쓰는 장점이 있는 것 외에는 속도면에서 좋은 포퍼먼스를 보여주지 못한다. 오히려 XML로 전송했을때보다 속도면에서 좋지 못하다.

보통 TEXT방식으로 전송할때와 AMFPHP를 이용해 TEXT로 전송할때를 보면
별로 차이가 없다는 것을 발견할 수 있다.

결론을 말하자면
  1. 순수하게 구분자(| 나 \n 등)를 이용한 순수 TEXT방식으로 데이타를 전송하는 것이 가장 좋은 포퍼먼스를 보인다. 속도를 위해서라면 순수 TEXT방식을 사용하자.
  2. 대용량 데이타 전송시 AMFPHP를 사용하는 것은 적절하지 못하다.
  3. 데이타에 대한 표준규약이 관계가 있을때만 XML 또는 JSON을 사용한다.
  4. Array형태로 데이타를 전송할 필요가 있을때 AMFPHP를 사용하면 편하다.
  5. 데이타 전송속도를 최상으로 올리기 위해서는 FDS(Flex Data Service)를 사용한다. 하지만 유료라는거... ㅡㅡ;

더욱 세밀하게 테스트 하기 위해서는 데이타를 만드는 시간과 Flex에서 이를 Parsing하는 시간도 Check해야한다. 그리고 만약 데이타베이스에서 읽어올 때는 데이타베이스 접속시간과 Query수행시간도 고려해야할 것이다.

참고로 위의 테스트 결과는 100MB의 LAN환경에서 시험해본 것이기 때문에 실제론 저 시간보다 더 걸린다.


테스트 소스 다운로드

소스는 아래 링크에서 다운로드 받을 수 있다.


참고사이트


AMFPHP 공식사이트 - http://amfphp.org/
Amfphp 1.9 beta 2 - ridiculously faster - http://www.5etdemi.com/blog/archives/2007/01/amfphp-19-beta-2-ridiculously-faster/
PHP and Flex - JSON, XML or AMFPHP? - http://blogs.adobe.com/mikepotter/2006/07/php_and_flex_js.html
Using Flex2 and AMFPHP : http://www.adobe.com/devnet/flex/articles/flex2_amfphp.html
Using Amfphp 1.9 with the Adobe Flex 2 SDK : http://howtoforge.com/amfphp_adobe_flex2_sdk
Flex RemoteObject and AMFPHP 1.9  : http://www.sephiroth.it/tutorials/flashPHP/flex_remoteobject/
Zend Framework : http://framework.zend.com/home


글쓴이 : 지돌스타(http://blog.jidolstar.com/164)
크리에이티브 커먼즈 라이선스
Creative Commons License

Adobe Flash Platform , , ,

Trackback 주소: http://blog.jidolstar.com/trackback/164
  1. 2007/07/19 17:40
    [Flex] OpenAMF-Java Flash Remoting 속도테스트 Tracked from 지돌스타 블로그
  2. 2007/07/20 11:18
  1. Blog Icon
    째코

    돌스타님 원래 php하셧나요?

  2. 예~ 약간 할 줄 압니다. ^^

  3. Blog Icon
    째코

    넹 걍 한번 물어봤어염

  4. Blog Icon
    플렉서

    안녕하세요? 위에거 해보고싶어서 지돌스타님 설명대로 그대로 따라했는데, 3개 에러가 생기고 실행이 안되네요. 좀 봐주세요ㅠㅠ
    1.Unable to load SWC corelib.swc: multiple points
    2.1017:기본 클래스 정의를 찾을수 없습니다.
    3.Could not resolve <mx:Application> to a component implementation
    이렇게 3개요.
    좀 도와주세요. 어떻게해야되요?

  5. 만약 JSON Lib가 없는 경우 http://labs.adobe.com/wiki/index.php/ac ··· 3corelib 에서 corelib를 다운로드 받아서 corelib.swc파일을 자신의 프로젝트 라이브러리에 추가해야 한다.
    이와 관련된 내용는 http://tong.nate.com/lhs0806/28443586 를 참고한다.

    이 내용을 참고하시지 않은 것 같군요.

  6. Blog Icon
    플렉서

    그거보고 했거든요.
    글중에 2번째 Show View 만 제 플렉스메뉴에는 Other views로 되어있어서 그거만 틀리게 하고 처음하는거라 조심조심똑같이 했는데, 안되요ㅠㅠ
    벌써 5번째똑같은것만 계속...

  7. 음... Unable to load SWC corelib.swc 이 걸립니다.
    이걸 로드할 수 없다는 것인데... 전 잘되거든요. corelib.swc를 찾아야합니다. 프로젝트 속성에서 Flex Build Path에서 Library path에 corelib.swc를 넣은 것은 맞죠?
    전 사실.. 아무 문제 없이 잘 되거든요.

  8. Blog Icon
    플렉서

    계속 실패해서 힘드네요. 이제 1주일됐는데, 어려운거 해보려고 하니까 ㅋ.
    혹시 그걸 하면 corelib.swc가 Navigator패널에 그게 들어와야하나요?
    들어와서 그파일이 보여야 하나요?
    만약 그렇다면 그게 안들어왔네요. bin폴더나 html-template폴더에도 그파일은 찾을수가 없네요.

  9. Blog Icon
    플렉서

    아 하나면 더요. 자꾸 물어봐서 죄송....
    처음에 하는 Open Perspective > Flex Development하니까 아무런 반응이 없는데, 맞는건가요?

    아또 하나급질문이 생각났네요.
    corelib.swc가 들어가는 폴더를 보니까 6개는 압축파일이고, 하나는 폴더이고,
    corelib.swc만 혼자 swc파일인데, 맞는건가요?

    그럼, 해결되는 친절한 답변 기다리고 있을께요. 지돌스타님^^
    ㅋ 저 오늘 지돌스타님이 괜히 좋아졌어요. 팬될거같아요.ㅋㅋ

  10. Blog Icon
    플렉서

    헐...해결됐어요.
    이것저것해보다가, 그림에는 있었지만, 설명에는 없어서 그냥 기본값으로
    설치했던, 마지막부분
    Open Perspective > Flex Development
    Build path에서 Main source folder를 bin폴더로 지정하니까
    에러가 하나도 없이 컴파일잘됐어요.
    신난다~~~~

    근데, 실행화면 익스플로러에는 아무것도 안나타나넹..왜그러지...
    또 이것저것해봐야겠네요.^^
    혹시 아세요? 에러는 없어졌는데, 화면에는 배경색만 들어가있고
    아무것도 없는지...

  11. 그렇군요. 꼭 잘 하셔서 원하는 결과 나오길 바래요~
    화이팅입니다.
    이 문서에 문제가 있다면 꼭 지적 부탁드리고요 ^^

  12. Blog Icon
    구르미

    안녕하세요
    항상 보고만 다니다 오늘 한번 예제를 따라해보았습니다.
    좋은글 감사합니다.
    지돌님 사양하고는 약간차이가 나지만
    php 익스텐션중에 amfext라것을 설치하니까
    50000만 건정도에서는 xml만 쓰는 것보다는 10%-30% 정도빨라지는
    것같네요 그냥 심심해서 연습하던중에 참고로 적어보았습니다. ^^

  13. 좋은 정보 감사합니다.
    이 글쓴게 꽤 오래되서 지금은 더 좋은 것들이 많을 거예요.
    ZendAMF 같은 것은 AMF3 스펙에 따라 설계되었기 때문에 이것을 사용하면 PHPAMF보다 훨씬 좋은 퍼포먼스를 보일겁니다. ^^

  14. Blog Icon
    구르미

    예 그렇겠군요
    amfphp설계자가 zendamf를 만드는데 참여하고 있다는 것 같은데^^ 그리고 제가 패스워드를 않넣고 글을 써서 수정이 안되네요 5000만이 아니고 50000건이네요 미안합니다. 좋은 일요일 되세요 자주 놀러 오겠습니다. ^^

  15. 네~ 자주 뵈요 ^^