Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Redis

เพิ่มประสิทธิภาพ Web API ของคุณด้วย Redis:คู่มือการเพิ่มประสิทธิภาพประสิทธิภาพที่ได้รับการพิสูจน์แล้ว

เพิ่มประสิทธิภาพ Web API ของคุณด้วย Redis:คู่มือการเพิ่มประสิทธิภาพประสิทธิภาพที่ได้รับการพิสูจน์แล้ว

โดย Tarique Ejaz

ประสิทธิภาพเป็นตัวแปรสำคัญที่ต้องพิจารณาเมื่อคุณออกแบบซอฟต์แวร์ชิ้นใดก็ตาม เป็นสิ่งสำคัญอย่างยิ่งเมื่อพูดถึงสิ่งที่เกิดขึ้นเบื้องหลัง

ในฐานะนักพัฒนาและนักเทคโนโลยี เรานำการปรับแต่งและการใช้งานหลายอย่างมาใช้เพื่อปรับปรุงประสิทธิภาพ นี่คือจุดที่แคชเข้ามามีบทบาท

การแคชถูกกำหนดให้เป็นกลไกในการจัดเก็บข้อมูลหรือไฟล์ในตำแหน่งที่เก็บข้อมูลชั่วคราวซึ่งสามารถเข้าถึงได้ทันทีเมื่อจำเป็น

การแคชกลายเป็นสิ่งจำเป็นในเว็บแอปพลิเคชันในปัจจุบัน เราสามารถใช้ Redis เพื่อเพิ่มประสิทธิภาพเว็บ API ของเรา ซึ่งสร้างขึ้นโดยใช้ Node.js และ MongoDB

เพิ่มประสิทธิภาพ Web API ของคุณด้วย Redis:คู่มือการเพิ่มประสิทธิภาพประสิทธิภาพที่ได้รับการพิสูจน์แล้ว "แคชดูเหมือนจะยังคงมีบทบาทสำคัญอย่างยิ่งต่อไปอีก 100 ถึง 200 ปีข้างหน้า"

Redis:ภาพรวมของคนธรรมดา

ตามเอกสารประกอบอย่างเป็นทางการ Redis ถูกกำหนดให้เป็นที่จัดเก็บโครงสร้างข้อมูลในหน่วยความจำซึ่งใช้เป็นฐานข้อมูล นายหน้าข้อความ หรือที่เก็บข้อมูลแคช รองรับโครงสร้างข้อมูล เช่น สตริง แฮช รายการ ชุด ชุดที่เรียงลำดับด้วยการสืบค้นแบบช่วง บิตแมป ไฮเปอร์ล็อกล็อก ดัชนีเชิงพื้นที่พร้อมการสืบค้นแบบรัศมีและสตรีม

โอเค นั่นเป็นโครงสร้างข้อมูลที่ค่อนข้างเยอะตรงนี้ เพื่อให้ง่าย โครงสร้างข้อมูลเกือบทั้งหมดที่รองรับสามารถย่อให้เป็นสตริงรูปแบบเดียวหรืออีกรูปแบบหนึ่งได้ คุณจะได้รับความชัดเจนมากขึ้นเมื่อเราดำเนินการใช้งาน

แต่สิ่งหนึ่งที่ชัดเจน Redis ทรงพลัง และเมื่อใช้อย่างถูกต้องจะทำให้แอปพลิเคชันของเราไม่เพียงแต่เร็วขึ้น แต่ยังมีประสิทธิภาพอย่างน่าทึ่งอีกด้วย พูดพอแล้ว. มาทำให้มือของเราสกปรกกันเถอะ

มาคุยกันเรื่องโค้ด

ก่อนที่เราจะเริ่ม คุณจะต้องตั้งค่า Redis ในระบบภายในเครื่องของคุณก่อน คุณสามารถทำตามขั้นตอนการตั้งค่าด่วนนี้เพื่อเริ่มต้นและใช้งาน Redis ได้

เสร็จแล้วเหรอ? เย็น. เริ่มกันเลย เรามีแอปพลิเคชันง่ายๆ ที่สร้างขึ้นใน Express ซึ่งใช้อินสแตนซ์ใน MongoDB Atlas เพื่ออ่านและเขียนข้อมูล

เรามี API หลักสองรายการที่สร้างขึ้นใน /blogs ไฟล์เส้นทาง

...
// GET - Fetches all blog posts for required user
blogsRouter.route('/:user')
 .get(async (req, res, next) => {
 const blogs = await Blog.find({ user: req.params.user });
 res.status(200).json({
 blogs,
 });
 });
// POST - Creates a new blog post
blogsRouter.route('/')
 .post(async (req, res, next) => {
 const existingBlog = await Blog.findOne({ title: req.body.title });
 if (!existingBlog) {
 let newBlog = new Blog(req.body);
 const result = await newBlog.save();
 return res.status(200).json({
 message: `Blog ${result.id} is successfully created`,
 result,
 });
 }
 res.status(200).json({
 message: 'Blog with same title exists',
 });
 });
...

โรยความดีของ Redis

เราเริ่มต้นด้วยการดาวน์โหลดแพ็คเกจ npm redis เพื่อเชื่อมต่อกับเซิร์ฟเวอร์ Redis ในเครื่อง

const mongoose = require('mongoose');
const redis = require('redis');
const util = require('util');
const redisUrl = 'redis://127.0.0.1:6379';
const client = redis.createClient(redisUrl);
client.hget = util.promisify(client.hget);
...

เราใช้ utils.promisify ฟังก์ชั่นการแปลง client.hget ฟังก์ชั่นเพื่อคืนสัญญาแทนการโทรกลับ คุณสามารถอ่านเพิ่มเติมเกี่ยวกับ promisification ที่นี่

มีการเชื่อมต่อ Redis แล้ว ก่อนที่เราจะเริ่มเขียนโค้ดแคชเพิ่มเติม ให้เราย้อนกลับไปและพยายามทำความเข้าใจว่าข้อกำหนดใดบ้างที่เราต้องปฏิบัติตามและความท้าทายที่อาจเกิดขึ้นที่เราอาจเผชิญ

กลยุทธ์การแคชของเราควรจะสามารถจัดการกับประเด็นต่อไปนี้ได้

  • แคชคำขอสำหรับโพสต์บล็อกทั้งหมดสำหรับผู้ใช้รายใดรายหนึ่ง
  • ล้างแคชทุกครั้งที่สร้างโพสต์บล็อกใหม่

    ความท้าทายที่เป็นไปได้ที่เราควรระวังในขณะที่เราดำเนินกลยุทธ์ของเราคือ:

  • วิธีที่ถูกต้องในการจัดการการสร้างคีย์เพื่อจัดเก็บข้อมูลแคช

  • ตรรกะการหมดอายุของแคชและการบังคับใช้การหมดอายุเพื่อรักษาความใหม่ของแคช
  • การนำลอจิกแคชไปใช้ซ้ำได้

เอาล่ะ. เรามีการจดประเด็นของเราและเชื่อมโยงกันใหม่ ไปยังขั้นตอนต่อไป

การเอาชนะฟังก์ชัน Mongoose Exec เริ่มต้น

เราต้องการให้ตรรกะแคชของเรานำมาใช้ซ้ำได้ และไม่เพียงแต่สามารถนำมาใช้ซ้ำได้ เรายังต้องการให้เป็นจุดตรวจสอบแรกก่อนที่เราจะทำการสืบค้นใดๆ ในฐานข้อมูล ซึ่งสามารถทำได้ง่ายๆ โดยใช้การแฮ็กแบบ piggy-backing ง่ายๆ บนฟังก์ชัน mongoose exec

