برنامه نویسی

ذخیره سازی NextJs SSR با استفاده از Redis

Summarize this content to 400 words in Persian Lang

معرفی

این اولین مقاله من است و می خواهم موضوع کش کردن با استفاده از Redis و SSR (رندر سمت سرور) را بررسی کنم. من اهمیت حافظه پنهان را توضیح می دهم، مثالی از پیاده سازی اولیه ارائه می دهم و در مورد برخی از چالش هایی که در پروژه هایم با آن روبرو بوده ام صحبت می کنم. من از NextJs استفاده خواهم کرد زیرا یک پیاده سازی خارج از جعبه SSR دارد.

ذخیره سازی

ابتدا، بیایید در مورد اهمیت ذخیره سازی بحث کنیم. حافظه پنهان برای افزایش عملکرد پروژه شما و به حداقل رساندن درخواست های پایگاه داده بسیار مهم است. نقشی حیاتی در بهبود زمان پاسخگویی، کاهش بار سرور و تضمین تجربه کاربری روان‌تر ایفا می‌کند.

اگر روی یک وب سایت عمومی کار می کنید، کش کردن یک ویژگی ضروری است. صرف نظر از سرعت سرور و پایگاه داده شما، آنها می توانند در زیر بار زیاد به گلوگاه تبدیل شوند.

علاوه بر این، وابستگی مستقیم ترافیک سایت به سرعت بارگذاری صفحه وجود دارد، زیرا گوگل این موضوع را در هنگام نمایه سازی در نظر می گیرد و سایت های سریع تر را در موقعیت های بالاتر قرار می دهد. به همین دلیل است که حافظه پنهان و عملکرد سایت برای دستیابی به موفقیت بسیار مهم است. در پروژه‌های بزرگ، هر میلی‌ثانیه مهم است، زیرا حتی تاخیرهای جزئی نیز می‌تواند منجر به زیان‌های مالی قابل توجهی شود.

انواع حافظه پنهان

انواع مختلفی از کش وجود دارد مانند کش کردن API، کش HTML، CDN، و حافظه پنهان (حافظه، ذخیره سازی محلی و غیره). هر پروژه باید مجموعه دقیقی از ابزارها را با توجه به نیازها، بارها و سایر ویژگی های منحصر به فرد خود داشته باشد. برای افزایش پیچیدگی پروژه و هزینه های نگهداری آن، انتخاب مجموعه ای از ابزارهای مناسب بسیار مهم است.

در این مقاله، من می‌خواهم ذخیره API دقیق با استفاده از Redis و SSR را انتخاب کنم زیرا به کاهش پاسخ سرور کمک می‌کند که نقش مهمی در بهینه‌سازی دارد. فرقی نمی کند که کد ظاهری شما زمانی که سرعت سرور کاهش می یابد کار کند. به این ترتیب اغلب مشتری فقط صفحه را می بندد و سایت در موقعیت های گوگل پایین می آید.

همچنین ابزارهایی مانند Google Page Insights و Light House وجود دارند که به سرعت پاسخگویی سرور اهمیت زیادی می دهند.

چرا به SSR نیاز داریم؟

شاید برخی از شما سوالی داشته باشید – چرا باید درخواستی را روی سرور ارائه کنیم؟ چرا فقط یک قالب HTML به مشتری نمی دهید و همه کارها را در آنجا انجام نمی دهید – داده ها را واکشی کنید و صفحه را بسازید؟ و من می توانم بگویم – بله، این مشکلی ندارد، اما فقط زمانی که وب سایتی دارید که نیازی به نمایه سازی موتور جستجو ندارد. در همین حال، اگر می خواهید وب سایت خود را ایندکس کنید و ترافیک جستجو را افزایش دهید، محتوای شما باید در HTML بازگردانده شده توسط سرور گنجانده شود. گوگل از وب سایت هایی با محتوای رندر شده در سمت سرور نسبت به وب سایت هایی که به شدت به بارگذاری محتوای سمت مشتری متکی هستند، ترجیح می دهد.

چرا ردیس؟

