使用redis实现缓存_用下一个js实现一个简单的redis缓存

使用redis实现缓存

For most websites, the changing pieces don’t actually vary that often. That immutability makes them first-rate candidates to be cached to avoid the roundtrip to fetch the content from either a database, web service or third party end-points.

对于大多数网站,变化的部分实际上并不经常变化。 这种不变性使它们成为一流的候选对象,可以避免从数据库,Web服务或第三方端点获取内容的往返过程。

Consider a blog article that you wrote. It will be rather unlikely that this article will change over time more than once or twice, for instance, to adjust outdated information or to add a correction suggested by a reader. However, in a regular scenario, every time a visitor hits that blog page, your database will also get a hit. If your page gets 100,000 visitors, then you will get that many database-reads. And, granted, reads are generally cheap, however, in a scenario of high-performance, high-availability, each millisecond counts.

考虑您撰写的博客文章。 本文不太可能会随着时间的推移进行一次或两次以上的更改,例如,调整过时的信息或添加读者建议的更正。 但是,在正常情况下,每当访问者访问该博客页面时,您的数据库也会受到访问。 如果您的页面有100,000位访问者,那么您将获得那么多的数据库读取值。 而且,理所当然的是,读取通常很便宜,但是,在高性能,高可用性的情况下,每一毫秒都很重要。

Furthermore, if your data comes from a third-party solution –say a remote web service– then each roundtrip to access the information you require will add up, in terms of network bandwidth, how many concurrent connections your remote server can handle, or even just dealing with an error on a system that you can’t debug or fix quickly.

此外,如果您的数据来自第三方解决方案(例如远程Web服务),则每次访问所需的信息的往返都会增加网络带宽,远程服务器可以处理多少个并发连接,甚至只是在无法快速调试或修复的系统上处理错误。

Having an in-memory cache database can definitely help in optimization, unexpected datastores behaviors, consistency, and even save you a few bucks.

拥有内存中的缓存数据库绝对可以帮助优化,数据存储意外行为,保持一致性,甚至为您节省几美元。

实施内存缓存之前要记住的陷阱 (Pitfalls to keep in mind before implementing an in-memory cache)

At this point, I must make the caveat that there are a few pitfalls that you need to be aware of before going full throttle with Redis, Memcache or any other in-memory database alternative. Please review the following list of potential pitfalls:

在这一点上,我必须发出警告,在使用Redis,Memcache或任何其他内存数据库替代品进行全面控制之前,您需要注意一些陷阱。 请查看以下潜在陷阱列表:

Take notice, cache invalidation is #1
注意,缓存无效为#1
  • Cache invalidation is, still today, one of most challenging tasks there is in computer engineering. The other two, according to Jeff Atwood from CodingHorror and founder of StackOverflow.com are naming things and off-by-one errors. Really, it can potentially make you lose hours on a wild goose chase. For this, I recommend having a strong game plan (cache coherence protocol), a cache expiration strategy, avoid bloating your cache with unnecessary data (such as volatile information), and finally having a periodic cache validator tuned in such a way that makes sense for your product and traffic.

    直到今天,缓存失效仍然是计算机工程学中最具挑战性的任务之一 。 根据CodingHorror和StackOverflow.com的创始人Jeff Atwood的说法,另外两个是事物的名称和一个错误。 确实,它有可能使您在追逐野鹅时浪费时间。 为此,我建议您制定强有力的游戏计划(缓存一致性协议),缓存过期策略,避免不必要的数据(例如易失性信息)使缓存膨胀,并最终以合理的方式调整周期性的缓存验证器为您的产品和流量。

  • Be on the lookout for extra-greedy caching. This one is somewhat related to the previous and the next item. In essence, you want to be 100% in control of what chunks you are caching and for how long you will be storing said cache. A sporting event that happened last week will have the same score forever, it will not change. So you can, with confidence, store it and cache it. However if the game is live and happening right now you need to be able to update the score often, unobtrusively, probably not caching at all.

    注意超贪婪的缓存 。 这一点与上一项和下一项有关。 本质上,您希望100%地控制要缓存的块以及将存储该缓存多长时间。 上周发生的一项体育赛事将永远拥有相同的分数,并且不会改变。 因此,您可以放心地对其进行存储和缓存。 但是,如果游戏是实时运行的,那么您需要能够经常,不引人注目地更新分数,可能根本不缓存。

  • Not every project is a good candidate for caching. Ideally, you want to be very careful and strategic on what sections of your site you will be caching. It makes total sense to cache an article, while it would be a really bad idea to cache a comments section, private user information such as passwords or credit cards. As mentioned above, cache invalidation is hard as it is, and you do not want to go down the rabbit hole trying to figure out why your page is not refreshing properly.

    并非每个项目都适合缓存 。 理想情况下,您要对要缓存的站点部分非常谨慎并且具有战略意义。 缓存文章是完全有意义的,而缓存评论部分,私人用户信息(例如密码或信用卡)则是一个非常糟糕的主意。 如上所述,高速缓存失效本身就很难解决,并且您不希望陷入困境以试图弄清为什么页面无法正确刷新。

  • Avoid storing complex structures. While Redis is more than capable of storing Lists, Set, Hashes, and even Streams (among others), you may want to adhere to the KISS principle. As you introduce more intricacy within your data structures you will end up defeating the purpose of having a super fast off-the-shelve data from an in-memory database. Keeping it at key-value construction will generally pay off for what we want.

    避免存储复杂的结构 。 尽管Redis不仅具有存储列表,集合,哈希值甚至流(以及其他功能)的能力 ,但您可能需要遵守KISS原则 。 当您在数据结构中引入更多的复杂性时,您最终将无法实现从内存数据库中获得超快速的现成数据的目的。 保持键值构建通常会为我们想要的东西带来回报。

  • Maintenance. This one is true to practically every software you install and let live in the wild of the public Internet. You are going to need someone with a fair level of expertise on how to implement and maintain your system. Nevertheless, I should also mention that Redis is very well documented and creating a single node in-memory cache database is generally straight forward.

    维修保养 。 这实际上适用于您安装并在公共Internet上狂奔的所有软件。 您将需要一个在实施和维护系统方面具有相当专业知识的人员。 尽管如此,我还应该提到Redis的文档非常详尽,创建单节点内存缓存数据库通常很简单。

Having said the above, let’s try and create a very simple Next.js application that connects to HealthCare.gov open API.

综上所述,让我们尝试创建一个非常简单的Next.js应用程序,该应用程序连接到HealthCare.gov开放API。

步骤1:创建一个简单的Next.js应用程序 (Step 1: Creating a simple Next.js Application)

Using Next.js as our engine to connect to an API is quite a wise decision, since we can take advantage of its server-side rendering to fetch the results to serve a fully hydrated page.

将Next.js用作连接API的引擎是一个明智的决定,因为我们可以利用其服务器端呈现来获取结果以提供完全水化的页面。

Let’s start with an empty standard project:

让我们从一个空的标准项目开始:

npm init -y

Next, we’ll get Next.js and React dependencies:

接下来,我们将获得Next.js和React依赖项:

npm i -S next react react-dom

If your project runs with Typescript, install all the necessary libraries and types here as well. For the sake of simplicity we’ll make it non-Typescript, however the example could be transformed easily.

如果您的项目使用Typescript运行,请在此处也安装所有必需的库和类型。 为了简单起见,我们将其设置为非Typescript,但是可以轻松转换示例。

For the bare minimum Next.js project to run we need an index page. Create an index.jsx file under src/pages/index.jsx.

为了运行Next.js项目,我们需要一个索引页。 在src/pages/index.jsx下创建一个index.jsx文件。

For starters, let’s drop this code in this index file:

首先,让我们将此代码放在此索引文件中:

A very simple index page.
一个非常简单的索引页面。

Then to keep track of how your page is going you’ll need to add a script to call next dev from your package.json, like this:

然后,要跟踪页面的运行情况,您需要添加一个脚本,以从package.json调用next dev ,如下所示:

...
"scripts": {"dev": "next dev"
},
...

Now, if all goes well, you can run this command from the console:

现在,如果一切顺利,则可以从控制台运行以下命令:

npm run dev

And after a few seconds you should see this show up in the console: event — compiled successfully. This is your queue that you can go to your browser and navigate to http://localhost:3000. The output should be like this:

几秒钟后,您应该会在控制台中看到它: event — compiled successfully 。 这是您可以进入浏览器并导航至http:// localhost:3000的队列。 输出应如下所示:

Image for post
Your initial render should be like this.
您的初始渲染应该是这样的。

At this point you have a working environment of Next.js with a very simple home page. From here we will be using Next.js’s getStaticSideProps function to query the HealthCare.gov API. Let’s get these logic in place.

至此,您已经有了一个具有非常简单主页的Next.js工作环境。 在这里,我们将使用Next.js的getStaticSideProps函数查询HealthCare.gov API。 让我们将这些逻辑放在适当的位置。

We will create an uncomplicated articles index in a regular HTML Table. For now let’s add the skeleton of our app using with these placeholders in our index file:

我们将在常规HTML表中创建简单的文章索引。 现在,让我们在索引文件中使用这些占位符添加应用程序的框架:

Initial skeleton of our app.
我们应用的初始框架。

We will be using Next.js built-in tools for performance measurement. This is very easy to accomplish by creating a custom app file. For this create a file src/pages/_app.jsx and add the following content:

我们将使用Next.js内置工具进行性能评估。 通过创建自定义应用程序文件,这非常容易实现。 为此,创建一个文件src/pages/_app.jsx并添加以下内容:

Custom app file to capture the page stats.
自定义应用程序文件以捕获页面统计信息。

Now if we reload our index page in a browser you’d see this:

现在,如果我们在浏览器中重新加载索引页面,您将看到以下内容:

Image for post
Basic home page.
基本首页。

Not very exciting at this point. However, one important thing to notice is, in the console, you’ll see the item value: 15.56, which defines how long it took for the page to hydrate on Next.js. We will be referring to this value to measure the success of our performance optimizations. This number is expressed in milliseconds.

在这一点上还不是很令人兴奋。 但是,需要注意的一件事是,在控制台中,您将看到item value: 15.56 ,它定义了页面在Next.js上混合所需的时间。 我们将参考此值来衡量我们的性能优化是否成功。 此数字以毫秒为单位。

This page loads very fast but, of course, this is only a static page, no remote server fetching is involved at this point. Let’s move and interact with HealthCare.gov API.

该页面加载速度非常快,但是,当然,这只是一个静态页面,此时不涉及远程服务器获取。 让我们移动并与HealthCare.gov API进行交互。

步骤2:连接到远程API:HealthCare.gov (Step 2: Connecting to a remote API: HealthCare.gov)

HealthCare.gov has a very simple open API that allows us to fetch their content and, according to their documentation, “so that innovators, entrepreneurs, and partners can turn it into new products and services”. Let’s do that.

HealthCare.gov有一个非常简单的开放API,使我们能够获取他们的内容,并根据他们的文档 ,“ 以便创新者,企业家和合作伙伴可以将其转化为新产品和服务 ”。 来做吧。

We will connect to an end-point that will return a list of recent articles from HealthCare.gov. URL for this API is https://www.healthcare.gov/api/articles.json. The returned document is of type JSON, which is great news since Next.js understands JSON out of the box.

我们将连接到一个端点,该端点将返回HealthCare.gov的近期文章列表。 该API的URL为https://www.healthcare.gov/api/articles.json 。 返回的文档为JSON类型,这是个好消息,因为Next.js可以立即理解JSON。

Additionally, we will install a new package, react-uuid, to uniquely identify iterative items within JSX loops, it uses 128-bit numbers.

此外,我们将安装一个新的包react-uuid,以使用128位数字唯一标识JSX循环内的迭代项。

npm i -SD react-uuid

Let’s rewire our index.jsx file as follows:

让我们重新连接index.jsx文件,如下所示:

Connecting to HealthCare.gov API.
连接到HealthCare.gov API。

A few things are happening here. getStaticProps will fetch the information needed for the page before sending it to the client. This function also calls another function, fetchData, which is the one that does the heavy work of actually connecting to the API.

这里发生了一些事情。 getStaticProps将获取页面所需的信息,然后再将其发送给客户端。 此函数还调用另一个函数fetchData ,该函数完成了实际连接到API的繁重工作。

The other thing that is worth mentioning is that now the Home component receives a props argument, which is de-structured into a single articles variable. Then, this variable is used and iterated through in the returned JSX code. Notice the uuid() method call that will guarantee the uniqueness of each row when we run the loop.

值得一提的另一件事是,现在Home组件收到一个props参数,该参数被分解为单个articles变量。 然后,使用此变量并在返回的JSX代码中对其进行迭代。 注意,在运行循环时, uuid()方法调用将保证每一行的唯一性。

Then, our rendered page will end up looking like this:

然后,我们渲染的页面将最终看起来像这样:

Image for post
Rendered page connecting to HealthCare.gov API.
渲染页面连接到HealthCare.gov API。

Aha! We now have some real articles rendered. Notice our hydration value ballooned up to 53.49. This is, in all fairness, really fast, but let’s be clear that this is almost 4 times our previous static page value.

啊哈! 现在,我们呈现了一些真实的文章。 请注意,我们的水合值Swift增加到53.49。 公平地说,这确实非常快,但是我们需要清楚的是,这几乎是我们之前的静态页面价值的4倍。

It would make sense to store this response and keep it for a sensible amount of time so that we don’t have to request it from HealthCare.gov servers on each page reload. Let’s throw Redis into the mix.

存储此响应并将其保留合理的时间很有意义,这样我们就不必在每次重新加载页面时都从HealthCare.gov服务器请求它。 让我们将Redis投入使用。

第三步:安装Redis (Step 3: Installing Redis)

Redis is generally used as an in-memory cache for fast access to key-valued data. But Redis is so much more. It can also be used as a regular database, as a message broker, and even can work in replication in a cluster. For simplicity sake, in this article we will use Redis as an in-memory cache storage where we will save the responses from HealthCare.gov API.

Redis通常用作内存中的缓存,用于快速访问键值数据。 但是Redis不仅如此。 它也可以用作常规数据库,消息代理,甚至可以在集群中的复制中使用。 为简单起见,在本文中,我们将Redis用作内存中的缓存存储,我们将在其中保存HealthCare.gov API的响应。

A very easy way to install Redis is with Docker containers. Why? You only need to run this command:

安装Redis的一种非常简单的方法是使用Docker容器。 为什么? 您只需要运行以下命令:

docker run -p 6379:6379 --name my-redis -d redis

And you’re all done. You have a running instance of Redis locally and accessible at port 6379.

大功告成。 您在本地有一个正在运行的Redis实例,可通过端口6379访问。

Of course, be mindful that, if you kill the my-redis container, you will lose your data. This is not a big deal at the moment, since we are using Redis as an in-memory cache, meaning that in it we will be storing volatile data only.

当然,请注意,如果杀死my-redis容器,则会丢失数据。 目前,这并不是什么大问题,因为我们将Redis用作内存中的缓存,这意味着我们将仅在其中存储易失性数据。

步骤4:缓存API结果 (Step 4: Caching the API results)

The fun part is next. We will make our running Redis instance act as a “middle man” between HealthCare.gov API and our app. Before fetching data remotely, our app will confirm with Redis if the data is already living locally in-memory. This operation is often called checking for a ‘cache hit-miss’.

接下来是有趣的部分。 我们将使运行中的Redis实例充当HealthCare.gov API与我们的应用程序之间的“中间人”。 在远程获取数据之前,我们的应用程序将通过Redis确认数据是否已经存在于本地内存中。 此操作通常称为检查“高速缓存命中未命中”。

Let’s kick it off by installing a Redis npm package, there are a bunch around. The one we are going to use is the one plainly called redis. Also we are installing bluebird, a library that allows us to ‘promisify’ all redis methods (redis npm package will support promises in version 4).

让我们通过安装Redis npm软件包开始它,周围有很多。 我们将要使用的是一个俗称的redis 。 另外,我们正在安装bluebird ,这是一个允许我们“承诺”所有redis方法的库(redis npm软件包将在版本4中支持promise)。

npm i -S redis bluebird

So, we need to update our index file like this:

因此,我们需要像这样更新索引文件:

Our articles list, connecting first with Redis to check if we already have a response locally.
我们的文章列表,首先与Redis连接,以检查我们是否已经在本地得到响应。

Most exciting things are happening inside the getStaticProps method. With bluebird.promisifyAll we effectively create shadow methods with the ending -Async, plus all of them return a promise now. So, cache.get() now becomes cache.getAsync(), additionally you can attach the await keyword in front of it. Again, when redis packages version 4.0 gets released, you can skip this step.

最令人兴奋的事情发生在getStaticProps方法内部。 使用bluebird.promisifyAll我们有效地创建了以-Async结尾的影子方法,并且所有这些方法现在都返回了promise。 因此, cache.get()现在变为cache.getAsync() ,此外,您可以在其前面附加await关键字。 同样,当Redis软件包4.0版发布时,您可以跳过此步骤。

And then, a little logic which just checks if the key exists in our cache database; if it doesn’t (a cache miss) then we go and fetch it from the HealthCare.gov API servers. If the key is found (a cache hit), then we just go ahead and get that value, without fetching data from the remote server.

然后是一个逻辑,它只是检查密钥是否存在于我们的缓存数据库中; 如果没有( 缓存未命中 ),那么我们从HealthCare.gov API服务器中获取它。 如果找到了密钥( 命中了缓存 ),那么我们就继续获取该值,而无需从远程服务器获取数据。

A little bit JSON.stringify and JSON.parse acrobatics are needed, since we are storing the JSON response from the API as a plain string.

由于我们将来自API的JSON响应存储为纯字符串,因此需要一点JSON.stringifyJSON.parse杂技。

The output in the browser should be exactly the same:

浏览器中的输出应完全相同:

Image for post
Output of our articles list, from Redis cache.
我们的文章列表的输出,来自Redis缓存。

Notice how the first time you will be connecting to the remote API, subsequent reloads will not connect to the remote server and will be using the cached version of the response JSON.

请注意,第一次连接到远程API时,后续的重装将不会连接到远程服务器,并且将使用响应JSON的缓存版本。

In this particular example, we got a response with a Next.js hydration value of 30.57, which almost cuts in half the total hydration time when connecting to the remote server. Now, this is a very simple, almost naïve example, so there will be a big margin of variance in our hydration time. For a more robust and complete application the optimization and time saving will be more visible.

在此特定示例中,我们收到一个Next.js水化值为30.57的响应,当连接到远程服务器时,该值几乎减少了总水化时间的一半。 现在,这是一个非常简单,几乎是幼稚的示例,因此我们的水合作用时间会有很大的差异。 对于更健壮和完整的应用程序,优化和节省时间将更加明显。

This is all good and works, however, what if HealthCare.gov publishes a new article? Unfortunately, with our setup, this article will not be seen in our app, since our cache prevents us from refreshing our articles list with new publications. It is at this point that you want to try some good-ole cache invalidation techniques.

一切都很好,并且行得通,但是,如果HealthCare.gov发表新文章怎么办? 不幸的是,使用我们的设置后,由于我们的缓存阻止我们使用新的出版物刷新文章列表,因此无法在我们的应用中看到此文章。 正是在这一点上,您想尝试一些良好的缓存无效化技术。

步骤5:使缓存结果无效 (Step 5: Invalidating cache results)

As mentioned earlier, cache invalidation is one of the most challenging tasks you could encounter in computer science. There are several techniques, one more complex than the previous. For our case, we will go with a simple cache expiration technique.

如前所述,缓存失效是您在计算机科学中可能遇到的最具挑战性的任务之一。 有几种技术,一种比以前的技术复杂。 对于我们的情况,我们将使用一种简单的缓存过期技术。

