Laravel API용 캐싱 레이어를 구축하는 방법

PHPz
풀어 주다: 2024-08-10 06:59:32
원래의
781명이 탐색했습니다.

Katakan anda sedang membina API untuk menyampaikan beberapa data, anda mendapati respons GET agak perlahan. Anda telah mencuba mengoptimumkan pertanyaan anda, mengindeks jadual pangkalan data anda dengan lajur yang sering ditanya dan anda masih tidak mendapat masa respons yang anda inginkan. Langkah seterusnya yang perlu diambil ialah menulis lapisan Caching untuk API anda. 'Lapisan cache' di sini hanyalah istilah mewah untuk perisian tengah yang menyimpan respons yang berjaya dengan pantas untuk mendapatkan semula kedai. cth. Redis, Memcached dsb. kemudian sebarang permintaan lanjut kepada API menyemak sama ada data tersedia di kedai dan memberikan respons.

Prasyarat

  • Laravel
  • Redis

Sebelum kita mulakan

Saya mengandaikan jika anda telah sampai di sini, anda tahu cara membuat aplikasi laravel. Anda juga harus mempunyai sama ada contoh Redis tempatan atau awan untuk disambungkan. Jika anda mempunyai docker tempatan, anda boleh menyalin fail karang saya di sini. Juga, untuk panduan tentang cara menyambung kepada pemacu cache Redis baca di sini.

Mencipta Data Dummy kami

Untuk membantu kami melihat lapisan caching kami berfungsi seperti yang diharapkan. sudah tentu kita memerlukan beberapa data, katakan kita mempunyai model bernama Post. jadi saya akan membuat beberapa siaran, saya juga akan menambah beberapa penapisan kompleks yang boleh menjadi intensif pangkalan data dan kemudian kita boleh mengoptimumkan dengan caching.

Sekarang mari kita mula menulis middleware kami:

Kami mencipta rangka middleware kami dengan menjalankan

php artisan make:middleware CacheLayer
로그인 후 복사

Kemudian daftarkannya dalam apl/Http/Kernel.php anda di bawah kumpulan perisian tengah api seperti:

protected $middlewareGroups = [ 'api' => [ CacheLayer::class, ], ];
로그인 후 복사

Tetapi jika anda menjalankan Laravel 11. daftarkannya dalam bootstrap/app.php
anda

->withMiddleware(function (Middleware $middleware) { $middleware->api(append: [ \App\Http\Middleware\CacheLayer::class, ]); })
로그인 후 복사

Terminologi Caching

  • Cache Hit: berlaku apabila data yang diminta ditemui dalam cache.
  • Cache Miss: berlaku apabila data yang diminta tidak ditemui dalam cache.
  • Cache Flush: mengosongkan data yang disimpan dalam cache supaya ia boleh diisi semula dengan data baharu.
  • Teg cache: Ini adalah ciri unik untuk Redis. tag cache ialah ciri yang digunakan untuk mengumpulkan item berkaitan dalam cache, menjadikannya lebih mudah untuk mengurus dan membatalkan data berkaitan secara serentak.
  • Masa untuk Hidup (TTL): ini merujuk kepada jumlah masa objek cache kekal sah sebelum ia tamat tempoh. Satu salah faham biasa ialah memikirkan bahawa setiap kali objek diakses daripada cache (cache hit), masa tamatnya ditetapkan semula. Walau bagaimanapun, ini tidak benar. Sebagai contoh, jika TTL ditetapkan kepada 5 minit, objek cache akan tamat tempoh selepas 5 minit, tidak kira berapa kali ia diakses dalam tempoh tersebut. Selepas 5 minit tamat, permintaan seterusnya untuk objek itu akan menyebabkan entri baharu dibuat dalam cache.

Mengira Kunci Cache Unik

Jadi pemacu cache ialah stor nilai kunci. jadi anda mempunyai kunci maka nilainya ialah json anda. Oleh itu, anda memerlukan kunci cache unik untuk mengenal pasti sumber, kunci cache unik juga akan membantu dalam ketidaksahihan cache iaitu mengalih keluar item cache apabila sumber baharu dibuat/kemas kini. Pendekatan saya untuk penjanaan kunci cache ialah menukar url permintaan, parameter pertanyaan dan badan menjadi objek. kemudian sirikannya kepada rentetan. Tambahkan ini pada perisian tengah cache anda:

class CacheLayer { public function handle(Request $request, Closure $next): Response { } private function getCacheKey(Request $request): string { $routeParameters = ! empty($request->route()->parameters) ? $request->route()->parameters : [auth()->user()->id]; $allParameters = array_merge($request->all(), $routeParameters); $this->recursiveSort($allParameters); return $request->url() . json_encode($allParameters); } private function recursiveSort(&$array): void { foreach ($array as &$value) { if (is_array($value)) { $this->recursiveSort($value); } } ksort($array); } }
로그인 후 복사

Jom kita lalui kod baris demi baris.

  • Mula-mula kami menyemak parameter permintaan yang dipadankan. kami tidak mahu mengira kunci cache yang sama untuk /users/1/posts dan /users/2/posts.
  • Dan jika tiada parameter yang sepadan kami masukkan dalam id pengguna. Bahagian ini adalah pilihan. Jika anda mempunyai laluan seperti /user yang mengembalikan butiran untuk pengguna yang sedang disahkan. ia sesuai untuk memasukkan id pengguna dalam kunci cache. jika tidak, anda boleh menjadikannya tatasusunan kosong([]).
  • Kemudian kami mendapat semua parameter pertanyaan dan menggabungkannya dengan parameter permintaan
  • Kemudian kami mengisih parameter, mengapa langkah pengisihan ini sangat penting supaya kami dapat mengembalikan data yang sama untuk katakan /posts?page=1&limit=20 dan /posts?limit=20&page=1. jadi tanpa mengira susunan parameter kami masih mengembalikan kunci cache yang sama.

