Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

Önbellek

Önbellek [keş], bir kez zorlukla elde edilen verileri bir sonraki kullanım için saklayarak uygulamanızı hızlandırır. Göstereceğiz:

  • önbellek nasıl kullanılır
  • depolama nasıl değiştirilir
  • önbellek nasıl doğru bir şekilde geçersiz kılınır

Nette'de önbellek kullanımı çok kolaydır, ancak çok gelişmiş ihtiyaçları bile karşılar. Performans ve %100 dayanıklılık için tasarlanmıştır. Temelde en yaygın arka uç depolama alanları için adaptörler bulacaksınız. Etiket tabanlı geçersizleştirmeyi, zaman aşımını destekler, önbellek izdihamına karşı koruması vardır vb.

Kurulum

Kütüphaneyi Composer aracını kullanarak indirip kurabilirsiniz:

composer require nette/caching

Temel Kullanım

Önbellekle çalışmanın merkezi noktası Nette\Caching\Cache nesnesidir. Bir örneğini oluştururuz ve kurucuya parametre olarak depolama adı verilen bir nesne geçiririz. Bu, verilerin fiziksel olarak depolanacağı yeri (veritabanı, Memcached, diskteki dosyalar, …) temsil eden bir nesnedir. Depolamaya, Nette\Caching\Storage türüyle dependency injection kullanarak geçirmemizi isteyerek erişiriz. Tüm önemli bilgileri Depolama bölümünde bulacaksınız.

Sürüm 3.0'da, arayüzün hala I öneki vardı, bu nedenle adı Nette\Caching\IStorage idi. Ayrıca, Cache sınıfının sabitleri büyük harflerle yazılmıştı, örneğin Cache::Expire yerine Cache::EXPIRE.

Aşağıdaki örnekler için, Cache takma adını oluşturduğumuzu ve $storage değişkeninde bir depolama alanına sahip olduğumuzu varsayalım.

use Nette\Caching\Cache;

$storage = /* ... */; // Nette\Caching\Storage örneği

Önbellek aslında bir anahtar-değer deposudur, yani verileri ilişkisel dizilerde olduğu gibi anahtarlar altında okur ve yazarız. Uygulamalar bir dizi bağımsız bölümden oluşur ve hepsi tek bir depolama alanı kullanırsa (diskte tek bir dizin düşünün), er ya da geç anahtar çakışmaları meydana gelir. Nette Framework, tüm alanı ad alanlarına (alt dizinlere) bölerek sorunu çözer. Programın her bölümü daha sonra benzersiz bir ada sahip kendi alanını kullanır ve artık çakışma olmaz.

Alan adını Cache sınıfının kurucusunun ikinci parametresi olarak belirtiriz:

$cache = new Cache($storage, 'Full Html Pages');

Şimdi $cache nesnesini kullanarak önbellekten okuyabilir ve ona yazabiliriz. Her ikisi için de load() yöntemi kullanılır. İlk argüman anahtardır ve ikincisi, anahtar önbellekte bulunamadığında çağrılan bir PHP geri çağrısıdır. Geri çağrı değeri oluşturur, döndürür ve önbelleğe kaydedilir:

$value = $cache->load($key, function () use ($key) {
	$computedValue = /* ... */; // pahalı hesaplama
	return $computedValue;
});

İkinci parametreyi belirtmezsek $value = $cache->load($key), öğe önbellekte yoksa null döndürülür.

Harika olan şey, önbelleğe herhangi bir serileştirilebilir yapının kaydedilebilmesidir, yalnızca dizeler olması gerekmez. Ve aynı şey anahtarlar için bile geçerlidir.

Öğeyi önbellekten remove() yöntemiyle sileriz:

$cache->remove($key);

Bir öğeyi önbelleğe kaydetmek için $cache->save($key, $value, array $dependencies = []) yöntemi de kullanılabilir. Ancak, yukarıda belirtilen load() yöntemini kullanmak tercih edilir.

Memoizasyon

Memoizasyon, bir fonksiyon veya metodun çağrısının sonucunu önbelleğe almak anlamına gelir, böylece aynı şeyi tekrar tekrar hesaplamadan bir dahaki sefere kullanabilirsiniz.