Redis ابزار مورد علاقه من به دلیل سهولت استفاده و انعطاف پذیری آن است. در میان بسیاری از مزایای استفاده از Redis برای ذخیره سازی، چندین مورد برجسته است. این اجازه می دهد تا برای مدیریت حالت پویا، امکان ردیابی کارآمد و حذف انتخابی داده ها را فراهم کند. Redis همچنین از انتقال پایگاه داده به سرور جداگانه پشتیبانی می کند و اشتراک گذاری را برای توزیع بهینه داده ها تسهیل می کند. قابلیت آن برای پیاده سازی کش هوشمند، که در مقاله بعدی به تفصیل توضیح خواهم داد، یکی دیگر از مزایای کلیدی است. علاوه بر این، Redis ایجاد نسخه پشتیبان را آسان می کند، که برای بازیابی سریع پایگاه داده های بزرگ بسیار مهم است.

بنابراین، اجازه دهید شروع به اجرا کنیم. بیایید فرض کنیم شما قبلاً برنامه Next را نصب کرده اید و لیستی از موجودیت ها وجود دارد که از سرور دریافت می کنید و می خواهید آن را کش کنید.

1. نصب Redis

می توانید Redis را بر روی هر سروری اجرا کنید یا آن را مستقیماً روی رایانه خود نصب کنید. من شخصا ترجیح می دهم از Docker استفاده کنم:

فایل: .docker-compose.yml

services:
redis:
image: ‘bitnami/redis:latest’
environment:
– REDIS_PASSWORD=password
– REDIS_AOF_ENABLED=no
volumes:
– “./docker/redis:/bitnami”
ports:
– “6379:6379”
redisinsight:
container_name: redisinsight
image: redislabs/redisinsight
ports:
– “8081:8001”

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

2. راه اندازی Redis

برای شروع، اجازه دهید یک فایل env برای ذخیره تنظیمات اتصال خود ایجاد کنیم.

فایل: .env

REDIS_HOST=localhost
REDIS_PASSWORD=password
REDIS_PORT=6379
And set up Redis client.

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

فایل: lib/redis.js

import Redis from “ioredis”;

const options = {
port: process.env.REDIS_PORT,
host: process.env.REDIS_HOST,
password: process.env.REDIS_PASSWORD
};

const client = new Redis(options);
export default client;

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

3. تابع API

در مرحله بعد، یک API ساده ایجاد می کنیم که از پوشش Redis استفاده می کند:

فایل: api/index.js

وارد کردن کاتالوگ از “./catalog”;

const config = {
API_URL: ‘http://localhost:3000/api/catalog’
}
export const Api = {
catalog: catalog(config)
}
File: api/catalog.js

import {redisGetHandler} from “../utils/redis”;

export default (config) => ({
async getItems(Redis = null, reset = false) {
return await redisGetHandler(Redis, config.API_URL, ‘getItems’, reset);
}
});

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

4. عملکرد کنترل کننده Redis

در مرحله بعد، ما باید یک تابع کنترل کننده برای API ایجاد کنیم که بتواند داده ها را از Redis ذخیره و بازیابی کند.

فایل: utils/redis.js

import axios from “axios”;

export async function redisGetHandler(Redis, apiPath, apiCode, reset = false) {
const url = `${apiPath}/${apiCode}`;
if (!Redis) {
return await fetchData(url);
}

try {
if (!reset) {
const cachedData = await Redis.get(apiCode);
if (cachedData) {
return JSON.parse(cachedData);
}
}
const serverData = await fetchData(url);
if (serverData) {
Redis.set(apiCode, JSON.stringify(serverData));
}
return serverData;
} catch(e) {
console.log(e);
}
return await fetchData(url);
}