...
const exec = mongoose.Query.prototype.exec;
...
mongoose.Query.prototype.exec = async function() {
 ...
 const result = await exec.apply(this, arguments);
 console.log('Data Source: Database');
 return result;
}
...

เราใช้วัตถุต้นแบบของพังพอนเพื่อเพิ่มโค้ดตรรกะการแคชของเราเป็นการดำเนินการครั้งแรกในการสืบค้น

การเพิ่มแคชเป็นแบบสอบถาม

เพื่อระบุว่าแบบสอบถามใดควรพร้อมสำหรับการแคช เราจึงสร้างแบบสอบถามแบบพังพอน เราจัดให้มีความสามารถในการส่งผ่าน user เพื่อใช้เป็นแฮชคีย์ผ่าน options วัตถุ

หมายเหตุ: Hashkey ทำหน้าที่เป็นตัวระบุสำหรับโครงสร้างข้อมูลแฮช ซึ่งสามารถระบุได้ว่าเป็นคีย์หลักสำหรับชุดของคู่คีย์-ค่า ด้วยเหตุนี้ จึงทำให้สามารถแคชชุดค่าคิวรีจำนวนมากขึ้นได้ คุณสามารถอ่านเพิ่มเติมเกี่ยวกับแฮชใน Redis ได้ที่นี่

...
mongoose.Query.prototype.cache = function(options = {}) {
 this.enableCache = true;
 this.hashKey = JSON.stringify(options.key || 'default');
 return this;
};
...

เมื่อดำเนินการดังกล่าวแล้ว เราก็สามารถใช้ cache(<options argument>) ได้อย่างง่ายดาย ข้อความค้นหาพร้อมกับข้อความค้นหาที่เราต้องการแคชในลักษณะดังต่อไปนี้

...
const blogs = await Blog
 .find({ user: req.params.user })
 .cache({ key: req.params.user });
...

การสร้างลอจิกแคช

เราได้ตั้งค่าการสืบค้นที่ใช้ซ้ำได้ทั่วไปเพื่อแสดงว่าการสืบค้นใดจำเป็นต้องแคช เรามาเขียนตรรกะแคชกลางกันดีกว่า

...
mongoose.Query.prototype.exec = async function() {
 if (!this.enableCache) {
 console.log('Data Source: Database');
 return exec.apply(this, arguments);
 }
 const key = JSON.stringify(Object.assign({}, this.getQuery(), {
 collection: this.mongooseCollection.name,
 }));
 const cachedValue = await client.hget(this.hashKey, key);
 if (cachedValue) {
 const parsedCache = JSON.parse(cachedValue);
 console.log('Data Source: Cache');
 return Array.isArray(parsedCache) 
 ? parsedCache.map(doc => new this.model(doc)) 
 : new this.model(parsedCache);
 }
 const result = await exec.apply(this, arguments);
 client.hmset(this.hashKey, key, JSON.stringify(result), 'EX', 300);
 console.log('Data Source: Database');
 return result;
};
...

เมื่อใดก็ตามที่เราใช้ cache() ข้อความค้นหาพร้อมกับข้อความค้นหาหลักของเรา เราตั้งค่า enableCache กุญแจสำคัญที่จะเป็นจริง

หากคีย์เป็นเท็จ เราจะส่งคืนคีย์หลัก exec แบบสอบถามเป็นค่าเริ่มต้น ถ้าไม่เช่นนั้น เราจะสร้างคีย์สำหรับการดึงและจัดเก็บ/รีเฟรชข้อมูลแคชก่อน

เราใช้ collection ชื่อพร้อมกับข้อความค้นหาเริ่มต้นเป็นชื่อคีย์เพื่อความเป็นเอกลักษณ์ แฮชคีย์ที่ใช้คือชื่อของ user ซึ่งเราได้ตั้งค่าไว้แล้วก่อนหน้านี้ใน cache() นิยามฟังก์ชัน

