
Advanced PHP Programming
.pdf
388 Chapter 15 Building a Distributed Environment
Reliance on auto-detection is discouraged, though. As the developer, you have a much better idea of where a query should be serviced than auto-detection does.The mysqli interface provides assistance in this case as well. Acting on a single resource, you can also specify a query to be executed either on a slave or on the master:
$dbh = mysqli_init();
mysqli_real_connect($dbh, $host, $user, $password, $dbname);
mysqli_slave_query($dbh, $readonly_query);
mysqli_master_query($dbh, $write_query);
You can, of course, conceal these routines inside the wrapper classes. If you are running MySQL prior to 4.1 or another RDBMS system that does not seamlessly support automatic query dispatching, you can emulate this interface inside the wrapper as well:
class Mysql_Replicated extends DB_Mysql { protected $slave_dbhost;
protected $slave_dbname; protected $slave_dbh;
public function _ _construct($user, $pass, $dbhost, $dbname, $slave_dbhost, $slave_dbname)
{
$this->user = $user; $this->pass = $pass; $this->dbhost = $dbhost; $this->dbname = $dbname;
$this->slave_dbhost = $slave_dbhost; $this->slave_dbname = $slave_dbname;
}
protected function connect_master() {
$this->dbh = mysql_connect($this->dbhost, $this->user, $this->pass); mysql_select_db($this->dbname, $this->dbh);
}
protected function connect_slave() {
$this->slave_dbh = mysql_connect($this->slave_dbhost, $this->user, $this->pass);
mysql_select_db($this->slave_dbname, $this->slave_dbh);
}
protected function _execute($dbh, $query) { $ret = mysql_query($query, $dbh); if(is_resource($ret)) {
return new DB_MysqlStatement($ret);
}
return false;
}


390 Chapter 15 Building a Distributed Environment
public function _ _construct() { $this->databases[0] = new DB_Mysql_Email0; $this->databases[1] = new DB_Mysql_Email1; $this->databases[2] = new DB_Mysql_Email2; $this->databases[3] = new DB_Mysql_Email3;
}
On both insertion and retrieval, you hash the recipient to determine which database his or her data belongs in. crc32 is used because it is faster than any of the cryptographic hash functions (md5, sha1, and so on) and because you are only looking for a function to distribute the users over databases and don’t need any of the security the stronger oneway hashes provide. Here are both insertion and retrieval functions, which use a crc32- based hashing scheme to spread load across multiple databases:
public function insertEmail(Email $email) { $query = “INSERT INTO emails
(recipient, sender, body) VALUES(:1, :2, :3)”;
$hash = crc32($email->recipient) % count($this->databases); $this->databases[$hash]->prepare($query)->execute($email->recipient,
$email->sender, $email->body);
}
public function retrieveEmails($recipient) {
$query = “SELECT * FROM emails WHERE recipient = :1”;
$hash = crc32($email->recipient) % count($this->databases);
$result = $this->databases[$hash]->prepare($query)->execute($recipient); while($hr = $result->fetch_assoc) {
$retval[] = new Email($hr);
}
}
Alternatives to RDBMS Systems
This chapter focuses on RDBMS-backed systems.This should not leave you with the impression that all applications are backed against RDBMS systems. Many applications are not ideally suited to working in a relational system, and they benefit from interacting with custom-written application servers.
Consider an instant messaging service. Messaging is essentially a queuing system. Sending users’ push messages onto a queue for a receiving user to pop off of. Although you can model this in an RDBMS, it is not ideal. A more efficient solution is to have an application server built specifically to handle the task.
Such a server can be implemented in any language and can be communicated with over whatever protocol you build into it. In Chapter 16,“RPC: Interacting with Remote Services,” you will see a sample of so-called Web services–oriented protocols. You will also be able to devise your own protocol and talk over low-level network sockets by using the sockets extension in PHP.

Further Reading |
391 |
An interesting development in PHP-oriented application servers is the SRM project, which is headed up by Derick Rethans. SRM is an application server framework built around an embedded PHP interpreter. Application services are scripted in PHP and are interacted with using a bundled communication extension. Of course, the maxim of maximum code reuse means that having the flexibility to write a persistent application server in PHP is very nice.
Further Reading
Jeremy Zawodny has a great collection of papers and presentations on scaling MySQL and MySQL replication available online at http://jeremy.zawodny.com/mysql/.
Information on hardware load balancers is available from many vendors, including the following:
nAlteon—www.alteon.com
nBigIP—www.f5.com
nCisco—www.cisco.com
nFoundry— www.foundry.com
nExtreme Networks—www.extremenetworks.com
nmod_backhand— www.backhand.org
Leaders in the field include Alteon, BigIP, Cisco, Foundry, and Extreme Networks. LVS and mod_backhand are excellent software load balancers.
You can find out more about SRM at www.vl-srm.net.


16
RPC: Interacting with Remote Services
SIMPLY PUT, REMOTE PROCEDURE CALL (RPC) services provide a standardized interface for making function or method calls over a network.
Virtually every aspect of Web programming contains RPCs. HTTP requests made by Web browsers to Web servers are RPC-like, as are queries sent to database servers by database clients. Although both of these examples are remote calls, they are not really RPC protocols.They lack the generalization and standardization of RPC calls; for example, the protocols used by the Web server and the database server cannot be shared, even though they are made over the same network-level protocol.
To be useful, an RPC protocol should exhibit the following qualities:
nGeneralized—Adding new callable methods should be easy.
nStandardized— Given that you know the name and parameter list of a method, you should be able to easily craft a request for it.
nEasily parsable—The return value of an RPC should be able to be easily converted to the appropriate native data types.
HTTP itself satisfies none of these criteria, but it does provide an extremely convenient transport layer over which to send RPC requests.Web servers have wide deployment, so it is pure brilliance to bootstrap on their popularity by using HTTP to encapsulate RPC requests. XML-RPC and SOAP, the two most popular RPC protocols, are traditionally deployed via the Web and are the focus of this chapter.

394 Chapter 16 RPC: Interacting with Remote Services
Using RCPs in High-Traffic Applications
Although RPCs are extremely flexible tools, they are intrinsically slow. Any process that utilizes RPCs immediately ties itself to the performance and availability of the remote service. Even in the best case, you are looking at doubling the service time on every page served. If there are any interruptions at the remote endpoint, the whole site can hang with the RPC queries. This may be fine for administrative or low-traffic services, but it is usually unacceptable for production or high-traffic pages.
The magic solution to minimizing impact to production services from the latency and availability issues of Web services is to implement a caching strategy to avoid direct dependence on the remote service. Caching strategies that can be easily adapted to handling RPC calls are discussed in Chapter 10, “Data Component Caching,” and Chapter 11, “Computational Reuse.”
XML-RPC
XML-RPC is the grandfather of XML-based RPC protocols. XML-RPC is most often encapsulated in an HTTP POST request and response, although as discussed briefly in Chapter 15,“Building a Distributed Environment,” this is not a requirement. A simple XML-RPC request is an XML document that looks like this:
<?xml version=”1.0” encoding=”UTF-8”?>
<methodCall>
<methodName>system.load</methodName>
<params>
</params>
</methodCall>
This request is sent via a POST method to the XML-RPC server.The server then looks up and executes the specified method (in this case, system.load), and passes the specified parameters (in this case, no parameters are passed).The result is then passed back to the caller.The return value of this request is a string that contains the current machine load, taken from the result of the Unix shell command uptime. Here is sample output:
<?xml version=”1.0” encoding=”UTF-8”?>
<methodResponse>
<params>
<param>
<value>
<string>0.34</string>
</value>
</param>
</params>
</methodResponse>

XML-RPC 395
Of course you don’t have to build and interpret these documents yourself.There are a number of different XML-RPC implementations for PHP. I generally prefer to use the PEAR XML-RPC classes because they are distributed with PHP itself. (They are used by the PEAR installer.) Thus, they have almost 100% deployment. Because of this, there is little reason to look elsewhere. An XML-RPC dialogue consists of two parts: the client request and the server response.
First let’s talk about the client code.The client creates a request document, sends it to a server, and parses the response.The following code generates the request document shown earlier in this section and parses the resulting response:
require_once ‘XML/RPC.php’;
$client = new XML_RPC_Client(‘/xmlrpc.php’, ‘www.example.com’); $msg = new XML_RPC_Message(‘system.load’);
$result = $client->send($msg); if ($result->faultCode()) {
echo “Error\n”;
}
print XML_RPC_decode($result->value());
You create a new XML_RPC_Client object, passing in the remote service URI and address.
Then an XML_RPC_Message is created, containing the name of the method to be called (in this case, system.load). Because no parameters are passed to this method, no additional data needs to be added to the message.
Next, the message is sent to the server via the send() method.The result is checked to see whether it is an error. If it is not an error, the value of the result is decoded from its XML format into a native PHP type and printed, using XML_RPC_decode().
You need the supporting functionality on the server side to receive the request, find and execute an appropriate callback, and return the response. Here is a sample implementation that handles the system.load method you requested in the client code:
require_once ‘XML/RPC/Server.php’;
function system_load()
{
$uptime = `uptime`;
if(preg_match(“/load average: ([\d.]+)/”, $uptime, $matches)) {
return new XML_RPC_Response( new XML_RPC_Value($matches[1], ‘string’));
}
}
$dispatches = array(‘system.load’ => array(‘function’ => ‘system_uptime’)); new XML_RPC_Server($dispatches, 1);

396 Chapter 16 RPC: Interacting with Remote Services
The PHP functions required to support the incoming requests are defined.You only need to deal with the system.load request, which is implemented through the function system_load(). system_load() runs the Unix command uptime and extracts the one-minute load average of the machine. Next, it serializes the extracted load into an XML_RPC_Value and wraps that in an XML_RPC_Response for return to the user.
Next, the callback function is registered in a dispatch map that instructs the server how to dispatch incoming requests to particular functions.You create a $dispatches array of functions that will be called.This is an array that maps XML-RPC method names to PHP function names. Finally, an XML_RPC_Server object is created, and the dispatch array $dispatches is passed to it.The second parameter, 1, indicates that it should immediately service a request, using the service() method (which is called internally).
service() looks at the raw HTTP POST data, parses it for an XML-RPC request, and then performs the dispatching. Because it relies on the PHP autoglobal $HTTP_RAW_POST_DATA, you need to make certain that you do not turn off always_populate_raw_post_data in your php.ini file.
Now, if you place the server code at www.example.com/xmlrpc.php and execute the client code from any machine, you should get back this:
> php system_load.php
0.34
or whatever your one-minute load average is.
Building a Server: Implementing the MetaWeblog API
The power of XML-RPC is that it provides a standardized method for communicating between services.This is especially useful when you do not control both ends of a service request. XML-RPC allows you to easily set up a well-defined way of interfacing with a service you provide. One example of this is Web log submission APIs.
There are many Web log systems available, and there are many tools for helping people organize and post entries to them. If there were no standardize procedures, every tool would have to support every Web log in order to be widely usable, or every Web log would need to support every tool.This sort of tangle of relationships would be impossible to scale.
Although the feature sets and implementations of Web logging systems vary considerably, it is possible to define a set of standard operations that are necessary to submit entries to a Web logging system.Then Web logs and tools only need to implement this interface to have tools be cross-compatible with all Web logging systems.
In contrast to the huge number of Web logging systems available, there are only three real Web log submission APIs in wide usage: the Blogger API, the MetaWeblog API, and the MovableType API (which is actually just an extension of the MetaWeblog API). All

XML-RPC 397
the Web log posting tools available speak one of these three protocols, so if you implement these APIs, your Web log will be able to interact with any tool out there.This is a tremendous asset for making a new blogging system easily adoptable.
Of course, you first need to have a Web logging system that can be targeted by one of the APIs. Building an entire Web log system is beyond the scope of this chapter, so instead of creating it from scratch, you can add an XML-RPC layer to the Serendipity Web logging system.The APIs in question handle posting, so they will likely interface with the following routines from Serendipity:
function serendipity_updertEntry($entry) {} function serendipity_fetchEntry($key, $match) {}
serendipity_updertEntry() is a function that either updates an existing entry or inserts a new one, depending on whether id is passed into it. Its $entry parameter is an array that is a row gateway (a one-to-one correspondence of array elements to table columns) to the following database table:
CREATE TABLE serendipity_entries ( id INT AUTO_INCREMENT PRIMARY KEY, title VARCHAR(200) DEFAULT NULL, timestamp INT(10) DEFAULT NULL, body TEXT,
author VARCHAR(20) DEFAULT NULL, isdraft INT
);
serendipity_fetchEntry() fetches an entry from that table by matching the specified key/value pair.
The MetaWeblog API provides greater depth of features than the Blogger API, so that is the target of our implementation.The MetaWeblog API implements three main methods:
metaWeblog.newPost(blogid,username,password,item_struct,publish) returns string
metaWeblog.editPost(postid,username,password,item_struct,publish) returns true
metaWeblog.getPost(postid,username,password) returns item_struct
blogid is an identifier for the Web log you are targeting (which is useful if the system supports multiple separate Web logs). username and password are authentication criteria that identify the poster. publish is a flag that indicates whether the entry is a draft or should be published live.
item_struct is an array of data for the post.
Instead of implementing a new data format for entry data, Dave Winer, the author of the MetaWeblog spec, chose to use the item element definition from the Really Simple Syndication (RSS) 2.0 specification, available at http://blogs.law.harvard.edu/ tech/rss. RSS is a standardized XML format developed for representing articles and journal entries. Its item node contains the following elements: