מהירות טעינת האתר הינו פקטור מאוד משמעותי אשר משפיע על דירוג האתר במנועי החיפוש, Cache היא טכנולוגיה אשר יכולה לשפר פלאות את מהירות הטעינה של האתר.
מהירות טעינת הדף הינו מרכיב עיקרי וחשוב מאוד בבניית האתר, ידוע כי מנועי החיפוש, בדגש על גוגל, נותנים פקטור עיקרי למהירות עליית הדף בחישוב ציון האתר שלנו ומיקומו בתוצאות החיפוש.
ככל שלאתר לוקח יותר זמן לעלות, כך הדירוג שלו יהיה נמוך יותר במנועי החיפוש כקב ירידה בכמות הכניסות או עלייה ב-Bounce Rate.
אופטימיזציה היא חלק שנוגע לא רק בקידום האתר שלכם אלא גם בחווית המשתמש של הגולש – גולש לא מרוצה זהו הפסד הפוטנציאל להפיכתו ללקוח.
אחד הפתרונות הקלים והיעילים ביותר בקיצור מהירות עליית הדף היא טכנולוגיה הנקראת Caching.
Cache (שימוש בזיכרון מטמון), הינו פתרון אשר מייעל את צורת הגשת האתר וחוסכת במשאבי שרת בשעות עומס ע"י הגשת תוכן בצורה סטטית.
מהם הגורמים המשפיעים על מהירות עליית האתר?
לפני שנסביר ונתחיל לבנות את מנוע ה-Caching שלנו, אנחנו צריכים להבין קודם, מהם בעצם הגורמים אשר משפיעים על זמן עליית הדף שלנו:
- מסד נתונים // כאשר האתר שלנו גדול ומכיל עשרות אלפי רשומות במסד נתונים, שליפה או חיפוש של מידע במסד נתונים יתחיל לדרוש מעט יותר זמן מהרגיל.
- סוג שרת // לרכיבי השרת חלק גדול ובלתי נפרד במהירות הגשת הדף, ככל שלשרת שלנו יהיו רכיבים יותר חדישים ומהירים, כך יקוצר זמן עליית הדף.
- מיקום פיזי // מיקומו הפיזי של השרת גם לוקח חלק בזמן טעינת הדף, אתר אשר מיועד לקהל הישראלי ויואחסן על שרת בארה"ב לדוגמא, יקח לאתר יותר זמן להגיע למשתמש.
- קוד האתר // כאשר אתר נבנה בצורה לא נכונה, או קורא להמון קבצי CSS ו-JS הדפדפן יעכב את הצגת העמוד עד שהקבצים האלו יטענו לזיכרון הדפדפן (בד"כ).
ישנם עוד גורמים אשר משפיעים על מהירות עליית האתר, אך אלו הם הגורמים העיקריים.
מהי טכנולוגיית ה-Caching?
תחילה נסביר שני מושגים:
- אתר סטטי // אתר אשר כל אחד מדפיו השונים הינו קובץ נפרד אשר מכיל את המידע (טקסט) ואת מבנה העמוד בקובץ אחד, אתר זה יבנה ע"י שימוש בשפת HTML.
- אתר דינאמי // אתר אשר מבוסס על מערכת ניהול תוכן (CMS) ותבנית אשר פה יש הפרדה בין המידע אשר נשמר במסד נתונים לבין מבנה העמוד אשר נשמר בתבנית.
לרוב, כאשר מספר גולשים יכנסו לאותו העמוד, יוצג לכל אחד מהם אותו התוכן, דבר אשר יצריך כל פעם גישה למסד הנתונים, ביצוע שאילתה ועיבוד התוצאות לכדי עמוד תוכן.
קריאה למסד הנתונים ועיבוד הבקשה גוזלים משאבים יקרים מהשרת – כאן נכנסת טכנולוגיית ה-Caching, אשר מאפשרת לנו להגיש לגולש עמודים דינאמיים כסטטים.
טכנולוגיית ה-Caching עובדת בצורה פשוטה, כאשר גולש נכנס לעמוד מסויים, נוצרת בקשה אשר בודקת האם קיים עותק סטטי של העמוד.
במידה וקיים עותק סטטי של העמוד, נבדק האם היא עדכנית ועומדת במגבלת הזמן של העותק, אם כן העותק מוגש לגולש.
במידה ואין עותק או שהוא אינו עומד במגבלת הזמן, מוגש לגולש העמוד הדינאמי אך מאחורי הקלעים נשמר עותק סטטי עבור שימוש עתידי.
בניית מנוע Cache
לאחר שהבנו מהם הגורמים המשפיעים על זמן טעינת העמוד ומהי הטכנולוגיה, ניגש לחלק הטכני.
שלב ראשון – בניית מחלקת Caching
בשלב הראשון, ניצור את השלד של המחלקה אשר תהיה אחראית על טיפול יצירה ותיעוד של מנוע ה-Caching שלנו.
<?php
/**
* Simple Caching Engine Class
*
* @author Dor Zuberi <dor@dorzki.co.il>
* @copyright dorzki
* @link https://www.dorzki.io
*/
class CacheEngine
{
/**
* The path to the caching directory.
*
* @var string
*/
private $cacheDirectory = '../cache/';
/**
* Cache file extension.
*
* @var string
*/
private $cacheExtension = '.html';
/**
* Cache max life.
*
* @var integer
*/
private $cacheTime = 36000;
/**
* List of pages not to cache.
*
* @var array
*/
private $dontCache = array();
/**
* The last file name of the cached page.
*
* @var string
*/
private $lastCached = '';
/**
* Log file name.
*
* @var string
*/
private $logFile = '.cache-log';
/**
* Initiates the class by ensuring the caching directory exists.
*/
function __construct() {
}
/**
* Get class property.
*
* @param mixed $property a class property
* @return mixed class property value.
*/
public function __get( $property ) {
}
/**
* Set class property.
*
* @param mixed $property a class property
* @param mixed $value new property value.
*/
public function __set( $property, $value ) {
}
/**
* The beginning of the caching process, including checking the copy and
* delivering the static copy or regenerating it.
*/
public function header() {
}
/**
* The end of the caching process, only acts if no static updated copy
* exists.
*/
public function footer() {
}
/**
* Checks if the cached file exists.
*
* @return boolean is the page cached?
*/
private function isCached() {
}
/**
* Checks if the cached file hasn't expired.
*
* @return boolean has the page expired?
*/
private function notExpired() {
}
/**
* Add page name or url for the engine to skip caching it.
*
* @param string $page page url or name to skip.
*/
public function skipPage( $page ) {
}
/**
* Purge a single page from the cache directory.
*
* @return boolean was the file deleted?
*/
public function purgeFile( $page ) {
}
/**
* Purge the entire cache directory.
*
* @return boolean was the directory cleared?
*/
public function purgeCache() {
}
/**
* Opens the log file and add to the top of the file a custom
* event details.
*
* @param string $event the event description.
*/
private function logEvent( $event ) {
}
}
השלד מורכב ממספר משתנים אשר מגדירים את התנהגות המנוע.
- שורה 18 // מיקום התיקייה אשר תכיל את קבצי ה-Cache, מומלץ שהתיקייה תשב מחוץ לתיקייה הראשית של השרת, כלומר מחוץ לתיקייה
public_html
אוhttpdocs
. - שורה 25 // סיומת קבצי ה-Cache שיווצרו.
- שורה 32 // אורך חיי המדף של קבצי ה-Cache, נמדד בשניות.
- שורה 39 // מערך אשר יכיל את רשימת העמודים אותם איננו רוצים להגיש בצורה סטטית.
- שורה 46 // שם הקובץ Cache האחרון שנוצר.
- שורה 53 // שם קובץ התיעוד של המנוע.
מלבד המשתנים הנ"ל, המחלקה מכילה גם מספר פונקציות אשר מבצעות את תפקידי המנוע, נבחן את כל אחת מהפונקציות העיקריות לעומק.
פונקציית הבנאי (אתחול)
הפונקצייה הראשונה היא פונקציית הבנאי, היא הפונקציה אשר רצח בעת אתחול / יצירה של אובייקט חדש של המחלקה שלנו.
/**
* Initiates the class by ensuring the caching directory exists.
*/
function __construct() {
if( !file_exists( $this->cacheDirectory ) OR !is_writeable( $this->cacheDirectory ) ) {
mkdir( $this->cacheDirectory );
chmod( $this->cacheDirectory, 755 );
$this->logEvent( 'Engine cache directory created;' );
}
$this->logEvent( 'Engine Initiated;' );
}
- שורה 7 // בשורה זאת אנו בודקים האם התיקייה השומרת את קבצי ה-Cache קיימת והאם היא ניתנת לכתיבה.
- שורות 8-9 // במידה והתיקייה לא קיימת או לא ניתנת לכתיבה, אנחנו יוצרים את התיקייה ונותנים לה הרשאות כתיבה.
פונקציית header
הפונקצייה העיקרית במנוע, אחראית על ביצוע בדיקה האם קיים עותק סטטי של העמוד והאם הוא עדכני.
במידה ולא קיים עותק, היא ופונקצייה footer()
יחדיו מציגות את העמוד ומייצרות עותק סטטי של העמוד לפעם הבאה.
/**
* The beginning of the caching process, including checking the copy and
* delivering the static copy or regenerating it.
*/
public function header() {
// Generate cache file name.
$this->lastCached = sprintf( '%scached-%s%s', $this->cacheDirectory, base64_encode( $_SERVER['REQUEST_URI'] ), $this->cacheExtension );
if( !in_array( $_SERVER['REQUEST_URI'], $this->dontCache ) ) {
if( $this->isCached() AND $this->notExpired() ) {
echo file_get_contents( $this->lastCached );
$this->logEvent( "Cached page served [{$this->lastCached}]" );
exit();
} else {
ob_start();
}
}
}
- שורה 8 // המנוע מייצר את שם הקובץ cache ע"י הצפנת כתובת העמוד באלגוריתם base64 ומצרף לפניו את כתובת התיקייה ובסופו את הסיומת של הקובץ.
- שורה 10 // מתבצעת בדיקה האם העמוד הוא ברשימת העמודים שלא אמורים להיות סטטים בכלל.
- שורה 11 // מתבצעת בדיקה האם קיים קובץ cache והאם הוא עדכני, במידה ואחד מהבדיקות תחזיר ערך שקרי, נגיש את העמוד דינאמי, במידה ומחוזרים ערכי אמת, נגיש את העותק הסטטי.
- שורה 12 // מתבצעת "הדפסה" של תוכן הקובץ הסטטי.
- שורה 16 // עוצרים את הסקריט על מנת שלא נמשיך.
- שורה 18 // קוראים לפונקציה אשר שומרת בזיכרון את כל מה שמוגש לגולש.
פונקציית footer
הפונקצייה אשר סוגרת את פעילות מנוע ה-caching שלנו, פועלת אך ורק אם פונקצייה header()
הגישה עמוד דינאמי ולא סטטי.
/**
* The end of the caching process, only acts if no static updated copy
* exists.
*/
public function footer() {
$cacheComment = "\n\n";
if( !in_array( $_SERVER['REQUEST_URI'], $this->dontCache ) ) {
if( !$this->isCached() OR !$this->notExpired() ) {
file_put_contents( $this->lastCached, $cacheComment . ob_get_contents() );
ob_end_flush();
$this->logEvent( "Cached page generated [{$this->lastCached}]" );
}
} else {
$this->logEvent( "Page served dynamically [{$_SERVER['REQUEST_URI']}]" );
}
}
- שורה 7 // מכילה הערה ב-HTML בה נשמר התאריך שבו נוצר עותק ה-cache.
- שורה 9 // מתבצעת בדיקה האם העמוד הוא ברשימת העמודים שלא אמורים להיות סטטים בכלל.
- שורה 10 // מתבצעת בדיקה האם לא קיים עותק או האם הוא לא בתוקף, במידה ואחת מהבדיקות תחזיר ערך שקר, לא יתבצע דבר.
- שורה 11 // נשמור לתוך קובץ ה-cache את כל התוכן אשר הוגש לגולש כולל ההערה שהגדרנו, על מנת לשמור עותק סטטי של העמוד.
- שורה 12 // משחררים את הזיכרון אשר שמר את התוכן שמוגש לגולש על מנת לשחרר משאבי שרת.
פונקציית isCached
הפונקציה בודקת האם קיים עותק של העמוד בתיקייה, במידה וכן הפונקצייה תחזיר ערך אמת, אחרת תחזיר ערך שקר.
/**
* Checks if the cached file exists.
*
* @return boolean is the page cached?
*/
private function isCached() {
return (boolean) file_exists( $this->lastCached );
}
פונקציית notExpired
הפונקציה מבצעת חישוב פשוט אשר קובץ האם הקובץ ששמור הוא קובץ עדכני.
החישוב הוא: זמן נוכחי - זמן יצירת הקובץ < זמן הוגדר כעדכני
/**
* Checks if the cached file hasn't expired.
*
* @return boolean has the page expired?
*/
private function notExpired() {
return ( time() - filemtime( $this->lastCached ) < $this->cacheTime ) ? true : false;
}
פונקציית skipPage
הפונקציה מקבלת כפרמטר את כתובת העמוד הרצוי ומכניסה אותו לרשימת עמודים אשר לא אמורים להיות סטטים בכלל.
/**
* Add page name or url for the engine to skip caching it.
*
* @param string $page page url or name to skip.
*/
public function skipPage( $page ) {
$this->dontCache[] = $page;
}
פונקציית purgeFile
הפונקציה מקבלת כפרמטר את כתובת העמוד אשר מעוננינים למחוק את העותק הסטטי שלו (לדוגמא לטובת יצירת עותק חדש אחרי עדכון העמוד).
הפונקציה תחזיר ערך אמת אם הקובץ נמחק ושקר אם התרחשה שגיאה.
/**
* Purge a single page from the cache directory.
*
* @return boolean was the file deleted?
*/
public function purgeFile( $page ) {
$cachedFile = sprintf( '%scached-%s%s', $this->cacheDirectory, base64_encode( $_SERVER['REQUEST_URI'] ), $this->cacheExtension );
$this->logEvent( "Page cahced file purged [{$page}]" );
return (boolean) unlink( $cachedFile );
}
- שורה 8 // המנוע מייצר את שם קובץ ה-cache ע"י הצפנת כתובת העמוד ומצרף לפניו את כתובת התיקייה ובסופו את הסיומת של הקובץ.
- שורה 12 // מתבצעת מחיקה של הקובץ ומחוזר ערך האם נמחק או לא.
פונקציית purgeCache
הפונקציה מבצעת מחיקה מלאה של התיקייה המכילה את ה-cache ששמורים בה, למעט קובץ התיעוד.
בסיום התהליך יחוזר ערך אמת (True) אם כלל הקבצים נמחקו או ערך שקר (False) בעת שגיאה.
/**
* Purge the entire cache directory.
*
* @return boolean was the directory cleared?
*/
public function purgeCache() {
$cachedFiles = glob( $this->cacheDirectory . '*' . $this->cacheExtension );
foreach ( $cachedFiles as $file ) {
if( !unlink( $file ) ) {
$this->logEvent( "Error trying to empty cache folder" );
return false;
}
}
$this->logEvent( "Cache folder emptied" );
return true;
}
- שורה 8 // מתבצעת סריקה של כלל התיקייה אחר קבצי cache ושמותיהם נשמרים במערך.
- שורה 10 // מתחילה לולאה אשר עוברת על כל אחד משמות הקבצים שנשמרו במערך.
- שורה 11 // מתבצע ניסיון של מחיקת הקובץ, במידה והיא נכשלת הפונקציה נעצרת ומחזירה ערך שקרי.
- שורה 20 // אם כלל הקבצים נמחקו הפונקציה מחזירה ערך אמת.
פונקציית logEvent
הפונקציה מקבלת כפרמטר את הערך שיש לתעד ושומרת אותו בקובץ התיעוד של המנוע.
הקובץ שימושי כאשר רוצים לבדוק את פעולת המנוע, במידה והתרחשה שגיאה או בשביל לייצר סטטיסטיקה אודות פעילות המנוע.
/**
* Opens the log file and add to the top of the file a custom
* event details.
*
* @param string $event the event description.
*/
private function logEvent( $event ) {
// Event Timestamp
$eventTime = date( 'd-m-Y, H:i:s' );
// Event Row Template
$eventTemp = sprintf( "[ %s | > %s; ]\n", $eventTime, $event );
$logger = fopen( $this->cacheDirectory . $this->logFile, 'a' );
fwrite( $logger, $eventTemp );
// Free up resources.
fclose( $logger );
}
שלב שני – שימוש במנוע ה-Caching
על מנת להטמיע את מנוע ה-Cache יש לשמור את הקובץ בתיקייה ראשית של האתר ולהטמיע אותו בראש העמוד ובתחתית העמוד.
קוד להטמעה בראש העמוד
<?php
// Including the cache engine class.
include( 'cache.class.php' );
// Initiating the engine & calling the header() function.
$cache = new cacheEngine();
$cache->header();
?>
קוד להטמעה בתחתית העמוד
<?php
// Calling the footer() function to end the engine's operation.
$cache->footer();
?>
סיכום
Cache הינו אחד מבין מגוון רחב של שיטות לקיצור זמני הטעינה של דפי האתר וביצוע אופטימיזציה לאתר.
שימוש במנוע Cache יאפשר לנו גם לחסוך בצורה משמעותית (כמעט 70%) במשאבי השרת ע״י הגשת עמודים סטיים.
ישנן מגוון רב של מנועי Cache אשר מאפשרים לבצע דברים יותר מוכבים ע״י שימוש בשיטות שונות ובטכניקות מורכבות יותר.