ข้อมูลแคชจะถูกดึงออกมาโดยใช้ client.hget() ฟังก์ชั่นที่ต้องใช้แฮชคีย์และคีย์ที่ตามมาเป็นพารามิเตอร์

หมายเหตุ: เราใช้ JSON.parse() เสมอ ในขณะที่ดึงข้อมูลใดๆ จาก Redis และในทำนองเดียวกัน เราใช้ JSON.stringify() บนคีย์และข้อมูลก่อนที่จะจัดเก็บสิ่งใดๆ ลงใน Redis ซึ่งทำได้เนื่องจาก Redis ไม่รองรับโครงสร้างข้อมูล JSON

เมื่อเราได้รับข้อมูลที่แคชไว้แล้ว เราจะต้องแปลงแต่ละออบเจ็กต์ที่แคชไว้ให้เป็นโมเดลพังพอน ซึ่งสามารถทำได้โดยใช้ new this.model(<object>) .

หากแคชไม่มีข้อมูลที่จำเป็น เราจะทำการสืบค้นไปยังฐานข้อมูล จากนั้น เมื่อส่งคืนข้อมูลไปยัง API แล้ว เราจะรีเฟรชแคชโดยใช้ client.hmset() . นอกจากนี้เรายังตั้งเวลาหมดอายุแคชเริ่มต้นไว้ที่ 300 วินาที สิ่งนี้สามารถปรับแต่งได้ตามกลยุทธ์การแคชของคุณ

ตรรกะการแคชอยู่ในสถานที่ นอกจากนี้เรายังตั้งเวลาหมดอายุเริ่มต้นอีกด้วย ต่อไป เราจะดูที่การบังคับให้แคชหมดอายุทุกครั้งที่มีการสร้างโพสต์บนบล็อกใหม่

บังคับให้แคชหมดอายุ

ในบางกรณี เช่น เมื่อผู้ใช้สร้างโพสต์ในบล็อกใหม่ ผู้ใช้คาดหวังว่าโพสต์ใหม่ควรจะพร้อมใช้งานเมื่อดึงข้อมูลโพสต์ทั้งหมด

ในการทำเช่นนั้น เราต้องล้างแคชที่เกี่ยวข้องกับผู้ใช้นั้นและอัปเดตด้วยข้อมูลใหม่ เราจึงต้องบังคับให้หมดอายุ เราสามารถทำได้โดยการเรียกใช้ del() ฟังก์ชั่นที่จัดทำโดย Redis

...
module.exports = {
 clearCache(hashKey) {
 console.log('Cache cleaned');
 client.del(JSON.stringify(hashKey));
 }
}
...

เราต้องจำไว้ด้วยว่าเราจะบังคับให้มีการหมดอายุในหลายเส้นทาง วิธีหนึ่งที่สามารถขยายได้คือการใช้ clearCache() นี้ เป็นมิดเดิลแวร์และเรียกมันเมื่อแบบสอบถามใด ๆ ที่เกี่ยวข้องกับเส้นทางเสร็จสิ้นการดำเนินการ

const { clearCache } = require('../services/cache');
module.exports = async (req, res, next) => {
 // wait for route handler to finish running
 await next(); 
 clearCache(req.body.user);
}

มิดเดิลแวร์นี้สามารถเรียกได้อย่างง่ายดายบนเส้นทางเฉพาะด้วยวิธีต่อไปนี้