Metotlar ve fonksiyonlar call(callable $callback, ...$args) kullanılarak memoize edilebilir:

$result = $cache->call('gethostbyaddr', $ip);

gethostbyaddr() fonksiyonu böylece her $ip parametresi için yalnızca bir kez çağrılır ve bir dahaki sefere değer önbellekten döndürülür.

Ayrıca, daha sonra çağrılabilecek bir metot veya fonksiyon üzerinde memoize edilmiş bir sarmalayıcı oluşturmak da mümkündür:

function factorial($num)
{
	return /* ... */;
}

$memoizedFactorial = $cache->wrap('factorial');

$result = $memoizedFactorial(5); // ilk kez hesaplar
$result = $memoizedFactorial(5); // ikinci kez önbellekten

Sona Erme & Geçersizleştirme

Önbelleğe kaydetme ile birlikte, daha önce kaydedilen verilerin ne zaman geçersiz hale geleceği sorusunu çözmek gerekir. Nette Framework, verilerin geçerliliğini sınırlamak veya kontrollü bir şekilde silmek (framework terminolojisinde “geçersiz kılmak”) için bir mekanizma sunar.

Verilerin geçerliliği, kaydetme anında save() yönteminin üçüncü parametresi kullanılarak ayarlanır, örneğin:

$cache->save($key, $value, [
	$cache::Expire => '20 minutes',
]);

Veya load() yönteminin geri çağrısına referansla iletilen $dependencies parametresi kullanılarak, örneğin:

$value = $cache->load($key, function (&$dependencies) {
	$dependencies[Cache::Expire] = '20 minutes';
	return /* ... */;
});

Veya load() yöntemindeki 3. parametre kullanılarak, örneğin:

$value = $cache->load($key, function () {
	return ...;
}, [Cache::Expire => '20 minutes']);

Sonraki örneklerde, ikinci varyantı ve dolayısıyla $dependencies değişkeninin varlığını varsayacağız.

Sona Erme

En basit sona erme, bir zaman sınırıdır. Bu şekilde verileri 20 dakika geçerlilik süresiyle önbelleğe kaydederiz:

// saniye sayısını veya UNIX zaman damgasını da kabul eder
$dependencies[Cache::Expire] = '20 minutes';

Her okumada geçerlilik süresini uzatmak istersek, bunu aşağıdaki gibi yapabiliriz, ancak dikkatli olun, önbellek ek yükü artacaktır:

$dependencies[Cache::Sliding] = true;

Bir dosya veya birden fazla dosyadan herhangi biri değiştiğinde verilerin süresinin dolmasına izin verme seçeneği kullanışlıdır. Bu, örneğin bu dosyaların işlenmesinden kaynaklanan verileri önbelleğe kaydederken kullanılabilir. Mutlak yolları kullanın.

$dependencies[Cache::Files] = '/path/to/data.yaml';
// veya
$dependencies[Cache::Files] = ['/path/to/data1.yaml', '/path/to/data2.yaml'];

Bir öğenin süresinin başka bir öğenin (veya birden fazla öğeden herhangi birinin) süresi dolduğunda dolmasına izin verebiliriz. Bu, örneğin tüm bir HTML sayfasını önbelleğe kaydettiğimizde ve parçalarını başka anahtarlar altında sakladığımızda kullanılabilir. Parça değiştiğinde, tüm sayfa geçersiz kılınır. Parçaları örneğin frag1 ve frag2 anahtarları altında sakladıysak, şunu kullanırız:

$dependencies[Cache::Items] = ['frag1', 'frag2'];

Sona erme, her okumada öğenin hala geçerli olup olmadığına karar veren özel fonksiyonlar veya statik metotlar kullanılarak da kontrol edilebilir. Bu şekilde, örneğin PHP sürümü değiştiğinde öğenin süresinin dolmasına izin verebiliriz. Mevcut sürümü parametreyle karşılaştıran bir fonksiyon oluştururuz ve kaydederken bağımlılıklar arasına [fonksiyon adı, ...argümanlar] şeklinde bir dizi ekleriz:

