PHP Script quite CPU hungry. Ideas on an workaround - php

Because Server costs are the greatest spending, we want to get more out of everyone.
How can we achieve, that more scripts can run on this server?
What the Scrips are doing:
We run 80 PHP Scripts on one Server and feed them with Jobs through Gearman.
The Scripts are looking up Websites with cURL, extract the needed Informations with Zend_Dom_Query and store the Data in an DB.
Each Script get feeded with ca. 1000 URLs which they have to look up.
Script example is down below.
What's the Server made of:
lshws outpu:
description: Computer
width: 64 bits
capabilities: vsyscall64 vsyscall32
*-core
description: Motherboard
physical id: 0
*-memory
description: System memory
physical id: 0
size: 8191GiB
*-cpu
product: Intel(R) Xeon(R) CPU E31230 # 3.20GHz
vendor: Intel Corp.
physical id: 1
bus info: cpu#0
width: 64 bits
capabilities: fpu fpu_exception wp vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx rdtscp x86-64 constant_tsc arch_perfmon pebs bts rep_good xtopology nonstop_tsc aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic popcnt tsc_deadline_timer xsave avx lahf_lm ida arat epb xsaveopt pln pts tpr_shadow vnmi flexpriority ept vpid
Nevertheless this is an V-Server it's the only V-Server running on that server. It has also not 8191GB Memory more like 16GB.
To show you how exhausted the server is, here's tops output:
top - 14:45:04 up 8 days, 3:10, 1 user, load average: 72.96, 72.51, 71.82
Tasks: 100 total, 72 running, 28 sleeping, 0 stopped, 0 zombie
Cpu(s): 87.5%us, 12.2%sy, 0.0%ni, 0.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.3%st
Mem: 8589934588k total, 4349016k used, 8585585572k free, 0k buffers
Swap: 0k total, 0k used, 0k free, 282516k cached
Not to forget here's the scripts main structure:
// Get the Infos on which to crawl on
$asin = explode(',', $job->workload());
try {
$userproducts = new App_Dbservices_U...();
$konkurrenz = new App_Dbservices_K...();
$repricingstream = new App_Dbservices_R...();
$err = 0;
for ($i = 0; $i < count($asin) - 3; $i = $i + 50) {
$mh = curl_multi_init();
$handles = array();
for ($j = $i; $j < $i + 50; $j++) {
if ((count($asin) - 3) > $j) {
if (isset($asin[$j])) {
// create a new single curl handle
$ch = curl_init();
// setting several options like url, timeout, returntransfer
// simulate multithreading by calling the wait.php scipt and sleeping for $rand seconds
$url = // URL
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 80);
// add this handle to the multi handle
$erroro[$j] = curl_errno($ch);
$errmsg[$j] = curl_error($ch);
curl_multi_add_handle($mh, $ch);
// put the handles in an array to loop this later on
$handles[] = $ch;
}
}
}
}
// execute the multi handle
$running = null;
do {
curl_multi_exec($mh, $running);
} while ($running > 0);
// get the content (if there is any)
$output = '';
for ($k = 0; $k < count($handles); $k++) {
// get the content of the handle
$output[$k] = curl_multi_getcontent($handles[$k]);
$_asin[$k]['asin'] = $asin[$j - 50 + $k];
$_asin[$k]['condition'] = $condition[$j - 50 + $k];
$_asin[$k]['pId'] = $pId[$j - 50 + $k];
if ($output[$k] != '')
{
// get the dom of each page
$dom = new Zend_Dom_Query($output[$k]);
// get the sellerInfos of each page
$seller = $dom->query('div.Offer');
if (count($seller) > 0) {
// get the price out of the string
$seller_i = 0;
$selfCameOver = false;
foreach ($seller as $d2) {
if ($seller_i <= 6 OR $selfCameOver === false) {
$itemHtml = '';
foreach($d2->childNodes as $node) {
$itemHtml .= $node->ownerDocument->saveHTML($node);
}
$dom = new Zend_Dom_Query($itemHtml);
$itemPrice = $dom->query('span.Price');
foreach($itemPrice as $ItemPrice)
{
$_asin[$k]['price_end'][$seller_i] = 0.00;
$_asin[$k]['shipping_end'][$seller_i] = 0.00;
if (preg_match('/[0-9]++(?>[,.][0-9]+)?+/', $ItemPrice->textContent, $rueckgabe)) {
$priceEnd = str_replace(',', '', str_replace('.', '', $rueckgabe[0][0]));
$priceLength = strlen($priceEnd);
$priceEnd = substr($priceEnd, 0, ($priceLength - 2)) . '.' . substr($priceEnd, ($priceLength - 2), 2);
$_asin[$k]['price_end'][$seller_i] = (float)$priceEnd;
}
}
}
$shippingPrice = $dom->query('span.ShippingPrice');
foreach($shippingPrice as $ShippingPrice)
{
preg_match_all('/[0-9]{1,}([\,\. ]?[0-9])*/', $ShippingPrice->textContent, $rueckgabe);
if (isset($rueckgabe[0][0])) {
// ...
}
}
$_asin[$k]['price_total_end'][$seller_i] = $_asin[$k]['price_end'][$seller_i] + $_asin[$k]['shipping_end'][$seller_i];
$conditionTag = $dom->query('.Condition');
foreach($conditionTag as $ConditionTag)
{
$_asin[$k]['main_con'][$seller_i]= 0;
$_asin[$k]['sub_con'][$seller_i] = 0;
$conditionValue = explode(' - ', $ConditionTag->textContent);
if(isset($conditionValue[0])){
// ...
}
if(isset($conditionValue[1])) {
// ...
}
}
$ratingItem = $dom->query('.Rating');
$_asin[$k]['bewertung_end'][$seller_i] = -1;
$_asin[$k]['stars_end'][$seller_i] = -1;
foreach($ratingItem as $RatingItem)
{
echo $RatingItem->textContent; // 99% positiv ... 12 Monaten ... 11.719 Bewertungen ...
// I want to get 99 (which is stars ) and 11719 (which is bewertungen )
preg_match_all('/[0-9]{1,}([\,\. ]?[0-9])*/', preg_replace('/,/', '.', $RatingItem->textContent), $rueckgabe);
if (isset($rueckgabe[0]) AND count($rueckgabe[0]) > 0) {
$_asin[$k]['bewertung_end'][$seller_i] = (int)str_replace('.', '', $rueckgabe[0][count($rueckgabe[0]) - 1]);
$_asin[$k]['stars_end'][$seller_i] = $rueckgabe[0][0];
}
}
$sellerType = $dom->query('.Name img');
$_asin[$k]['merchant_end'][$seller_i] = "N/A";
$_asin[$k]['name_end'][$seller_i] = "N/A";
$_asin[$k]['img_end'][$seller_i] = "N/A";
$_asin[$k]['konk_type'][$seller_i] = 'ERROR';
if(count($sellerType) == 1)
{
foreach($sellerType as $SellerType)
{
$imgAltText = $SellerType->getAttribute('alt');
$a = explode('.', $imgAltText);
// ...
}
}
elseif(count($sellerType) == 0)
{
$_asin[$k]['img_end'][$seller_i] = 'NO_IMG';
$_asin[$k]['konk_type'][$seller_i] = 'WO_IMG';
$sellerName = $dom->query('.Name b');
foreach($sellerName as $SellerName)
{
$_asin[$k]['name_end'][$seller_i] = $SellerName->textContent;
}
$sellerMerchant = $dom->query('.Name a');
foreach($sellerMerchant as $SellerMerchant)
{
$_asin[$k]['merchant_end'][$seller_i] = str_replace('=', '', substr($SellerMerchant->getAttribute('href'), -14));
}
}
unset($rueckgabe);
}
$seller_i++;
}
}
}
// remove the handle from the multi handle
curl_multi_remove_handle($mh, $handles[$k]);
}
// Update Price ...
// Update Shipping ...
// Update Conc ...
unset($_asin);
// close the multi curl handle to free system resources
curl_multi_close($mh);
}
} catch (Exception $e) {
$error = new Repricing_Dbservices_Error();
$error->setError($id, $e->getMessage(), $e->getLine(), $e->getFile());
}
And also the script for the price update ( the other update-statements looks similiar)
$this->db->beginTransaction();
try {
for ($i = 0; $i < count($asin); $i++) {
if (isset($asin[$i]['price_total_end'])) {
if (count($asin[$i]['price_total_end']) > 1) {
if ($asin[$i]['price_total_end'][0] > 0) {
$this->db->query("UPDATE u... SET lowest_price = ? , last_lowest_price_update = ? WHERE id = ?", array(
$asin[$i]['price_total_end'][1],
date("Y-m-d H:i:s", time()),
$asin[$i]['pId']
));
}
} elseif (count($asin[$i]['price_total_end']) == 1) {
if ($asin[$i]['price_total_end'][0] >= 0) {
$this->db->query("UPDATE u... SET lowest_price = ? , last_lowest_price_update = ? WHERE id = ?", array(
-1,
date("Y-m-d H:i:s", time()),
$asin[$i]['pId']
));
}
}
}
}
$this->db->commit();
} catch (Exception $e) {
$this->db->rollBack();
echo $e->getMessage();
}
$this->db->closeConnection();
Do we have an big performance leak in our script, should we go along with an other language, or any other techniques? Every suggestion is highly appreciated.

