Trong bài viết này về Upstash cho Redis và API hiệu suất, chúng tôi xem cách bạn có thể sử dụng Upstash cho Redis tốt nhất trong ứng dụng Deno. Upstash cho Redis là cơ sở dữ liệu không có máy chủ lý tưởng cho bộ nhớ đệm phía máy chủ . Một ứng dụng web mà tôi đang phát triển có điểm kém về Thời gian phản hồi của máy chủ ban đầu . Lighthouse đang báo cáo 500 ms . Bằng cách thêm bộ đệm Upstash, tôi đã giảm tốc độ này xuống dưới 150 ms và đã vượt qua cuộc kiểm toán. Phần khó khăn là không thêm bộ nhớ đệm; khi điều đó xảy ra, việc tìm ra nơi sử dụng bộ đệm là rất quan trọng. Chỉ bằng cách đo lường hiệu suất, tôi mới có thể xác định được điểm nghẽn để tăng hiệu suất với công việc tối thiểu. Chúng ta xem xét kỹ hơn cách đo lường hiệu suất trong bài viết này.
Dự án của tôi là ứng dụng web Deno Fresh. Deno có bản dựng và triển khai ngay lập tức. Điều này làm cho nó trở thành một môi trường mơ ước để làm việc nhằm tối ưu hóa. Vòng phản hồi ngắn. Bạn có thể mã hóa nội dung tối ưu hóa cục bộ, đẩy nó lên máy chủ và có thể kiểm tra trang web từ xa ngay lập tức.
Ngăn xếp
Ở đây, tôi sẽ nói về việc cải thiện hiệu suất bằng ứng dụng bộ xương. Nó sử dụng công cụ sau:
upstash_redis:mô-đun Deno để làm việc với Upstash cho Redis- Deno Fresh:Khung mới, sẵn sàng sản xuất để xây dựng các ứng dụng được hiển thị phía máy chủ (SSR) trong Deno
- ghi nhật ký không cần máy chủ:chúng tôi sử dụng bảng điều khiển ở đây, nhưng đối với ứng dụng đã triển khai của bạn, để truy cập các phép đo trực tiếp, bạn sẽ cần một dịch vụ như Logtail
Thiết lập
Điểm khác biệt chính giữa Node và Deno là cách bạn truy cập các mô-đun của bên thứ ba trong mã của mình. Deno hoạt động với URL và nhập bản đồ thay vì package.json tập tin. URL đầy đủ cho upstash_redis , ví dụ:là https://deno.land/x/upstash_redis@v1.20.0 . Để bắt đầu, hãy tạo một ứng dụng Deno Fresh mới.
deno run -A -r https://fresh.deno.dev upstash-redis-deno-perf Bạn có thể tự thiết lập Deno trên hệ thống của mình bằng một vài lệnh Terminal nếu bạn đang thử nó lần đầu tiên.
Bây giờ bạn có thể thêm Upstash vào import_map.json trong thư mục gốc của dự án:
{
"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/"
}
} @/xác định bí danh nhập để thuận tiện. Điều này cho phép bạn nhậpcomponents(trong thư mục gốc của dự án) bằng cách sử dụng@/componentsbất kể tệp nguồn của bạn nằm trong thư mục nào.$std/là bí danh của chúng tôi cho Thư viện chuẩn Deno có chức năng tiện ích để đọc.envcác tệp biến môi trường.upstash/cho phép chúng tôi truy cập thư viện Upstash cho Redis từ bất kỳ tệp nguồn TypeScript hoặc JavaScript nào trong dự án.
Upstash cho Redis và API hiệu suất:Ứng dụng Skeleton
Ứng dụng bộ xương sẽ kéo vào:
- lượt xem trang trong 28 ngày qua bằng cách sử dụng phân tích trang web từ Tinybird (Clickhouse không có máy chủ)
- lượt thích trang bằng cách sử dụng Webmentions
Những dữ liệu này có nguồn gốc bằng cách sử dụng các yêu cầu tìm nạp, làm cho các tác vụ máy chủ của chúng tôi khá đại diện cho một ứng dụng kinh doanh trong thế giới thực. likes và views các biến chứa phản hồi từ hai API này mà chúng tôi hiển thị ở giao diện người dùng.