function checkPhpVersion($ver): bool
{
	return $ver === PHP_VERSION_ID;
}

$dependencies[Cache::Callbacks] = [
	['checkPhpVersion', PHP_VERSION_ID] // checkPhpVersion(...) === false olduğunda süresi dolar
];

Tüm kriterler elbette birleştirilebilir. Önbellek daha sonra en az bir kriter karşılanmadığında sona erer.

$dependencies[Cache::Expire] = '20 minutes';
$dependencies[Cache::Files] = '/path/to/data.yaml';

Etiketlerle Geçersizleştirme

Çok kullanışlı bir geçersizleştirme aracı etiketlerdir. Önbellekteki her öğeye, herhangi bir dize olabilen bir etiket listesi atayabiliriz. Örneğin, önbelleğe alacağımız bir makale ve yorumları içeren bir HTML sayfamız olsun. Kaydederken etiketleri belirtiriz:

$dependencies[Cache::Tags] = ["article/$articleId", "comments/$articleId"];

Yönetim paneline geçelim. Burada makaleyi düzenlemek için bir form bulacağız. Makaleyi veritabanına kaydetmekle birlikte, etikete göre önbellekten öğeleri silen clean() komutunu çağıracağız:

$cache->clean([
	$cache::Tags => ["article/$articleId"],
]);

Benzer şekilde, yeni bir yorum ekleme (veya bir yorumu düzenleme) yerinde, ilgili etiketi geçersiz kılmayı unutmayacağız:

$cache->clean([
	$cache::Tags => ["comments/$articleId"],
]);

Bununla ne başardık? Makale veya yorumlar değiştiğinde HTML önbelleğimizin geçersiz kılınmasını (silinmesini) sağladık. ID = 10 olan bir makale düzenlendiğinde, article/10 etiketinin zorunlu geçersizleştirilmesi gerçekleşir ve belirtilen etiketi taşıyan HTML sayfası önbellekten silinir. Aynı şey, ilgili makalenin altına yeni bir yorum eklendiğinde de olur.

Etiketler Journal gerektirir.

Öncelikle Geçersizleştirme

Önbellekteki bireysel öğelere bir öncelik ayarlayabiliriz, bu sayede örneğin önbellek belirli bir boyutu aştığında bunları silebiliriz:

$dependencies[Cache::Priority] = 50;

100'e eşit veya daha düşük önceliğe sahip tüm öğeleri sileceğiz:

$cache->clean([
	$cache::Priority => 100,
]);

Öncelikler Journal gerektirir.

Önbelleği Silme

Cache::All parametresi her şeyi siler:

$cache->clean([
	$cache::All => true,
]);

Toplu Okuma

Önbelleğe toplu okuma ve yazma işlemleri için bulkLoad() yöntemi kullanılır, buna anahtar dizisini geçiririz ve değer dizisini alırız:

$values = $cache->bulkLoad($keys);

bulkLoad() yöntemi, oluşturulan öğenin anahtarını alan ikinci bir geri çağırma parametresiyle load() yöntemine benzer şekilde çalışır:

$values = $cache->bulkLoad($keys, function ($key, &$dependencies) {
	$computedValue = /* ... */; // pahalı hesaplama
	return $computedValue;
});

PSR-16 ile Kullanım

Nette Cache'i PSR-16 arayüzüyle kullanmak için PsrCacheAdapter adaptörünü kullanabilirsiniz. Nette Cache ile PSR-16 uyumlu bir önbellek bekleyen herhangi bir kod veya kütüphane arasında sorunsuz entegrasyon sağlar.

$psrCache = new Nette\Bridges\Psr\PsrCacheAdapter($storage);

Şimdi $psrCache'i PSR-16 önbelleği olarak kullanabilirsiniz:

$psrCache->set('key', 'value', 3600); // değeri 1 saatliğine kaydeder
$value = $psrCache->get('key', 'default');

Adaptör, getMultiple(), setMultiple() ve deleteMultiple() dahil olmak üzere PSR-16'da tanımlanan tüm yöntemleri destekler.

