Оптимизиране на бързодействието на сайт

Пиша тази статия като продължение на статията PHP парсване на XML новини за твоя сайт. Разглеждам оптимизиране на бързодействието на динамичен сайт, който зарежда информация от база данни.

Сайтът ganbox.com е разделен на две части, които се различават и визуално. Основен сайт за SEO оптимизация и SEO блог. Реших в лявата колона на основния сайт под менюто, да извеждам последните X на брой статии от блога.   Вече имах готов код за извличане на новини от RSS хранилка, но си поставих и две допълнителни условия: бързодействие и сигурност при зареждане на страницата.

Бързодействие при зареждане на страница

Колкото и да е голям по обем твоя сайт, зареждането на началната страница трябва да бъде максимално бързо. Това се постига, чрез цялостно или частично кеширане на страницата. Бързодействието е важно основно по две причини:

1. Посетителите бързо ще загубят доверие в сайта, ако той редовно се зарежда бавно.  Повечето ще се подразнят и ще спрат да го посещават и ще потърсят друг подобен сайт.

2. Поради SEO причини. Роботите на търсачките имат зададено време на изчакване за зареждане на страницата, която индексират. Ако страницата е прекалено бавна, тя няма да се индексира изобщо или ще се индексира частично. В резултат на това сайтът започва да се класира лошо по все повече ключови думи за търсене.

Сигурност при зареждане на страница

Тук под термина сигурност имам предвид, че началната страница е особено критична и трябва да се зареди на всяка цена. Щом уеб сървърът работи, страницата трябва да се зареди, дори и ако SQL сървъра е претоварен или недостъпен. Глупаво е да падне началната страница на твоя бизнес сайт, защото имало проблем с базата данни. При сайтове на споделен хостинг често много сайтове ползват един SQL сървър. Например към момента superhosting не ползват специален софтуер за виртуализация и ако един сайт претовари базата данни, много други сайтове, които нямат вина ще пострадат. Към момента те си решават този проблем с денонощно наблюдение.

Решаването на проблема с отпадане на базата данни се постига, като блокове от началната страница, в които се зарежда информация от базата данни или информация от друг сайт, се обработват по специален начин. Има два подхода или да се използва кеширане на тези блокове или да се ограничи времето за изчакване на всеки блок и ако няма резултат този блок просто не се зарежда. В примера от тази статия аз използвам комбиниран подход. Само ще спомена, че има вариант, при който може да се кешира цялата страница, но той не е толкова гъвкав, защото различни блокове може да е необходимо да се опресняват през различни интервали от време в зависимост от информацията, която съдържат. Например блок с новини извличани от друг сайт ще искаш да се опресняват през 5 мин., защото каква е ползата да съобщиш за заметресение часове след като се е случило. Докато блок със статии от блога, в който пишеш веднъж седмично е достатъчно да се опреснява веднъж на 12 часа. Има и още един вариант - блоковете да се зареждат с ajax, но това не е добре от SEO гледна точка.

Кеширане на съдържанието

Добре, стига приказки! Давай кода! 🙂 Посочения код изисква PHP >=5 заради DOMDocument и curl модул, като и двете се предлагат стандартно от повечето хостинг доставчици.

# извличане на последните статии от блога
$rssArticleLimit = 2; # колко новини се показват
$cacheTime = 7200; # опресняване през 2 часа (в секунди 7200)
$rssCacheFilePath = 'blognews.html'; # път до кеш файла
$urlBlogRSS = 'http://feeds.feedburner.com/ganbox'; # url на RSS новини

# следните две се ползват за проверка дали са извлечени всички данни
$rssMinLen = 200; # минимален брой символи във кеш файла
$rssLastTag = '

'; # кеш файла завършва с този таг