For this, we will use a new key in our Redis database, to avoid messing up with the previous articles key. For this example we will use exp-articles.

为此,我们将在Redis数据库中使用一个新密钥,以避免与先前的articles密钥混淆。 在此示例中,我们将使用exp-articles

The other adjustment we need to do is to add the extra 'EX' and 60 * 60 as the third and fourth parameters of the cache.set() method. The 'EX' parameter just flags that this key will have an expiration date, while the last one multiplies 60 seconds by 60, in other words, this key will expire in 1 hour.

我们需要做的cache.set()调整是添加额外的'EX'60 * 60作为cache.set()方法的第三和第四参数。 'EX'参数只是标记该密钥将具有失效日期,而最后一个密钥将60秒乘以60,换句话说,此密钥将在1小时内失效。

With this, we have made it so that our cached key will expire every hour, and so, we will have fresh content from the remote server constantly. This expiration number should be a value that makes sense for your business without compromising the integrity of the content or the cache lifespan.

这样,我们做到了使缓存的密钥每隔一小时失效,因此,我们将不断从远程服务器获取新内容。 此到期号应该是对您的业务有意义的一个值,而不会损害内容的完整性或缓存的使用寿命。

其他优化任务 (Additional optimization tasks)

While you can store binary data in a Redis Database –which means you can cache images, scripts, PDF files–, you may want to use a specific tool to cache binary files, like as a high performance HTTP Accelerator, such as Varnish. If you use Varnish to speed up your binary files HTTP transfers, you can see improvements up to hundreds of times in faster downloads:

虽然您可以将二进制数据存储在Redis数据库中(这意味着您可以缓存图像,脚本,PDF文件),但是您可能希望使用特定的工具来缓存二进制文件,例如高性能HTTP Accelerator,例如Varnish 。 如果使用Varnish加快二进制文件HTTP传输的速度,则可以通过更快的下载速度看到多达数百次的改进:

“Varnish Cache is really, really fast. It typically speeds up delivery with a factor of 300–1000x, depending on your architecture.”

“ Varnish Cache确实非常快。 根据您的体系结构,通常可以将交付速度提高300-1000倍。”

Varnish Introduction

清漆简介

Having Redis Store targeted sections of your site –articles, quick relatively volatile statistics, session management– while at the same time implementing a Varnish Cache to return your assets –images, almost-static scripts or stylesheets, SVG graphics, videos– will more likely make your site extremely fast and available.

使用Redis Store定位到您网站的目标部分– 文章,快速相对不稳定的统计信息,会话管理 –同时实现Varnish Cache以返回资产– 图像,几乎静态的脚本或样式表,SVG图形,视频 –使您的网站变得非常快速且可用。

Another improvement you can make is that, both Redis and Varnish can work in clusters, meaning you can have several instances of your in-memory database. A common practice is to have one or several read-only instances while updates and writes will be handled by a master instance.

您可以做的另一个改进是,Redis和Varnish都可以在集群中工作,这意味着您可以拥有内存数据库的多个实例。 一种常见的做法是拥有一个或几个只读实例,而更新和写入将由一个主实例处理。

Given the current cloud-based-serverless world we are living in now, it would be fairly easy to kick-off a Redis Databases farm, dockerize each instance and allow them to be privately accessed by your app or web services.

考虑到我们现在生活的当前基于云的无服务器世界,启动Redis数据库场,对每个实例进行泊坞操作并允许您的应用程序或Web服务对其进行私有访问相当容易。

接下来的内容和进一步阅读 (What’s Next and Further Reading)

Learn more about performance and speeding up your website:

了解有关性能和加速网站的更多信息:

Further reading:

进一步阅读:

谢谢阅读! (Thanks for reading!)

Find me on LinkedIn, Medium, GitHub.

LinkedInMediumGitHub上找到我。

翻译自: https://medium.com/swlh/implementing-a-simple-redis-cache-with-next-js-5103976de188

使用redis实现缓存

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值