Ö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;
});
PSR-16 ile kullanma
Nette Cache'i PSR-16 arayüzü ile kullanmak için PsrCacheAdapter
adresinden yararlanabilirsiniz. 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);
Artık $psrCache
adresini PSR-16 önbelleği olarak kullanabilirsiniz:
$psrCache->set('key', 'value', 3600); // değeri 1 saat boyunca saklar
$value = $psrCache->get('key', 'default');
Bağdaştırıcı, getMultiple()
, setMultiple()
ve deleteMultiple()
dahil olmak üzere
PSR-16'da tanımlanan tüm yöntemleri destekler.
Çı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.