You can replace all these kind of lines:
preg_match_all('/[0-9]{1,}([\,\. ]?[0-9])*/', $ItemPrice->textContent, $rueckgabe);
if (isset($rueckgabe[0])) {
// ...
}
by:
if (preg_match('/([0-9]++)(?>[.,]([0-9]++))?+/', $ItemPrice->textContent, $rueckgabe)) {
unset($rueckgabe[0]);
$priceEnd = sprintf("%01.2f", implode('.', $rueckgabe));
$_asin[$k]['price_end'][$seller_i] = $priceEnd;
}
You should replace all your for loops with foreach (then you avoid the count on each loop as RaymondN notices it). Example:
Instead of:
for ($k = 0; $k < count($handles); $k++) {
you write:
foreach($handles as $k=>$handle) {
// you can replace $handles[$k] by $handle
It is not very useful to convert the current datetime and format it in "Y-m-d H:i:s" since you can do directly the same with the mySQL statement NOW().

Don´t use count functions in the for loops that would save some CPU cycles ..
but use.
$m = count($array) - 1;
for ($i = 0 ; $i < $m ; $i++) {}
Whats the PHP version a new PHP version could possible perform much better.

The most notable thing here is that you are sharding the data too much - when your load average is higher than the number of CPUs, then the OS will start pre-emppting jobs instead of waiting for them to yeild the CPU. As a result overall throughput drops significantly. You seem to have a single CPU - for a cpu bound system like this running on a single core, I'd try 2, 4, 8 and 16 processes to see which gives the optimal behaviour (assuming you need to make the code fit the hardware rather than the other way around).
Your next problem is that the zend framework is very cpu and memory hungry: Zend is 4 times slower than unadorned PHP.
You've got a lot of inlined code in the loops here - while inlining does help performance it makes it a lot more difficult to get useful data out of a profiler - hence my next step after making the Zend-free and reducing the concurrency would be to structure the code into functions and profile it.

Related

How do i precisely track the script execution time. This includes HTTP requests / CURL requests in the script

I would like to know how to precisely track the script execution time and tapout before it reaches the timeout. For some reason there is a weird issue when multiple processes are running at the same time (different scripts, same file, with two browser tabs). The script execution time for the second script is twice as long compared to the script which was executed first. It would sound perfectly reasonable when the tracker would also account for that but for some reason the second script's timer seems to provide false estimates so i'm unable to tapout before it reaches timeout.
Here's the script which i'm running.
public function insertProducts(DB $db, $type){
//Import variations, first parent then children
$startedInsert = microtime(true);
list($variantProducts,$simpleProducts) = $this->prepareProducts($db,'insert',$type);
$importedSkus = [];
$importedIds = [];
$tracker = [];
switch($type){
case 'variable':
if(!empty($variantProducts)){
foreach($variantProducts as $variationIdentifier => $variants){
//if((microtime(true) - $startedInsert) / 60 >= 3) break;
$startVariant = microtime(true);
$existingVariantParent = $this->getVariantParent($db,$variationIdentifier);
$exists = false;
if($existingVariantParent){
$variantParent = $this->request('GET', 'v3/products/P'.$existingVariantParent.'T');
$parentId = $existingVariantParent;
$exists = isset($variantParent['code']) ? false : true;
}
// If the parent does not exist, insert a new one
if(!$exists){
$parent = $variants[0];
$parent['sku'] = 'P'.$variants[0]['sku'].'T';
$existingImages = $this->getMedias(array_column($parent['images'], 'name'));
if(!empty($existingImages)){
$parent['images'] = array_map(function($item) use($existingImages){
$item = current(array_filter($existingImages, function($exisitngImage) use($item){ return $item['name'] === $exisitngImage['name'] ? true : false; })) ?: $item;
return $item;
}, $parent['images']);
}
$variantParent = $this->request('POST', 'v3/products', $parent);
$tracker['variant']['parent'][] = $variantParent;
if(isset($variantParent['code'])){
if(isset($variantParent['data']['resource_id'])){
$parentId = $variantParent['data']['resource_id'];
}
}else{
$parentId = $variantParent['id'];
}
$this->setVariantParent($db,$variationIdentifier,$parentId,$variants[0]['sku']);
$importedSkus[] = $variants[0]['sku'];
}
if(!isset($parentId)) continue;
$importedIds[] = $parentId;
$tracker['variant']['parent']['stopwatch'][] = (microtime(true) - $startVariant) / 60;
$startVariants = microtime(true);
if(!empty($variants)){
$images = [];
$variantImageNames = array_unique(array_reduce($variants, function($carry, $item){
$carry = array_merge($carry, array_column($item['images'], 'name'));
return $carry;
}, []));
$existingImages = $this->getMedias($variantImageNames);
$variants = array_map(function($item) use($variantParent,&$images,$existingImages){
$attributes = [];
if(isset($variantParent['attributes'])){
foreach($item['attributes'] as $attr){
$attributes[] = array_reduce($variantParent['attributes'], function($carry, $item) use($attr){
if($attr['name'] === $item['name'] && $item['variation']){
$carry = [
'id' => $attr['id'],
'option' => $attr['options'][0]
];
}
return $carry;
});
}
$item['attributes'] = array_filter($attributes);
}
if(!empty($item['images'])){
if(!in_array($item['images'][0]['name'], $images)){
$imageName = $item['images'][0]['name'];
$item['image'] = current(array_filter($existingImages, function($exisitngImage) use ($imageName){ return $exisitngImage['name'] == $imageName ? true : false; })) ?: $item['images'][0];
$images[] = $imageName;
}
}
return $item;
},$variants);
//$variants
$variants = $this->request('POST', 'v3/products/'.$parentId.'/variations/batch', ['create' => $variants]);
$tracker['variant']['variants'][] = $variants;
$skus = array_reduce($variants['create'], function($carry, $item){
if(!isset($item['error'])){
$carry[] = $item['sku'];
}
return $carry;
});
if(!empty($skus)){
$importedSkus = array_merge($importedSkus,$skus);
}
}
$tracker['variant']['variant']['stopwatch'][] = (microtime(true) - $startVariants) / 60;
}
$tracker['endedVariant'] = (microtime(true) - $startedInsert) / 60;
}
break;
case 'simple':
if(!empty($simpleProducts)){
$simpleImageNames = array_unique(array_reduce($simpleProducts, function($carry, $item){
$carry = array_merge($carry, array_column($item['images'], 'name'));
return $carry;
}, []));
$existingImages = $this->getMedias($simpleImageNames);
$images = [];
$simpleProducts = array_map(function($item) use($existingImages,&$images){
foreach($item['images'] as $imageKey => $image){
$imageName = $image['name'];
$item['images'][$imageKey] = current(array_filter($existingImages, function($exisitngImage) use ($imageName){ return $exisitngImage['name'] == $imageName ? true : false; })) ?: $item['images'][$imageKey];
if(!isset($item['images'][$imageKey]['id'])){
if(!in_array($item['images'][$imageKey]['name'],$images)){
$images[] = $item['images'][$imageKey]['name'];
}else{
// Prevent duplicate image uploads on simple products
unset($item['images'][$imageKey]);
}
}
}
return $item;
},$simpleProducts);
$simpleProduct = $this->request('POST', 'v3/products/batch', ['create' => $simpleProducts]);
$importedSImples = array_reduce($simpleProduct['create'], function($carry, $item){
if(!isset($item['error'])){
$carry['skus'][] = $item['sku'];
$carry['ids'][] = $item['id'];
}else if(isset($item['error']['data']['resource_id'])){
$carry['ids'][] = $item['error']['data']['resource_id'];
}
return $carry;
},['skus' => [],'ids' => []]);
$importedSkus = array_merge($importedSkus,$importedSImples['skus']);
$importedIds = array_merge($importedSkus,$importedSImples['ids']);
$tracker['endedSimple'] = (microtime(true) - $startedInsert) / 60;
}
$tracker['simple'] = $simpleProduct;
break;
}
// $startDelete = microtime(true);
// $this->request('POST', 'v3/products/batch', ['delete' => $importedIds]);
// $endDelete = microtime(true);
// $tracker['deleted'] = ($endDelete - $startDelete) / 60;
if(!empty($importedIds)){
$existingProducts = $this->request('GET','v3/products', ['include' => $importedIds]);
$importedSkus = array_merge($importedSkus, array_column($existingProducts,'sku'));
}
if(!empty($importedSkus)){
$this->updateDB($db, 'insert',array_filter($importedSkus));
}
$tracker['imported_ids'] = $importedIds;
$tracker['imported_skus'] = $importedSkus;
var_dump($tracker);
}
At first i initate the tracker and prepare products for insertion
//Import variations, first parent then children
$startedInsert = microtime(true);
list($variantProducts,$simpleProducts) = $this->prepareProducts($db,'insert',$type);
Then incase the type is set variable i execute the variable product insertion and in a perfect scenario this would break out of the foreach loop, finish rest of the processes and end the script once it has reached to 3 minute line. Right now, this seems to malfunction.
//if((microtime(true) - $startedInsert) / 60 >= 3) break; // This would break out in 3 minutes
enter code here
At the end of this script i get how long did it take to execute the whole process for variable products insertion
$tracker['endedVariant'] = (microtime(true) - $startedInsert) / 60;
The same tracking logic applies to simple type as well, except there is no breaking out.
Now when i would init both of these scripts on different browser tabs, the simple product would show 1.5min and the variable approx 2. So a great idea would be to increase the payload right? The timeout happens at 300s so i would prefer if the scripts end a little before that and wait for the next execution.
Whenever i would increase the payload sent to the api, by a little, it timouts and rest of the script is ignored. The part which is suppose to breakout for variable products is ignored which makes me believe that the tracking is wrong.
I tested by starting both of these scripts on different browser tabs and also starting a stopwatch on my phone. The numbers don't match. For variable products the execution time is twice as long on the phone compared to the stopwatch that tracks the execution time. When i execute these scripts one at a time, the stopwatch is correct. What is happening here?
Btw, i am aware of increasing the timeout, i'm on a shared hosting so i'm limited to 300s. Also starting the script on shell, still timeouts on 300s. What i want to accomplish is to squeeze out as much processing as possible in a 5min/300sec timeframe. The scripts are later executed using cron jobs but for now i'm just testing these on a browser.

Loop breaks after certain amount of iteration

I've a job to do some migration on wordpress sites, although I don't think wordpress has anything to do with my problem.
So I'm getting a set of data from a table (id, value) which is serialized.
I want to put the serialized data into arrays, according to the function. The algorithm doing everything fine for the first 86 records.
If I run the algorithm on just 86 records I get all the right data instantly. But when I run it 87 or more times its just goes into loading. Doesn't matter how long the max execution time limit is it's still gonna reach it apparently.
I checked the 87th record if theres something odd with it, but nothing i can see. Is there any idea what could couse this?
Here's the code:
global $wpdb;
$houses_built_years = $wpdb->get_results('SELECT post_id, meta_value FROM bp100_postmeta WHERE meta_key = "epites_eve"');
foreach ($houses_built_years as $house_built_years) {
if ($house_built_years->meta_value) {
$years = unserialize($house_built_years->meta_value);
} else {
continue;
}
$build = array(array());
$series = 0; $year = 0;
while (count($years)) {
if ($build[$series][$year] && (min($years) - $build[$series][$year] == 1)) {
$year++;
$build[$series][$year] = min($years);
unset($years[array_search(min($years), $years)]);
} else if ($build[$series][$year]) {
$year = 0;
$series++;
$build[$series][$year] = min($years);
unset($years[array_search(min($years), $years)]);
} else {
$build[$series][$year] = min($years);
unset($years[array_search(min($years), $years)]);
}
}
$repeater = array();
foreach ($build as $series) {
if (count($series) > 1) {
$repeater[] = $series[0] . ' - ' . $series[count($series) - 1];
} else {
$repeater[] = $series[0];
}
}
}
Here's some example of records (algoithm breaking at id = 30517):
30279
1201
epites_eve
a:1:{i:0;s:4:"1898";}
30338
1202
epites_eve
a:1:{i:0;s:4:"1891";}
30397
1203
epites_eve
a:1:{i:0;s:4:"1894";}
30517
1205
epites_eve
a:1:{i:0;s:4:"1897";}
30577
1206
epites_eve
a:1:{i:0;s:4:"1891";}

Weighted Load Balancing Algorithm into PHP Application

I want to resolve weighted an Adapter from an factory which could be configured by user (enable/disable and weight %).
Example:
AdapterW ≃ 20% of transaction
AdapterX ≃ 30% of transaction
AdapterY ≃ 40% of transaction
AdapterZ ≃ 10% of transaction
I can grant that all items will never sum more than one hundred (100%), but sometimes any adapter could be deactivated.
I have the following parameters:
public function handleAdapter()
{
$isWActive = (boolean)$this->_config[self::W];
$isXActive = (boolean)$this->_config[self::X];
$isYActive = (boolean)$this->_config[self::Y];
$isZActive = (boolean)$this->_config[self::Z];
$WPercentage = (int)$this->_config[self::LOAD_BALANCE_W];
$XPercentage = (int)$this->_config[self::LOAD_BALANCE_X];
$YPercentage = (int)$this->_config[self::LOAD_BALANCE_Y];
$ZPercentage = (int)$this->_config[self::LOAD_BALANCE_Z];
.
.
.
return (self::W | self::X | self::Y | self::Z);
}
How can i balance weighted between this adapters dynamically?
Edit
created a gist to a executable code: https://gist.github.com/markomafs/5d892d06d6670909f9b4
This may not be the best approach, but you can try something like this:
public function handleAdapter()
{
//an array to return the balanced entries
$balancedEntries[] = false;
//verifies which of the options are active
$isWActive = (boolean)$this->_config[self::W];
$isXActive = (boolean)$this->_config[self::X];
$isYActive = (boolean)$this->_config[self::Y];
$isZActive = (boolean)$this->_config[self::Z];
//get configured percentage of each
$WPercentage = (int)$this->_config[self::LOAD_BALANCE_W];
$XPercentage = (int)$this->_config[self::LOAD_BALANCE_X];
$YPercentage = (int)$this->_config[self::LOAD_BALANCE_Y];
$ZPercentage = (int)$this->_config[self::LOAD_BALANCE_Z];
//here you fill the array according to the proportion defined by the percentages
if ($isWActive) {
for ($i = 0; $i < $WPercentage; $i++) {
$balancedEntries[] = self::W;
}
}
if ($isXActive) {
for ($i = 0; $i < $XPercentage; $i++) {
$balancedEntries[] = self::X;
}
}
if ($isYActive) {
for ($i = 0; $i < $YPercentage; $i++) {
$balancedEntries[] = self::Y;
}
}
if ($isZActive) {
for ($i = 0; $i < $ZPercentage; $i++) {
$balancedEntries[] = self::Z;
}
}
return $balancedEntries;
}
And then, in case you want a proportion of 1 to 100 (as in percentages):
$balancedResult = $balancedEntries[array_rand($balancedEntries, 1)];
Since array_rand will return 1 key from the original array, you use it to get it's value.
Another try, this should work for your case - But it only work if you have an adapter as a single char string, this is not visible by your question.
public function handleAdapter()
{
# a map with all adapters
$map = array(
self::W => self::LOAD_BALANCE_W,
self::X => self::LOAD_BALANCE_X,
self::Y => self::LOAD_BALANCE_Y,
self::Z => self::LOAD_BALANCE_Z
);
# generate a string map with one char per percentage point
$stringMap = "";
foreach($map as $key => $value){
# skip if disabled
if(!$this->_config[$key]) continue;
# repeat the key for each percentage point
$stringMap .= str_repeat($key, (int)$this->_config[$value]);
}
# return a random string char from the map
return $stringMap[rand(0, strlen($stringMap) - 1)];
}
Edit: I've misunderstood the question, the answer is wrong.
I understand your question so that you always want to return the adapter with the lowest load to force traffic to this adapter.
public function handleAdapter()
{
$isWActive = (boolean)$this->_config[self::W];
$isXActive = (boolean)$this->_config[self::X];
$isYActive = (boolean)$this->_config[self::Y];
$isZActive = (boolean)$this->_config[self::Z];
$WPercentage = (int)$this->_config[self::LOAD_BALANCE_W];
$XPercentage = (int)$this->_config[self::LOAD_BALANCE_X];
$YPercentage = (int)$this->_config[self::LOAD_BALANCE_Y];
$ZPercentage = (int)$this->_config[self::LOAD_BALANCE_Z];
$map = array();
if($isWActive) $map[self::W] = $WPercentage;
if($isXActive) $map[self::X] = $XPercentage;
if($isYActive) $map[self::Y] = $YPercentage;
if($isZActive) $map[self::Z] = $ZPercentage;
asort($map);
return key($map);
}
Edit: Fixed wrong sort(), you need asort() to maintain the index.

Python socket recv (client-side)

i have to connect to an api via socket and send / recv some data.
the company send me a php-example-file with this code for reading data from the socket:
function readAnswer() {
$size = fgets($this->socketPtr, 64);
$answer = "";
$readed = 0;
while($readed < $size) {
$part = fread($this->socketPtr, $size - $readed);
$readed += strlen($part);
$answer .= $part;
}
return $answer;
}
This works for me. But in python i get from times to times an error.
not everything from the socket is recv.
my python try looks like this:
def read_answer(self,the_socket,timeout=0.5):
the_socket.setblocking(0)
total_data=[]
data=''
begin=time.time()
while 1:
if total_data and time.time()-begin > timeout:
break
elif time.time()-begin > timeout*2:
break
try:
data = the_socket.recv(8192)
if data:
total_data.append(data)
begin=time.time()
else:
time.sleep(0.1)
except:
pass
return ''.join(total_data)
i recv data as a dict / array. and from time to time i only get a int (msg length i think)
so what would be a better way to read the data from socket.
ah the api sends the data in a correct way, i checked this. it's only this little function ;(
After using the code below (thanks falsetru) and added a readed=len(data) i run into another problem:
this is the working php code:
function _parse_answer($answerData)
{
$result = array();
$lines = explode("\n", $answerData);
$data = explode("&", $lines[0]);
foreach($data as $piece)
{
$keyval = explode("=", $piece, 2);
$result[$keyval[0]] = $keyval[1];
}
for($i=1;$i<count($lines);$i++)
{
$result["csv"][]=$lines[$i];
}
return $result;
}
and this my crappy python code:
def parse_answer(self,data):
#print "dd_demo_api: answer: (%s)" % (data)
if data:
result = {}
lines = data.split("\n")
index_list = 0
if len(lines) == 1:
index_list = 0
else:
index_list = 1
pieces = lines[index_list].split("&")
for x in pieces:
keyval = x.split("=")
result[keyval[0]] = keyval[1]
iterlines = iter(lines)
next(iterlines)
next(iterlines)
count = 1
result["csv"] = {}
for x in iterlines:
result["csv"][count] = x.split(";")
return result
else:
return 0
i think here is some optimization required? ;(
Python version does not do the same thing with PHP version.
Try following code:
def read_answer(self, sock):
size = int(sock.recv(64).strip().rstrip('\0'))
# Above is not exactly same as `fgets`.
# If that causes an issue, use following instead.
#
# f = sock.makefile('r')
# size = int(f.readline(64).rstrip('\0'))
#
# and replace `sock.recv(n)` with `f.read(n)` in the following loop.
total_data = []
readed = 0
while readed < size:
data = sock.recv(size - readed)
if data:
total_data.append(data)
readed += len(data)
return b''.join(total_data)

Limit execution of a php-page to requests made from a certain ip-range

I have a PHP page I need to limit execution access of
to only clients inside our firewall.
How would I write a php-script that can look up the clients
ip-address and match it to a ip-range (for instance 10...* or 200.10.10.*).
You can use ip2long to convert dotted quads to long values, then just perform some arithmetic to check a given network/mask combination:
$network=ip2long("200.10.10.0");
$mask=ip2long("255.255.255.0");
$remote=ip2long($_SERVER['REMOTE_ADDR']);
if (($remote & $mask) == $network)
{
//match!
}
else
{
//does not match!
}
Well, assuming you're using Apache, there's a module called mod_authz_host that you can use.
Together with the file directive, you could limit access to a given php script for a range of ip addresses.
Here is the link to the documentation on that module:
http://httpd.apache.org/docs/2.2/mod/mod_authz_host.html
Here's a quick example (assuming your php file is called admin.php):
<file admin.php>
Order Deny,Allow
Deny from all
Allow from 200.10.10
</file>
The added benefit to the other solution suggested here is that you can control the security aspects from outside your application logic - a more flexible approach that does not impose any limitations on your PHP code.
PHP Limit/Block Website requests for Spiders/Bots/Clients etc.
Here i have written a PHP function which can Block unwanted Requests to reduce your Website-Traffic. God for Spiders, Bots and annoying Clients.
DEMO:
http://szczepan.info/9-webdesign/php/1-php-limit-block-website-requests-for-spiders-bots-clients-etc.html
/* Function which can Block unwanted Requests
* #return boolean/array status
*/
function requestBlocker()
{
/*
Version 1.0 11 Jan 2013
Author: Szczepan K
http://www.szczepan.info
me[#] szczepan [dot] info
###Description###
A PHP function which can Block unwanted Requests to reduce your Website-Traffic.
God for Spiders, Bots and annoying Clients.
*/
$dir = 'requestBlocker/'; ## Create & set directory writeable!!!!
$rules = array(
#You can add multiple Rules in a array like this one here
#Notice that large "sec definitions" (like 60*60*60) will blow up your client File
array(
//if >5 requests in 5 Seconds then Block client 15 Seconds
'requests' => 5, //5 requests
'sek' => 5, //5 requests in 5 Seconds
'blockTime' => 15 // Block client 15 Seconds
),
array(
//if >10 requests in 30 Seconds then Block client 20 Seconds
'requests' => 10, //10 requests
'sek' => 30, //10 requests in 30 Seconds
'blockTime' => 20 // Block client 20 Seconds
),
array(
//if >200 requests in 1 Hour then Block client 10 Minutes
'requests' => 200, //200 requests
'sek' => 60 * 60, //200 requests in 1 Hour
'blockTime' => 60 * 10 // Block client 10 Minutes
)
);
$time = time();
$blockIt = array();
$user = array();
#Set Unique Name for each Client-File
$user[] = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : 'IP_unknown';
$user[] = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
$user[] = strtolower(gethostbyaddr($user[0]));
# Notice that i use files because bots does not accept Sessions
$botFile = $dir . substr($user[0], 0, 8) . '_' . substr(md5(join('', $user)), 0, 5) . '.txt';
if (file_exists($botFile)) {
$file = file_get_contents($botFile);
$client = unserialize($file);
} else {
$client = array();
$client['time'][$time] = 0;
}
# Set/Unset Blocktime for blocked Clients
if (isset($client['block'])) {
foreach ($client['block'] as $ruleNr => $timestampPast) {
$left = $time - $timestampPast;
if (($left) > $rules[$ruleNr]['blockTime']) {
unset($client['block'][$ruleNr]);
continue;
}
$blockIt[] = 'Block active for Rule: ' . $ruleNr . ' - unlock in ' . ($left - $rules[$ruleNr]['blockTime']) . ' Sec.';
}
if (!empty($blockIt)) {
return $blockIt;
}
}
# log/count each access
if (!isset($client['time'][$time])) {
$client['time'][$time] = 1;
} else {
$client['time'][$time]++;
}
#check the Rules for Client
$min = array(
0
);
foreach ($rules as $ruleNr => $v) {
$i = 0;
$tr = false;
$sum[$ruleNr] = '';
$requests = $v['requests'];
$sek = $v['sek'];
foreach ($client['time'] as $timestampPast => $count) {
if (($time - $timestampPast) < $sek) {
$sum[$ruleNr] += $count;
if ($tr == false) {
#register non-use Timestamps for File
$min[] = $i;
unset($min[0]);
$tr = true;
}
}
$i++;
}
if ($sum[$ruleNr] > $requests) {
$blockIt[] = 'Limit : ' . $ruleNr . '=' . $requests . ' requests in ' . $sek . ' seconds!';
$client['block'][$ruleNr] = $time;
}
}
$min = min($min) - 1;
#drop non-use Timestamps in File
foreach ($client['time'] as $k => $v) {
if (!($min <= $i)) {
unset($client['time'][$k]);
}
}
$file = file_put_contents($botFile, serialize($client));
return $blockIt;
}
if ($t = requestBlocker()) {
echo 'dont pass here!';
print_R($t);
} else {
echo "go on!";
}

Categories