# 緩存
緩存通過存儲數據(一旦幾乎不檢索)來加速您的應用程序,以備將來使用。 我們將向您展示:
如何使用緩存
如何更改緩存存儲
如何正確無效緩存
Nette Framework提供了一個非常直觀的API用于緩存操作。 畢竟,你不會期望什么,對吧? ;-)在我們繼續第一個例子之前,我們需要考慮地方在哪里存儲數據。 我們可以使用數據庫,Memcached服務器,或最可用的存儲 - 硬盤驅動器:
~~~
// the `temp` directory will be the storage
$storage = new Nette\Caching\Storages\FileStorage('temp');
~~~
Nette \ Caching \ Storages \ FileStorage存儲是非常優化的性能,首先,它提供操作的完全原子性。 這意味著什么? 當我們使用緩存時,我們可以確保我們沒有讀取一個尚未完全寫入的文件(由另一個線程),或者該文件被刪除“在我們手中”。 因此使用緩存是完全安全的。 有關高速緩存存儲部分的更多信息。
對于使用緩存的操作,我們使用Nette \ Caching \ Cache
~~~
use Nette\Caching\Cache;
$cache = new Cache($storage); // $storage from the previous example
~~~
讓我們將'$ data'變量的內容保存在'$ key'鍵下:
~~~
$cache->save($key, $data);
~~~
這樣,我們可以從緩存中讀取:(如果緩存中沒有這樣的項,則返回NULL值)
~~~
$value = $cache->load($key);
if ($value === NULL) ...
~~~
方法load()有第二個參數callable $ fallback,當緩存中沒有這樣的項時,它被調用。 這個回調通過引用接收數組$ dependencies,您可以使用它來設置過期規則。
~~~
$value = $cache->load($key, function(& $dependencies) {
// some calculation
return 15;
});
~~~
我們可以通過保存NULL或調用remove()方法從緩存中刪除項:
~~~
$cache->save($key, NULL);
// or
$cache->remove($key);
~~~
Web應用程序通常由多個獨立部分組成,如果它們都在同一存儲(例如同一目錄)中緩存數據,遲早會出現名稱沖突。 Nette Framework通過將整個存儲分割成段來解決這個問題(在使用子目錄的FileStorage案例中)。 應用程序的每個部分都使用它自己的具有唯一名稱的段,因此不會發生沖突。 該節的名稱可以作為第二個參數傳遞給Cache類構造函數。 (這些部分通常稱為緩存命名空間。)
~~~
$cache = new Cache($storage, 'htmlOutput');
~~~
## 在模板中緩存
在模板中緩存很容易,只需用{cache} ... {/ cache}包裝模板的一部分。 當源模板更改時,緩存自動失效,包括{cache}宏中包含的任何模板。 {cache}塊可以嵌套,并且當嵌套塊被無效(例如通過標簽)時,父塊也被無效。
可以定義緩存將要綁定的鍵(在這種情況下,$ id變量),并設置過期和標簽為無效
~~~
{cache $id, expire => '20 minutes', tags => [tag1, tag2]}
...
{/cache}
~~~
所有參數都是選項,因此您不必指定到期,標簽或鍵。
使用緩存也可以使用“if”條件 - 只有在滿足條件時才會緩存內容:
~~~
{cache $id, if => !$form->isSubmitted()}
{$form}
{/cache}
~~~
如果我們只從模板中檢索數據(按需原則或延遲),緩存將特別有效。
## 緩存功能結果
緩存函數或方法調用的結果可以使用call()方法實現:
~~~
$name = $cache->call('gethostbyaddr', $ip);
~~~
因此,gethostbyaddr($ ip)只會被調用一次,下一次只返回緩存中的值。 當然,對于不同的$ ip,不同的結果被緩存。
## 輸出緩存
輸出不僅可以緩存在模板中:
~~~
if ($block = $cache->start($key)) [
... printing some data ...
$block->end(); // save the output to the cache
}
~~~
如果輸出已經存在于高速緩存中,start()方法將打印它并返回NULL。 否則,它開始緩沖輸出并返回$ block對象,我們最終將數據保存到緩存中。
## 到期和無效
這里有兩個問題在緩存中存儲數據。 首先,存儲空間可能已完全填滿,您無法在其中保存更多數據。 并且可能發生的是,先前保存的數據中的一些將隨時間變得無效。 因此,Nette Framework提供了一種機制,如何限制數據的有效性以及如何以受控的方式刪除它們(使用框架的術語來“使它們無效”)。
使用save()方法的第三個參數保存數據時,設置數據有效性:
~~~
$cache->save($key, $data, [
Cache::EXPIRE => '20 minutes', // accepts also seconds or a timestamp.
]);
~~~
從代碼本身很明顯,我們保存了接下來20分鐘的數據。 在這段時間之后,緩存將報告在“$ key”鍵下沒有記錄(即,將返回NULL)。 事實上,您可以使用任何時間值,這是PHP函數strToTime()或DateTime類的有效值。 如果我們想要在每次閱讀時延長有效期,可以這樣做:
~~~
$cache->save($key, $data, [
Cache::EXPIRE => '20 minutes',
Cache::SLIDING => TRUE,
]);
~~~
非常方便的是,當特定文件被改變或者幾個文件之一時,讓數據過期的能力。 這可以用于將由于將這些文件解析到緩存而產生的數據。 為了無故障功能,建議使用絕對路徑。
~~~
$cache->save($key, $data, [
Cache::FILES => 'data.yaml', // an array of files can also be specified
]);
~~~
Cache :: FILES標準,當然可以使用Cache :: EXPIRE等與時間到期結合。
緩存也可以依賴于其他緩存的項目。 這可以在我們將整個HTML頁面保存在緩存中和不同的鍵(其某些片段)時使用。 一旦零件更改,整個頁面將失效。
~~~
$cache->save('page', $html, [
// will expire if frag1 or frag2 expires
Cache::ITEMS => ['frag1', 'frag2'],
]);
~~~
過期可以通過自己的回調來控制:
~~~
function controlExpiration($val)
{
return $val;
}
$cache->save($key, $value, [
Cache::CALLBACKS => [['controlExpiration', 1]],
]);
~~~
## 到期使用標簽和優先級
所謂的標簽是一個非常有用的無效工具。 我們可以為存儲在緩存中的每個項目分配一個標簽列表。 例如,假設我們有一個HTML頁面,其中包含我們要緩存的文章和注釋。 所以我們在保存到緩存時指定標簽:
~~~
$cache->save($articleId, $html, [
Cache::TAGS => ["article/$articleId", "comments/$articleId"],
]);
~~~
現在,讓我們轉到管理。 這里我們有一個文章編輯的表單。 與將文章保存到數據庫一起,我們調用clean()命令,它將通過標簽刪除緩存的項目:
~~~
$cache->clean([
Cache::TAGS => ["article/$articleId"],
]);
~~~
在添加新評論(或編輯它們)的地方,不要忘記使適當的標記無效:
~~~
$cache->clean([
Cache::TAGS => ["comments/$articleId"],
]);
~~~
我們已經實現了什么? HTML緩存將自動失效。 每當有人更改ID為10的文章時,它會強制文章/ 10標記無效,并且緩存中標記的HTML頁面被清除。 當某人在文章下面插入新評論時,也會發生同樣的情況。
與標簽類似,您可以按優先級控制到期日期:
~~~
$cache->save($key, $value, [
Cache::PRIORITY => 50,
]);
// all cached items with priority less than or equal to 100 will be removed.
$cache->clean([
Cache::PRIORITY => 100,
]);
~~~
## 緩存存儲
除了已經提到的FileStorage之外,Nette Framework還提供了MemcachedStorage,用于將數據存儲到Memcached服務器,還提供MemoryStorage用于在請求期間將數據存儲在內存中。
## 存儲服務
當然,可以創建自己的存儲。 唯一的要求是實現IStorage接口。
我們可以使用依賴注入,所以我們不必在任何地方創建$ storage對象。 Nette框架提供了一種實現IStorage接口的服務。 如果沒有在配置中指定具體的實現,默認情況下使用FileStorage,它將數據保存到由bootstrap.php中的$ configurator-> setTempDirectory()指定的目錄中。
~~~
use Nette;
class MyPresenter
{
/**
* @inject
* @var Nette\Caching\IStorage
*/
public $storage;
public function actionDefault()
{
$cache = new Cache($this->storage, 'htmlFront');
$cache->save($key, $data);
}
}
~~~
## 為測試目的禁用緩存
Nette \ Caching \ IStorage的特殊實現是一個DevNullStorage。 它不保存任何數據。 當我們想要消除緩存的影響時,這通常在測試時很有用。
在config.neon中配置存儲:
~~~
services:
cacheStorage:
class: Nette\Caching\Storages\DevNullStorage
~~~
## 并發緩存
刪除緩存是將新應用程序版本上傳到服務器時的常見操作。 然而,在那一刻,服務器變得非常困難,因為它必須構建一個完整的新緩存。 檢索一些數據可能相當困難,例如RobotLoader緩存構建。 此外,如果例如30個請求在短時間內來到,則資源消耗甚至更高。
解決方案是修改應用程序行為,使數據只由一個線程創建,而其他線程正在等待。 為此,請將該值指定為回調或使用匿名函數:
~~~
$result = $cache->save($key, function() {
return buildData(); // difficult operation
});
~~~
框架將確保函數的主體將只被一個線程一次調用,而其他線程將等待。 如果線程由于某種原因失敗,就找另一個機會。
## 在整個應用程序中使用緩存
當在服務中使用緩存時,我們面臨的決定是將緩存或IStorage對象傳遞到服務中。 當服務只需要自己的緩存,我們傳遞IStorage:
~~~
use Nette\Caching;
class MyService
{
/** @var Caching\Cache */
private $cache;
public function __construct(Caching\IStorage $storage)
{
$this->cache = new Caching\Cache($storage, 'my-service');
}
}
~~~
服務從DI容器獲取存儲:
~~~
services:
- MyService
~~~
另一種情況是,當我們需要更多的相同服務的實例,但是使用分離的緩存時:
~~~
use Nette\Caching;
class MyService
{
/** @var Caching\Cache */
private $cache;
public function __construct(Caching\Cache $cache)
{
$this->cache = $cache;
}
}
~~~
我們分別為每個服務創建一個緩存對象:
~~~
services:
one: MyService( Nette\Caching\Cache(namespace: 'one') )
two: MyService( Nette\Caching\Cache(namespace: 'two') )
~~~
- Nette簡介
- 快速開始
- 入門
- 主頁
- 顯示文章詳細頁
- 文章評論
- 創建和編輯帖子
- 權限驗證
- 程序員指南
- MVC應用程序和控制器
- URL路由
- Tracy - PHP調試器
- 調試器擴展
- 增強PHP語言
- HTTP請求和響應
- 數據庫
- 數據庫:ActiveRow
- 數據庫和表
- Sessions
- 用戶授權和權限
- 配置
- 依賴注入
- 獲取依賴關系
- DI容器擴展
- 組件
- 字符串處理
- 數組處理
- HTML元素
- 使用URL
- 表單
- 驗證器
- 模板
- AJAX & Snippets
- 發送電子郵件
- 圖像操作
- 緩存
- 本土化
- Nette Tester - 單元測試
- 與Travis CI的持續集成
- 分頁
- 自動加載
- 文件搜索:Finder
- 原子操作