Önbellekleme
Bu kurs hapi v17 ile uyumludur
İstemci taraflı önbellekleme
HTTP protokolü, tarayıcı ve benzeri istemcilerin kaynakları önbelleklemeleri için bazı HTTP başlıkları tayin eder. Bu başlıklar hakkında daha fazla bilgi edinmek ve kullanım senaryonuza hangisinin yatkın olduğuna karar vermek için Google tarafından oluşturulan bu rehbere bir göz atabilirsiniz.
Bu öğreticinin ilk bölümü, hapinin istemcilere bu başlıkları göndermek üzere nasıl yapılandırılacağını gösterir.
Cache-Control
Cache-Control başlığı tarayıcıya ve tüm ara önbelleklere bir kaynağın önbelleklenebilir olup olmadığını ve eğer öyleyse ne kadar süre önbelleklenmesi gerektiğini söyler. Örneğin, Cache-Control:max-age=30, must-revalidate, private ilgili kaynağın tarayıcı tarafından otuz saniye boyunca önbelleklenebileceğini ve private ara önbellekler tarafından değil yalnızca tarayıcı tarafından önbelleklenmesi gerektiği söyler. must-revalidate önbellek süresi dolduğunda sunucudan kaynağın yenisinin istenmesi gerektiği anlamına gelir.
Hadi bu başlığı hapi ile nasıl ayarlayabileceğimize bakalım:
server.route({
path: '/hapi/{ttl?}',
method: 'GET',
handler: function (request, h) {
const response = h.response({ be: 'hapi' });
if (request.params.ttl) {
response.ttl(request.params.ttl);
}
return response;
},
options: {
cache: {
expiresIn: 30 * 1000,
privacy: 'private',
},
},
});Yukarıdaki örnek bir yol (route) üzerinde nasıl cache seçeneklerini ayarlayabileceğini gösteriyor. Burada expiresIn değerini 30 saniye ve privacy değerini private olarak ayarladık.
Bu örnek aynı zamanda expiresIn değerinin üzerine yanıt nesnesi arayüzünde bulunan ttl(msec) yöntemi ile yazılabileceğini gösteriyor.
/hapiye bir istekte bulunursak yanıt başlığı olarak cache-control: max-age=30, must-revalidate, private alırız. Eper isteği /hapi/5000e yaparsak bunun yerine şu başlık gelir: cache-control: max-age=5, must-revalidate, private.
yol-secenekleri (route options) bağlantısından daha fazla genel cache yapılandırma seçeneği bulabilirsiniz.
Last-Modified
Bazı durumlarda, sunucu bir kaynağın son düzenlendiği tarihi sağlayabilir. Durağan içerikler sunmak için inert eklentisini kullanırken otomatik olarak her yanıta Last-Modified başlığı eklenir.
Bir yanıtta Last-Modified başlığı ayarlandığında hapi bu bilgiyi istemciden gelen If-Modified-Since başlığıyla karşılaştırarak yanıt durum kodunun 304 Not Modified olup olmayacağına karar verir. Bu olay anı zamanda koşullu GET isteği olarak da bilinir. Bu yanıtın güzel tarafı, 304 yanıtı alan tarayıcının aynı içeriği tekrar indirmesine gerek kalmamasıdır.
lastModified bilgsinin bir Date nesnesi olduğunu varsayarsak bu başlığı yanıt nesnesi (response object) kullanarak şu şekilde ayarlayabilirsiniz:
return h.response(result).header('Last-Modified', lastModified.toUTCString());Bu yazının son bölümünde Last-Modified kullanan bir örnek daha var.
ETag
ETag, zaman damgası paylaşan Last-Modified başlığına alternatif olarak sunucunun bir jeton (genellikle kaynağın sağlama toplamı (checksum)) paylaştığı başlıktır. Tarayıcı bu jetonu bir sonraki isteğinde If-None-Match başlığını ayarlamak için kullanır. Sunucu bu başlığın değerini güncel ETag sağlama toplamıyla karşılaştırarak aynı ise 304 döner.
Başlığınızda ETag kullanmak için yalnızca etag(tag, options) işlevini kullanmanız yeterli:
return h.response(result).etag('xxxxxxxxx');Kullanılabilecek daha fazla argüman ve seçenek için yanıt nesnesi (response object) altında bulunan etag dokümantasyonuna bir göz atabilirsin.
Sunucu taraflı önbellekleme
hapi catbox ile güçlü ve pratik sunucu taraflı önbellekleme sunar. Bu öğretici bölümü catbox'ı nasıl kullanman gerektiğini anlamana yardımcı olacaktır.
Catbox'ın iki arayüzü bulunur: istemci (client) ve politika (policy).
Client (Istemci)
İstemci (client) anahtar-değer eşleri alıp-verebileceğin alt seviye bir arayüzdür. Şu adaptörlerden birisi ile başlatılır: (Hafıza (memory), Redis, mongoDB, Memcached, ya da Riak).
hapi varsayılan olarak catbox memory adaptörünü kullanarak bir İstemci (client) başlatır. Nasıl daha fazla istemci başlatacağına bir bakalım:
'use strict';
const Hapi = require('@hapi/hapi');
const server = Hapi.server({
port: 8000,
cache: [
{
name: 'mongoCache',
engine: require('catbox-mongodb'),
host: '127.0.0.1',
partition: 'cache',
},
{
name: 'redisCache',
engine: require('catbox-redis'),
host: '127.0.0.1',
partition: 'cache',
},
],
});Yukarıdaki örnekte, iki tane catbox istemcisi tanımladık: mongoCache ve redisCache. hapi tarafından oluşturulan hafıza önbelleğini de sayarsak üç tane önbellek istemcisi oldu. Yeni bir önbellek istemcisi tanımlarken name özelliğini es geçerek varsayılan istemciyi değiştirebilirsiniz. partition adaptöre önbelleğin nasıl adlandırılacağını söyler (varsayılan olarak 'catbox'). mongoDB'de bu veri tabanı adı ve redis'te anahtar ön eki olarak karşımıza çıkar.
Policy (Politika)
Politika (policy) istemciye nazaran daha üst seviye bir arayüzdür. Aşağıda iki sayıyı birbirine ekleyip sonucu önbellekleyen basit bir örnek bulunuyor. Bu basit örneğin ortaya koyduğu prensipleri kullanarak bir fonksiyon çağrısının sonucunu, asenkron olsun olmasın önbellekleyebilirsin. server.cache(options) daha sonra yol işleyici (route handler) tarafından kullanılmak üzere yeni bir politika (policy) oluşturur.
const start = async () => {
const add = async (a, b) => {
await Hoek.wait(1000); // Yavaş I/O simülasyonu
return Number(a) + Number(b);
};
const sumCache = server.cache({
cache: 'mongoCache',
expiresIn: 10 * 1000,
segment: 'customSegment',
generateFunc: async (id) => {
return await add(id.a, id.b);
},
generateTimeout: 2000,
});
server.route({
path: '/add/{a}/{b}',
method: 'GET',
handler: async function (request, h) {
const { a, b } = request.params;
const id = `${a}:${b}`;
return await sumCache.get({ id, a, b });
},
});
await server.start();
console.log('Server running at:', server.info.uri);
};
start();Eğer http://localhost:8000/add/1/5 adresine bir istekte bulunursan bir saniye sonra 6 yanıtı almalısın. İsteği tekrarlarsan sonuç anında gelir çünkü bu kez sonuç önbellek tarafından sunulmaktadır. Eğer on saniye bekleyip isteği tekrar etseydin yine biraz beklediğini görecektin çünkü önbellek değeri önbellekten silinmiş olacaktı.
cache seçeneği hapiye hangi istemci (client)yi kullanacağını söyler.
sumCache.get() işlevinin ilk değiştirgesi id olarak kullanılmak üzere bir metin ya da içerisinde tekil tanımlayıcı olarak kullanılmak üzere id özelliği olan bir nesne olabilir.
partitionlara ek olarak, bir istemci (client) bölmelendirmesi içerisinde daha fazla izolasyon sağlayacak segmentler var. İki farklı yöntem sonucunu önbelleklemek isterseniz genelde bunları birbirine karıştırmak istemezsiniz. mongoDB adaptörlerinde segment bir koleksiyonu temsil ederken, redis'te partition için eklenen öneke ek olarak eklenen bir önektir.
Bir eklenti içerisinden server.cache() çağırıldığında segmentin varsayılan değeri '!pluginName' olur. Sunucu yöntemleri (server methods) oluştururken segment değeri '#methodName' olur. Eğer bir segmenti paylaşan birden fazla politikanın olduğu bir durum varsa, bunun için de paylasılan (shared) seçeneği mevcut.
Sunucu Yöntemi
Daha iyi olabilir! Yüzde doksan beş ihtimal önbellekleme için sunucu yöntemleri (server methods)ni kullanacaksınız çünkü daha az basmakalıp kullanmak gerekiyor. Hadi önceki örneği sunucu yöntemi ile tekrar yazalım:
const start = async () => {
// ...
server.method('sum', add, {
cache: {
cache: 'mongoCache',
expiresIn: 10 * 1000,
generateTimeout: 2000,
},
});
server.route({
path: '/add/{a}/{b}',
method: 'GET',
handler: async function (request, h) {
const { a, b } = request.params;
return await server.methods.sum(a, b);
},
});
await server.start();
// ...
};
start();server.method() otomatik olarak segment: '#sum' kesitiyle yeni bir politika (policy) oluşturdu. Ayrıca değiştirgelerle tekil bir id(önbellek anahtarı)'de oluşturuldu. Varsayılan olarak, string, number ve boolean değiştirgelerle çalışabilir. Daha kompleks değiştirgeler için değiştirgeleri kullanarak nasıl tekil tanımlayıcılar üretileceğini belirleyen bir generateKey işlevi tanımlamalısın. - Sunucu yöntemleri öğreticisinden daha fazla bilgi alabilirsin.
Sırada ne var?
- catbox politikasının seçeneklerinin içine bakarak ve catbox önbelleklemenin tüm potansiyelinden faydalanmak için
staleIn,staleTimeout,generateTimeoutkısımlarına özellikle dikkat etmelisiniz. - Daha fazla örnek için sunucu yöntemleri (server methods) öğreticisine göz atabilirsin.
İstemci ve Sunucu önbellekleme
İsteğe bağlı olarak, Catbox Politikası (Catbox Policy) önbellekten döndürülen değer hakkında daha fazla bilgi verebilir. Bunun için politikayı yaratırken getDecoratedValue seçeneği true olarak ayarlanır. Bu şekilde sunucu yönteminden dönen tüm değerler { value, cached, report } şeklinde birer nesne olur. value önbellekten dönen değeri barındırırken, cached ve report değeri önbellek durumu hakkında daha fazla bilgi sunar.
cached.stored zaman damgasının last-modified başlığını ayarlamak için beraber çalışan kullanışı güzel bir sunucu taraflı-istemci taraflı önbellekleme örneğidir.
const start = async () => {
//...
server.method('sum', add, {
cache: {
cache: 'mongoCache',
expiresIn: 10 * 1000,
generateTimeout: 2000,
getDecoratedValue: true,
},
});
server.route({
path: '/add/{a}/{b}',
method: 'GET',
handler: async function (request, h) {
const { a, b } = request.params;
const { value, cached } = await server.methods.sum(a, b);
const lastModified = cached ? new Date(cached.stored) : new Date();
return h.response(value).header('Last-modified', lastModified.toUTCString());
},
});
await server.start();
// ...
};cached ve report hakkında daha fazlasını Catbox Politika API dokümanları (Catbox Policy API docs)nda bulabilirsin.