# създаване или опресняване на кеш файла
if(!file_exists($rssCacheFilePath) # ако файлът все още не е създаден
|| filemtime($rssCacheFilePath) + $cacheTime < time() # или е променен преди повече от 2 часа ){ $xml = get_page_content($urlBlogRSS, 8, 14); /* само 8 сек. време за свързване и 14 сек. общо време за изпълнение, увеличи ако трябва */ if(!empty($xml)){ $htmlData = ''; /* от XML в масив */ $doc = new DOMDocument(); if($doc->loadXML($xml)){
$arrFeeds = array();
foreach ($doc->getElementsByTagName('item') as $node) {
$itemRSS = array (
'title' => $node->getElementsByTagName('title')->item(0)->nodeValue,
'desc' => $node->getElementsByTagName('description')->item(0)->nodeValue,
'link' => $node->getElementsByTagName('link')->item(0)->nodeValue,
'date' => $node->getElementsByTagName('pubDate')->item(0)->nodeValue,
);
array_push($arrFeeds, $itemRSS);
}
/* от масив в HTML */
$rssTitle = (1 == $rssArticleLimit) ? 'Последна статия' : "Последните $rssArticleLimit статии от блога";
$htmlData .= "

$rssTitle

";
$feedCount=0;
foreach($arrFeeds as $oneItem){
/* при мен базата има кодиране utf-8, а началната страница cp1251 следващия ред вероятно няма да ти трябва */
foreach($oneItem as $key=>$value){ $oneItem[$key]=iconv('utf-8','cp1251',$value); }
$feedCount++;
$htmlData .= '

'.$oneItem['title'].'

';
$htmlData .= '

'.my_chunk_split($oneItem['desc']).'

';
$htmlData .= $rssLastTag;
if($rssArticleLimit>0 && $feedCount>=$rssArticleLimit) break;
}
}else{
/* това което е върнал сървъра не е XML и вероятно е някаква грешка изпрати си имейл с този $xml */
$mail_text = 'FILE'.__FILE__."\nLINE:".__LINE__."\nERROR: това което върна RSS сървъра не прилича на XML\nXML:$xml\n";
SendEmail($mail_text);
}
if(!empty($htmlData)){ /* ако са извлечени данни успешно */
if(strlen($htmlData) > $rssMinLen && preg_match('|'.$rssLastTag.'$|', $htmlData)){ /* още една проверка дали са извлечени всички данни */
$fh = @fopen($rssCacheFilePath, 'w'); /* презаписва файл */
if($fh){
@fwrite($fh, $htmlData);
@fclose($fh);
}else{ /* имейл до теб, че има проблем с писане в кеш файла */
$mail_text = 'FILE'.__FILE__."\nLINE:".__LINE__."\nERROR: проблем с писане в кеш файла $rssCacheFilePath\n";
SendEmail($mail_text);
}
}
}
}else{ /* времето за изчакване на RSS сървъра да върне отговор е изтекло */
$mail_text = 'FILE'.__FILE__."\nLINE:".__LINE__."\nERROR: времето за изчакване на RSS е изтекло\nXML:$xml\n";
SendEmail($mail_text);
}
}
/* показване на кеш файла, ако съдържа нещо */
if(file_exists($rssCacheFilePath)) {
$htmlData = file_get_contents($rssCacheFilePath);
if(strlen($htmlData) > $rssMinLen && preg_match('|'.$rssLastTag.'$|', $htmlData)){ /* проверка дали са извлечени всички данни */
print $htmlData;
}else{ /* едва ли ще се случи но за всеки случай си изпрати имейл, че кеш файла е непълен */
$mail_text = 'FILE'.__FILE__."\nLINE:".__LINE__."\nERROR: непълен кеш файл $rssCacheFilePath чупи началната страница\nHTML: $htmlData\n";
SendEmail($mail_text);
}
}

Обяснения на кода

При първото зареждане на страницата от посетител се извличат данните от RSS, преобразуват се в HTML и се записват във файл. При всяко следващо зареждане се проверява възрастта на кеш файла, ако е пресен се взема съдържанието му е се отпечатва светкавично в блока. Всичко останало от страницата, което няма нещо общо с база данни, се генерира от PHP.  Ако файлът е остарял се прави опит да се извлекат данни от RSS, като се изчаква само няколко секунди. Ако времето е недостатъчно или в момента няма връзка с базата данни, администратора на сайта ще получи имейл и данните ще се заредят от кеш файла. Това ще забави зареждането на страницата само няколко секунди.  Администраторът ще трябва да увеличи времето за изчакване, иначе ще получава периодично писма. В този пример са дадени стойности 8 и 14 сек. По този начин имаме само две положения: страницата се зарежда моментално или страницата се бави няколко секунди, след което се зарежда, като съдържанието на блока е от кеша и се уведомява администратора, че има проблем. Посочения метод има само един недостатък - след като пуснеш статия тя ще се появи със закъснение в началната страница. Това закъснение в примера е от 0 до 2 часа. Ако пишеш често, например по няколко статии на ден, може да намалиш времето на $cacheTime=1800; # половин час.