Máy chủ handler mã cho trang đó trông giống như thế này:
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 });
},
};
Chúng tôi trích xuất pathname từ request đến đối tượng, sau đó sử dụng pathname trong các hàm trợ giúp dữ liệu và cuối cùng trả về các giá trị có nguồn gốc từ xa.
Để đạt hiệu quả, bạn có thể cơ cấu lại lệnh gọi hàm trợ giúp:
const [likes, views] = await Promise.all([
getWebmentionLikes(pathname),
getTinybirdViews({ days: 28 }),
]); API web hiệu suất JavaScript
Đo lường hiệu suất thường là bước đầu tiên trong quá trình tối ưu hóa. Bước giới hạn tỷ lệ không phải lúc nào cũng như bạn mong đợi. Nếu không đo lường, bạn có thể dễ dàng lãng phí thời gian và nguồn lực cho những giải pháp được cho là chưa tối ưu. API hiệu suất có thể giúp ích không ngừng ở đây. Trong phần này, chúng ta sẽ thấy cách có thể sử dụng nó để xác định vị trí hợp lý nhất khi sử dụng Upstash cho Redis trong ứng dụng.
window.performance cung cấp cho bạn quyền truy cập vào API Web Hiệu suất từ trình duyệt máy khách. Deno hỗ trợ sử dụng API Web trên máy chủ và do đó performance có sẵn trên toàn cầu trong mã phía máy chủ Deno của bạn. Dưới đây là hai phương pháp hiệu suất mà bạn sẽ muốn sử dụng:
performance.mark('your-mark-name'):tạo mộtPerformanceMarkđối tượng, đại diện cho một thời điểm. Tham số tên được sử dụng khi bạn tạo thước đo bằng dấu.performance.measure('your description', startMarkName, finishMarkName):tạo mộtPerformanceMeasuređối tượng. Điều này liên kết các dấu thời gian bắt đầu và kết thúc với một nhãn, hữu ích cho việc ghi nhật ký và tính toán thời gian diễn ra sự kiện.
timeEvent :Chức năng trợ giúp hiệu suất
Bây giờ chúng ta đã biết những điều cơ bản, hãy tạo một timeEvent chức năng. Nó sẽ lấy một mảng PerformanceMeasure các đối tượng và một hàm mà chúng ta muốn tính thời gian làm đầu vào. timeEvent sẽ tạo dấu bắt đầu, gọi hàm đã truyền vào, sau đó tạo ngay dấu kết thúc. Cuối cùng, nó sẽ biến đổi mảng PerformanceMeasure các đối tượng mà nó nhận được làm đầu vào, thêm đối tượng mới. Đây là mã từ 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;
}
Chức năng này mang tính chung chung, tuy nhiên đối với ứng dụng bộ xương, EventReturnType sẽ luôn là một con số.
Cập nhật mã máy chủ với số đo
Chúng ta có thể sử dụng timeEvent mới trong trình xử lý, sau đó bắt đầu chạy so sánh. Đây là mã xử lý máy chủ được cập nhật:
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 hàm trả về kết quả của hàm mà chúng ta truyền vào nó. Thuộc tính này cho phép chúng ta gói gọn hai hàm trợ giúp dữ liệu mà chúng ta đã có trước đây trong timeEvent cuộc gọi. Chúng tôi đang chạy cục bộ ở đây. Đối với ứng dụng sản xuất, bạn cần chạy các phép đo trên trang web trực tiếp của mình vì các kết nối đường trục trên máy chủ của bạn sẽ hoạt động khác với các kết nối cục bộ. Khi chạy trên máy chủ, hãy sử dụng dịch vụ ghi nhật ký như Logtail để ghi lại các biện pháp.

Trong quá trình ghi lại nhật ký bảng điều khiển, bạn có thể thấy PerformanceMeasure đối tượng cung cấp các giá trị đo sau (mà chúng tôi đã đề cập trước đó):
- tên
- thời gian bắt đầu
- thời lượng (tính bằng mili giây)
Lý tưởng nhất là chúng ta cần cả giá trị dữ liệu (lượt thích và lượt xem) để hiển thị ứng dụng. Ở đây, họ mất khoảng thời gian tương tự nhau (2 giây). Nếu một hàm chạy chậm hơn nhiều so với hàm kia, chúng tôi sẽ thêm Upstash cho bộ nhớ đệm Redis để có giá trị chậm nhất. Thay vào đó, ở đây chúng tôi sẽ thêm nó vào cả hai. Lưu ý, trong quá trình sản xuất, chúng tôi cần ít nhất vài trăm điểm dữ liệu. Sau đó, chúng tôi có thể sử dụng thước đo tổng hợp như giá trị trung bình hoặc P90 để so sánh.
Thêm Upstash cho Redis vào Mã trợ giúp Analytics
Hãy xem mã để thêm Upstash cho Redis vào chức năng trợ giúp phân tích. Chức năng trợ giúp Webmentions cũng tương tự và bạn có thể xem đầy đủ trong kho GitHub (đường dẫn liên kết bên dưới).
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,
});
Đầu tiên, chúng ta phải khởi tạo đối tượng Upstash cho Redis. Bạn cần UPSTASH_REDIS_REST_TOKEN và UPSTASH_REDIS_REST_URL các giá trị từ bảng điều khiển Upstash. Việc thiết lập tài khoản Upstash rất nhanh chóng nếu bạn chưa có tài khoản. Thêm cả hai giá trị (UPSTASH_REDIS_REST_TOKEN và UPSTASH_REDIS_REST_URL ) tới .env tập tin trong thư mục gốc của dự án.
Lưu ý ở dòng đầu tiên ở trên, chúng tôi sử dụng upstash key từ bản đồ nhập mà chúng tôi đã thiết lập trước đó. Điều này thuận tiện hơn việc thêm URL nhập đầy đủ vào mỗi tệp TypeScript. Khi phiên bản tiếp theo của upstash_redis khả dụng, bạn sẽ chỉ phải cập nhật số phiên bản ở một nơi.
Đây là getTinybirdViews chức năng:
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
}
} Chúng tôi đây:
- Kiểm tra xem đã có giá trị được lưu trong bộ nhớ đệm Upstash cho Redis cho
view-counthay chưa với cuộc gọi tớiredis.get('view-count'). - Trả về giá trị được lưu trong bộ nhớ đệm nếu có, nếu không thì nhận giá trị mới từ Tinybird.
- Lưu trữ và đặt thời hạn cho giá trị mới trong bộ nhớ đệm Upstash for Redis bằng cách gọi
redis.set('view-count', value)thìredis.expire(TTL). Điều này đặt giá trị với thời gian tồn tại (TTL ) giá trị mà sau đó dữ liệu được coi là cũ. Chúng tôi đặtTTLđến bốn giờ ở đây. Đối với cuộc gọi đếngetTinybirdViewssau khoảng thời gian đó, chúng tôi sẽ đạt được Tinybird để có được giá trị mới.

Làm mới trang (một vài lần) với Upstash for Redis được tích hợp vào cả hai truy vấn dữ liệu, chúng tôi thấy mọi thứ tăng tốc. Ở đây, chúng tôi đã giảm từ chỉ hơn 2 giây xuống còn khoảng 0,29 giây. Đừng đọc quá nhiều vào các con số ở đây vì chúng tôi đang chạy cục bộ và chúng tôi cũng không có nhiều điểm dữ liệu. Hãy dùng thử dịch vụ này trên ứng dụng của riêng bạn để xem bạn có thể đạt được những lợi ích gì.
Upstash cho Redis và API hiệu suất:Kết thúc
Tại đây, chúng tôi đã thấy cách bạn có thể sử dụng API hiệu suất JavaScript để giúp hướng dẫn các quyết định về nơi cần tập trung nỗ lực tối ưu hóa. Ngoài ra, chúng tôi còn biết cách bạn có thể triển khai Upstash cho Redis trong Deno Fresh. Cuối cùng, chúng tôi phát hiện ra rằng Deno hỗ trợ phía máy chủ API Web, làm phẳng quá trình học tập. Lợi ích to lớn khác của Deno trong việc tối ưu hóa là triển khai ngay lập tức, cung cấp cho bạn vòng phản hồi ngắn và cho phép bạn tiến hành nhanh hơn khi tinh chỉnh hiệu suất.
Tôi hy vọng bạn thấy việc đọc về Upstash for Redis và API hiệu suất có giá trị. Nếu bạn chưa quen với Deno hoặc Deno Fresh, hãy xem một số nội dung tôi đã tạo khi bắt đầu với Deno. Bạn có thể mở mã đầy đủ của ứng dụng trên GitHub.