REDIS和Performance API的升级:CACHE值
#serverless #性能 #redis #deno

REDIS和JavaScript Performance API的Upstash

在REDIS的Upstash和Performance API的本文中,我们看到了如何最好地用DENO应用中的Redis使用Upstash。 redis的Upstash 是无服务器数据库的理想选择服务器端缓存。我正在使用的网络应用程序在初始服务器响应时间上得分很差。 Lighthouse报道了 500 MS 。通过添加一个上施加缓存,我将其降至150 ms 以下,并通过了审核。困难的部分不是添加缓存。碰巧的是,锻炼在哪里使用缓存至关重要。只有通过测量性能,我才能识别瓶颈以最少的工作来提高性能。我们仔细研究了本文的性能测量。

我的项目是Deno Fresh Web应用程序。 Deno具有即时构建和部署。这使其成为一个梦想的环境,以进行优化。反馈循环很短。您可以在本地对优化进行编码,将其推向服务器,并能够立即测试远程站点。

在这里,我将谈论使用骨骼应用程序提高性能。它使用以下工具:

  • koude0:用于与REDIS的Upstash的DENO模块
  • Deno Fresh:一个用于构建服务器端渲染的新的,准备生产的框架(SSR)应用程序
  • 无服务器记录:我们在这里使用该控制台,但是对于您已部署的应用程序,要访问实时测量结果,您将需要诸如LogTail
  • 之类的服务

设置

节点和DENO之间的关键区别是您如何访问代码中的第三方模块。 DENO使用URL和导入地图而不是package.json文件。例如,upstash_redis的完整URL是https://deno.land/x/upstash_redis@v1.20.0。首先创建一个新的DenoFreshâ应用。

deno run -A -r https://fresh.deno.dev upstash-redis-deno-perf

如果您是第一次尝试的话,您可以set up Deno itself on your system with a few Terminal commands

现在,您可以在项目root目录中的import_map.json添加到upstash:

{
  "imports": {
    "@/": "./",
    "$fresh/": "https://deno.land/x/fresh@1.1.2/",
    // ...TRUNCATED
    "$std/": "https://deno.land/std@0.177.0/",
    "upstash/": "https://deno.land/x/upstash_redis@v1.20.0/",
  }
}
  • 为方便起见,@/定义了进口别名。这使您可以使用@/components导入components(在项目root目录中),无论您的源文件是哪个文件夹。
  • $std/是我们对Deno Standard Library的别名,它具有用于读取.env环境变量的实用程序函数。
  • upstash/使我们可以从项目中的任何打字稿或JavaScript源文件访问Redis库的UpStash。

REDIS和Performance API的Upstash:Skeleton应用程序

骨架应用将吸入:

  • 在过去28天内使用Tinybird(无服务器Clickhouse)的Web Analytics在过去28天内的页面视图
  • 页面喜欢使用Webmentions

这些数据是使用获取请求来源的,使我们的服务器任务相当代表了现实世界的业务应用程序。 likesviews变量保留了我们在前端显示的这两个API的响应。

Upstash for Redis and Performance API: Skeleton app show likes and views counts

该页面的服务器handler代码看起来像这样:

export const handler: Handlers<Data> = {
  async GET(request, context) {
    const { url } = request;
    const { pathname } = new URL(url);

    const likes = await getWebmentionLikes(pathname);
    const views = await getTinybirdViews({ days: 28 });

    return context.render({ likes, views });
  },
};

我们从传入的request对象提取pathname,然后在数据助手功能中使用pathname,最后返回远程来源的值。

为提高效率,可以重组对辅助功能的呼叫:

    const [likes, views] = await Promise.all([
      getWebmentionLikes(pathname),
      getTinybirdViews({ days: 28 }),
    ]);

JavaScript性能Web API

绩效测量通常应该是优化的第一步。限制步骤并不总是您期望的。在不进行测量的情况下,您可以轻松地将时间和资源花在事实上是次优的解决方案。性能API在这里无能为力。在本节中,我们看到了如何使用它来确定在应用程序中使用的upis的最有意义的地方。

