1. Trang chủ
  2. » Công Nghệ Thông Tin

Tài liệu Advanced PHP Programming- P4 pptx

50 326 0
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Implementing With PHP: Standalone Scripts
Trường học Unknown University
Chuyên ngành Computer Science
Thể loại Thesis
Năm xuất bản 2023
Thành phố Unknown City
Định dạng
Số trang 50
Dung lượng 506,78 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

131 Creating and Managing Child ProcessesWhen you call pcntl_forkin a script, a new process is created, and it continuesexecuting the script from the point of the pcntl_forkcall.The orig

Trang 1

if($i++ == 10) { break;

} } print “ \n\n ” ; }

?>

The script works by reading in a logfile on STDINand matching each line against $regex

to extract individual fields.The script then computes summary statistics, counting thenumber of requests per unique IP address and per unique Web server user agent Becausecombined-format logfiles are large, you can output a .tostderrevery 1,000 lines toreflect the parsing progress If the output of the script is redirected to a file, the endreport will appear in the file, but the .’s will only appear on the user’s screen

Parsing Command-Line Arguments

When you are running a PHP script on the command line, you obviously can’t passarguments via $_GETand$_POSTvariables (the CLI has no concept of these Web proto-cols) Instead, you pass in arguments on the command line Command-line argumentscan be read in raw from the $argvautoglobal

The following script:

#!/usr/bin/env php

<?php print_r($argv);

?>

when run as this:

> /dump_argv.php foo bar barbaragives the following output:

Array ( [0] => dump_argv.php [1] => foo

[2] => bar [3] => barbara )

Notice that $argv[0]is the name of the running script

Taking configuration directly from $argvcan be frustrating because it requires you toput your options in a specific order A more robust option than parsing options by hand

is to use PEAR’sConsole_Getoptpackage.Console_Getoptprovides an easy interface

to use to break up command-line options into an easy-to-manage array In addition to

Trang 2

129 Parsing Command-Line Arguments

simple parsing,Console_Getopthandles both long and short options and provides basicvalidation to ensure that the options passed are in the correct format

Console_Getoptworks by being given format strings for the arguments you expect

Two forms of options can be passed: short options and long options

Short options are single-letter options with optional data.The format specifier for the

short options is a string of allowed tokens Option letters can be followed with a single :

to indicate that the option requires a parameter or with a double ::to indicate that theparameter is optional

Long options are an array of full-word options (for example, help).The optionstrings can be followed by a single =to indicate that the option takes a parameter or by adouble ==if the parameter is optional

For example, for a script to accept the -hand helpflags with no options, and forthe fileoption with a mandatory parameter, you would use the following code:

require_once “ Console/Getopt.php ” ;

$shortoptions = “ h ” ;

$longoptons = array( “ file= ” , “ help ” );

$con = new Console_Getopt;

$args = Console_Getopt::readPHPArgv();

$ret = $con->getopt($args, $shortoptions, $longoptions);

The return value of getopt()is an array containing a two-dimensional array.The firstinner array contains the short option arguments, and the second contains the longoption arguments.Console_Getopt::readPHPARGV()is a cross-configuration way ofbringing in $argv(for instance, if you have register_argc_argvset to offin yourphp.inifile)

I find the normal output of getopt()to be a bit obtuse I prefer to have my optionspresented as a single associative array of key/value pairs, with the option symbol as thekey and the option value as the array value.The following block of code uses

Console_Getoptto achieve this effect:

function getOptions($default_opt, $shortoptions, $longoptions) {

$opts[$arr[0]][] = $rhs;

}

Trang 3

else {

$opts[$arr[0]] = array($opts[$arr[0]], $rhs);

} } else {

$opts[$arr[0]] = $rhs;

} } if(is_array($default_opt)) { foreach ($default_opt as $k => $v) { if(!array_key_exists($k, $opts)) {

$opts[$k] = $v;

} } } return $opts;

}

If an argument flag is passed multiple times, the value for that flag will be an array of allthe values set, and if a flag is passed without an argument, it is assigned the Booleanvaluetrue Note that this function also accepts a default parameter list that will be used

if no other options match

Using this function, you can recast the help example as follows:

$shortoptions = “ h ” ;

$longoptions = array( “ file= ” , “ help ” );

$ret = getOptions(null, $shortoptions, $longoptions);

