Önbellekleme

Önbellek, bir kez zor alınan verileri gelecekte kullanılmak üzere depolayarak uygulamanızı hızlandırır. Size göstereceğiz:

  • Önbellek nasıl kullanılır
  • Önbellek depolama alanı nasıl değiştirilir
  • Önbellek nasıl düzgün şekilde geçersiz kılınır

Nette'de önbelleği kullanmak çok kolaydır, aynı zamanda çok gelişmiş önbellekleme ihtiyaçlarını da karşılar. Performans ve %100 dayanıklılık için tasarlanmıştır. Temel olarak, en yaygın arka uç depolama için adaptörler bulacaksınız. Etiket tabanlı geçersiz kılma, önbellek izdiham koruması, zaman aşımı vb. sağlar.

Kurulum

Composer'ı kullanarak paketi indirin ve yükleyin:

composer require nette/caching

Temel Kullanım

Önbellek ile çalışmanın merkezi Nette\Caching\Cache nesnesidir. Örneğini oluştururuz ve depolama alanını yapıcıya parametre olarak aktarırız. Bu, verilerin fiziksel olarak depolanacağı yeri temsil eden bir nesnedir (veritabanı, Memcached, diskteki dosyalar, …). Depolama nesnesini Nette\Caching\Storage tipi ile bağımlılık enjeksiyonu kullanarak geçirerek elde edersiniz. Depolama bölümündeki tüm temel bilgileri öğreneceksiniz.

Sürüm 3.0'da arayüz hala I prefix, so the name was Nette\Caching\IStorage şeklindeydi. Ayrıca, Cache sınıfının sabitleri büyük harfle yazılıyordu, yani örneğin Cache::Expire yerine Cache::EXPIRE.

Aşağıdaki örnekler için, bir Cache takma adımız ve $storage değişkeninde bir depolama alanımız olduğunu varsayalım.

use Nette\Caching\Cache;

$storage = /* ... */; // instance of Nette\Caching\Storage

Önbellek aslında bir anahtar-değer deposudur, bu nedenle anahtarlar altındaki verileri tıpkı ilişkisel diziler gibi okur ve yazarız. Uygulamalar bir dizi bağımsız parçadan oluşur ve hepsi tek bir depolama alanı kullansaydı (fikir için: bir diskteki bir dizin), er ya da geç bir anahtar çarpışması olurdu. Nette Framework bu sorunu tüm alanı isim alanlarına (alt dizinler) bölerek çözer. Böylece programın her bir parçası kendi alanını benzersiz bir isimle kullanır ve hiçbir çarpışma meydana gelmez.

Alanın adı, Cache sınıfının yapıcısının ikinci parametresi olarak belirtilir:

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

Artık önbellekten okumak ve yazmak için $cache nesnesini kullanabiliriz. Her ikisi için de load() yöntemi kullanılır. İlk argüman anahtar, ikincisi ise anahtar önbellekte bulunamadığında çağrılan PHP geri çağrısıdır. Geri arama bir değer üretir, geri döndürür ve önbelleğe alır:

$value = $cache->load($key, function () use ($key) {
	$computedValue = /* ... */; // ağır hesaplamalar
	return $computedValue;
});

İkinci parametre belirtilmezse $value = $cache->load($key), öğe önbellekte değilse null döndürülür.

Harika olan şey, yalnızca dizelerin değil, serileştirilebilir tüm yapıların önbelleğe alınabilmesidir. Ve aynı şey anahtarlar için de geçerlidir.

Öğe, remove() yöntemi kullanılarak önbellekten temizlenir:

$cache->remove($key);

Bir öğeyi $cache->save($key, $value, array $dependencies = []) yöntemini kullanarak da önbelleğe alabilirsiniz. Ancak, load() adresini kullanan yukarıdaki yöntem tercih edilir.

Memoizasyon

Hafızaya alma, aynı şeyi tekrar tekrar hesaplamak yerine bir dahaki sefere kullanabilmeniz için bir işlevin veya yöntemin sonucunu önbelleğe almak anlamına gelir.

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

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

gethostbyaddr() işlevi her parametre için yalnızca bir kez çağrılır $ip ve bir sonraki sefer önbellekten değer döndürülür.

Daha sonra çağrılabilecek bir yöntem veya fonksiyon için memoize edilmiş bir sarmalayıcı oluşturmak da mümkündür:

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

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

$result = $memoizedFactorial(5); // sayar
$result = $memoizedFactorial(5); // önbellekten döndürür

Sona Erme ve Geçersiz Kılma

