Khi xây dựng các ứng dụng web thì thì khái niệm về cache được nhắc đến khá phổ biến. Chúng ta có cache lại kết quả của các câu truy vấn (queries) để trả về kết quả nhanh hơn.
Có 3 ưu điểm của Web Caching là:
- Giảm tải băng thông.
- Giảm gánh nặng cho server.
- Giảm sự tiềm ẩn.
Web cache được đặt giữa các Web Servers (hoặc các servers chính) và client (hoặc nhiều clients).
Trong bài viết này chúng ta sẽ cùng tìm hiểu một Laravel package “laravel responsecache”
Laravel package này có thể lưu trữ toàn bộ response. Mặc định thì nó sẽ lưu trữ tất cả các kết quả trả về của phương thức get-requests thành công dưới dạng json hoặc html trong một tuần. Điều này có khả năng tăng tốc độ phản hồi khá đáng kể.
Cài đặt package
Ta cài package này bằng cách dùng composer
composer require spatie/laravel-responsecache
Nội dung file config/responsecache.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | <span class="token keyword">return</span> <span class="token punctuation">[</span> <span class="token comment">/* * Determine if the response cache middleware should be enabled. */</span> <span class="token single-quoted-string string">'enabled'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token function">env</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'RESPONSE_CACHE_ENABLED'</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token comment">/* * The given class will determinate if a request should be cached. The * default class will cache all successful GET-requests. * * You can provide your own class given that it implements the * CacheProfile interface. */</span> <span class="token single-quoted-string string">'cache_profile'</span> <span class="token operator">=</span><span class="token operator">></span> Spatie<span class="token package">ResponseCacheCacheProfilesCacheAllSuccessfulGetRequests</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token comment">/* * When using the default CacheRequestFilter this setting controls the * default number of seconds responses must be cached. */</span> <span class="token single-quoted-string string">'cache_lifetime_in_seconds'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token function">env</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'RESPONSE_CACHE_LIFETIME'</span><span class="token punctuation">,</span> <span class="token number">60</span> <span class="token operator">*</span> <span class="token number">24</span> <span class="token operator">*</span> <span class="token number">7</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token comment">/* * This setting determines if a http header named with the cache time * should be added to a cached response. This can be handy when * debugging. */</span> <span class="token single-quoted-string string">'add_cache_time_header'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token function">env</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'APP_DEBUG'</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token comment">/* * This setting determines the name of the http header that contains * the time at which the response was cached */</span> <span class="token single-quoted-string string">'cache_time_header_name'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token function">env</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'RESPONSE_CACHE_HEADER_NAME'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'laravel-responsecache'</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token comment">/* * Here you may define the cache store that should be used to store * requests. This can be the name of any store that is * configured in app/config/cache.php */</span> <span class="token single-quoted-string string">'cache_store'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token function">env</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'RESPONSE_CACHE_DRIVER'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'file'</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token comment">/* * Here you may define replacers that dynamically replace content from the response. * Each replacer must implement the Replacer interface. */</span> <span class="token single-quoted-string string">'replacers'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">[</span> <span class="token package">SpatieResponseCacheReplacersCsrfTokenReplacer</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token comment">/* * If the cache driver you configured supports tags, you may specify a tag name * here. All responses will be tagged. When clearing the responsecache only * items with that tag will be flushed. * * You may use a string or an array here. */</span> <span class="token single-quoted-string string">'cache_tag'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token single-quoted-string string">''</span><span class="token punctuation">,</span> <span class="token comment">/* * This class is responsible for generating a hash for a request. This hash * is used to look up an cached response. */</span> <span class="token single-quoted-string string">'hasher'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token package">SpatieResponseCacheHasherDefaultHasher</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token comment">/* * This class serializes cache data and expands it. * Serialization can save the data to be returned in an appropriate form. */</span> <span class="token single-quoted-string string">'serializer'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token package">SpatieResponseCacheSerializerDefaultSerializer</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> |
Sau đó cài đặt provided middlewares SpatieResponseCacheMiddlewaresCacheResponse::class
và SpatieResponseCacheMiddlewaresDoNotCacheResponse
trong kernel.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <span class="token keyword">protected</span> <span class="token variable">$middlewareGroups</span> <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token single-quoted-string string">'web'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">[</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token package">SpatieResponseCacheMiddlewaresCacheResponse</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token keyword">protected</span> <span class="token variable">$routeMiddleware</span> <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token single-quoted-string string">'doNotCacheResponse'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token package">SpatieResponseCacheMiddlewaresDoNotCacheResponse</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> |
Chúng ta đã thực hiện xong bước cài đặt vậy sử dụng package như nào?
Sử dụng
Như ở phần mở đầu cũng có nói mặc định package sẽ lưu trữ tất cả các kết quả của get-requests thành công trong một tuần. Mỗi user đã đăng nhập sẽ có bộ nhớ cache riêng.
- Xóa bộ nhớ cache
ResponseCache::clear();
khi dùng câu lệnh trên thì sẽ xóa mọi thứ khỏi kho lưu trữ cache được chỉ định trong file config config/responsecache.php
.
hoặc có thể dùng lệnh artisan command php artisan responsecache:clear
sử dụng model events
Sử dụng các event trong model như self::created, self::updated, self::deleted
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <span class="token keyword">namespace</span> <span class="token package">AppTraits</span><span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">SpatieResponseCacheFacadesResponseCache</span><span class="token punctuation">;</span> <span class="token keyword">trait</span> <span class="token class-name">ClearsResponseCache</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">function</span> <span class="token function">bootClearsResponseCache</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> self<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">created</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> ResponseCache<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> self<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">updated</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> ResponseCache<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> self<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">deleted</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> ResponseCache<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Forget một hoặc một vài URI(s)
1 2 3 4 5 6 7 8 | <span class="token comment">// Forget một URI</span> ResponseCache<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">forget</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'/some-uri'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Forget hai hay nhiêu URI</span> ResponseCache<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">forget</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token single-quoted-string string">'/some-uri'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'/other-uri'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// hoặc</span> ResponseCache<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">forget</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'/some-uri'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'/other-uri'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Không cho request được lưu vào bộ nhớ cache
Các request có thể chặn bằng cách sử dụng middleware doNotCacheResponse. Middleware này có thể sử dụng ở controller hoặc routes.
sử dụng ở routes
1 2 | Route<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'/auth/logout'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token single-quoted-string string">'middleware'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token single-quoted-string string">'doNotCacheResponse'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'uses'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token single-quoted-string string">'<a class="__cf_email__" href="/cdn-cgi/l/email-protection">[email protected]</a>'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
sử dụng ở controller
1 2 3 4 5 6 7 8 | <span class="token keyword">class</span> <span class="token class-name">UserController<span class="token punctuation">.</span>php</span> <span class="token keyword">extends</span> <span class="token class-name">Controller</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">__construct</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">middleware</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'doNotCacheResponse'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token single-quoted-string string">'only'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">[</span><span class="token single-quoted-string string">'fooAction'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'barAction'</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Để xác định những request nào sẽ được lưu trong bộ nhớ cache và trong bao lâu ta sử dụng class SpatieResponseCacheCacheProfilesCacheAllSuccessfulGetRequests
. Hoặc có thể tự tạo một interface SpatieResponseCacheCacheProfilesCacheProfile.php.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | <span class="token keyword">interface</span> <span class="token class-name">CacheProfile</span> <span class="token punctuation">{</span> <span class="token comment">/* * Determine if the response cache middleware should be enabled. */</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">enabled</span><span class="token punctuation">(</span>Request <span class="token variable">$request</span><span class="token punctuation">)</span><span class="token punctuation">:</span> bool<span class="token punctuation">;</span> <span class="token comment">/* * Determine if the given request should be cached. */</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">shouldCacheRequest</span><span class="token punctuation">(</span>Request <span class="token variable">$request</span><span class="token punctuation">)</span><span class="token punctuation">:</span> bool<span class="token punctuation">;</span> <span class="token comment">/* * Determine if the given response should be cached. */</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">shouldCacheResponse</span><span class="token punctuation">(</span>Response <span class="token variable">$response</span><span class="token punctuation">)</span><span class="token punctuation">:</span> bool<span class="token punctuation">;</span> <span class="token comment">/* * Return the time when the cache must be invalidated. */</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">cacheRequestUntil</span><span class="token punctuation">(</span>Request <span class="token variable">$request</span><span class="token punctuation">)</span><span class="token punctuation">:</span> DateTime<span class="token punctuation">;</span> <span class="token comment">/** * Return a string to differentiate this request from others. * * For example: if you want a different cache per user you could return the id of * the logged in user. * * @param IlluminateHttpRequest $request * * @return mixed */</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">useCacheNameSuffix</span><span class="token punctuation">(</span>Request <span class="token variable">$request</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Caching cho từng route
thay vì đăng kí một middleware cacheResponse cho tất cả các route thì ta có thể sử dùng middleware cho từng route riêng biệt.
1 2 3 4 5 | <span class="token keyword">protected</span> <span class="token variable">$routeMiddleware</span> <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token single-quoted-string string">'cacheResponse'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token package">SpatieResponseCacheMiddlewaresCacheResponse</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> |
1 2 3 4 5 6 7 8 9 10 | <span class="token comment">// cache route trong vòng 5p</span> Route<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'/my-special-snowflake'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'<a class="__cf_email__" href="/cdn-cgi/l/email-protection">[email protected]</a>'</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">middleware</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'cacheResponse:300'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// cache route trong vòng 10p</span> Route<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">group</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> Route<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'/another-special-snowflake'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'<a class="__cf_email__" href="/cdn-cgi/l/email-protection">[email protected]</a>'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> Route<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'/yet-another-special-snowflake'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'<a class="__cf_email__" href="/cdn-cgi/l/email-protection">[email protected]</a>'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">middleware</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'cacheResponse:600'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Events
ta có thể sự dụng các sự kiện (event) được giới thiệu ở dưới đây để theo dõi cũng như debug.
- ResponseCacheHit
SpatieResponseCacheEventsResponseCacheHit
event này dùng để FeedbackCache và phản hồi được lưu trong bộ nhớ cache đã được tìm thấy và trả về. - CacheMissed
SpatieResponseCacheEventsCacheMissed
FeedbackCache nhưng không tìm thấy hoặc trả về kết quả bộ nhớ cache. - ClearingResponseCache và ClearedResponseCache
SpatieResponseCacheEventsClearingResponseCache
SpatieResponseCacheEventsClearedResponseCache
2 sự kiên này được kích hoạt khi bắt đầu và kết thúc của việc responsecache:clear
Creating a Replacer
Để thay thế nội dung được lưu trong bộ nhớ cache bằng nội dung động, ta có thể tạo một trình thay thế bằng cách thêm một CsrfTokenReplacer trong file config hoặc implementing interface sau SpatieResponseCacheReplacersReplacer.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <span class="token keyword">interface</span> <span class="token class-name">Replacer</span> <span class="token punctuation">{</span> <span class="token comment">/* * Transform the initial response before it gets cached. * * For example: replace a generated csrf_token by '<csrf-token-here>' that you can * replace with its dynamic counterpart when the cached response is returned. */</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">transformInitialResponse</span><span class="token punctuation">(</span>Response <span class="token variable">$response</span><span class="token punctuation">)</span><span class="token punctuation">:</span> void<span class="token punctuation">;</span> <span class="token comment">/* * Replace any data you want in the cached response before it gets * sent to the browser. * * For example: replace '<csrf-token-here>' by a call to csrf_token() */</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">replaceCachedResponse</span><span class="token punctuation">(</span>Response <span class="token variable">$response</span><span class="token punctuation">)</span><span class="token punctuation">:</span> void<span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Sau đó ta sửa lại file config responsecache.php
1 2 3 4 | <span class="token single-quoted-string string">'replacers'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">[</span> <span class="token package">SpatieResponseCacheReplacersCsrfTokenReplacer</span><span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">,</span> |