Използвани функции

get_page_content($url) - извлича съдържанието на страница като ползва curl библиотека, ако зададеното време изтече без да има резултат скрипта продължава.
Кодът на функцията е даден в статията PHP: Извличане текст от интернет страница.

my_chunk_split($str) - Ако текста съдържа дълги низове ги пренася на следващия ред при достигане на width последователни знака. При мен лявата колона е тясна и ако текста съдържа низ с повече от 28 знака един до друг без интервал, това кара текста да излезе от лявата колона и да навлезе в средната бяла колона.

function my_chunk_split($str, $width=28) {
$stmp = base64_encode($str);
$len = strlen($stmp);
$out =  "";
$done=0;
while( $done<$len ) { $out.=($len-$done>$width) ? substr($str, $done, $width). "\n"
: substr($str, $done, $len-$done). "\n";
$done+=$width;
}
return $out;
}

SendEmail($text) - изпраща text до администратора на сайта. Всеки може да си напише или да ползва библиотека (или просто да постъпи небрежно и да коментира тази функция в кода), затова няма да дам кода на функцията тук.

Ако твоя сайт се зарежда бавно и искаш оптимизиране на бързодействието може да се свържеш с мен за оферта от тук.

При проблеми с този код пиши коментари по статията.
.
.
Подобни статии:
PHP парсване на XML новини за твоя сайт
PHP: Извличане текст от интернет страница

Прочетена:15697
1 - лоша2 - слаба3 - средна4 - добра5 - отлична (1 гласа, оценка: 5,00 от 5. Моля изберете оценка!)
Loading...
Георги Стефанов
Магистър по информатика, който се занимава с компютри от 1988 г., професионално с програмиране от 1998 г., а с уеб технологии от 2002 г. Има богат опит при оптимизиране на бизнес сайтове. Повече от 15 години развива успешно свои уеб проекти и работи с български и международни компании за постоянно подобряване на техните сайтове и увеличаване на онлайн продажбите. 

5 comments on “Оптимизиране на бързодействието на сайт”

  1. При мен, като я няма базата, връщам хедър за грешка 500 (Internal Server Error). Така ботът знае, че в момента имам проблем и да намине по-късно. На потребителите им се показва каквото е възможно, като пак на места има съобщения, че базата я няма и затова липсва съдържание.

  2. Да прихванеш грешката и да изведеш човешко съобщение е по-добрия вариант отколкото изобщо да не я обработваш. Имам скриншотове на dir.bg с блокове, в които се вижда SQL грешка 🙂 Все пак по принцип да занимаваш потребителя с това, че базата я няма не ми се струва уместно, а за корпоративен сайт е направо смешно. Може би, ако при отпаднала връзка, няма какво да се покаже на потребителя, по-добре целия блок да изчезва.

  3. Определено бързото зареждане на страницата е от значение особено на заглавната страница, затова и избягвам да правя индекс страницата динамична.

  4. Хитро си се сетил да ползваш библиотеката DOMDocument. Само да допълня, че с нея няма да можете да вземате снимки от даден xml,rss файл - не към теб, а към хората, които четат поста. Идеята ми допада и ще я ползвам на новия проект. Досега не бях разглеждал възможността за пренатоварване на mysql и какви ще са последствията 🙂

  5. @Мартин, не знам защо реши, че няма да можеш да вземаш снимки от RSS емисия с DOMDocument. Всичко зависи дали самата емисия е форматирана според стандарта. Прочети моята статия "PHP парсване на xml новини за твоя сайт" и по-специално коментарите, в които говорим за enclosure таг.

споделиха
linkedin facebook pinterest youtube rss twitter instagram facebook-blank rss-blank linkedin-blank pinterest youtube twitter instagram