Önbellekleme ile, daha önce kaydedilen verilerin bazılarının zaman içinde geçersiz hale geleceği sorusunu ele almak gerekir. Nette Framework, verilerin geçerliliğinin nasıl sınırlandırılacağı ve kontrollü bir şekilde nasıl silineceği (“geçersiz kılmak”, framework'ün terminolojisini kullanarak) konusunda bir mekanizma sağlar.

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

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

Ya da load() yöntemindeki geri aramaya referans olarak aktarılan $dependencies parametresini kullanarak, örn:

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

Veya load() yönteminde 3. parametreyi kullanarak, örn:

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

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

Son kullanma tarihi

En basit sona erme zaman sınırıdır. İşte 20 dakika boyunca geçerli verileri nasıl önbelleğe alacağınız:

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

Her okumada geçerlilik süresini uzatmak istiyorsak, bu şekilde elde edilebilir, ancak dikkat edin, bu önbellek yükünü artıracaktır:

$dependencies[Cache::Sliding] = true;

Kullanışlı seçenek, belirli bir dosya veya birkaç dosyadan biri değiştirildiğinde verilerin süresinin dolmasına izin verme yeteneğidir. Bu, örneğin, bu dosyaların işlenmesinden kaynaklanan verileri önbelleğe almak için 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'];

Başka bir öğenin (veya diğerlerinden birinin) süresi dolduğunda önbellekteki bir öğenin süresinin dolmasına izin verebiliriz. Bu, HTML sayfasının tamamını ve diğer anahtarlar altındaki parçalarını önbelleğe aldığımızda kullanılabilir. Parçacık değiştiğinde, sayfanın tamamı geçersiz hale gelir. frag1 ve frag2 gibi anahtarlar altında depolanan parçalarımız varsa, kullanacağız:

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

Süre dolumu, öğenin hala geçerli olup olmadığına okuma sırasında her zaman karar veren özel işlevler veya statik yöntemler kullanılarak da kontrol edilebilir. Örneğin, PHP sürümü her değiştiğinde öğenin süresinin dolmasına izin verebiliriz. Geçerli sürümü parametre ile karşılaştıran bir işlev oluşturacağız ve kaydederken şu biçimde bir dizi ekleyeceğiz [function name, ...arguments] bağımlılıklara:

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

$dependencies[Cache::Callbacks] = [
	['checkPhpVersion', PHP_VERSION_ID] // checkPhpVersion(...) === false olduğunda sona erer
];

Elbette tüm kriterler birleştirilebilir. En az bir kriter karşılanmadığında önbellek sona erer.

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

Etiket Kullanarak Geçersiz Kılma

Etiketler çok kullanışlı bir geçersiz kılma aracıdır. Önbellekte depolanan her bir öğeye rastgele dizeler olan bir etiket listesi atayabiliriz. Örneğin, önbelleğe almak istediğimiz bir makale ve yorumlar içeren bir HTML sayfamız olduğunu varsayalım. Bu yüzden önbelleğe kaydederken etiketleri belirtiriz:

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

Şimdi, yönetime geçelim. Burada makale düzenleme için bir formumuz var. Makaleyi bir veritabanına kaydetmekle birlikte, önbelleğe alınmış öğeleri etikete göre silecek olan clean() komutunu çağırıyoruz:

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

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

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

Ne elde ettik? Makale veya yorumlar her değiştiğinde HTML önbelleğimizin geçersiz kılınmasını (silinmesini). ID = 10 olan bir makaleyi düzenlerken, article/10 etiketi geçersiz kılınmaya zorlanır ve etiketi taşıyan HTML sayfası önbellekten silinir. Aynı şey ilgili makalenin altına yeni bir yorum eklediğinizde de gerçekleşir.

Etiketler Dergi gerektirir.

Önceliğe Göre Geçersiz Kılma

Önbellekteki her bir öğe için önceliği ayarlayabiliriz ve örneğin önbellek belirli bir boyutu aştığında bunları kontrollü bir şekilde silmek mümkün olacaktır:

$dependencies[Cache::Priority] = 50;

Önceliği 100'e eşit veya daha az olan tüm öğeleri silin:

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

Öncelikler sözde Dergi gerektirir.

Önbelleği Temizle

Cache::All parametresi her şeyi temizler:

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

Toplu Okuma

Önbelleğe toplu okuma ve yazma için, bir anahtar dizisi ilettiğimiz ve bir değer dizisi elde ettiğimiz bulkLoad() yöntemi kullanılır:

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

bulkLoad() yöntemi, oluşturulan öğenin anahtarının aktarıldığı ikinci geri arama parametresiyle load() yöntemine benzer şekilde çalışır:

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

Çıktı Önbelleğe Alma

Çıktı çok zarif bir şekilde yakalanabilir ve önbelleğe alınabilir:

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

	echo ... // bazı verileri yazdırıyor

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

Çıktının önbellekte zaten mevcut olması durumunda, capture() yöntemi çıktıyı yazdırır ve null döndürür, böylece koşul yürütülmez. Aksi takdirde, çıktıyı tamponlamaya başlar ve sonunda verileri önbelleğe kaydettiğimiz $capture nesnesini döndürür.

Sürüm 3.0'da bu yöntem $cache->start() olarak adlandırılmıştır.

Latte'de Önbellekleme

Latte şablonlarında önbelleğe almak çok kolaydır, şablonun bir kısmını etiketlerle sarmanız yeterlidir {cache}...{/cache}. Kaynak şablon değiştiğinde önbellek otomatik olarak geçersiz kılınır ( {cache} etiketleri içinde bulunan tüm şablonlar dahil). Etiketler {cache} iç içe geçebilir ve iç içe geçmiş bir blok geçersiz kılındığında (örneğin, bir etiket tarafından), üst blok da geçersiz kılınır.

Etikette, önbelleğin bağlanacağı anahtarları (burada $id değişkeni) belirtmek ve sona erme ve geçersiz kılma etiketlerini ayarlamak mümkündür

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

Tüm parametreler isteğe bağlıdır, bu nedenle süre sonu, etiket veya anahtar belirtmeniz gerekmez.

Önbelleğin kullanımı if tarafından da koşullandırılabilir – içerik yalnızca koşul karşılandığında önbelleğe alınacaktır:

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

Depolar

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

Depolama Açıklama
FileStorage diskteki dosyalara kaydetme ile varsayılan depolama
MemcachedStorage Memcached sunucusunu kullanır
MemoryStorage veriler geçici olarak bellektedir
SQLiteStorage veriler SQLite veritabanında saklanır
DevNullStorage veriler saklanmıyor – test amaçlı

Depolama nesnesini Nette\Caching\Storage türüyle bağımlılık enjeksiyonu kullanarak geçirerek elde edersiniz. Varsayılan olarak Nette, verileri geçici dosyalar için dizindeki cache alt klasöründe depolayan bir FileStorage nesnesi sağlar.

Yapılandırmadaki depolama alanını değiştirebilirsiniz:

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

FileStorage

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

Bu depolama alanı ayrıca, önbellek temizlendiğinde veya soğuk olduğunda (yani oluşturulmadığında) CPU kullanımında aşırı bir artışı önleyen önemli bir yerleşik özelliğe sahiptir. Bu önbellek izdihamı önlemesidir. Bir anda önbellekten aynı şeyi isteyen birkaç eşzamanlı istek olur (örneğin pahalı bir SQL sorgusunun sonucu) ve önbelleğe alınmadığı için tüm işlemler aynı SQL sorgusunu yürütmeye başlar. İşlemci yükü katlanır ve hatta hiçbir iş parçacığı zaman sınırı içinde yanıt veremez, önbellek oluşturulmaz ve uygulama çökebilir. Neyse ki, Nette'deki önbellek, bir öğe için birden fazla eşzamanlı istek olduğunda, yalnızca ilk iş parçacığı tarafından oluşturulacak, diğerleri bekleyecek ve ardından oluşturulan sonucu kullanacak şekilde çalışır.

Bir FileStorage oluşturma örneği:

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

MemcachedStorage

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

PHP uzantısı gerektirir memcached.

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

MemoryStorage

Nette\Caching\Storages\MemoryStorage verileri bir PHP dizisinde saklayan ve böylece istek sonlandırıldığında kaybolan bir depolama alanıdır.

SQLiteStorage

SQLite veritabanı ve bağdaştırıcı Nette\Caching\Storages\SQLiteStorage disk üzerinde tek bir dosyada önbellekleme için bir yol sunar. Yapılandırma bu dosyanın yolunu belirtecektir.

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

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

DevNullStorage

Özel bir depolama uygulaması olan Nette\Caching\Storages\DevNullStorage, aslında hiç veri depolamaz. Bu nedenle, önbelleğin etkisini ortadan kaldırmak istiyorsak test etmek için uygundur.

Kodda Önbellek Kullanımı

Kodda önbellekleme kullanırken, bunu yapmanın iki yolu vardır. Birincisi, bağımlılık enjeksiyonu kullanarak depolama nesnesini geçirerek elde etmeniz ve ardından Cache adresinde bir nesne oluşturmanızdı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 yol ise depolama nesnesini Cache adresinden almanızdır:

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

Cache nesnesi daha sonra aşağıdaki gibi doğrudan yapılandırmada oluşturulur:

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

Dergi

Nette etiketleri ve öncelikleri günlük olarak adlandırılan bir dosyada saklar. Bunun için varsayılan olarak SQLite ve journal.s3db dosyası kullanılır ve PHP uzantıları pdo ve pdo_sqlite gereklidir.

Yapılandırmada günlüğü değiştirebilirsiniz:

services:
	cache.journal: MyJournal

DI Hizmetleri

Bu hizmetler DI konteynerine eklenir:

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

Önbelleği Kapatma

Uygulamada önbelleğe almayı kapatmanın yollarından biri depolamayı DevNullStorage olarak ayarlamaktır:

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

Bu ayar, Latte veya DI konteynerindeki şablonların önbelleğe alınmasını etkilemez, çünkü bu kütüphaneler nette/caching hizmetlerini kullanmaz ve önbelleklerini bağımsız olarak yönetir. Ayrıca, geliştirme modunda önbelleklerinin kapatılmasına gerek yoktur.

versiyon: 3.x