If this is run with the parameters -h file=error.log,$retwill have the followingstructure:

Array ( [h] => 1 [ file] => error.log )

Creating and Managing Child Processes

PHP has no native support for threads, which makes it difficult for developers comingfrom thread-oriented languages such as Java to write programs that must accomplishmultiple tasks simultaneously All is not lost, though: PHP supports traditional Unix mul-titasking by allowing a process to spawn child processes viapcntl_fork()(a wrapperaround the Unix system call fork()).To enable this function (and all the pcntl_*func-tions), you must build PHP with the enable-pcntlflag

Trang 4

131 Creating and Managing Child Processes

When you call pcntl_fork()in a script, a new process is created, and it continuesexecuting the script from the point of the pcntl_fork()call.The original process alsocontinues execution from that point forward.This means that you then have two copies

of the script running—the parent (the original process) and the child (the newly created

process)

pcntl_fork()actually returns twice—once in the parent and once in the child Inthe parent, the return value is the process ID (PID) of the newly created child, and inthe child, the return value is 0.This is how you distinguish the parent from the child

The following simple script creates a child process:

My pid is 4286 pcntl_fork() return 4287, this is the parent

My pid is 4287 pcntl_fork() returned 0, this is the childNote that the return value of pcntl_fork()does indeed match the PID of the childprocess Also, if you run this script multiple times, you will see that sometimes the parentprints first and other times the child prints first Because they are separate processes, theyare both scheduled on the processor in the order in which the operating system sees fit,not based on the parent–child relationship

Closing Shared Resources

When you fork a process in the Unix environment, the parent and child processes bothhave access to any file resources that are open at the time fork()was called As conven-ient as this might sound for sharing resources between processes, in general it is not whatyou want Because there are no flow-control mechanisms preventing simultaneous access

to these resources, resulting I/O will often be interleaved For file I/O, this will usuallyresult in lines being jumbled together For complex socket I/O such as with databaseconnections, it will often simply crash the process completely

Because this corruption happens only when the resources are accessed, simply beingstrict about when and where they are accessed is sufficient to protect yourself; however,

Trang 5

it is much safer and cleaner to simply close any resources you will not be using ately after a fork.

immedi-Sharing Variables

Remember: Forked processes are not threads.The processes created with pcntl_fork()are individual processes, and changes to variables in one process after the fork are notreflected in the others If you need to have variables shared between processes, you caneither use the shared memory extensions to hold variables or use the “tie” trick fromChapter 2, “Object-Oriented Programming Through Design Patterns.”

Cleaning Up After Children

In the Unix environment, a defunct process is one that has exited but whose status has

not been collected by its parent process (this is also called reaping the child process) A

responsible parent process always reaps its children

PHP provides two ways of handing child exits:

n pcntl_wait($status, $options)—pcntl_wait()instructs the calling process tosuspend execution until any of its children terminates.The PID of the exitingchild process is returned, and $statusis set to the return status of the function

n pcntl_waitpid($pid, $status, $options)—pcntl_waitpid()is similar topcntl_wait(), but it only waits on a particular process specified by $pid.$statuscontains the same information as it does for pcntl_wait()

For both functions,$optionsis an optional bit field that can consist of the followingtwo parameters:

n WNOHANG—Do not wait if the process information is not immediately available

n WUNTRACED—Return information about children that stopped due to a SIGTTIN,SIGTTOU,SIGSTP, orSIGSTOPsignal (These signals are normally not caught bywaitpid().)

Here is a sample process that starts up a set number of child processes and waits for them to exit:

} else {

Trang 6

133 Creating and Managing Child Processes

$children[] = $pid;

} }

}