window.performance使您可以从客户端浏览器中访问性能Web API。 DENO支持使用服务器上的Web API,因此performance在DENO服务器端代码中全球可用。这是您要使用的两种性能方法:

  • performance.mark('your-mark-name'):创建一个PerformanceMark对象,代表一个时间点。当您创建具有标记的度量时,请使用名称参数。
  • performance.measure('your description', startMarkName, finishMarkName):创建一个PerformanceMeasure对象。这将开始和结束时间标记与标签相关联,可用于记录和计算活动需要多长时间。

timeEvent:性能助手功能

现在我们知道了基础知识,让我们创建一个timeEvent函数。它将采用一个PerformanceMeasure对象和我们想要时间作为输入的函数。 timeEvent将创建一个起始标记,调用传递的功能中,然后立即创建一个完成标记。最后,它将将接收到的输入的PerformanceMeasure对象变异,并添加新的对象。这是utils/performance.ts的代码:

export async function timeEvent<EventReturnType>(
  eventFunction: () => Promise<EventReturnType>,
  {
    description,
    performanceMeasures,
  }: { description: string; performanceMeasures: PerformanceMeasure[] },
): Promise<EventReturnType> {
  // prepare
  const startName = `${description}-started`;
  const finishName = `${description}-finished`;

  // time
  performance.mark(startName);
  const result = await eventFunction();
  performance.mark(finishName);

  // record
  performanceMeasures.push(
    performance.measure(description, startName, finishName),
  );

  return result;
}

该功能是通用的,尽管对于骨架应用,EventReturnType始终是一个数字。

使用测量更新服务器代码

我们可以在处理程序中使用新的timeEvent功能,然后开始运行比较。这是更新的服务器处理程序:

import type { Handlers, PageProps } from "$fresh/server.ts";
import "$std/dotenv/load.ts";  /* included for visibility here, typically you 
 can import once for project in `dev.ts` */
import { timeEvent } from "@/utils/performance.ts";

// ...TRUNCATED

export const handler: Handlers<Data> = {
  async GET(request, context) {
    // ...TRUNCATED

    const performanceMeasures: PerformanceMeasure[] = [];

    const [likes, views] = await Promise.all([
      timeEvent<number>(() => getWebmentionLikes(pathname), {
        description: "web-mention-likes",
        performanceMeasures,
      }),
      timeEvent<number>(() => getTinybirdViews({ days: 28 }), {
        description: "analytics-views",
        performanceMeasures,
      }),
    ]);

    // Replace with a serverless logging service for production
    console.log({ performanceMeasures });

    return context.render({ likes, views });
  },
};

timeEvent函数返回我们传递到的函数的结果。此属性使我们只需包装timeEvent调用中以前拥有的两个数据助手功能即可。我们在这里本地运行。对于生产应用程序,您需要在实时网站上运行测量值,因为服务器上的backbone connections的性能与本地设备的性能不同。在服务器上运行时,请使用logtail之类的记录服务来记录测量。

Upstash for Redis and Performance API: screen capture shows measures for analytics views and web-mention-likes, duration for each is just over 2 seconds

在捕获控制台日志中,您可以看到PerformanceMeasure对象提供以下度量值(我们之前提到的):

  • 名称
  • 开始时间
  • 持续时间(以毫秒为单位)

理想情况下,我们需要数据值(喜欢和视图)才能渲染应用程序。在这里,他们与彼此相同的时间(2秒)。如果一个函数的运行速度比另一个函数慢得多,我们将添加升级以重新缓存以获得最慢的值。相反,在这里我们将其添加到两者中。请注意,在生产中,我们至少需要几百个数据点。然后,我们可以使用均值或P90等汇总度量进行比较。

将REDIS的Upist添加到分析辅助代码中

让我们查看用于将重新施加到分析辅助功能的上施加的代码。 Webmentions助手功能相似,您可以在GitHub存储库中完整看到它(下面的链接)。

import { Redis } from "upstash/mod.ts";

const UPSTASH_REDIS_REST_TOKEN = Deno.env.get("UPSTASH_REDIS_REST_TOKEN");
if (typeof UPSTASH_REDIS_REST_TOKEN === "undefined") {
  console.error("env `UPSTASH_REDIS_REST_TOKEN` must be set");
}
const UPSTASH_REDIS_REST_URL = Deno.env.get("UPSTASH_REDIS_REST_URL");
if (typeof UPSTASH_REDIS_REST_URL === "undefined") {
  console.error("env `UPSTASH_REDIS_REST_URL` must be set");
}

const redis = new Redis({
  token: UPSTASH_REDIS_REST_TOKEN,
  url: UPSTASH_REDIS_REST_URL,
});

首先,我们必须初始化redis对象的升级。您需要从船上控制台中的UPSTASH_REDIS_REST_TOKENUPSTASH_REDIS_REST_URL值。如果您还没有一个,那就是quick to set up an Upstash account。将两个值(UPSTASH_REDIS_REST_TOKENUPSTASH_REDIS_REST_URL)添加到项目root目录中的.env文件中。

在第一行中注意,上面,我们使用了我们之前设置的导入映射中的upstash键。这比在每个打字稿中添加完整的导入URL更方便。当upstash_redis的下一个版本可用时,您只需要在一个地方更新版本号。

这是getTinybirdViews函数:

export async function getTinybirdViews({
  days,
}: {
  days: number;
}): Promise<number> {
  try {
    // ...TRUNCATED

    const cachedCount = (await redis.get("view-count")) as number | null;

    if (cachedCount != null) {
      return cachedCount;
    }

    // ...TRUNCATED
    const response = await fetch(
      `https://api.tinybird.co/v0/pipes/${TINYBIRD_PIPE_NAME}.json?${params.toString()}`,
      {
        headers: {
          Authorization: `Bearer ${TINYBIRD_TOKEN}`,
        },
      },
    );

    const {
      data: [{ count_sessions: count = -1 }],
    } = await response.json();

    if (typeof count === "number" && count > 0) {
      const CACHE_TTL_SECONDS = 14_400;
      await redis.set("view-count", count);
      await redis.expire("view-count", CACHE_TTL_SECONDS);
    }

    return count;
  } catch (error: unknown) {
    // ...TRUNCATED
  }
}

在这里我们:

  1. 检查view-count的redis cached值是否已经有一个呼叫,并打电话给redis.get('view-count')
  2. 如果有一个,则返回缓存的值,否则从tinybird那里获得新的值。
  3. Store and set an expiry通过致电redis.set('view-count', value)然后将redis.expire(TTL)打电话给Redis Cache的新鲜值。这将值设置为的时间 ttl )值,之后数据被视为陈旧。我们将TTL设置为四个小时。在此期间打电话给getTinybirdViews,我们将击中Tinybird以获得新的价值。

Upstash for Redis and Performance API: screen capture shows measures for analytics views and web-mention-likes, duration for each is around 0.29 seconds

刷新页面(几次),并将REDIS的Upis升级为两个数据查询,我们看到了速度。在这里,我们从仅2秒钟下降到大约0.29秒。当我们在本地运行时,请不要对这里的数字进行太多阅读,而且我们也没有很多数据点。在您自己的应用程序上尝试服务,以查看您可以实现的收益。

REDIS和Performance API的Upstash:结束

在这里,我们看到了如何使用JavaScript Performance API来帮助指导您在何处关注优化工作的决策。除此之外,我们还看到了如何在DeNo Fresh中实施Redis的Upstash。最终,我们发现DeNo支持Web API服务器端,从而使学习曲线变平。 DENO优化的另一个巨大好处是即时部署,为您提供短暂的反馈循环,并让您在进行微调时更快地移动。

我希望您能够阅读有关Redis的Upis和Performance API有价值的信息。如果您是Deno或Deno Fresh的新手,请看一下content I have created on getting started with Deno。您可以open the full code for the app on GitHub