...
blogsRouter.route('/')
 .post(cleanCache, async (req, res, next) => {
 ...
 }
...

และเราทำเสร็จแล้ว ฉันยอมรับว่ามันเป็นรหัสที่ค่อนข้างมาก แต่ในส่วนสุดท้ายนี้ เราได้ตั้งค่า Redis ให้กับแอปพลิเคชันของเรา และจัดการกับความท้าทายที่อาจเกิดขึ้นเกือบทั้งหมด ถึงเวลาที่จะได้เห็นกลยุทธ์การแคชของเราใช้งานจริง

กำลังดำเนินการ Redis

เราใช้บุรุษไปรษณีย์เป็นไคลเอ็นต์ API เพื่อดูกลยุทธ์การแคชของเราในการดำเนินการ เอาล่ะ. มาดูการดำเนินการของ API กันทีละรายการ

  1. เราสร้างโพสต์บล็อกใหม่โดยใช้ /blogs เส้นทาง

เพิ่มประสิทธิภาพ Web API ของคุณด้วย Redis:คู่มือการเพิ่มประสิทธิภาพประสิทธิภาพที่ได้รับการพิสูจน์แล้ว การสร้างบล็อกโพสต์ใหม่

  1. จากนั้นเราจะดึงข้อมูลโพสต์ในบล็อกทั้งหมดที่เกี่ยวข้องกับผู้ใช้ tejaz

เพิ่มประสิทธิภาพ Web API ของคุณด้วย Redis:คู่มือการเพิ่มประสิทธิภาพประสิทธิภาพที่ได้รับการพิสูจน์แล้ว ดึงข้อมูลโพสต์ในบล็อกทั้งหมดสำหรับผู้ใช้ tejaz

  1. เราดึงข้อมูลโพสต์บล็อกทั้งหมดสำหรับผู้ใช้ tejaz อีกครั้งหนึ่ง

เพิ่มประสิทธิภาพ Web API ของคุณด้วย Redis:คู่มือการเพิ่มประสิทธิภาพประสิทธิภาพที่ได้รับการพิสูจน์แล้ว ดึงข้อมูลโพสต์ในบล็อกทั้งหมดสำหรับผู้ใช้ tejaz อีกครั้ง

คุณจะเห็นได้อย่างชัดเจนว่าเมื่อเราดึงข้อมูลจากแคช เวลาที่ใช้ลดลงจาก 409ms ถึง 24ms . ซึ่งจะช่วยเพิ่มประสิทธิภาพ API ของคุณโดยการลดเวลาที่ใช้ลงเกือบ 95%

นอกจากนี้เรายังเห็นได้อย่างชัดเจนว่าการดำเนินการแคชหมดอายุและอัปเดตเป็นไปตามที่คาดไว้

คุณสามารถค้นหาซอร์สโค้ดแบบเต็มได้ใน redis-express โฟลเดอร์ที่นี่

บทสรุป

การแคชเป็นขั้นตอนบังคับสำหรับแอปพลิเคชันที่เน้นประสิทธิภาพและต้องใช้ข้อมูลมาก Redis ช่วยให้คุณบรรลุเป้าหมายนี้ได้อย่างง่ายดายบนเว็บแอปพลิเคชันของคุณ มันเป็นเครื่องมือที่ทรงพลังอย่างยิ่ง และหากใช้อย่างถูกต้อง ก็จะสามารถมอบประสบการณ์ที่ยอดเยี่ยมให้กับนักพัฒนาและผู้ใช้ทั่วโลกได้อย่างแน่นอน

คุณสามารถดูชุดคำสั่ง Redis ทั้งหมดได้ที่นี่ คุณสามารถใช้มันกับ redis-cli เพื่อตรวจสอบข้อมูลแคชและกระบวนการแอปพลิเคชันของคุณ

ความเป็นไปได้ที่นำเสนอโดยเทคโนโลยีใดๆ นั้นไม่มีที่สิ้นสุดอย่างแท้จริง หากคุณมีข้อสงสัยใดๆ คุณสามารถติดต่อเราได้ที่ [LinkedIn](https://www.linkedin.com/in/tarique-ejaz/) .

ในระหว่างนี้ ให้เขียนโค้ดต่อไป

เรียนรู้การเขียนโค้ดฟรี หลักสูตรโอเพ่นซอร์สของ freeCodeCamp ช่วยให้ผู้คนมากกว่า 40,000 คนได้งานในตำแหน่งนักพัฒนา เริ่มต้น