function child_main() {

One aspect of this example worth noting is that the code to be run by the child process

is all located in the function child_main() In this example it only executes sleep(10),but you could change that to more complex logic

Also, when a child process terminates and the call to pcntl_wait()returns, you cantest the status with pcntl_wifexited()to see whether the child terminated because

it called exit()or because it died an unnatural death If the termination was due to the script exiting, you can extract the actual code passed to exit()by callingpcntl_wexitstatus($status) Exit status codes are signed 8-bit numbers, so valid val-ues are between –127 and 127

Here is the output of the script if it runs uninterrupted:

> /5.php Starting child pid 4451 Starting child pid 4452 Starting child pid 4453 Starting child pid 4454 Starting child pid 4455 pid 4453 returned exit code: 1 pid 4452 returned exit code: 1 pid 4451 returned exit code: 1 pid 4454 returned exit code: 1 pid 4455 returned exit code: 1

Trang 7

If instead of letting the script terminate normally, you manually kill one of the children,you get output like this:

> /5.php Starting child pid 4459 Starting child pid 4460 Starting child pid 4461 Starting child pid 4462 Starting child pid 4463

4462 was unnaturally terminated pid 4463 returned exit code: 1 pid 4461 returned exit code: 1 pid 4460 returned exit code: 1 pid 4459 returned exit code: 1

Signals

Signals send simple instructions to processes.When you use the shell command killtoterminate a process on your system, you are in fact simply sending an interrupt signal(SIGINT) Most signals have a default behavior (for example, the default behavior forSIGINTis to terminate the process), but except for a few exceptions, these signals can becaught and handled in custom ways inside a process

Some of the most common signals are listed next (the complete list is in the signal(3)man page):

Signal Name Description Default Behavior

SIGCHLD Child termination IgnoreSIGINT Interrupt request Terminate process

SIGHUP Terminal hangup Terminate process

SIGALRM Alarm timeout Terminate process

To register your own signal handler, you simply define a function like this:

function sig_usr1($signal) {

print “ SIGUSR1 Caught.\n ” ; }

and then register it with this:

declare(ticks=1);

pcntl_signal(SIGUSR1, “ sig_usr1 ” );

Trang 8

135 Creating and Managing Child Processes

Because signals occur at the process level and not inside the PHP virtual machine itself,the engine needs to be instructed to check for signals and run the pcntlcallbacks.Toallow this to happen, you need to set the execution directive ticks.ticksinstructs theengine to run certain callbacks every Nstatements in the executor.The signal callback isessentially a no-op, so setting declare(ticks=1)instructs the engine to look for signals

on every statement executed

The following sections describe the two most useful signal handlers for multiprocessscripts—SIGCHLDandSIGALRM—as well as other common signals

SIGCHLDSIGCHLDis a common signal handler that you set in applications where you fork a num-ber of children In the examples in the preceding section, the parent has to loop onpcntl_wait()orpcntl_waitpid()to ensure that all children are collected on Signalsprovide a way for the child process termination event to notify the parent process thatchildren need to be collected.That way, the parent process can execute its own logicinstead of just spinning while waiting to collect children

To implement this sort of setup, you first need to define a callback to handle SIGCHLDevents Here is a simple example that removes the PID from the global $childrenarrayand prints some debugging information on what it is doing:

function sig_child($signal) {

global $children;

pcntl_signal(SIGCHLD, “ sig_child ” );

fputs(STDERR, “ Caught SIGCHLD\n ” );

while(($pid = pcntl_wait($status, WNOHANG)) > 0) {

$children = array_diff($children, array($pid));

fputs(STDERR, “ Collected pid $pid\n ” );

} }TheSIGCHLDsignal does not give any information on which child process has terminat-

ed, so you need to call pcntl_wait()internally to find the terminated processes In fact,because multiple processes may terminate while the signal handler is being called, youmust loop on pcntl_wait()until no terminated processes are remaining, to guaranteethat they are all collected Because the option WNOHANGis used, this call will not block inthe parent process

Most modern signal facilities restore a signal handler after it is called, but for ity to older systems, you should always reinstate the signal handler manually inside thecall

portabil-When you add a SIGCHLDhandler to the earlier example, it looks like this:

#!/usr/bin/env php

<?php

Trang 9

} else {

$children[] = $pid;

} }

