Computer >> 컴퓨터 >  >> 프로그래밍 >> Redis

Redis로 웹 API 강화:입증된 성능 최적화 가이드

Redis로 웹 API 강화:입증된 성능 최적화 가이드

타리케 에자즈

성능은 소프트웨어를 설계할 때 고려해야 할 필수 매개변수입니다. 특히 뒤에서 일어나는 일이 중요합니다.

우리는 개발자이자 기술자로서 성능을 향상시키기 위해 여러 가지 조정과 구현을 채택합니다. 캐싱이 작동하는 곳입니다.

캐싱은 필요할 때마다 즉시 액세스할 수 있는 임시 저장 위치에 데이터나 파일을 저장하는 메커니즘으로 정의됩니다.

캐싱은 요즘 웹 애플리케이션에서 필수 요소가 되었습니다. Redis를 사용하면 Node.js와 MongoDB를 사용하여 구축된 웹 API를 강화할 수 있습니다.

Redis로 웹 API 강화:입증된 성능 최적화 가이드 "캐싱은 100~200년 후에도 여전히 매우 중요한 역할을 할 것 같습니다."

Redis:일반인의 개요

공식 문서에 따르면 Redis는 데이터베이스, 메시지 브로커 또는 캐시 저장소로 사용되는 메모리 내 데이터 구조 저장소로 정의됩니다. 문자열, 해시, 목록, 집합, 범위 쿼리가 포함된 정렬된 집합, 비트맵, 하이퍼로그 로그, 반경 쿼리가 포함된 지리공간 인덱스 및 스트림과 같은 데이터 구조를 지원합니다.

좋아요, 바로 거기에 꽤 많은 데이터 구조가 있습니다. 간단히 말해서, 지원되는 거의 모든 데이터 구조는 한 가지 형태의 문자열 또는 다른 형태로 압축될 수 있습니다. 구현을 진행하면서 더욱 명확해질 것입니다.

하지만 한 가지는 분명합니다. Redis는 강력하며 올바르게 사용하면 애플리케이션을 더 빠르게 만들 뿐만 아니라 놀랍도록 효율적으로 만들 수 있습니다. 충분한 이야기. 손을 더럽히자.

코드에 대해 이야기해보자

시작하기 전에 로컬 시스템에 Redis를 설정해야 합니다. 이 빠른 설정 프로세스에 따라 Redis를 시작하고 실행할 수 있습니다.

끝났나요? 시원한. 시작합시다. MongoDB Atlas의 인스턴스를 사용하여 데이터를 읽고 쓰는 Express로 작성된 간단한 애플리케이션이 있습니다.

/blogs에서는 두 가지 주요 API가 생성되었습니다. 경로 파일입니다.

...
// 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 함수 재정의

우리는 캐싱 논리를 재사용할 수 있기를 원합니다. 재사용이 가능할 뿐만 아니라 데이터베이스에 쿼리를 하기 전 첫 번째 체크포인트가 되기를 원합니다. 이는 몽구스 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을 통해 해시 키로 사용됩니다. 개체.

참고: 해시키는 해시 데이터 구조에 대한 식별자 역할을 하며, 일반 용어로 키-값 쌍 집합의 상위 키로 표시될 수 있습니다. 따라서 더 많은 수의 쿼리 값 집합을 캐싱할 수 있습니다. 여기에서 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를 설정했습니다. 사실이 되는 열쇠.

키가 false이면 기본 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 실행

우리는 캐싱 전략이 어떻게 작동하는지 확인하기 위해 Postman을 API 클라이언트로 사용합니다. 여기 있습니다. API 작업을 하나씩 실행해 보겠습니다.

  1. /blogs를 사용하여 새 블로그 게시물을 만듭니다. 경로

Redis로 웹 API 강화:입증된 성능 최적화 가이드 새 블로그 게시물 작성

  1. 그런 다음 사용자 tejaz와 관련된 모든 블로그 게시물을 가져옵니다.

Redis로 웹 API 강화:입증된 성능 최적화 가이드 사용자 tejaz에 대한 모든 블로그 게시물을 가져오는 중

  1. 사용자 tejaz의 모든 블로그 게시물을 가져옵니다. 한 번 더.

Redis로 웹 API 강화:입증된 성능 최적화 가이드 사용자 tejaz의 모든 블로그 게시물을 한 번 더 가져옵니다.

캐시에서 가져올 때 소요 시간이 409ms에서 단축되었음을 분명히 알 수 있습니다. 24ms . 이는 소요 시간을 거의 95% 줄여 API를 강화합니다.

또한 캐시 만료 및 업데이트 작업이 예상대로 작동하는 것을 확실히 확인할 수 있습니다.

redis-express에서 전체 소스 코드를 찾을 수 있습니다. 여기에 폴더를 넣으세요.

결론

캐싱은 성능 효율적이고 데이터 집약적인 애플리케이션의 필수 단계입니다. Redis를 사용하면 웹 애플리케이션에서 이를 쉽게 달성할 수 있습니다. 이는 매우 강력한 도구이며 올바르게 사용하면 개발자는 물론 사용자 모두에게 탁월한 경험을 제공할 수 있습니다.

여기에서 전체 redis 명령 세트를 찾을 수 있습니다. redis-cli와 함께 사용할 수 있습니다. 캐시 데이터 및 애플리케이션 프로세스를 모니터링합니다.

특정 기술이 제공하는 가능성은 정말 끝이 없습니다. 문의사항이 있으시면 [LinkedIn](https://www.linkedin.com/in/tarique-ejaz/)로 연락주세요. .

그동안 코딩을 계속하세요.

무료로 코딩을 배우세요. freeCodeCamp의 오픈 소스 커리큘럼은 40,000명 이상의 사람들이 개발자로 취업하는 데 도움을 주었습니다. 시작하세요