Flex HTTPService performance with MySQL and PHP: XML : 1 - AMF : 0
French version - Traduire/Translate (by Google): Espagnol - Deutsch
We present various methods to improve the performance of reading XML through an HTTPService.
Updated : 4 may 2010 see History
PerfReadXML.zip - 6 081 Ko (Flex 3 Export Project with Zend Framework 1.10.4 minimal)
PerfReadXMLnozend.zip - 500 Ko (Flex 3 Export Project without Zend Framework)
This article compares 9 methods for reading data (consist of 1 000 to 10 000 records) to fill a datagrid.
A record is a customer described by a id, a lastname, a firstname, gender (male/female), a address and a city.
The tests use a MySQL database (to store data) and the PHP language to write Web-Services on an Apache server.
Optimizations concern the Flex part and the flow between server and browser. Access to data in MySql are the same for all examples.
All the tests use a shared hosting (a 240plan in OVH - phpinfo) that allows the manipulation of the .htaccess file.
If you have a private or dedicated server, it's recommended to modify the httpd.conf file.
The "basic" method with XML nodes : e4x_node
To represent the data, we use XML nodes. Here is how a customer is described:
<customer>
<id>1</id>
<lastname>Deschamps</lastname>
<firstname>Amandine</firstname>
<sex>1</sex>
<address>23, rue de la Lancette</address>
<city>Yerres</city>
</customer>
In Flex, we use a HTTPService component to read data. The data are transformed into Flex objects and we displays them in a Datagrid via a dataProvider (ArrayCollection).
Disadvantage: For a data (name of customer), the names of XML nodes are (sometimes) bigger than the data itself. For example: we use <lastname>Deschamps</lastname>
(30 characters) for the lastname Deschamps
(9 characters).
The method with XML attributes : e4x_attribute
The idea behind this method is to use attributes instead of nodes and see the impact on the performance.
Here is how a customer is described:
<customer id="1" lastname="Deschamps" firstname="Amandine" sex="1" address="23, rue de la Lancette" city="Yerres"/>
Disadvantage: For a data (name of customer), the names of XML attributes are (sometimes) bigger than the data itself. For example: we use lastname="Deschamps"
(20 characters) for the lastname Deschamps
(9 characters).
The method with short attributes : e4x_short_attribute
In this method, we use attributes with short names: 1 to 2 letters.
A customer is represented as follows:
<c id="1" ln="Deschamps" fn="Amandine" s="1" ad="23, rue de la Lancette" ci="Yerres"/>
Enable XM compression
When you have 1000 to 10000 records, a good pratice is to compress the XML file between the server and browser. It's a speed tip proposed by pageSpeed of Google.
For this, we enable dynamic compression of Apache. On a shared server, we modify the .htaccess file in the directory of web-services to activate mod_deflate.
In our example, we enable compression for text/xml and application/xml MIME type and disable compression for image:
<IfModule mod_deflate.c>
SetOutputFilter DEFLATE
# Insert filter on selected content types only
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE application/xml
# Don't compress images
SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png)$ no-gzip dont-vary
# Make sure proxies don't deliver the wrong content
Header append Vary User-Agent env=!dont-vary
</IfModule>
We will use the 3 methods above to see the performance impact:
e4x_node_gzip
e4x_attribute_gzip
e4x_short_attribute_gzip
How to check if compression is enable or not ?
For this, we use Firefox and theHttpFox extension : an HTTP analyzer to watch request and response headers.
Just manually browse the web service and check the answer with this extension:
http://www.inwayvideo.com/xmlperf/ws_gzip/gzipcustomerAttribute.php
Check gzip for Content-Encoding in Response Header
Content type application/xml, text/xml and browser cache : e4x_cache_attribute_gzip
If your data aren't modified very often (every 4 hours or days or weeks or months), the idea is to use the browser's cache to store query results.
An HTTPService with POST mode forces a refresh for a page: the results will not be kept in the browser cache.
The idea is to use the GET method. But there is a subtlety in Flex. If you use the contentType (or MIME type) text/xml, Flex converts the GET method in POST method (and thus prevents the browser from storing the result in the cache). See the send method in mx.rpc.http.HTTPService (in Flex 3 SDK and SDK 3.3) or sendBody method in mx.rpc.http.AbstractOperation (in Flash Flash Builder 4 and SDK 3.5.0)
if (contentType == CONTENT_TYPE_XML && message.method == HTTPRequestMessage.GET_METHOD)
message.method = HTTPRequestMessage.POST_METHOD;
To activate the browser's cache, you must use the GET method with contentType text/xml.
If you have a sharing hosting, check the compression for the different MIME types. At OVH, the compression is enabled for application/xml but the compression is disabled for text/xml.
To enable the compression for text/xml and application/xml and have a cache of 30 minutes, configure the .htaccess file in the web-services directory as follows :
<IfModule mod_deflate.c>
SetOutputFilter DEFLATE
# Insert filter on selected content types only
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE application/xml
</IfModule>
<IfModule mod_expires.c>
#CACHE ExpiresActive on
#xml
ExpiresByType text/xml "access plus 30 minutes"
#tout le reste
#ExpiresDefault "modification plus 2 years"
ExpiresDefault "access plus 2 days"
</IfModule>
This is the fastest method. With this method, click twice on the button. During the second click, the cache of the browser is used.
Method Amfphp : amfphp
Amfphp has been the first open-source solution using the protocol AMF (Action Message Format) on PHP.
Unfortunately, this project is no longer maintained. This project has a new version. The advantage of this method is to use a class mapping between PHP and Flex (to avoid encoding and decoding between the different languages).
In our example, we use amfphp 1.9 version of 2010-02-02.
Method Zend-AMF : zend_amf
This method uses Zend-AMF. As in AMFPHP, we use the class mapping between PHP and Flex.
In our example, we use Zend Frameword version 1.9.7 1.10.4
Unfortunately, it's a "bad" good idea. Performances are not at the rendez-vous.
To try to improve performance, we use another method (zend_flex4). This method is based on the generated code in Flex 4 and gives better performance. This solution use the cast of the results in PHP with Zend-AMF (this method has no impact on the performance with amfphp).
For information, version 1.10.4 contains a patch to improve the performance of Zend AMF:
http://framework.zend.com/changelog/1.10.4
The performance is actually improved but are below AMFPHP and XML.
The Zend Framework is very interesting when you develop PHP applications, but on this particular example, we have performance problems on a shared server.
For performance problems with Zend-AMF, see :
http://framework.zend.com/issues/browse/ZF-7493
If you are an expert on AMF or Zend, don't hesitate to send me your tips to improve performance.
Results
You can test the application directly on our server and you will see that the fastest method is e4x_short_attribute_gzip.
Indeed, the Flash Player has a very efficient reader for XML with E4X.
Th browser's cache improves performance with the e4x_cache_attribute_gzip method. But that's normal: it uses the browser cache.
Installation on your server
To test the different methods on your server :
- Install the xmlperf directory on your PHP server (PHP5 is required).
- Configure the xmlperf/xmlperfDefine.php file:
- for access to MySQL (DATABASE_SERVER, DATABASE_USERNAME, DATABASE_PASSWORD, DATABASE_NAME)
- the maximum number of records to be generated: MAXIMUM_RECORDS (10 000 by default).
- the directory for Zend Framework : ZEND_LIBRARY (relative path to ws_zend directory)
By default, Zend Framework version "1.10.4 minimal" is included in xmlperf/zf directory.
- Run in your browser: http://www.myserver.com/xmlperf/generateDb/generateCustomer.php
This will generate MAXIMUM_RECORDS in your database.
The script takes about between 30 seconds and 1 minute.
- Run in your browser: http://www.myserver.com/xmlperf/flex
This allows to test different methods.
Sources
Sources files for Flex and PHP are delivered.
Flex
If you install the sources on your server, modify the serverURL variable in initApp and the two uri variables in the services-config.xml file (for amfphp and Zend Framework).
In Flex, we use a Customer class for XML. For class-mapping (for AMF), we use a CustomerVO class.
PHP
Each method uses a directory with ws prefix (for Web-services). Each directory contains the files called by the HTTPService component.
The directories are:
ws_nogzip : methods without compression:
- customerNode.php (list of customers with XML nodes),
- customerAttribute.php (list of customers with XML attributes)
- customerShortAttribute.php (list of customers with shortnames XML attributes).
ws_gzip : method with gzip compression
ws_amfphp : for amfphp.
- add two files into xmlperf/ws_amfphp/services directory: CustomerService.php and inway/CustomerVO.php.
- modify xmlperf/ws_amfphp/globals.php file to configure $voPath variable (for class-mapping)
- modify xmlperf/ws_amfphp/gateway.php for UTF-8 charset with $gateway->setCharsetHandler("iconv","UTF-8","UTF-8"); (assuming that iconv is installed on the server).
ws_zend : for Zend AMF
- add two files: CustomerService.php and gateway.php (for service and and class-mapping).
References and link
census: Ajax and Flex Data Loading Benchmarks from James Ward. In this test, AMF is the fastest method (but the method based on XML uses nodes and no compression).
http://www.jamesward.com/2007/04/30/ajax-and-flex-data-loading-benchmarks/
Zend AMF: http://framework.zend.com/download/latest (use zend framework version "1.x.x minimal" at the bottom of the page)
Wade Arnold blog author of Zend AMF
http://wadearnold.com/blog/Get to know Flex and Zend_Amf (Zend developper zone)
Data-centric Adobe Flash Builder development with the Zend Framework (Zend developper zone)
Amfphp: http://www.amfphp.org/
Sean Christmann: Optimizing Adobe AIR for Code Execution, Memory, and Rendering
MySQL Performance Blog: http://www.mysqlperformanceblog.com
Optimizing and configuring Apache: http://www.askapache.com/
History
version 1.02 - 4 May 2010
- updated to Zend Framework version 1.10.4 (Released 2010-04-28)
This version has a patch for Zend AMF performance issue
- updated to amfphp 1.9 (Released 2010-02-02)
version 1.01 - 14 January 2010
- updated to Zend Framework version 1.9.7
version 1.00 - 10 January 2010
- Initial release with :
. amfphp 1.9 beta2 - 2008-01-20
. Zend Framework 1.9.6