while($children) { sleep(10); // or perform parent logic }

pcntl_alarm(0);

function child_main() {

sleep(rand(0, 10)); // or perform child logic return 1;

}

function sig_child($signal) {

global $children;

pcntl_signal(SIGCHLD, “ sig_child ” );

fputs(STDERR, “ Caught SIGCHLD\n ” );

while(($pid = pcntl_wait($status, WNOHANG)) > 0) {

$children = array_diff($children, array($pid));

if(!pcntl_wifexited($status)) { fputs(STDERR, “ Collected killed pid $pid\n ” );

} else { fputs(STDERR, “ Collected exited pid $pid\n ” );

} } }

?>

Running this yields the following output:

> /8.php Caught SIGCHLD

Trang 10

137 Creating and Managing Child Processes

Collected exited pid 5000 Caught SIGCHLD

Collected exited pid 5003 Caught SIGCHLD

Collected exited pid 5001 Caught SIGCHLD

Collected exited pid 5002 Caught SIGCHLD

Collected exited pid 5004

SIGALRMAnother useful signal is SIGALRM, the alarm signal Alarms allow you to bail out of tasks ifthey are taking too long to complete.To use an alarm, you define a signal handler, regis-ter it, and then call pcntl_alarm()to set the timeout.When the specified timeout isreached, a SIGALRMsignal is sent to the process

Here is a signal handler that loops through all the PIDs remaining in $childrenandsends them a SIGINTsignal (the same as the Unix shell command kill):

function sig_alarm($signal) {

global $children;

fputs(STDERR, “ Caught SIGALRM\n ” );

foreach ($children as $pid) { posix_kill($pid, SIGINT);

} }Note the use of posix_kill().posix_kill()signals the specified process with thegiven signal

You also need to register the sig_alarm() SIGALRMhandler (alongside the SIGCHLDhandler) and change the main block as follows:

} else {

Trang 11

$children[] = $pid;

} }

while($children) { sleep(10); // or perform parent logic }

pcntl_alarm(0);

It is important to remember to set the alarm timeout to 0when it is no longer ed; otherwise, it will fire when you do not expect it Running the script with thesemodifications yields the following output:

need-> /9.php Caught SIGCHLD Collected exited pid 5011 Caught SIGCHLD

Collected exited pid 5013 Caught SIGALRM

Caught SIGCHLD Collected killed pid 5014 Collected killed pid 5012 Collected killed pid 5010

In this example, the parent process uses the alarm to clean up (via termination) anychild processes that have taken too long to execute

Other Common Signals

Other common signals you might want to install handlers for are SIGHUP,SIGUSR1, andSIGUSR2.The default behavior for a process when receiving any of these signals is to terminate.SIGHUPis the signal sent at terminal disconnection (when the shell exits) Atypical process in the background in your shell terminates when you log out of your ter-minal session

If you simply want to ignore these signals, you can instruct a script to ignore them byusing the following code:

pcntl_signal(SIGHUP, SIGIGN);

Rather than ignore these three signals, it is common practice to use them to send simplecommands to processes—for instance, to reread a configuration file, reopen a logfile, ordump some status information

Writing Daemons

A daemon is a process that runs in the background, which means that once it is started, it

takes no input from the user’s terminal and does not exit when the user’s session ends

Trang 12

139 Writing Daemons

Once started, daemons traditionally run forever (or until stopped) to perform recurrenttasks or to handle tasks that might last beyond the length of the user’s session.TheApache Web server,sendmail, and the crondaemoncrondare examples of commondaemons that may be running on your system Daemonizing scripts is useful for handlinglong jobs and recurrent back-end tasks

To successfully be daemonized, a process needs to complete the two following tasks:

n Process detachment

n Process independence

In addition, a well-written daemon may optionally perform the following:

n Setting its working directory

n Dropping privileges

n Guaranteeing exclusivityYou learned about process detachment earlier in this chapter, in the section “Creatingand Managing Child Processes.”The logic is the same as for daemonizing processes,except that you want to end the parent process so that the only running process isdetached from the shell.To do this, you execute pnctl_fork()and exit if you are in theparent process (that is, if the return value is greater than zero)

In Unix systems, processes are associated with process groups, so if you kill the leader

of a process group, all its associates will terminate as well.The parent process for thing you start in your shell is your shell’s process.Thus, if you create a new process withfork()and do nothing else, the process will still exit when you close the shell.To avoidhaving this happen, you need the forked process to disassociate itself from its parentprocess.This is accomplished by calling pcntl_setsid(), which makes the callingprocess the leader of its own process group

every-Finally, to sever any ties between the parent and the child, you need to fork theprocess a second time.This completes the detachment process In code, this detachmentprocess looks like this:

if(pcntl_fork()) { exit;

} pcntl_setsid();

if(pcntl_fork()) { exit;

}

# process is now completely daemonized

It is important for the parent to exit after both calls to pcntl_fork(); otherwise, ple processes will be executing the same code

Trang 13

multi-Changing the Working Directory

When you’re writing a daemon, it is usually advisable to have it set its own workingdirectory.That way, if you read from or write to any files via a relative path, they will be

in the place you expect them to be Always qualifying your paths is of course a goodpractice in and of itself, but so is defensive coding.The safest way to change your work-ing directory is to use not only chdir(), but to use chroot()as well

chroot()is available inside the PHP CLI and CGI versions and requires the program

to be running as root.chroot()actually changes the root directory for the process tothe specified directory.This makes it impossible to execute any files that do not lie with-

in that directory.chroot()is often used by servers as a security device to ensure that it

is impossible for malicious code to modify files outside a specific directory Keep in mindthat while chroot()prevents you from accessing any files outside your new directory,any currently open file resources can still be accessed For example, the following codeopens a logfile, calls chroot()to switch to a data directory, and can still successfully log

to the open file resource:

Giving Up Privileges

A classic security precaution when writing Unix daemons is having them drop allunneeded privileges Like being able to access files outside where they need to be, pos-sessing unneeded privileges is a recipe for trouble In the event that the code (or PHPitself) has an exploitable flaw, you can minimize damage by ensuring that a daemon isrunning as a user with minimal rights to alter files on the system

One way to approach this is to simply execute the daemon as the unprivileged user.This is usually inadequate if the program needs to initially open resources (logfiles, datafiles, sockets, and so on) that the unprivileged user does not have rights to

If you are running as the root user, you can drop your privileges by using theposix_setuid()andposiz_setgid()functions Here is an example that changes therunning program’s privileges to those of the user nobody:

$pw= posix_getpwnam( ‘ nobody ’ );

posix_setuid($pw[ ‘ uid ’ ]);

posix_setgid($pw[ ‘ gid ’ ]);

Trang 14

141 Combining What You’ve Learned: Monitoring Services

As with chroot(), any privileged resources that were open prior to dropping privilegesremain open, but new ones cannot be created

Guaranteeing Exclusivity

You often want to require that only one instance of a script can be running at any giventime For daemonizing scripts, this is especially important because running in the back-ground makes it easy to accidentally invoke instances multiple times

The standard technique for guaranteeing exclusivity is to have scripts lock a specificfile (often a lockfile, used exclusively for that purpose) by using flock() If the lock fails,the script should exit with an error Here’s an example:

Locking mechanisms are discussed in greater depth in Chapter 10, “Data ComponentCaching.”

Combining What You’ve Learned: Monitoring Services

In this section you bring together your skills to write a basic monitoring engine in PHP

Because you never know how your needs will change, you should make it as flexible aspossible

The logger should be able to support arbitrary service checks (for example, HTTPand FTP services) and be able to log events in arbitrary ways (via email, to a logfile, and

so on).You, of course, want it to run as a daemon, so you should be able to request it togive its complete current state

A service needs to implement the following abstract class:

abstract class ServiceCheck {

const FAILURE = 0;

const SUCCESS = 1;

protected $timeout = 30;

protected $next_attempt;

protected $current_status = ServiceCheck::SUCCESS;

protected $previous_status = ServiceCheck::SUCCESS;

protected $frequency = 30;

protected $description;

protected $consecutive_failures = 0;

Trang 15

protected $status_time;

protected $failure_time;

protected $loggers = array();

abstract public function _ _construct($params);

public function _ _call($name, $args) {

if(isset($this->$name)) { return $this->$name;

} }

public function set_next_attempt() {

$this->next_attempt = time() + $this->frequency;

}

public abstract function run();

public function post_run($status) {

if($status !== $this->current_status) {

$this->previous_status = $this->current_status;

} if($status === self::FAILURE) { if( $this->current_status === self::FAILURE ) {

$this->consecutive_failures++;

} else {

$this->failure_time = time();

} } else {

Trang 16

143 Combining What You’ve Learned: Monitoring Services

private function log_service_event() {

foreach($this->loggers as $logger) {

$logger->log_service_event($this);

} }

public function register_logger(ServiceLogger $logger) {

$this->loggers[] = $logger;

} }The_ _call()overload method provides read-only access to the parameters of aServiceCheckobject:

n timeout—How long the check can hang before it is to be terminated by theengine

n next_attempt—When the next attempt to contact this server should be made

n current_status—The current state of the service:SUCCESSorFAILURE

n previous_status—The status before the current one

n frequency—How often the service should be checked

n description—A description of the service

n consecutive_failures—The number of consecutive times the service check hasfailed because it was last successful

n status_time—The last time the service was checked

n failure_time—If the status is FAILED, the time that failure occurred

The class also implements the observer pattern, allowing objects of type ServiceLogger

to register themselves and then be called whenever log_current_status()orlog_service_event()is called

The critical function to implement is run(), which defines how the check should berun It should return SUCCESSif the check succeeded and FAILUREif not

Thepost_run()method is called after the service check defined in run()returns Ithandles setting the status of the object and performing logging

TheServiceLoggerinterface :specifies that a logging class need only implement twomethods,log_service_event()andlog_current_status(), which are called when arun()check returns and when a generic status request is made, respectively

The interface is as follows:

interface ServiceLogger { public function log_service_event(ServiceCheck $service);

public function log_current_status(ServiceCheck $service);

}

Trang 17

Finally, you need to write the engine itself.The idea is similar to the ideas behind thesimple programs in the “Writing Daemons” section earlier in this chapter:The servershould fork off a new process to handle each check and use a SIGCHLDhandler to checkthe return value of checks when they complete.The maximum number of checks thatwill be performed simultaneously should be configurable to prevent overutilization ofsystem resources All the services and logging will be defined in an XML file.

The following is the ServiceCheckRunnerclass that defines the engine:

class ServiceCheckRunner {

private $num_children;

private $services = array();

private $children = array();

public function _ _construct($conf, $num_children) {

exit;

} } foreach($conf->services->service as $service) {

$class = new Reflection_Class( “ $service->class ” );

exit;

} } }

Trang 18

145 Combining What You’ve Learned: Monitoring Services

private function next_attempt_sort($a, $b) {

if($a->next_attempt() == $b->next_attempt()) { return 0;

} return ($a->next_attempt() < $b->next_attempt()) ? -1 : 1;

pcntl_signal(SIGCHLD, array($this, “ sig_child ” ));

pcntl_signal(SIGUSR1, array($this, “ sig_usr1 ” ));

exit($service->run());

} } } }

public function log_current_status() {

foreach($this->services as $service) {

$service->log_current_status();

} }

Trang 19

private function sig_child($signal) {

$status = ServiceCheck::FAILURE;

pcntl_signal(SIGCHLD, array($this, “ sig_child ” ));

while(($pid = pcntl_wait($status, WNOHANG)) > 0) {

$service = $this->children[$pid];

unset($this->children[$pid]);

if(pcntl_wifexited($status) &&

pcntl_wexitstatus($status) == ServiceCheck::SUCCESS) {

$status = ServiceCheck::SUCCESS;

}

$service->post_run($status);

} }

private function sig_usr1($signal) {

pcntl_signal(SIGUSR1, array($this, “ sig_usr1 ” ));

$this->log_current_status();

} }This is an elaborate class.The constructor reads in and parses an XML file, creating allthe services to be monitored and the loggers to record them.You’ll learn more details onthis in a moment

Theloop()method is the main method in the class It sets the required signal dlers and checks whether a new child process can be created If the next event (sorted bynext_attempttimestamp) is okay to run now, a new process is forked off Inside thechild process, an alarm is set to keep the test from lasting longer than its timeout, andthen the test defined by run()is executed

han-There are also two signal handlers.The SIGCHLDhandlersig_child()collects on theterminated child processes and executes their service’s post_run()method.The SIGUSR1handlersig_usr1()simply calls the log_current_status()methods of all registeredloggers, which can be used to get the current status of the entire system

As it stands, of course, the monitoring architecture doesn’t do anything First, youneed a service to check.The following is a class that checks whether you get back a 200Server OK response from an HTTP server:

class HTTP_ServiceCheck extends ServiceCheck {

Trang 20

147 Combining What You’ve Learned: Monitoring Services

$this->$k = “ $v ” ; }

}

public function run() {

if(is_resource(@fopen($this->url, “ r ” ))) { return ServiceCheck::SUCCESS;

} else { return ServiceCheck::FAILURE;

} } }Compared to the framework you built earlier, this service is extremely simple—and that’sthe point: the effort goes into building the framework, and the extensions are very sim-ple