async function fetchData(url) {
try {
const {data} = await axios.get(url);
return data;
} catch (e) {
console.log(e);
}
return null;
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

5. کامپوننت صفحه و تابع getServerSideProps

بیایید API را در serverFunction ادغام کنیم. استفاده از Redis در توابع سرور ضروری است، زیرا Redis به طور خاص برای کار بر روی سرور طراحی شده است. عدم انجام این کار منجر به خطا در سمت مشتری می شود.

فایل: pages/index.js

import {useEffect, useState} from “react”;
import {Api} from “../api”;
import Redis from “../lib/redis”;

export default function Home({items: serverItems}) {
const [clientItems, setClientItems] = useState([]);
const [isClientItemsLoading, setIsClientItemsLoading] = useState(true);

const getClientItems = async () => {
setIsClientItemsLoading(true);
setClientItems(await Api.catalog.getItems());
setIsClientItemsLoading(false);
}

useEffect(() => {
getClientItems();
}, []);

return (

Items from client
{isClientItemsLoading && (Loading…)}
{!isClientItemsLoading &&
{clientItems.map(item => {item.name})}
}
Items from server

{serverItems.map(item => {item.name})}

);
}

export async function getServerSideProps({query}) {
const reset = Boolean(query.reset);
const items = await Api.catalog.getItems(Redis, reset)
return {
props: {
items
}
};
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

6. نتیجه

به طور خلاصه، این طرح است: ما دو تماس API داریم، یکی روی سرور و دیگری روی کلاینت.

مخزن: https://github.com/xvandevx/blog-examples/tree/main/nestjs-cache

شاید متوجه شده باشید که درخواست‌های ما فقط روی سرور ذخیره می‌شوند. هنگامی که API از مشتری فراخوانی می شود، Redis استفاده نمی شود زیرا در سمت مشتری کار نمی کند. این مشکلی بود که در پروژه خود در اولین راه اندازی کش با آن مواجه شدم. دلیل آن این است که استفاده از Redis به طور مستقیم بر روی کلاینت ناامن است و یک کتابخانه جلویی وجود ندارد که از این قابلیت پشتیبانی کند.

بنابراین، Next.JS می تواند به ما در رفع آن کمک کند. این به ما اجازه می دهد تا یک بسته بندی API داخلی در برنامه خود ایجاد کنیم. در اینجا نحوه ادامه کار آمده است:

برای پیاده سازی آن فقط به چند تغییر در کد خود نیاز داریم.

7. تابع API

باید API_WRAPPER_URL و تابع getItemsWrapper را پیاده سازی کنیم.

فایل: api/index.js

import catalog from “./catalog”;

const config = {
API_URL: ”,
API_WRAPPER_URL: ”
}
export const Api = {
catalog: catalog(config)
}
File: api/catalog.js

import {redisGetHandler} from “../utils/redis”;
import axios from “axios”;

export default (config) => ({
async getItems(Redis = null, reset = false) {
return await redisGetHandler(Redis, config.API_URL, ‘getItems’, reset);
},
async getItemsWrapper(reset = false) {
const {data} = await axios.get(`${config.API_WRAPPER_URL}/getItems?reset=${reset}`);
return data;
}
});

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

8. لفاف API سرور را راه اندازی کنید

این مرحله مهم شامل پیکربندی API سرور است تا به عنوان یک پوشش برای نقطه پایانی API شما عمل کند.

فایل: pages/api/apiWrapper/getItems.js

import Redis from “/lib/redis”;
import {Api} from “../../../api”;

export default async function handler(req, res) {
const data = await Api.catalog.getItems(Redis, req.query.reset === ‘true’);
res.status(200).json(data);
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

مزیت دیگر این است که می توانید URL نقطه پایانی API خود را با استفاده از این پوشش پنهان کنید که امنیت را افزایش می دهد.

9. تماس های API را از خارجی به بسته بندی API تغییر دهید

تنها کاری که باید انجام دهید این است که تماس را از Api.catalog.getItems() به Api.catalog.getItemsWrapper() تغییر دهید.

فایل: pages/index.js

import {useEffect, useState} from “react”;
import {Api} from “../api”;

export default function Home({items: serverItems}) {
const [clientItems, setClientItems] = useState([]);
const [isClientItemsLoading, setIsClientItemsLoading] = useState(true);
const getClientItems = async () => {
setIsClientItemsLoading(true);
setClientItems(await Api.catalog.**getItemsWrapper**());
setIsClientItemsLoading(false);
};
useEffect(() => {
getClientItems();
}, []);
return (
….component
);
}
export async function getServerSideProps({query}) {
const reset = Boolean(query.reset);
const items = await Api.catalog.getItemsWrapper(reset);
return {
props: {
items
}
}
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بنابراین، مهم نیست که از کجا درخواست می کنیم (سرور یا مشتری) – ما همیشه داده ها را از Redis دریافت می کنیم، به این معنی که آن را سریع و بهینه دریافت می کنیم.

مخزن: https://github.com/xvandevx/blog-examples/tree/main/nestjs-cache-wrapper

بازنشانی حافظه پنهان

تنظیم مجدد حافظه پنهان یک موضوع پیچیده است که من قصد دارم در مقاله آینده خود به آن بیشتر بپردازم. اجرای آن مستلزم درک کامل مکانیک پروژه شما است. صرفاً کش کردن تمام نقاط پایانی API در Redis با مهلت زمانی کافی نیست. برخی از پروژه‌ها، مانند فروشگاه‌های آنلاین که در آن‌ها به‌روزرسانی قیمت‌ها در زمان واقعی بسیار مهم است، نمی‌توانند اختلاف بین داده‌های ذخیره‌شده در صفحات جزئیات و قیمت‌های پرداخت را تحمل کنند. حتی فروشگاه های کوچک و کم تردد نیز چنین اختلافاتی را برای مشتریان خود ناامید کننده می دانند.

بنابراین، اتخاذ استراتژی‌های کش مناسب برای هر بخش از برنامه ضروری است. در این مقاله، من روی سناریوهایی تمرکز می‌کنم که در آن به‌روزرسانی‌های حافظه نهان فوری حیاتی نیستند – من به سادگی یک طول عمر حافظه پنهان را تعریف می‌کنم و آن را به همان حال رها می‌کنم.

علاوه بر این، من توصیه می‌کنم برای هر پروژه بازنشانی کش دستی را اجرا کنید، زیرا پیاده‌سازی آن مفید و آسان است. هر از چند گاهی می خواهید داده های تغییر یافته را در صفحه مشاهده کنید بدون اینکه منتظر بمانید تا حافظه پنهان تمام شود. برای این، توصیه می کنم از پارامتر URL برای تنظیم مجدد حافظه پنهان استفاده کنید، به عنوان مثال ?reset_cache=y:

export async function getServerSideProps({query}) {
const reset = Boolean(query.reset);
const items = await Api.catalog.getItems(Redis, reset);
return {
props: {
items
}
};
}

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

مدیران محتوا و آزمایش کنندگان از این بابت از شما بسیار سپاسگزار خواهند بود!

و بس! اکنون API را در حافظه پنهان ذخیره کرده‌ایم که به ما امکان می‌دهد هر بار که با API تماس می‌گیرید، عملکرد را افزایش داده و از درخواست‌های سنگین پایگاه داده اجتناب کنیم.

خلاصه

در این مقاله، مورد استفاده اولیه Redis با NextJs را در نظر گرفتم که برای پروژه های کوچک کافی است و بیشتر برای اهداف یادگیری مناسب است. پروژه‌های مقیاس بزرگ معمولاً از سیستم‌های پیچیده‌تری استفاده می‌کنند که شامل Redis، KeyDB، Memcached، کش HTML، CDN، کش مشتری و سایر فناوری‌ها می‌شود. بسیار مهم است که مجموعه ای از ابزارها را انتخاب کنید که متناسب با پروژه خاص شما باشد، به طوری که پیچیدگی و سرعت توسعه را افزایش ندهد.

اگر در یک شرکت بزرگ هستید، نیازی نیست به مواردی مانند کش و سرعت بارگذاری صفحه اهمیت دهید. اغلب این مسئولیت به عهده یک تیم جداگانه است یا توسط پشتیبان مدیریت می شود یا شما در حال انجام کاری هستید که به هیچ وجه نیاز به کش نیست. با این وجود، از نظر من، هر توسعه‌دهنده فرانت‌اند باید از نحوه کارکرد و نحوه برخورد با آن آگاه باشد.

علاوه بر این، در زمان تغییر سریع ما که هوش مصنوعی وارد زندگی ما می شود، تقاضا برای توسعه دهندگان فول استک مانند 15 سال پیش دوباره شروع به رشد خواهد کرد. به همین دلیل است که چنین دانشی به شما کمک می کند تا از دیگر توسعه دهندگان موجود در بازار به شما اشاره کند.

معرفی

این اولین مقاله من است و می خواهم موضوع کش کردن با استفاده از Redis و SSR (رندر سمت سرور) را بررسی کنم. من اهمیت حافظه پنهان را توضیح می دهم، مثالی از پیاده سازی اولیه ارائه می دهم و در مورد برخی از چالش هایی که در پروژه هایم با آن روبرو بوده ام صحبت می کنم. من از NextJs استفاده خواهم کرد زیرا یک پیاده سازی خارج از جعبه SSR دارد.

ذخیره سازی

ابتدا، بیایید در مورد اهمیت ذخیره سازی بحث کنیم. حافظه پنهان برای افزایش عملکرد پروژه شما و به حداقل رساندن درخواست های پایگاه داده بسیار مهم است. نقشی حیاتی در بهبود زمان پاسخگویی، کاهش بار سرور و تضمین تجربه کاربری روان‌تر ایفا می‌کند.

اگر روی یک وب سایت عمومی کار می کنید، کش کردن یک ویژگی ضروری است. صرف نظر از سرعت سرور و پایگاه داده شما، آنها می توانند در زیر بار زیاد به گلوگاه تبدیل شوند.

علاوه بر این، وابستگی مستقیم ترافیک سایت به سرعت بارگذاری صفحه وجود دارد، زیرا گوگل این موضوع را در هنگام نمایه سازی در نظر می گیرد و سایت های سریع تر را در موقعیت های بالاتر قرار می دهد. به همین دلیل است که حافظه پنهان و عملکرد سایت برای دستیابی به موفقیت بسیار مهم است. در پروژه‌های بزرگ، هر میلی‌ثانیه مهم است، زیرا حتی تاخیرهای جزئی نیز می‌تواند منجر به زیان‌های مالی قابل توجهی شود.

انواع حافظه پنهان

انواع مختلفی از کش وجود دارد مانند کش کردن API، کش HTML، CDN، و حافظه پنهان (حافظه، ذخیره سازی محلی و غیره). هر پروژه باید مجموعه دقیقی از ابزارها را با توجه به نیازها، بارها و سایر ویژگی های منحصر به فرد خود داشته باشد. برای افزایش پیچیدگی پروژه و هزینه های نگهداری آن، انتخاب مجموعه ای از ابزارهای مناسب بسیار مهم است.

در این مقاله، من می‌خواهم ذخیره API دقیق با استفاده از Redis و SSR را انتخاب کنم زیرا به کاهش پاسخ سرور کمک می‌کند که نقش مهمی در بهینه‌سازی دارد. فرقی نمی کند که کد ظاهری شما زمانی که سرعت سرور کاهش می یابد کار کند. به این ترتیب اغلب مشتری فقط صفحه را می بندد و سایت در موقعیت های گوگل پایین می آید.

همچنین ابزارهایی مانند Google Page Insights و Light House وجود دارند که به سرعت پاسخگویی سرور اهمیت زیادی می دهند.

چرا به SSR نیاز داریم؟

شاید برخی از شما سوالی داشته باشید – چرا باید درخواستی را روی سرور ارائه کنیم؟ چرا فقط یک قالب HTML به مشتری نمی دهید و همه کارها را در آنجا انجام نمی دهید – داده ها را واکشی کنید و صفحه را بسازید؟ و من می توانم بگویم – بله، این مشکلی ندارد، اما فقط زمانی که وب سایتی دارید که نیازی به نمایه سازی موتور جستجو ندارد. در همین حال، اگر می خواهید وب سایت خود را ایندکس کنید و ترافیک جستجو را افزایش دهید، محتوای شما باید در HTML بازگردانده شده توسط سرور گنجانده شود. گوگل از وب سایت هایی با محتوای رندر شده در سمت سرور نسبت به وب سایت هایی که به شدت به بارگذاری محتوای سمت مشتری متکی هستند، ترجیح می دهد.

چرا ردیس؟

Redis ابزار مورد علاقه من به دلیل سهولت استفاده و انعطاف پذیری آن است. در میان بسیاری از مزایای استفاده از Redis برای ذخیره سازی، چندین مورد برجسته است. این اجازه می دهد تا برای مدیریت حالت پویا، امکان ردیابی کارآمد و حذف انتخابی داده ها را فراهم کند. Redis همچنین از انتقال پایگاه داده به سرور جداگانه پشتیبانی می کند و اشتراک گذاری را برای توزیع بهینه داده ها تسهیل می کند. قابلیت آن برای پیاده سازی کش هوشمند، که در مقاله بعدی به تفصیل توضیح خواهم داد، یکی دیگر از مزایای کلیدی است. علاوه بر این، Redis ایجاد نسخه پشتیبان را آسان می کند، که برای بازیابی سریع پایگاه داده های بزرگ بسیار مهم است.

بنابراین، اجازه دهید شروع به اجرا کنیم. بیایید فرض کنیم شما قبلاً برنامه Next را نصب کرده اید و لیستی از موجودیت ها وجود دارد که از سرور دریافت می کنید و می خواهید آن را کش کنید.

1. نصب Redis

می توانید Redis را بر روی هر سروری اجرا کنید یا آن را مستقیماً روی رایانه خود نصب کنید. من شخصا ترجیح می دهم از Docker استفاده کنم:

فایل: .docker-compose.yml

services:
  redis:
    image: 'bitnami/redis:latest'
    environment:
      - REDIS_PASSWORD=password
      - REDIS_AOF_ENABLED=no
    volumes:
      - "./docker/redis:/bitnami"
    ports:
      - "6379:6379"
  redisinsight:
    container_name: redisinsight
    image: redislabs/redisinsight
    ports:
      - "8081:8001"
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

2. راه اندازی Redis

برای شروع، اجازه دهید یک فایل env برای ذخیره تنظیمات اتصال خود ایجاد کنیم.

فایل: .env

REDIS_HOST=localhost
REDIS_PASSWORD=password
REDIS_PORT=6379
And set up Redis client.
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

فایل: lib/redis.js

import Redis from "ioredis";

const options = {
    port: process.env.REDIS_PORT,
    host: process.env.REDIS_HOST,
    password: process.env.REDIS_PASSWORD
};

const client = new Redis(options);
export default client;
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

3. تابع API

در مرحله بعد، یک API ساده ایجاد می کنیم که از پوشش Redis استفاده می کند:

فایل: api/index.js

وارد کردن کاتالوگ از “./catalog”;

const config = {
    API_URL: 'http://localhost:3000/api/catalog'
}
export const Api = {
    catalog: catalog(config)
}
File: api/catalog.js

import {redisGetHandler} from "../utils/redis";

export default (config) => ({
    async getItems(Redis = null, reset = false) {
        return await redisGetHandler(Redis, config.API_URL, 'getItems', reset);
    }
});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

4. عملکرد کنترل کننده Redis

در مرحله بعد، ما باید یک تابع کنترل کننده برای API ایجاد کنیم که بتواند داده ها را از Redis ذخیره و بازیابی کند.

فایل: utils/redis.js

import axios from "axios";

export async function redisGetHandler(Redis, apiPath, apiCode, reset = false) {
    const url = `${apiPath}/${apiCode}`;
    if (!Redis) {
        return await fetchData(url);
    }

    try {
        if (!reset) {
            const cachedData = await Redis.get(apiCode);
            if (cachedData) {
                return JSON.parse(cachedData);
            }
        }   
        const serverData = await fetchData(url);
        if (serverData) {
            Redis.set(apiCode, JSON.stringify(serverData));
        }
        return serverData;
    } catch(e) {
        console.log(e);
    }
    return await fetchData(url);
}

async function fetchData(url) {
    try {
        const {data} = await axios.get(url);
        return data;
    } catch (e) {
        console.log(e);
    }
    return null;
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

5. کامپوننت صفحه و تابع getServerSideProps

بیایید API را در serverFunction ادغام کنیم. استفاده از Redis در توابع سرور ضروری است، زیرا Redis به طور خاص برای کار بر روی سرور طراحی شده است. عدم انجام این کار منجر به خطا در سمت مشتری می شود.

فایل: pages/index.js

import {useEffect, useState} from "react";
import {Api} from "../api";
import Redis from "../lib/redis";

export default function Home({items: serverItems}) {
    const [clientItems, setClientItems] = useState([]);
    const [isClientItemsLoading, setIsClientItemsLoading] = useState(true);

    const getClientItems = async () => {
        setIsClientItemsLoading(true);
        setClientItems(await Api.catalog.getItems());
        setIsClientItemsLoading(false);
    }

    useEffect(() => {
        getClientItems();
    }, []);

    return (
        

Items from client

{isClientItemsLoading && (

Loading...

)} {!isClientItemsLoading &&
    {clientItems.map(item =>
  • {item.name}
  • )}
}

Items from server

    {serverItems.map(item =>
  • {item.name}
  • )}
); } export async function getServerSideProps({query}) { const reset = Boolean(query.reset); const items = await Api.catalog.getItems(Redis, reset) return { props: { items } }; }
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

6. نتیجه

به طور خلاصه، این طرح است: ما دو تماس API داریم، یکی روی سرور و دیگری روی کلاینت.

توضیحات تصویر

مخزن: https://github.com/xvandevx/blog-examples/tree/main/nestjs-cache

شاید متوجه شده باشید که درخواست‌های ما فقط روی سرور ذخیره می‌شوند. هنگامی که API از مشتری فراخوانی می شود، Redis استفاده نمی شود زیرا در سمت مشتری کار نمی کند. این مشکلی بود که در پروژه خود در اولین راه اندازی کش با آن مواجه شدم. دلیل آن این است که استفاده از Redis به طور مستقیم بر روی کلاینت ناامن است و یک کتابخانه جلویی وجود ندارد که از این قابلیت پشتیبانی کند.

بنابراین، Next.JS می تواند به ما در رفع آن کمک کند. این به ما اجازه می دهد تا یک بسته بندی API داخلی در برنامه خود ایجاد کنیم. در اینجا نحوه ادامه کار آمده است:

برای پیاده سازی آن فقط به چند تغییر در کد خود نیاز داریم.

7. تابع API

باید API_WRAPPER_URL و تابع getItemsWrapper را پیاده سازی کنیم.

فایل: api/index.js

import catalog from "./catalog";

const config = {
    API_URL: '',
    API_WRAPPER_URL: ''
}
export const Api = {
    catalog: catalog(config)
}
File: api/catalog.js

import {redisGetHandler} from "../utils/redis";
import axios from "axios";

export default (config) => ({
    async getItems(Redis = null, reset = false) {
        return await redisGetHandler(Redis, config.API_URL, 'getItems', reset);
    },
    async getItemsWrapper(reset = false) {
        const {data} = await axios.get(`${config.API_WRAPPER_URL}/getItems?reset=${reset}`);
        return data;
    }
});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

8. لفاف API سرور را راه اندازی کنید

این مرحله مهم شامل پیکربندی API سرور است تا به عنوان یک پوشش برای نقطه پایانی API شما عمل کند.

فایل: pages/api/apiWrapper/getItems.js

import Redis from "/lib/redis";
import {Api} from "../../../api";

export default async function handler(req, res) {
    const data = await Api.catalog.getItems(Redis, req.query.reset === 'true');
    res.status(200).json(data);
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

مزیت دیگر این است که می توانید URL نقطه پایانی API خود را با استفاده از این پوشش پنهان کنید که امنیت را افزایش می دهد.

9. تماس های API را از خارجی به بسته بندی API تغییر دهید

تنها کاری که باید انجام دهید این است که تماس را از Api.catalog.getItems() به Api.catalog.getItemsWrapper() تغییر دهید.

فایل: pages/index.js

import {useEffect, useState} from "react";
import {Api} from "../api";

export default function Home({items: serverItems}) {
    const [clientItems, setClientItems] = useState([]);
    const [isClientItemsLoading, setIsClientItemsLoading] = useState(true);
    const getClientItems = async () => {
        setIsClientItemsLoading(true);
        setClientItems(await Api.catalog.**getItemsWrapper**());
        setIsClientItemsLoading(false);
    };
    useEffect(() => {
        getClientItems();
    }, []);
    return (
        ....component
    );
}
export async function getServerSideProps({query}) {
    const reset = Boolean(query.reset);
    const items = await Api.catalog.getItemsWrapper(reset);
    return {
        props: {
            items
        }
    }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بنابراین، مهم نیست که از کجا درخواست می کنیم (سرور یا مشتری) – ما همیشه داده ها را از Redis دریافت می کنیم، به این معنی که آن را سریع و بهینه دریافت می کنیم.

مخزن: https://github.com/xvandevx/blog-examples/tree/main/nestjs-cache-wrapper

بازنشانی حافظه پنهان

تنظیم مجدد حافظه پنهان یک موضوع پیچیده است که من قصد دارم در مقاله آینده خود به آن بیشتر بپردازم. اجرای آن مستلزم درک کامل مکانیک پروژه شما است. صرفاً کش کردن تمام نقاط پایانی API در Redis با مهلت زمانی کافی نیست. برخی از پروژه‌ها، مانند فروشگاه‌های آنلاین که در آن‌ها به‌روزرسانی قیمت‌ها در زمان واقعی بسیار مهم است، نمی‌توانند اختلاف بین داده‌های ذخیره‌شده در صفحات جزئیات و قیمت‌های پرداخت را تحمل کنند. حتی فروشگاه های کوچک و کم تردد نیز چنین اختلافاتی را برای مشتریان خود ناامید کننده می دانند.

بنابراین، اتخاذ استراتژی‌های کش مناسب برای هر بخش از برنامه ضروری است. در این مقاله، من روی سناریوهایی تمرکز می‌کنم که در آن به‌روزرسانی‌های حافظه نهان فوری حیاتی نیستند – من به سادگی یک طول عمر حافظه پنهان را تعریف می‌کنم و آن را به همان حال رها می‌کنم.

علاوه بر این، من توصیه می‌کنم برای هر پروژه بازنشانی کش دستی را اجرا کنید، زیرا پیاده‌سازی آن مفید و آسان است. هر از چند گاهی می خواهید داده های تغییر یافته را در صفحه مشاهده کنید بدون اینکه منتظر بمانید تا حافظه پنهان تمام شود. برای این، توصیه می کنم از پارامتر URL برای تنظیم مجدد حافظه پنهان استفاده کنید، به عنوان مثال ?reset_cache=y:

export async function getServerSideProps({query}) {
    const reset = Boolean(query.reset);
    const items = await Api.catalog.getItems(Redis, reset);
    return {
        props: {
            items
        }
    };
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

مدیران محتوا و آزمایش کنندگان از این بابت از شما بسیار سپاسگزار خواهند بود!

و بس! اکنون API را در حافظه پنهان ذخیره کرده‌ایم که به ما امکان می‌دهد هر بار که با API تماس می‌گیرید، عملکرد را افزایش داده و از درخواست‌های سنگین پایگاه داده اجتناب کنیم.

خلاصه

در این مقاله، مورد استفاده اولیه Redis با NextJs را در نظر گرفتم که برای پروژه های کوچک کافی است و بیشتر برای اهداف یادگیری مناسب است. پروژه‌های مقیاس بزرگ معمولاً از سیستم‌های پیچیده‌تری استفاده می‌کنند که شامل Redis، KeyDB، Memcached، کش HTML، CDN، کش مشتری و سایر فناوری‌ها می‌شود. بسیار مهم است که مجموعه ای از ابزارها را انتخاب کنید که متناسب با پروژه خاص شما باشد، به طوری که پیچیدگی و سرعت توسعه را افزایش ندهد.

اگر در یک شرکت بزرگ هستید، نیازی نیست به مواردی مانند کش و سرعت بارگذاری صفحه اهمیت دهید. اغلب این مسئولیت به عهده یک تیم جداگانه است یا توسط پشتیبان مدیریت می شود یا شما در حال انجام کاری هستید که به هیچ وجه نیاز به کش نیست. با این وجود، از نظر من، هر توسعه‌دهنده فرانت‌اند باید از نحوه کارکرد و نحوه برخورد با آن آگاه باشد.

علاوه بر این، در زمان تغییر سریع ما که هوش مصنوعی وارد زندگی ما می شود، تقاضا برای توسعه دهندگان فول استک مانند 15 سال پیش دوباره شروع به رشد خواهد کرد. به همین دلیل است که چنین دانشی به شما کمک می کند تا از دیگر توسعه دهندگان موجود در بازار به شما اشاره کند.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا