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
这些数据是使用获取请求来源的,使我们的服务器任务相当代表了现实世界的业务应用程序。 likes
和views
变量保留了我们在前端显示的这两个API的响应。
该页面的服务器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之类的记录服务来记录测量。
在捕获控制台日志中,您可以看到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_TOKEN
和UPSTASH_REDIS_REST_URL
值。如果您还没有一个,那就是quick to set up an Upstash account。将两个值(UPSTASH_REDIS_REST_TOKEN
和UPSTASH_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
}
}
在这里我们:
- 检查
view-count
的redis cached值是否已经有一个呼叫,并打电话给redis.get('view-count')
。 - 如果有一个,则返回缓存的值,否则从tinybird那里获得新的值。
-
Store and set an expiry通过致电
redis.set('view-count', value)
然后将redis.expire(TTL)
打电话给Redis Cache的新鲜值。这将值设置为的时间( ttl )值,之后数据被视为陈旧。我们将TTL
设置为四个小时。在此期间打电话给getTinybirdViews
,我们将击中Tinybird以获得新的价值。
刷新页面(几次),并将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。