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

ลดเวลาโหลด v0.dev ลง 50% ด้วยการวางท่ออัตโนมัติ

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

ปัญหา

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

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

ไปป์ไลน์ปกติ

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

import { kv } from '@vercel/kv';
 
const pipeline = kv.pipeline();
 
pipeline.set("foo", "bar");
pipeline.get("foo");
 
const res = await pipeline.exec();
console.log(res); // ["OK", "bar"]

ข้อเสียของไปป์ไลน์

อย่างไรก็ตาม การใช้ไปป์ไลน์จะทำให้เกิดค่าใช้จ่ายที่สำคัญจากมุมมองของโปรแกรมเมอร์ Pipeline API แตกต่างจาก Redis API มาตรฐาน ทำให้การเปิดหรือปิดใช้งานเป็นงานที่ไม่สำคัญ

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

การวางท่ออัตโนมัติ

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

เรามาสำรวจว่าการวางท่ออัตโนมัติช่วยเพิ่มประสิทธิภาพสถานการณ์ทั่วไปได้อย่างไร:การดึงค่าสำหรับหลายคีย์:

const keys = ["key1", "key2", "key3"];
 
const values = await Promise.all(keys.map(key => kv.get(key)));

หากไม่มีไปป์ไลน์ โค้ดนี้จะส่งคำขอ HTTP สามคำขอไปยัง Redis อย่างไรก็ตาม เมื่อเปิดใช้งานการไปป์ไลน์อัตโนมัติ คำขอเหล่านี้จะถูกรวมเป็นคำขอ HTTP เดียว

ไปป์ไลน์อัตโนมัติสำหรับส่วนประกอบเซิร์ฟเวอร์ React แบบสำนวน

React Server Components (RSC) สามารถดึงข้อมูลของตนเองได้ ตัวอย่างทั่วไปคือองค์ประกอบทวีตที่อาจนำไปใช้ในลักษณะนี้

async function Tweet({id}) {
 const tweet = kv.get(`tweets:${id}`)
 return <div>{tweet.text}</div>
}

หากคุณเรียกส่วนประกอบนี้ในลักษณะวนซ้ำเช่น

{tweetIds.map(id => <Tweet id={id} />)}

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

มันทำงานอย่างไร

ไปป์ไลน์อัตโนมัติทำงานโดยคง 'ไปป์ไลน์ที่ใช้งานอยู่' ไว้ในเบื้องหลัง คำสั่งเพิ่มตัวเองเข้าไปในไปป์ไลน์และเรียกใช้ deferExecution :

private async deferExecution() {
 await Promise.resolve()
 return await Promise.resolve()
}

เมื่อโทรไปที่ deferExecution คำสั่งให้การควบคุมเธรดหลักของ Node.js GET ถัดไป คำสั่งในลำดับจะได้รับการควบคุมเธรดและดำเนินการดำเนินการต่อไป โดยทำสิ่งเดียวกันกับ GET แรกทุกประการ :การเพิ่มตัวเองลงในไปป์ไลน์ที่ใช้งานอยู่และให้การควบคุมเธรด

นี่คือลอจิกไปป์ไลน์อัตโนมัติเป็นรหัสหลอก:

let activePipeline: Pipeline;
let pipelinePromises: new WeakMap<Pipeline, Promise<Array<unknown>>>();
let commandIndex: number;
 
const executeCommand = (command) => {
 activePipeline = activePipeline || createNewPipeline();
 activePipeline.addCommand(command);
 commandIndex++;
 
 const pipelinePromise = deferExecution().then(() => {
 if (!pipelinePromises.has(activePipeline) {
 const pipelinePromise = pipeline.exec();
 
 pipelinePromises.set(pipeline, pipelinePromise);
 activePipeline = null;
 commandIndex = 0
 };
 return pipelinePromises.get(activePipeline)!;
 });
 
 const result = await pipelinePromise;
 return result[commandIndex];
};

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

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

มีโค้ดเบื้องหลังตรรกะการวางท่ออัตโนมัติอยู่ที่นี่

การวางท่ออัตโนมัติใน v0.dev

ผลกระทบของการวางท่ออัตโนมัติต่อประสิทธิภาพของ v0.dev เห็นได้จากการปรับปรุงในหน้า Landing Page หน้า Landing Page จะแสดงตัวอย่างจากคำค้นหาและรุ่นที่ผ่านมา ขั้นแรก ระบบจะดึงรายการที่จะแสดง จากนั้นทำการดึงข้อมูลแต่ละรายการสำหรับแต่ละรายการ ซึ่งส่งผลให้มีคำขอ Redis จำนวนมาก

ลดเวลาโหลด v0.dev ลง 50% ด้วยการวางท่ออัตโนมัติ

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

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

ในตอนแรกทีมงาน v0 ได้จัดส่งการวางท่ออัตโนมัติเพื่อเป็นการแฮ็กสำหรับตนเอง การวางท่ออัตโนมัติพร้อมใช้งานแล้วในเวอร์ชัน 1.31.3 ของไคลเอ็นต์ Redis ของ Upstash รวมถึงเวอร์ชัน 2.0 ของ Vercel KV