Tidak termasuk laluan

Jadi bergantung pada sifat aplikasi yang anda sedang bina. Akan ada beberapa laluan GET yang anda tidak mahu cache jadi untuk ini kami mencipta pemalar dengan regex untuk memadankan laluan tersebut. Ini akan kelihatan seperti:

private const EXCLUDED_URLS = [ '~^api/v1/posts/[0-9a-zA-Z]+/comments(\?.*)?$~i' ' ];
로그인 후 복사

Dalam kes ini, regex ini akan sepadan dengan semua ulasan siaran.

Mengkonfigurasi TTL

Untuk ini, tambahkan entri ini pada config/cache.php
anda

'ttl' => now()->addMinutes(5),
로그인 후 복사

Menulis Middleware kami

Sekarang kami telah menetapkan semua langkah awal kami, kami boleh menulis kod perisian tengah kami:

public function handle(Request $request, Closure $next): Response { if ('GET' !== $method) { return $next($request); } foreach (self::EXCLUDED_URLS as $pattern) { if (preg_match($pattern, $request->getRequestUri())) { return $next($request); } } $cacheKey = $this->getCacheKey($request); $exception = null; $response = cache() ->tags([$request->url()]) ->remember( key: $cacheKey, ttl: config('cache.ttl'), callback: function () use ($next, $request, &$exception) { $res = $next($request); if (property_exists($res, 'exception') && null !== $res->exception) { $exception = $res; return null; } return $res; } ); return $exception ?? $response; }
로그인 후 복사
  • First we skip caching for non-GET requests and Excluded urls.
  • Then we use the cache helper, tag that cache entry by the request url.
  • we use the remember method to store that cache entry. then we call the other handlers down the stack by doing $next($request). we check for exceptions. and then either return the exception or response.

Cache Invalidation

When new resources are created/updated, we have to clear the cache, so users can see new data. and to do this we will tweak our middleware code a bit. so in the part where we check the request method we add this:

if ('GET' !== $method) { $response = $next($request); if ($response->isSuccessful()) { $tag = $request->url(); if ('PATCH' === $method || 'DELETE' === $method) { $tag = mb_substr($tag, 0, mb_strrpos($tag, '/')); } cache()->tags([$tag])->flush(); } return $response; }
로그인 후 복사

So what this code is doing is flushing the cache for non-GET requests. Then for PATCH and Delete requests we are stripping the {id}. so for example if the request url is PATCH /users/1/posts/2 . We are stripping the last id leaving /users/1/posts. this way when we update a post, we clear the cache of all a users posts. so the user can see fresh data.

Now with this we are done with the CacheLayer implementation. Lets test it

Testing our Cache

Let's say we want to retrieve all a users posts, that has links, media and sort it by likes and recently created. the url for that kind of request according to the json:api spec will look like: /posts?filter[links]=1&filter[media]=1&sort=-created_at,-likes. on a posts table of 1.2 million records the response time is: ~800ms

How to build a caching layer for your Laravel API
and after adding our cache middleware we get a response time of 41ms

How to build a caching layer for your Laravel API

Great success!

Optimizations

Another optional step is to compress the json payload we store on redis. JSON is not the most memory-efficient format, so what we can do is use zlib compression to compress the json before storing and decompress before sending to the client.
the code for that will look like:

$response = cache() ->tags([$request->url()]) ->remember( key: $cacheKey, ttl: config('cache.ttl'), callback: function () use ($next, $request, &$exception) { $res = $next($request); if (property_exists($res, 'exception') && null !== $res->exception) { $exception = $res; return null; } return gzcompress($res->getContent()); } ); return $exception ?? response(gzuncompress($response));
로그인 후 복사

The full code for this looks like:

         getMethod(); if ('GET' !== $method) { $response = $next($request); if ($response->isSuccessful()) { $tag = $request->url(); if ('PATCH' === $method || 'DELETE' === $method) { $tag = mb_substr($tag, 0, mb_strrpos($tag, '/')); } cache()->tags([$tag])->flush(); } return $response; } foreach (self::EXCLUDED_URLS as $pattern) { if (preg_match($pattern, $request->getRequestUri())) { return $next($request); } } $cacheKey = $this->getCacheKey($request); $exception = null; $response = cache() ->tags([$request->url()]) ->remember( key: $cacheKey, ttl: config('cache.ttl'), callback: function () use ($next, $request, &$exception) { $res = $next($request); if (property_exists($res, 'exception') && null !== $res->exception) { $exception = $res; return null; } return gzcompress($res->getContent()); } ); return $exception ?? response(gzuncompress($response)); } private function getCacheKey(Request $request): string { $routeParameters = ! empty($request->route()->parameters) ? $request->route()->parameters : [auth()->user()->id]; $allParameters = array_merge($request->all(), $routeParameters); $this->recursiveSort($allParameters); return $request->url() . json_encode($allParameters); } private function recursiveSort(&$array): void { foreach ($array as &$value) { if (is_array($value)) { $this->recursiveSort($value); } } ksort($array); } }
로그인 후 복사

Summary

This is all I have for you today on caching, Happy building and drop any questions, commments and improvements in the comments!

위 내용은 Laravel API용 캐싱 레이어를 구축하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:dev.to
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿
회사 소개 부인 성명 Sitemap
PHP 중국어 웹사이트:공공복지 온라인 PHP 교육,PHP 학습자의 빠른 성장을 도와주세요!