Çıktıyı Önbelleğe Alma

Çıktıyı yakalamak ve önbelleğe almak çok zarif bir şekilde yapılabilir:

if ($capture = $cache->capture($key)) {

	echo ... // verileri yazdırıyoruz

	$capture->end(); // çıktıyı önbelleğe kaydediyoruz
}

Çıktı zaten önbellekteyse, capture() yöntemi onu yazdırır ve null döndürür, bu nedenle koşul yürütülmez. Aksi takdirde, çıktıyı yakalamaya başlar ve sonunda yazdırılan verileri önbelleğe kaydettiğimiz $capture nesnesini döndürür.

Sürüm 3.0'da yöntemin adı $cache->start() idi.

Latte'de Önbelleğe Alma

Latte şablonlarında önbelleğe alma çok kolaydır, şablonun bir bölümünü {cache}...{/cache} etiketleriyle sarmak yeterlidir. Kaynak şablon değiştiğinde (önbellek bloğu içindeki dahil edilen şablonlar dahil) önbellek otomatik olarak geçersiz kılınır. {cache} etiketleri iç içe yerleştirilebilir ve iç içe geçmiş bir blok geçersiz kılındığında (örneğin bir etiketle), üst blok da geçersiz kılınır.

Etikette, önbelleğin bağlanacağı anahtarları (burada $id değişkeni) belirtebilir ve sona erme süresini ve geçersizleştirme etiketlerini ayarlayabilirsiniz.

{cache $id, expire: '20 minutes', tags: [tag1, tag2]}
	...
{/cache}

Tüm öğeler isteğe bağlıdır, bu nedenle ne sona erme süresini ne de etiketleri, hatta anahtarları bile belirtmemiz gerekmez.

Önbellek kullanımı ayrıca if kullanılarak koşullandırılabilir – içerik yalnızca koşul karşılanırsa önbelleğe alınır:

{cache $id, if: !$form->isSubmitted()}
	{$form}
{/cache}

Depolama

Depolama, verilerin fiziksel olarak depolandığı yeri temsil eden bir nesnedir. Bir veritabanı, Memcached sunucusu veya en erişilebilir depolama alanı olan diskteki dosyaları kullanabiliriz.

Depolama Açıklama
FileStorage diske dosyalara kaydeden varsayılan depolama
MemcachedStorage Memcached sunucusunu kullanır
MemoryStorage veriler geçici olarak bellekte tutulur
SQLiteStorage veriler SQLite veritabanına kaydedilir
DevNullStorage veriler kaydedilmez, test için uygundur

Depolama nesnesine, Nette\Caching\Storage türüyle dependency injection kullanarak geçirmemizi isteyerek erişirsiniz. Nette, varsayılan depolama olarak verileri geçici dosyalar dizinindeki cache alt dizinine kaydeden bir FileStorage nesnesi sağlar.

Depolamayı yapılandırmada değiştirebilirsiniz:

services:
	cache.storage: Nette\Caching\Storages\DevNullStorage

FileStorage

Önbelleği diskteki dosyalara yazar. Nette\Caching\Storages\FileStorage depolama alanı, performans için çok iyi optimize edilmiştir ve özellikle işlemlerin tam atomikliğini sağlar. Bu ne anlama geliyor? Önbelleği kullanırken, başka bir iş parçacığı tarafından henüz tamamen yazılmamış bir dosyayı okumanız veya birinin onu “ellerinizin altından” silmesi mümkün değildir. Bu nedenle önbellek kullanımı tamamen güvenlidir.

Bu depolama alanı ayrıca, önbellek silindiğinde veya henüz ısınmadığında (yani oluşturulmadığında) CPU kullanımında aşırı artışı önleyen önemli bir yerleşik işleve sahiptir. Bu, önbellek izdihamı önlemesidir. Bazen, aynı anda daha fazla sayıda eşzamanlı istek, önbellekten aynı şeyi (örneğin pahalı bir SQL sorgusunun sonucu) ister ve önbellekte olmadığı için tüm işlemler aynı SQL sorgusunu yürütmeye başlar. Yük böylece katlanır ve hatta hiçbir iş parçacığının zaman sınırında yanıt verememesi, önbelleğin oluşturulmaması ve uygulamanın çökmesi bile olabilir. Neyse ki, Nette'deki önbellek, bir öğe için birden fazla eşzamanlı istek olduğunda, onu yalnızca ilk iş parçacığının oluşturduğu, diğerlerinin beklediği ve ardından oluşturulan sonucu kullandığı şekilde çalışır.