Here is a sample ServiceLoggerprocess that sends an email to an on-call personwhen a service goes down:

class EmailMe_ServiceLogger implements ServiceLogger { public function log_service_event(ServiceCheck $service) {

if($service->current_status == ServiceCheck::FAILURE) {

$message = “ Problem with {$service->description()}\r\n ” ; mail( ‘ oncall@example.com ’ , ‘ Service Event ’ , $message);

if($service->consecutive_failures() > 5) { mail( ‘ oncall_backup@example.com ’ , ‘ Service Event ’ , $message);

} } }

public function log_current_status(ServiceCheck $service) {

return;

} }

If the failure persists beyond the fifth time, the process also sends a message to a backupaddress It does not implement a meaningful log_current_status()method

You implement a ServiceLoggerprocess that writes to the PHP error log whenever

a service changes status as follows:

class ErrorLog_ServiceLogger implements ServiceLogger { public function log_service_event(ServiceCheck $service) {

Trang 21

if($service->current_status() !== $service->previous_status()) { if($service->current_status() === ServiceCheck::FAILURE) {

$status = ‘ DOWN ’ ; }

else {

$status = ‘ UP ’ ; }

error_log( “ {$service->description()} changed status to $status ” );

} }

public function log_current_status(ServiceCheck $service) {

error_log( “ {$service->description()}: $status ” );

} }Thelog_current_status()method means that if the process is sent a SIGUSR1signal,

it dumps the complete current status to your PHP error log

The engine takes a configuration file like the following:

Trang 22

149 Combining What You’ve Learned: Monitoring Services

Note

The constructor uses the Reflection_Class class to introspect the service and logger classes before you try to instantiate them This is not necessary, but it is a nice demonstration of the new Reflection API in PHP 5 In addition to classes, the Reflection API provides classes for introspecting almost any internal entity (class, method, or function) in PHP.

To use the engine you’ve built, you still need some wrapper code.The monitor shouldprohibit you from starting it twice—you don’t need double messages for every event Itshould also accept some options, including the following:

Option Description

[-f] A location for the engine’s configuration file, which defaults to

moni-tor.xml.[-n] The size of the child process pool the engine will allow, which defaults

to 5

[-d] A flag to disable the engine from daemonizing.This is useful if you

write a debugging ServiceLoggerprocess that outputs information tostdoutorstderr

Here is the finalized monitor script, which parses options, guarantees exclusivity, andruns the service checks:

require_once “ Service.inc ” ; require_once “ Console/Getopt.php ” ;

$shortoptions = “ n:f:d ” ;

$default_opts = array( ‘ n ’ => 5, ‘ f ’ => ‘ monitor.xml ’ );

Trang 23

$args = getOptions($default_opts, $shortoptions, null);

} posix_setsid();

if(pcntl_fork()) { exit;

} } fwrite($fp, getmypid());

is killed

This script is fairly complex, but there are still some easy improvements that are left as

an exercise to the reader:

n Add a SIGHUPhandler that reparses the configuration file so that you can changethe configuration without restarting the server

n Write a ServiceLoggerthat logs to a database for persistent data that can bequeried

n Write a Web front end to provide a nice GUI to the whole monitoring system

Further Reading

There are not many resources for shell scripting in PHP Perl has a much longer heritage

of being a useful language for administrative tasks Perl for Systems Administration by David

N Blank-Edelman is a nice text, and the syntax and feature similarity between Perl andPHP make it easy to port the book’s Perl examples to PHP

Trang 24

151 Further Reading

php|architect, an electronic (and now print as well) periodical, has a good article by

Marco Tabini on building interactive terminal-based applications with PHP and thencursesextension in Volume 1, Issue 12 php|architect is available online at

http://www.phparch.com.Although there is not space to cover it here, PHP-GTK is an interesting projectaimed at writing GUI desktop applications in PHP, using the GTK graphics toolkit

Information on PHP-GTK is available athttp://gtk.php.net

A good open-source resource monitoring system is Nagios, available athttp://nagios.org.The monitoring script presented in this chapter was inspired byNagios and designed to allow authoring of all your tests in PHP in an integrated fash-ion Also, having your core engine in PHP makes it easy to customize your front end

(Nagios is written in C and is CGI based, making customization difficult.)

Ngày đăng: 26/01/2014, 09:20

TỪ KHÓA LIÊN QUAN