FileStorage oluşturma örneği:

// depolama alanı diskteki '/path/to/temp' dizini olacak
$storage = new Nette\Caching\Storages\FileStorage('/path/to/temp');

MemcachedStorage

Memcached sunucusu, adaptörü Nette\Caching\Storages\MemcachedStorage olan yüksek performanslı bir dağıtılmış bellek depolama sistemidir. Yapılandırmada, standart 11211'den farklıysa IP adresini ve bağlantı noktasını belirtiriz.

PHP memcached uzantısı gerektirir.

services:
	cache.storage: Nette\Caching\Storages\MemcachedStorage('10.0.0.5')

MemoryStorage

Nette\Caching\Storages\MemoryStorage, verileri bir PHP dizisinde saklayan ve bu nedenle istek sona erdiğinde kaybolan bir depolama alanıdır.

SQLiteStorage

SQLite veritabanı ve Nette\Caching\Storages\SQLiteStorage adaptörü, önbelleği diskteki tek bir dosyaya kaydetmenin bir yolunu sunar. Yapılandırmada bu dosyanın yolunu belirtiriz.

PHP pdo ve pdo_sqlite uzantılarını gerektirir.

services:
	cache.storage: Nette\Caching\Storages\SQLiteStorage('%tempDir%/cache.db')

DevNullStorage

Depolamanın özel bir uygulaması, aslında verileri hiç saklamayan Nette\Caching\Storages\DevNullStorage'dır. Bu nedenle, önbelleğin etkisini ortadan kaldırmak istediğimizde test için uygundur.

Kodda Önbellek Kullanımı

Kodda önbellek kullanırken, bunu yapmanın iki yolu vardır. Birincisi, dependency injection kullanarak depolamayı geçirmemizi istemek ve bir Cache nesnesi oluşturmaktır:

use Nette;

class ClassOne
{
	private Nette\Caching\Cache $cache;

	public function __construct(Nette\Caching\Storage $storage)
	{
		$this->cache = new Nette\Caching\Cache($storage, 'my-namespace');
	}
}

İkinci seçenek, doğrudan bir Cache nesnesi geçirmemizi istemektir:

class ClassTwo
{
	public function __construct(
		private Nette\Caching\Cache $cache,
	) {
	}
}

Cache nesnesi daha sonra doğrudan yapılandırmada şu şekilde oluşturulur:

services:
	- ClassTwo( Nette\Caching\Cache(namespace: 'my-namespace') )

Journal

Nette, etiketleri ve öncelikleri journal adı verilen bir yerde saklar. Standart olarak bunun için SQLite ve journal.s3db dosyası kullanılır ve PHP pdo ve pdo_sqlite uzantıları gereklidir.

Journal'ı yapılandırmada değiştirebilirsiniz:

services:
	cache.journal: MyJournal

DI Servisleri

Bu servisler DI konteynerine eklenir:

Ad Tür Açıklama
cache.journal Nette\Caching\Storages\Journal journal
cache.storage Nette\Caching\Storage depolama

Önbelleği Devre Dışı Bırakma

Uygulamada önbelleği devre dışı bırakmanın bir yolu, depolama olarak DevNullStorage ayarlamaktır:

services:
	cache.storage: Nette\Caching\Storages\DevNullStorage

Bu ayarın Latte'deki şablonların veya DI konteynerinin önbelleğe alınması üzerinde bir etkisi yoktur, çünkü bu kütüphaneler nette/caching servislerini kullanmaz ve kendi önbelleklerini yönetirler. Ayrıca, geliştirme modunda önbelleklerini devre dışı bırakmaya gerek yoktur.

versiyon: 3.x