乐闻世界logo
搜索文章和话题

Deno

Deno 是一个简单、现代且安全的 JavaScript 和 TypeScript 运行时环境,由 Node.js 的创始人 Ryan Dahl 开发,目标是解决 Node.js 的一些设计缺陷。Deno 于2020年正式发布,它内置了 V8 JavaScript 引擎和 Tokio 事件循环,提供了一系列默认的安全限制,并支持 TypeScript 的运行而无需额外的转译步骤。
Deno
查看更多相关内容
什么是 Deno?它和 Node.js 有什么区别?Deno 是由 Node.js 创始人 Ryan Dahl 开发的现代化 JavaScript 和 TypeScript 运行时环境。Deno 的设计目标是解决 Node.js 在早期设计上的一些缺陷,并提供更安全、更简洁的开发体验。 ## 核心特性 Deno 具有以下核心特性: 1. **默认安全**:Deno 默认情况下脚本无法访问文件系统、网络或环境变量,除非显式授予相应权限 2. **内置 TypeScript 支持**:Deno 原生支持 TypeScript,无需额外的转译步骤 3. **去中心化模块系统**:使用 URL 导入模块,不依赖 package.json 和 node_modules 4. **单一可执行文件**:Deno 只有一个可执行文件,无需复杂的安装过程 5. **内置工具链**:包含格式化、测试、linting、打包等开发工具 6. **标准库**:提供经过审核的标准库,确保代码质量 ## 与 Node.js 的主要区别 | 特性 | Deno | Node.js | |------|------|---------| | 模块系统 | ES Modules (URL 导入) | CommonJS + ES Modules | | 包管理 | 无 package.json,直接使用 URL | npm + package.json | | 安全性 | 默认安全,需要显式授权 | 默认无安全限制 | | TypeScript | 原生支持 | 需要配置和转译 | | API 设计 | Promise-based | 回调和 Promise 混合 | | 架构 | Rust + V8 | C++ + V8 | ## 权限系统 Deno 的权限系统是其安全特性的核心: ```bash # 授予文件系统读取权限 deno run --allow-read script.ts # 授予网络访问权限 deno run --allow-net script.ts # 授予所有权限(不推荐) deno run --allow-all script.ts ``` ## 模块导入示例 ```typescript // 直接从 URL 导入模块 import { serve } from "https://deno.land/std/http/server.ts"; // 从本地文件导入 import { myFunction } from "./utils.ts"; // 使用标准库 import { assertEquals } from "https://deno.land/std/testing/asserts.ts"; ``` ## 典型应用场景 - Web 服务器和 API 开发 - 命令行工具开发 - 自动化脚本 - 微服务架构 - 实时应用(WebSocket) Deno 特别适合需要快速开发、安全性和现代化开发体验的项目。
服务端 · 2月21日 16:10
Deno 如何支持 TypeScript?Deno 原生支持 TypeScript,这是其最吸引人的特性之一。与 Node.js 需要配置 TypeScript 编译器不同,Deno 可以直接运行 TypeScript 文件,无需任何额外的转译步骤。 ## TypeScript 支持概述 Deno 内置了 TypeScript 编译器,使用 V8 引擎的 TypeScript 支持和 swc 编译器来提供快速的类型检查和转译。 ## 直接运行 TypeScript ### 1. 基本用法 ```typescript // app.ts interface User { id: number; name: string; email: string; } function createUser(user: User): User { console.log(`Creating user: ${user.name}`); return user; } const newUser: User = { id: 1, name: "John Doe", email: "john@example.com" }; createUser(newUser); ``` 运行: ```bash deno run app.ts ``` ### 2. 类型检查 Deno 会在运行时进行类型检查: ```bash # 运行时进行类型检查 deno run app.ts # 仅类型检查(不执行) deno check app.ts # 类型检查所有文件 deno check **/*.ts ``` ## 类型定义 ### 1. 使用 Deno 内置类型 Deno 提供了丰富的内置类型定义: ```typescript // 文件系统操作 const content: string = await Deno.readTextFile("./data.txt"); // HTTP 服务器 import { serve } from "https://deno.land/std@0.208.0/http/server.ts"; const handler = async (req: Request): Promise<Response> => { return new Response("Hello World"); }; ``` ### 2. 第三方库类型 从 URL 导入的模块通常包含类型定义: ```typescript import { Application, Router } from "https://deno.land/x/oak@v12.6.1/mod.ts"; const app = new Application(); const router = new Router(); ``` ### 3. 自定义类型定义 如果模块没有类型定义,可以创建自定义类型: ```typescript // types.d.ts declare module "https://example.com/module.js" { export function doSomething(): void; export const value: number; } ``` ## 配置选项 ### 1. tsconfig.json Deno 支持使用 `tsconfig.json` 进行配置: ```json { "compilerOptions": { "strict": true, "esModuleInterop": true, "skipLibCheck": true }, "include": ["src/**/*"], "exclude": ["node_modules"] } ``` ### 2. 命令行选项 ```bash # 禁用类型检查(不推荐) deno run --no-check app.ts # 使用特定的 tsconfig deno run --config=tsconfig.json app.ts # 启用严格模式 deno run app.ts # 默认启用严格模式 ``` ## 类型检查策略 ### 1. 本地类型检查 ```bash # 检查本地文件 deno check app.ts # 检查整个项目 deno check src/**/*.ts ``` ### 2. 远程类型检查 Deno 会缓存远程模块的类型定义: ```bash # 重新下载并检查远程类型 deno check --remote app.ts # 清除类型缓存 deno cache --reload app.ts ``` ## 实际应用示例 ### 1. 类型安全的 API 服务器 ```typescript // api-server.ts interface User { id: number; name: string; email: string; } interface CreateUserRequest { name: string; email: string; } const users: Map<number, User> = new Map(); function createUser(request: CreateUserRequest): User { const id = users.size + 1; const user: User = { id, ...request }; users.set(id, user); return user; } import { serve } from "https://deno.land/std@0.208.0/http/server.ts"; const handler = async (req: Request): Promise<Response> => { const url = new URL(req.url); if (req.method === "POST" && url.pathname === "/users") { try { const body: CreateUserRequest = await req.json(); const user = createUser(body); return new Response(JSON.stringify(user), { headers: { "Content-Type": "application/json" } }); } catch (error) { return new Response("Invalid request", { status: 400 }); } } return new Response("Not Found", { status: 404 }); }; await serve(handler, { port: 8000 }); ``` ### 2. 类型安全的数据库操作 ```typescript // database.ts interface DatabaseRecord { id: string; createdAt: Date; updatedAt: Date; } interface Post extends DatabaseRecord { title: string; content: string; authorId: string; } class Database<T extends DatabaseRecord> { private records: Map<string, T> = new Map(); async create(record: Omit<T, keyof DatabaseRecord>): Promise<T> { const id = crypto.randomUUID(); const now = new Date(); const newRecord: T = { id, createdAt: now, updatedAt: now, ...record } as T; this.records.set(id, newRecord); return newRecord; } async findById(id: string): Promise<T | undefined> { return this.records.get(id); } } const postDB = new Database<Post(); const newPost = await postDB.create({ title: "Hello Deno", content: "TypeScript support is amazing!", authorId: "user-1" }); ``` ### 3. 类型安全的工具函数 ```typescript // utils.ts type AsyncFunction<T = any> = (...args: any[]) => Promise<T>; async function retry<T>( fn: AsyncFunction<T>, maxAttempts: number = 3, delay: number = 1000 ): Promise<T> { let lastError: Error | undefined; for (let attempt = 1; attempt <= maxAttempts; attempt++) { try { return await fn(); } catch (error) { lastError = error as Error; if (attempt < maxAttempts) { await new Promise(resolve => setTimeout(resolve, delay)); } } } throw lastError; } async function fetchWithTimeout( url: string, timeout: number = 5000 ): Promise<Response> { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout); try { const response = await fetch(url, { signal: controller.signal }); clearTimeout(timeoutId); return response; } catch (error) { clearTimeout(timeoutId); throw error; } } ``` ## 类型检查性能优化 ### 1. 增量类型检查 Deno 会缓存类型检查结果: ```bash # 首次运行会进行完整类型检查 deno run app.ts # 后续运行会使用缓存 deno run app.ts ``` ### 2. 选择性类型检查 ```bash # 只检查修改的文件 deno check app.ts # 排除某些文件 deno check --exclude=**/*.test.ts src/**/*.ts ``` ## 与 Node.js 的对比 | 特性 | Deno | Node.js | |------|------|---------| | TypeScript 支持 | 原生支持 | 需要配置 tsc | | 运行方式 | 直接运行 .ts 文件 | 需要先编译为 .js | | 类型检查 | 运行时检查 | 编译时检查 | | 配置复杂度 | 零配置 | 需要 tsconfig.json | | 性能 | 使用 swc 快速编译 | 使用 tsc 较慢 | | 类型定义 | 自动加载 | 需要 @types 包 | ## 最佳实践 1. **始终使用类型**:充分利用 TypeScript 的类型系统 2. **启用严格模式**:使用 `strict: true` 获得更好的类型安全 3. **使用接口定义数据结构**:明确 API 和数据模型的类型 4. **避免 any 类型**:使用 unknown 或具体类型替代 5. **定期运行类型检查**:使用 `deno check` 确保代码质量 6. **利用泛型**:编写可复用的类型安全代码 Deno 的 TypeScript 支持使得开发者能够享受类型安全的好处,而无需复杂的配置和构建流程,大大提高了开发效率和代码质量。
服务端 · 2月21日 16:10
Deno 的测试框架如何使用?Deno 的测试框架提供了强大而简洁的测试功能,使得编写和运行测试变得简单高效。了解 Deno 的测试系统对于保证代码质量至关重要。 ## 测试框架概述 Deno 内置了测试框架,无需安装额外的测试库。测试文件通常以 `_test.ts` 或 `.test.ts` 结尾。 ## 基本测试 ### 1. 编写第一个测试 ```typescript // math_test.ts import { assertEquals } from "https://deno.land/std@0.208.0/testing/asserts.ts"; function add(a: number, b: number): number { return a + b; } Deno.test("add function adds two numbers", () => { assertEquals(add(1, 2), 3); assertEquals(add(-1, 1), 0); assertEquals(add(0, 0), 0); }); ``` 运行测试: ```bash deno test math_test.ts ``` ### 2. 测试异步代码 ```typescript // async_test.ts import { assertEquals } from "https://deno.land/std@0.208.0/testing/asserts.ts"; async function fetchData(id: number): Promise<{ id: number; name: string }> { // 模拟异步操作 await new Promise(resolve => setTimeout(resolve, 100)); return { id, name: `Item ${id}` }; } Deno.test("fetchData returns correct data", async () => { const result = await fetchData(1); assertEquals(result.id, 1); assertEquals(result.name, "Item 1"); }); ``` ## 断言函数 ### 1. 常用断言 ```typescript import { assertEquals, assertNotEquals, assertExists, assertStrictEquals, assertStringIncludes, assertArrayIncludes, assertMatch, assertThrows, assertRejects, } from "https://deno.land/std@0.208.0/testing/asserts.ts"; Deno.test("assertEquals examples", () => { assertEquals(1 + 1, 2); assertEquals("hello", "hello"); assertEquals({ a: 1 }, { a: 1 }); }); Deno.test("assertNotEquals examples", () => { assertNotEquals(1, 2); assertNotEquals("hello", "world"); }); Deno.test("assertExists examples", () => { assertExists("hello"); assertExists(42); assertExists({ key: "value" }); }); Deno.test("assertStrictEquals examples", () => { assertStrictEquals(1, 1); assertStrictEquals("hello", "hello"); // assertStrictEquals({ a: 1 }, { a: 1 }); // 会失败,因为引用不同 }); Deno.test("assertStringIncludes examples", () => { assertStringIncludes("hello world", "world"); assertStringIncludes("Deno is awesome", "Deno"); }); Deno.test("assertArrayIncludes examples", () => { assertArrayIncludes([1, 2, 3], [2]); assertArrayIncludes(["a", "b", "c"], ["b", "c"]); }); Deno.test("assertMatch examples", () => { assertMatch("hello@deno.com", /@/); assertMatch("12345", /^\d+$/); }); ``` ### 2. 测试异常 ```typescript import { assertThrows, assertRejects } from "https://deno.land/std@0.208.0/testing/asserts.ts"; function divide(a: number, b: number): number { if (b === 0) { throw new Error("Division by zero"); } return a / b; } Deno.test("divide throws error on zero", () => { assertThrows( () => divide(10, 0), Error, "Division by zero" ); }); async function asyncDivide(a: number, b: number): Promise<number> { if (b === 0) { throw new Error("Division by zero"); } return a / b; } Deno.test("asyncDivide rejects on zero", async () => { await assertRejects( () => asyncDivide(10, 0), Error, "Division by zero" ); }); ``` ## 测试配置 ### 1. 测试选项 ```typescript Deno.test({ name: "test with options", fn: () => { // 测试代码 }, permissions: { read: true, net: true, }, sanitizeOps: true, sanitizeResources: true, sanitizeExit: true, }); ``` ### 2. 超时设置 ```typescript Deno.test({ name: "slow test with timeout", fn: async () => { await new Promise(resolve => setTimeout(resolve, 2000)); }, timeout: 5000, // 5秒超时 }); ``` ### 3. 忽略测试 ```typescript Deno.test({ name: "ignored test", ignore: true, fn: () => { // 这个测试会被跳过 }, }); // 或者使用 only 只运行特定测试 Deno.test({ name: "only this test", only: true, fn: () => { // 只运行这个测试 }, }); ``` ## 测试组织 ### 1. 测试套件 ```typescript // user_test.ts import { assertEquals } from "https://deno.land/std@0.208.0/testing/asserts.ts"; class User { constructor( public id: number, public name: string, public email: string ) {} greet(): string { return `Hello, ${this.name}!`; } isValid(): boolean { return this.id > 0 && this.name.length > 0 && this.email.includes("@"); } } Deno.test("User class - constructor", () => { const user = new User(1, "John", "john@example.com"); assertEquals(user.id, 1); assertEquals(user.name, "John"); assertEquals(user.email, "john@example.com"); }); Deno.test("User class - greet", () => { const user = new User(1, "John", "john@example.com"); assertEquals(user.greet(), "Hello, John!"); }); Deno.test("User class - isValid", () => { const validUser = new User(1, "John", "john@example.com"); assertEquals(validUser.isValid(), true); const invalidUser = new User(0, "", "invalid"); assertEquals(invalidUser.isValid(), false); }); ``` ### 2. 测试钩子 ```typescript Deno.test({ name: "test with setup and teardown", async fn() { // Setup const db = await connectDatabase(); try { // Test const user = await db.createUser({ name: "John" }); assertEquals(user.name, "John"); } finally { // Teardown await db.close(); } }, sanitizeOps: false, sanitizeResources: false, }); ``` ## Mock 和 Stub ### 1. 简单的 Mock ```typescript // api_test.ts import { assertEquals } from "https://deno.land/std@0.208.0/testing/asserts.ts"; interface APIClient { fetchData(id: number): Promise<any>; } class RealAPIClient implements APIClient { async fetchData(id: number): Promise<any> { const response = await fetch(`https://api.example.com/data/${id}`); return response.json(); } } class MockAPIClient implements APIClient { private data: Map<number, any> = new Map(); setData(id: number, data: any) { this.data.set(id, data); } async fetchData(id: number): Promise<any> { return this.data.get(id); } } Deno.test("MockAPIClient returns mock data", async () => { const mockClient = new MockAPIClient(); mockClient.setData(1, { id: 1, name: "Test" }); const result = await mockClient.fetchData(1); assertEquals(result.id, 1); assertEquals(result.name, "Test"); }); ``` ### 2. 使用 Spy ```typescript // spy_test.ts import { assertEquals } from "https://deno.land/std@0.208.0/testing/asserts.ts"; class Spy<T extends (...args: any[]) => any> { calls: Array<{ args: Parameters<T>; result: ReturnType<T> }> = []; wrap(fn: T): T { return ((...args: Parameters<T>) => { const result = fn(...args); this.calls.push({ args, result }); return result; }) as T; } callCount(): number { return this.calls.length; } calledWith(...args: Parameters<T>): boolean { return this.calls.some(call => JSON.stringify(call.args) === JSON.stringify(args) ); } } function processData(data: string, callback: (result: string) => void) { const result = data.toUpperCase(); callback(result); } Deno.test("processData calls callback", () => { const spy = new Spy<(result: string) => void>(); const callbackSpy = spy.wrap((result: string) => { console.log(result); }); processData("hello", callbackSpy); assertEquals(spy.callCount(), 1); assertEquals(spy.calledWith("HELLO"), true); }); ``` ## 集成测试 ### 1. HTTP 服务器测试 ```typescript // server_test.ts import { assertEquals } from "https://deno.land/std@0.208.0/testing/asserts.ts"; import { serve } from "https://deno.land/std@0.208.0/http/server.ts"; async function startTestServer(): Promise<{ port: number; stop: () => Promise<void> }> { const handler = async (req: Request): Promise<Response> => { const url = new URL(req.url); if (url.pathname === "/") { return new Response("Hello, World!"); } if (url.pathname === "/api/users") { return new Response(JSON.stringify([{ id: 1, name: "John" }]), { headers: { "Content-Type": "application/json" }, }); } return new Response("Not Found", { status: 404 }); }; const server = await serve(handler, { port: 0 }); // 使用随机端口 const port = (server.addr as Deno.NetAddr).port; return { port, stop: async () => { server.shutdown(); }, }; } Deno.test("HTTP server responds to root", async () => { const { port, stop } = await startTestServer(); try { const response = await fetch(`http://localhost:${port}/`); assertEquals(response.status, 200); assertEquals(await response.text(), "Hello, World!"); } finally { await stop(); } }); Deno.test("HTTP server returns JSON for /api/users", async () => { const { port, stop } = await startTestServer(); try { const response = await fetch(`http://localhost:${port}/api/users`); assertEquals(response.status, 200); assertEquals(response.headers.get("Content-Type"), "application/json"); const data = await response.json(); assertEquals(Array.isArray(data), true); assertEquals(data[0].name, "John"); } finally { await stop(); } }); ``` ### 2. 文件系统测试 ```typescript // file_test.ts import { assertEquals, assertExists } from "https://deno.land/std@0.208.0/testing/asserts.ts"; async function createTestDirectory(): Promise<string> { const testDir = await Deno.makeTempDir(); return testDir; } Deno.test("file operations", async () => { const testDir = await createTestDirectory(); try { const filePath = `${testDir}/test.txt`; const content = "Hello, Deno!"; // 写入文件 await Deno.writeTextFile(filePath, content); // 读取文件 const readContent = await Deno.readTextFile(filePath); assertEquals(readContent, content); // 检查文件存在 const stat = await Deno.stat(filePath); assertExists(stat); assertEquals(stat.isFile, true); } finally { // 清理测试目录 await Deno.remove(testDir, { recursive: true }); } }); ``` ## 测试覆盖率 ### 1. 生成覆盖率报告 ```bash # 运行测试并生成覆盖率 deno test --coverage=coverage # 生成覆盖率报告 deno coverage coverage --lcov --output=coverage.lcov # 在浏览器中查看覆盖率 deno coverage coverage --html ``` ### 2. 覆盖率配置 ```json // deno.json { "compilerOptions": { "strict": true }, "test": { "include": ["src/**/*_test.ts", "tests/**/*.ts"], "exclude": ["node_modules/"] } } ``` ## 测试最佳实践 ### 1. 测试命名 ```typescript // 好的测试名称 Deno.test("add returns sum of two numbers", () => {}); Deno.test("divide throws error when dividing by zero", () => {}); Deno.test("fetchData returns user object when given valid ID", () => {}); // 不好的测试名称 Deno.test("test add", () => {}); Deno.test("it works", () => {}); ``` ### 2. AAA 模式(Arrange-Act-Assert) ```typescript Deno.test("calculateTotal returns correct total", () => { // Arrange - 准备测试数据 const items = [ { price: 10, quantity: 2 }, { price: 5, quantity: 3 }, ]; const expectedTotal = 35; // Act - 执行被测试的操作 const total = calculateTotal(items); // Assert - 验证结果 assertEquals(total, expectedTotal); }); ``` ### 3. 测试隔离 ```typescript Deno.test("user creation", async () => { // 每个测试使用独立的数据库连接 const db = await createTestDatabase(); try { const user = await db.createUser({ name: "John" }); assertEquals(user.name, "John"); } finally { // 清理资源 await db.close(); } }); ``` ### 4. 测试数据构建器 ```typescript // test-builder.ts class UserBuilder { private user: Partial<User> = { id: 1, name: "John Doe", email: "john@example.com", }; withId(id: number): UserBuilder { this.user.id = id; return this; } withName(name: string): UserBuilder { this.user.name = name; return this; } withEmail(email: string): UserBuilder { this.user.email = email; return this; } build(): User { return this.user as User; } } // 使用构建器创建测试数据 Deno.test("user validation", () => { const user = new UserBuilder() .withId(1) .withName("John") .withEmail("john@example.com") .build(); assertEquals(user.isValid(), true); }); ``` ## 运行测试 ### 1. 基本命令 ```bash # 运行所有测试 deno test # 运行特定测试文件 deno test math_test.ts # 监听模式(文件变化时自动运行) deno test --watch # 并行运行测试 deno test --parallel # 显示详细输出 deno test --verbose # 只运行失败的测试 deno test --fail-fast # 允许所有权限 deno test --allow-all ``` ### 2. 过滤测试 ```bash # 运行匹配模式的测试 deno test --filter="user" # 运行特定测试 deno test --filter="add function" ``` ## 最佳实践总结 1. **测试独立性**:每个测试应该独立运行,不依赖其他测试 2. **清晰的命名**:测试名称应该清楚地描述测试的内容 3. **AAA 模式**:使用 Arrange-Act-Assert 模式组织测试代码 4. **适当的断言**:使用最合适的断言函数 5. **测试边界情况**:测试正常情况和边界情况 6. **保持简单**:测试应该简单、快速、易于理解 7. **定期运行**:在 CI/CD 中定期运行测试 8. **覆盖率监控**:监控测试覆盖率,确保代码质量 Deno 的测试框架提供了强大而简洁的功能,通过合理使用这些功能,可以构建高质量的测试套件,确保代码的可靠性和可维护性。
服务端 · 2月21日 16:08
Deno 的任务系统如何工作?Deno 的任务系统(Task System)提供了一种在后台运行异步任务的方式,类似于浏览器中的 Web Workers。这个功能对于执行 CPU 密集型任务或需要并行处理的场景非常有用。 ## 任务系统概述 Deno 的任务系统允许你创建独立的工作线程,这些线程可以并行执行代码,不会阻塞主线程。每个任务都有自己的内存空间,通过消息传递与主线程通信。 ## 基本用法 ### 1. 创建简单任务 ```typescript // main.ts const worker = new Worker(new URL("./worker.ts", import.meta.url).href, { type: "module", }); worker.postMessage({ type: "start", data: 42 }); worker.onmessage = (event) => { console.log("Received from worker:", event.data); worker.terminate(); }; worker.onerror = (error) => { console.error("Worker error:", error); }; ``` ```typescript // worker.ts self.onmessage = (event) => { console.log("Worker received:", event.data); const result = event.data.data * 2; self.postMessage({ type: "result", data: result }); }; ``` 运行: ```bash deno run --allow-read main.ts ``` ### 2. 使用 Promise 封装 Worker ```typescript // main.ts function runWorker<T>(workerFile: string, data: any): Promise<T> { return new Promise((resolve, reject) => { const worker = new Worker(new URL(workerFile, import.meta.url).href, { type: "module", }); worker.postMessage(data); worker.onmessage = (event) => { resolve(event.data); worker.terminate(); }; worker.onerror = (error) => { reject(error); worker.terminate(); }; }); } async function main() { try { const result = await runWorker<number>("./worker.ts", { number: 10 }); console.log("Result:", result); } catch (error) { console.error("Error:", error); } } main(); ``` ```typescript // worker.ts self.onmessage = (event) => { const { number } = event.data; // 模拟耗时计算 let result = 0; for (let i = 0; i < number * 1000000; i++) { result += i; } self.postMessage(result); }; ``` ## 实际应用示例 ### 1. 图像处理 ```typescript // image-processor.ts self.onmessage = async (event) => { const { imageData, operation } = event.data; let result; switch (operation) { case "grayscale": result = applyGrayscale(imageData); break; case "invert": result = applyInvert(imageData); break; case "blur": result = applyBlur(imageData); break; default: throw new Error(`Unknown operation: ${operation}`); } self.postMessage({ result }); }; function applyGrayscale(data: Uint8ClampedArray): Uint8ClampedArray { const result = new Uint8ClampedArray(data.length); for (let i = 0; i < data.length; i += 4) { const r = data[i]; const g = data[i + 1]; const b = data[i + 2]; const gray = 0.299 * r + 0.587 * g + 0.114 * b; result[i] = gray; result[i + 1] = gray; result[i + 2] = gray; result[i + 3] = data[i + 3]; } return result; } function applyInvert(data: Uint8ClampedArray): Uint8ClampedArray { const result = new Uint8ClampedArray(data.length); for (let i = 0; i < data.length; i += 4) { result[i] = 255 - data[i]; result[i + 1] = 255 - data[i + 1]; result[i + 2] = 255 - data[i + 2]; result[i + 3] = data[i + 3]; } return result; } function applyBlur(data: Uint8ClampedArray): Uint8ClampedArray { // 简化的模糊算法 return data; // 实际实现会更复杂 } ``` ```typescript // main.ts import { runWorker } from "./worker-utils.ts"; async function processImage(imagePath: string) { const imageData = await Deno.readFile(imagePath); const grayscaleResult = await runWorker<Uint8ClampedArray>( "./image-processor.ts", { imageData, operation: "grayscale" } ); await Deno.writeFile(`${imagePath}.grayscale.png`, grayscaleResult); const invertResult = await runWorker<Uint8ClampedArray>( "./image-processor.ts", { imageData, operation: "invert" } ); await Deno.writeFile(`${imagePath}.invert.png`, invertResult); console.log("Image processing complete"); } processImage("input.png"); ``` ### 2. 并行数据处理 ```typescript // data-processor.ts self.onmessage = (event) => { const { data, chunkIndex, totalChunks } = event.data; console.log(`Processing chunk ${chunkIndex}/${totalChunks}`); // 模拟数据处理 const processed = data.map((item: number) => ({ value: item, processed: true, timestamp: Date.now(), })); self.postMessage({ chunkIndex, processed }); }; ``` ```typescript // main.ts import { runWorker } from "./worker-utils.ts"; async function processDataInParallel(data: number[], chunkSize: number = 1000) { const chunks: number[][] = []; for (let i = 0; i < data.length; i += chunkSize) { chunks.push(data.slice(i, i + chunkSize)); } console.log(`Processing ${chunks.length} chunks in parallel`); const promises = chunks.map((chunk, index) => runWorker("./data-processor.ts", { data: chunk, chunkIndex: index, totalChunks: chunks.length, }) ); const results = await Promise.all(promises); // 合并结果 const processedData = results .sort((a, b) => a.chunkIndex - b.chunkIndex) .flatMap((result) => result.processed); console.log(`Processed ${processedData.length} items`); return processedData; } // 生成测试数据 const testData = Array.from({ length: 10000 }, (_, i) => i); processDataInParallel(testData, 1000); ``` ### 3. 文件批量处理 ```typescript // file-processor.ts self.onmessage = async (event) => { const { filePath, operation } = event.data; try { const content = await Deno.readTextFile(filePath); let result: string; switch (operation) { case "uppercase": result = content.toUpperCase(); break; case "lowercase": result = content.toLowerCase(); break; case "reverse": result = content.split("").reverse().join(""); break; case "count": result = String(content.length); break; default: throw new Error(`Unknown operation: ${operation}`); } self.postMessage({ filePath, result, success: true }); } catch (error) { self.postMessage({ filePath, error: error.message, success: false }); } }; ``` ```typescript // main.ts import { runWorker } from "./worker-utils.ts"; async function processFilesInParallel( files: string[], operation: string ) { console.log(`Processing ${files.length} files with operation: ${operation}`); const promises = files.map((file) => runWorker("./file-processor.ts", { filePath: file, operation }) ); const results = await Promise.all(promises); results.forEach((result) => { if (result.success) { console.log(`✓ ${result.filePath}: ${result.result.substring(0, 50)}...`); } else { console.error(`✗ ${result.filePath}: ${result.error}`); } }); return results; } // 获取当前目录所有 .txt 文件 const files = Array.from(Deno.readDirSync(".")) .filter((entry) => entry.isFile && entry.name.endsWith(".txt")) .map((entry) => entry.name); processFilesInParallel(files, "uppercase"); ``` ### 4. 密码哈希计算 ```typescript // password-hasher.ts self.onmessage = async (event) => { const { password, algorithm = "SHA-256" } = event.data; const encoder = new TextEncoder(); const data = encoder.encode(password); const hashBuffer = await crypto.subtle.digest(algorithm, data); const hashArray = Array.from(new Uint8Array(hashBuffer)); const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join(""); self.postMessage({ password, hash: hashHex, algorithm }); }; ``` ```typescript // main.ts import { runWorker } from "./worker-utils.ts"; async function hashPasswords(passwords: string[]) { console.log(`Hashing ${passwords.length} passwords`); const promises = passwords.map((password) => runWorker("./password-hasher.ts", { password }) ); const results = await Promise.all(promises); results.forEach((result) => { console.log(`${result.password}: ${result.hash}`); }); return results; } const passwords = ["password123", "admin", "user123", "test"]; hashPasswords(passwords); ``` ## 高级用法 ### 1. Worker 池 ```typescript // worker-pool.ts export class WorkerPool { private workers: Worker[] = []; private taskQueue: Array<{ data: any; resolve: (value: any) => void; reject: (error: any) => void }> = []; private maxWorkers: number; constructor(workerFile: string, maxWorkers: number = 4) { this.maxWorkers = maxWorkers; for (let i = 0; i < maxWorkers; i++) { const worker = new Worker(new URL(workerFile, import.meta.url).href, { type: "module", }); worker.onmessage = (event) => { const task = this.taskQueue.shift(); if (task) { task.resolve(event.data); this.assignNextTask(worker); } }; worker.onerror = (error) => { const task = this.taskQueue.shift(); if (task) { task.reject(error); this.assignNextTask(worker); } }; this.workers.push(worker); } } private assignNextTask(worker: Worker) { const task = this.taskQueue[0]; if (task) { worker.postMessage(task.data); } } async execute(data: any): Promise<any> { return new Promise((resolve, reject) => { this.taskQueue.push({ data, resolve, reject }); // 查找空闲的 worker const idleWorker = this.workers.find((w) => !this.taskQueue.includes(w)); if (idleWorker) { this.assignNextTask(idleWorker); } }); } terminate() { this.workers.forEach((worker) => worker.terminate()); this.workers = []; } } ``` 使用 Worker 池: ```typescript // main.ts import { WorkerPool } from "./worker-pool.ts"; const pool = new WorkerPool("./data-processor.ts", 4); async function processWithPool(data: number[]) { const promises = data.map((item) => pool.execute({ data: item })); const results = await Promise.all(promises); pool.terminate(); return results; } processWithPool([1, 2, 3, 4, 5, 6, 7, 8]); ``` ### 2. 错误处理和重试 ```typescript // worker-with-retry.ts export async function runWorkerWithRetry<T>( workerFile: string, data: any, maxRetries: number = 3 ): Promise<T> { let lastError: Error | undefined; for (let attempt = 1; attempt <= maxRetries; attempt++) { try { return await runWorker<T>(workerFile, data); } catch (error) { lastError = error as Error; console.error(`Attempt ${attempt} failed: ${error.message}`); if (attempt < maxRetries) { await new Promise((resolve) => setTimeout(resolve, 1000 * attempt)); } } } throw lastError; } ``` ## 最佳实践 1. **合理使用 Worker**:只在 CPU 密集型任务中使用 Worker 2. **控制并发**:限制同时运行的 Worker 数量 3. **正确清理**:使用完成后终止 Worker 4. **错误处理**:妥善处理 Worker 错误 5. **消息大小**:避免传递过大的消息 6. **类型安全**:使用 TypeScript 确保消息类型正确 Deno 的任务系统为并行处理和后台任务提供了强大的支持,能够显著提高应用程序的性能和响应能力。
服务端 · 2月21日 16:08
Deno 的标准库有哪些常用模块?Deno 的标准库(Standard Library)是一组经过精心设计、测试和维护的模块,为开发者提供了高质量、可复用的代码。标准库涵盖了从文件系统操作到网络编程的各个方面,是 Deno 生态系统的重要组成部分。 ## 标准库概述 Deno 标准库托管在 `https://deno.land/std/`,所有模块都经过严格的代码审查和测试,确保代码质量和安全性。 ### 版本管理 标准库使用语义化版本控制,建议在导入时指定版本: ```typescript // 推荐:指定版本 import { serve } from "https://deno.land/std@0.208.0/http/server.ts"; // 不推荐:使用最新版本 import { serve } from "https://deno.land/std/http/server.ts"; ``` ## 主要模块 ### 1. HTTP 模块 #### HTTP 服务器 ```typescript import { serve } from "https://deno.land/std@0.208.0/http/server.ts"; const handler = async (req: Request): Promise<Response> => { const url = new URL(req.url); if (url.pathname === "/") { return new Response("Hello, Deno!", { headers: { "content-type": "text/plain" }, }); } return new Response("Not Found", { status: 404 }); }; await serve(handler, { port: 8000 }); ``` #### HTTP 客户端 ```typescript import { fetch } from "https://deno.land/std@0.208.0/http/fetch.ts"; const response = await fetch("https://api.example.com/data"); const data = await response.json(); console.log(data); ``` ### 2. 文件系统模块 #### 文件操作 ```typescript import { ensureDir, ensureFile } from "https://deno.land/std@0.208.0/fs/mod.ts"; // 确保目录存在 await ensureDir("./data/uploads"); // 确保文件存在 await ensureFile("./config.json"); // 复制文件 import { copy } from "https://deno.land/std@0.208.0/fs/copy.ts"; await copy("source.txt", "destination.txt"); // 移动文件 import { move } from "https://deno.land/std@0.208.0/fs/move.ts"; await move("old.txt", "new.txt"); ``` #### 遍历目录 ```typescript import { walk } from "https://deno.land/std@0.208.0/fs/walk.ts"; for await (const entry of walk("./src")) { console.log(entry.path); if (entry.isFile) { console.log(`File: ${entry.name}`); } else if (entry.isDirectory) { console.log(`Directory: ${entry.name}`); } } ``` ### 3. 路径模块 ```typescript import { join, basename, dirname, extname, resolve } from "https://deno.land/std@0.208.0/path/mod.ts"; const path = "/home/user/documents/file.txt"; console.log(basename(path)); // "file.txt" console.log(dirname(path)); // "/home/user/documents" console.log(extname(path)); // ".txt" console.log(join("/home", "user", "docs")); // "/home/user/docs" console.log(resolve("./src", "file.ts")); // 绝对路径 ``` ### 4. 编码模块 #### Base64 编码 ```typescript import { encodeBase64, decodeBase64 } from "https://deno.land/std@0.208.0/encoding/base64.ts"; const text = "Hello, Deno!"; const encoded = encodeBase64(text); console.log(encoded); // "SGVsbG8sIERlbm8h" const decoded = decodeBase64(encoded); console.log(decoded); // "Hello, Deno!" ``` #### Hex 编码 ```typescript import { encodeHex, decodeHex } from "https://deno.land/std@0.208.0/encoding/hex.ts"; const data = new TextEncoder().encode("Hello"); const hex = encodeHex(data); console.log(hex); // "48656c6c6f" const decoded = decodeHex(hex); console.log(new TextDecoder().decode(decoded)); // "Hello" ``` ### 5. 测试模块 ```typescript import { assertEquals, assertThrows } from "https://deno.land/std@0.208.0/testing/asserts.ts"; Deno.test("assertEquals example", () => { assertEquals(1 + 1, 2); assertEquals("hello", "hello"); }); Deno.test("assertThrows example", () => { assertThrows( () => { throw new Error("Test error"); }, Error, "Test error" ); }); ``` ### 6. 日志模块 ```typescript import { getLogger, setup, handlers } from "https://deno.land/std@0.208.0/log/mod.ts"; await setup({ handlers: { console: new handlers.ConsoleHandler("INFO"), }, loggers: { default: { level: "INFO", handlers: ["console"], }, }, }); const logger = getLogger(); logger.info("Application started"); logger.warning("This is a warning"); logger.error("An error occurred"); ``` ### 7. UUID 模块 ```typescript import { v4 as uuidv4, v5 as uuidv5 } from "https://deno.land/std@0.208.0/uuid/mod.ts"; const id1 = uuidv4(); console.log(id1); // 生成随机 UUID const id2 = uuidv5("hello", uuidv4()); console.log(id2); // 基于命名空间生成 UUID ``` ### 8. 日期时间模块 ```typescript import { format, parse } from "https://deno.land/std@0.208.0/datetime/mod.ts"; const now = new Date(); const formatted = format(now, "yyyy-MM-dd HH:mm:ss"); console.log(formatted); // "2024-01-15 10:30:45" const parsed = parse("2024-01-15", "yyyy-MM-dd"); console.log(parsed); // Date 对象 ``` ### 9. 颜色模块 ```typescript import { red, green, blue, bold } from "https://deno.land/std@0.208.0/fmt/colors.ts"; console.log(red("Error message")); console.log(green("Success message")); console.log(blue("Info message")); console.log(bold("Important message")); ``` ### 10. 异步模块 ```typescript import { delay, retry } from "https://deno.land/std@0.208.0/async/mod.ts"; // 延迟执行 await delay(1000); // 等待 1 秒 // 重试机制 const result = await retry(async () => { const response = await fetch("https://api.example.com"); if (!response.ok) throw new Error("Request failed"); return response.json(); }, { maxAttempts: 3, minTimeout: 1000, }); ``` ### 11. 流模块 ```typescript import { copy } from "https://deno.land/std@0.208.0/streams/copy.ts"; const file = await Deno.open("input.txt"); const output = await Deno.open("output.txt", { create: true, write: true }); await copy(file, output); file.close(); output.close(); ``` ### 12. 命令行模块 ```typescript import { parseArgs } from "https://deno.land/std@0.208.0/cli/parse_args.ts"; const args = parseArgs(Deno.args, { boolean: ["verbose", "help"], string: ["output"], default: { verbose: false }, }); console.log(args); // 运行: deno run script.ts --verbose --output=result.txt // 输出: { _: [], verbose: true, help: false, output: "result.txt" } ``` ## 实际应用示例 ### 文件上传服务器 ```typescript import { serve } from "https://deno.land/std@0.208.0/http/server.ts"; import { ensureDir } from "https://deno.land/std@0.208.0/fs/mod.ts"; import { extname } from "https://deno.land/std@0.208.0/path/mod.ts"; const UPLOAD_DIR = "./uploads"; await ensureDir(UPLOAD_DIR); const handler = async (req: Request): Promise<Response> => { const url = new URL(req.url); if (req.method === "POST" && url.pathname === "/upload") { const formData = await req.formData(); const file = formData.get("file") as File; if (file) { const filename = `${crypto.randomUUID()}${extname(file.name)}`; const filepath = `${UPLOAD_DIR}/${filename}`; const content = await file.arrayBuffer(); await Deno.writeFile(filepath, new Uint8Array(content)); return new Response(JSON.stringify({ filename }), { headers: { "Content-Type": "application/json" }, }); } } return new Response("Not Found", { status: 404 }); }; await serve(handler, { port: 8000 }); ``` ### 日志记录系统 ```typescript import { getLogger, setup, handlers, LogRecord } from "https://deno.land/std@0.208.0/log/mod.ts"; class CustomHandler extends handlers.BaseHandler { override format(logRecord: LogRecord): string { const { levelName, msg, datetime } = logRecord; return `[${datetime.toISOString()}] [${levelName}] ${msg}`; } } await setup({ handlers: { console: new CustomHandler("DEBUG"), }, loggers: { default: { level: "DEBUG", handlers: ["console"], }, }, }); const logger = getLogger(); async function processData(data: any) { logger.debug(`Processing data: ${JSON.stringify(data)}`); try { const result = await performOperation(data); logger.info("Operation completed successfully"); return result; } catch (error) { logger.error(`Operation failed: ${error.message}`); throw error; } } ``` ## 标准库的优势 1. **高质量代码**:所有模块都经过严格审查和测试 2. **类型安全**:完整的 TypeScript 类型定义 3. **文档完善**:详细的 API 文档和示例 4. **定期更新**:持续维护和功能增强 5. **一致性**:统一的代码风格和 API 设计 6. **安全可靠**:经过安全审计,无已知漏洞 ## 最佳实践 1. **指定版本**:始终使用特定版本的标准库 2. **优先使用**:在可能的情况下优先使用标准库而非第三方库 3. **查看文档**:使用 `deno doc` 查看模块文档 4. **贡献代码**:发现问题时可以提交 PR 贡献代码 5. **关注更新**:定期查看标准库的更新日志 Deno 标准库为开发者提供了强大而可靠的基础设施,大大简化了常见任务的实现,是 Deno 生态系统的重要支柱。
服务端 · 2月21日 16:08
Deno 的权限系统是如何工作的?Deno 的权限系统是其最核心的安全特性之一,采用"默认拒绝"的安全模型。这种设计确保了代码在未经明确授权的情况下无法访问敏感资源。 ## 权限系统概述 Deno 的安全模型基于最小权限原则,默认情况下脚本没有任何权限,所有资源访问都需要显式授权。 ## 权限类型 ### 1. 文件系统权限 ```bash # 允许读取所有文件 deno run --allow-read script.ts # 允许读取特定目录 deno run --allow-read=/app,/data script.ts # 允许写入所有文件 deno run --allow-write script.ts # 允许写入特定目录 deno run --allow-write=/tmp script.ts # 同时允许读写 deno run --allow-read --allow-write script.ts ``` ### 2. 网络权限 ```bash # 允许所有网络访问 deno run --allow-net script.ts # 允许访问特定域名 deno run --allow-net=api.example.com script.ts # 允许访问特定端口 deno run --allow-net=:8080 script.ts # 允许访问特定 IP 和端口 deno run --allow-net=127.0.0.1:8000 script.ts ``` ### 3. 环境变量权限 ```bash # 允许访问所有环境变量 deno run --allow-env script.ts # 允许访问特定环境变量 deno run --allow-env=API_KEY,DATABASE_URL script.ts ``` ### 4. 子进程权限 ```bash # 允许创建子进程 deno run --allow-run script.ts # 允许运行特定命令 deno run --allow-run=git,npm script.ts ``` ### 5. 系统信息权限 ```bash # 允许获取系统信息 deno run --allow-sys script.ts # 允许获取特定系统信息 deno run --allow-sys=hostname,osRelease,osVersion script.ts ``` ### 6. 高精度时间权限 ```bash # 允许访问高精度时间 deno run --allow-hrtime script.ts ``` ### 7. FFI(外部函数接口)权限 ```bash # 允许加载动态库 deno run --allow-ffi script.ts # 允许加载特定库 deno run --allow-ffi=/path/to/library.so script.ts ``` ## 权限组合使用 ```bash # 组合多个权限 deno run --allow-read --allow-write --allow-net --allow-env=API_KEY app.ts # 使用 --allow-all 授予所有权限(不推荐生产环境) deno run --allow-all app.ts ``` ## 代码中的权限检查 Deno 提供了 API 来检查当前拥有的权限: ```typescript // 检查是否具有读取权限 const canRead = await Deno.permissions.query({ name: "read", path: "/tmp" }); console.log(canRead.state); // "granted", "prompt", or "denied" // 检查是否具有网络权限 const canAccessNet = await Deno.permissions.query({ name: "net" }); console.log(canAccessNet.state); // 请求权限 const netPermission = await Deno.permissions.request({ name: "net" }); if (netPermission.state === "granted") { console.log("Network access granted"); } ``` ## 权限提示模式 Deno 支持交互式权限提示: ```bash # 使用 --prompt 权限标志 deno run --prompt=net script.ts ``` 当脚本尝试访问需要权限的资源时,Deno 会提示用户是否授权。 ## 实际应用示例 ### 1. 文件服务器 ```typescript // file-server.ts import { serve } from "https://deno.land/std@0.208.0/http/server.ts"; const handler = async (req: Request): Promise<Response> => { const url = new URL(req.url); const filePath = `.${url.pathname}`; try { const content = await Deno.readFile(filePath); return new Response(content); } catch { return new Response("File not found", { status: 404 }); } }; await serve(handler, { port: 8000 }); ``` 运行: ```bash deno run --allow-read --allow-net file-server.ts ``` ### 2. API 客户端 ```typescript // api-client.ts const API_KEY = Deno.env.get("API_KEY"); if (!API_KEY) { throw new Error("API_KEY environment variable not set"); } const response = await fetch("https://api.example.com/data", { headers: { "Authorization": `Bearer ${API_KEY}`, }, }); const data = await response.json(); console.log(data); ``` 运行: ```bash deno run --allow-net --allow-env=API_KEY api-client.ts ``` ### 3. 文件处理工具 ```typescript // file-processor.ts const inputFile = Deno.args[0]; const outputFile = Deno.args[1]; const content = await Deno.readTextFile(inputFile); const processed = content.toUpperCase(); await Deno.writeTextFile(outputFile, processed); console.log(`Processed ${inputFile} to ${outputFile}`); ``` 运行: ```bash deno run --allow-read --allow-write file-processor.ts input.txt output.txt ``` ## 权限最佳实践 1. **最小权限原则**:只授予必要的权限 2. **明确指定资源**:使用具体的路径和域名,而不是通配符 3. **避免 --allow-all**:在生产环境中绝不使用 4. **文档化权限需求**:在 README 中说明运行脚本所需的权限 5. **使用权限检查**:在代码中检查权限并提供友好的错误信息 6. **环境隔离**:在容器或沙箱环境中运行不受信任的代码 ## 安全优势 Deno 的权限系统提供了以下安全优势: - **防止数据泄露**:未经授权无法读取敏感文件 - **防止系统破坏**:未经授权无法写入或删除文件 - **防止网络攻击**:未经授权无法进行网络请求 - **防止环境泄露**:未经授权无法访问环境变量 - **防止命令注入**:未经授权无法执行系统命令 - **可审计性**:所有权限使用都是显式的,便于审计 ## 与 Node.js 的对比 | 特性 | Deno | Node.js | |------|------|---------| | 默认权限 | 无权限 | 完全访问 | | 权限控制 | 命令行标志 | 无内置机制 | | 安全模型 | 默认拒绝 | 默认允许 | | 权限粒度 | 细粒度控制 | 无控制 | | 审计能力 | 显式权限 | 隐式权限 | Deno 的权限系统为 JavaScript/TypeScript 运行时提供了企业级的安全保障,使其特别适合处理敏感数据和运行不受信任代码的场景。
服务端 · 2月21日 16:08
Deno 如何处理模块导入和依赖管理?Deno 的模块系统采用去中心化的设计,与 Node.js 的 npm 生态系统有显著不同。理解 Deno 的模块导入机制对于高效开发至关重要。 ## 模块导入方式 ### 1. URL 导入 Deno 使用 URL 直接导入模块,这是其最显著的特点: ```typescript // 从 Deno 标准库导入 import { serve } from "https://deno.land/std@0.208.0/http/server.ts"; // 从第三方库导入 import { oak } from "https://deno.land/x/oak@v12.6.1/mod.ts"; // 从 GitHub 导入 import { myLib } from "https://raw.githubusercontent.com/user/repo/main/mod.ts"; ``` ### 2. 本地文件导入 ```typescript // 相对路径导入 import { utils } from "./utils.ts"; import { helper } from "../helper/helper.ts"; // 绝对路径导入 import { config } from "/app/config.ts"; ``` ### 3. 导入映射(Import Maps) 使用 `import_map.json` 管理模块别名: ```json { "imports": { "std/": "https://deno.land/std@0.208.0/", "oak": "https://deno.land/x/oak@v12.6.1/mod.ts", "@utils/": "./src/utils/" } } ``` 使用方式: ```typescript import { serve } from "std/http/server.ts"; import { Application } from "oak"; import { formatDate } from "@utils/date.ts"; ``` 运行时指定 import map: ```bash deno run --import-map=import_map.json app.ts ``` ## 版本管理 ### 1. 版本锁定 Deno 推荐在 URL 中指定具体版本: ```typescript // 推荐:指定版本 import { serve } from "https://deno.land/std@0.208.0/http/server.ts"; // 不推荐:使用最新版本(可能不稳定) import { serve } from "https://deno.land/std/http/server.ts"; ``` ### 2. 依赖缓存 Deno 会缓存所有下载的模块: ```bash # 查看缓存位置 deno info # 重新下载依赖 deno cache --reload app.ts # 清除缓存 deno cache --reload all ``` ## 模块导出 ### 1. 命名导出 ```typescript // utils.ts export const add = (a: number, b: number): number => a + b; export const subtract = (a: number, b: number): number => a - b; export interface User { id: number; name: string; } ``` ### 2. 默认导出 ```typescript // app.ts export default class Application { start() { console.log("Application started"); } } ``` ### 3. 重新导出 ```typescript // index.ts export * from "./utils.ts"; export { default as App } from "./app.ts"; export type { User } from "./types.ts"; ``` ## 权限要求 导入远程模块时,Deno 需要网络权限: ```bash # 允许网络访问以下载模块 deno run --allow-net app.ts # 允许读取缓存 deno run --allow-read app.ts ``` ## 与 Node.js 的对比 | 特性 | Deno | Node.js | |------|------|---------| | 导入方式 | URL 导入 | npm 包名 | | 依赖管理 | 无 package.json | package.json + node_modules | | 版本控制 | URL 中的版本号 | package.json 版本范围 | | 模块解析 | 直接 URL | node_modules 查找算法 | | 缓存机制 | 全局缓存 | 本地 node_modules | | 类型支持 | 原生 TypeScript | 需要 @types 包 | ## 最佳实践 1. **始终指定版本**:在 URL 中使用明确的版本号 2. **使用 Import Maps**:简化模块路径管理 3. **依赖锁定**:考虑使用 `deno.lock` 文件 4. **权限最小化**:只授予必要的权限 5. **缓存管理**:定期清理和更新缓存 ## 实际示例 创建一个简单的 Web 服务器: ```typescript // server.ts import { serve } from "https://deno.land/std@0.208.0/http/server.ts"; const handler = async (req: Request): Promise<Response> => { const url = new URL(req.url); if (url.pathname === "/") { return new Response("Hello, Deno!", { headers: { "content-type": "text/plain" }, }); } return new Response("Not Found", { status: 404 }); }; await serve(handler, { port: 8000 }); ``` 运行: ```bash deno run --allow-net server.ts ``` Deno 的模块系统提供了更简单、更直接的依赖管理方式,消除了 node_modules 的复杂性,同时保持了良好的类型支持和开发体验。
服务端 · 2月21日 16:08
如何使用 deno compile 编译可执行文件?Deno 的 `deno compile` 命令可以将 TypeScript/JavaScript 代码编译为独立的可执行文件,这使得分发和部署变得更加简单。这个功能对于创建命令行工具和独立应用程序特别有用。 ## deno compile 概述 `deno compile` 将 Deno 脚本及其所有依赖打包成一个单一的可执行文件,无需在目标机器上安装 Deno。 ## 基本用法 ### 1. 简单编译 ```bash # 编译为可执行文件 deno compile app.ts # 默认输出文件名与输入文件相同(无扩展名) # app.ts → app (Linux/Mac) 或 app.exe (Windows) ``` ### 2. 指定输出文件名 ```bash # 指定输出文件名 deno compile --output=myapp app.ts # Windows deno compile --output=myapp.exe app.ts ``` ### 3. 指定目标平台 ```bash # 编译为 Linux 可执行文件 deno compile --target=x86_64-unknown-linux-gnu app.ts # 编译为 macOS 可执行文件 deno compile --target=x86_64-apple-darwin app.ts deno compile --target=aarch64-apple-darwin app.ts # Apple Silicon # 编译为 Windows 可执行文件 deno compile --target=x86_64-pc-windows-msvc app.ts ``` ### 4. 包含权限 ```bash # 编译时包含权限,运行时无需再次指定 deno compile --allow-net --allow-read app.ts # 包含所有权限 deno compile --allow-all app.ts ``` ### 5. 设置环境变量 ```bash # 设置编译时的环境变量 deno compile --env=API_KEY=secret app.ts ``` ## 实际应用示例 ### 1. 命令行工具 ```typescript // cli.ts #!/usr/bin/env -S deno run const name = Deno.args[0] || "World"; const verbose = Deno.args.includes("--verbose"); if (verbose) { console.log("Verbose mode enabled"); } console.log(`Hello, ${name}!`); // 文件操作示例 const filename = Deno.args[1]; if (filename) { try { const content = await Deno.readTextFile(filename); console.log(`File content: ${content}`); } catch (error) { console.error(`Error reading file: ${error.message}`); } } ``` 编译: ```bash deno compile --allow-read --output=hello cli.ts ./hello Deno --verbose ``` ### 2. Web 服务器 ```typescript // server.ts import { serve } from "https://deno.land/std@0.208.0/http/server.ts"; const PORT = Deno.env.get("PORT") || "8000"; const handler = async (req: Request): Promise<Response> => { const url = new URL(req.url); if (url.pathname === "/") { return new Response("Hello from Deno server!", { headers: { "Content-Type": "text/plain" }, }); } if (url.pathname === "/health") { return new Response(JSON.stringify({ status: "ok" }), { headers: { "Content-Type": "application/json" }, }); } return new Response("Not Found", { status: 404 }); }; console.log(`Server running on port ${PORT}`); await serve(handler, { port: parseInt(PORT) }); ``` 编译: ```bash deno compile --allow-net --allow-env --output=server server.ts ./server ``` ### 3. 文件处理工具 ```typescript // file-processor.ts import { walk } from "https://deno.land/std@0.208.0/fs/walk.ts"; const directory = Deno.args[0] || "."; const pattern = Deno.args[1]; console.log(`Scanning directory: ${directory}`); if (pattern) { console.log(`Pattern: ${pattern}`); } let count = 0; for await (const entry of walk(directory)) { if (entry.isFile) { if (!pattern || entry.name.includes(pattern)) { console.log(`Found: ${entry.path}`); count++; } } } console.log(`Total files found: ${count}`); ``` 编译: ```bash deno compile --allow-read --output=scan file-processor.ts ./scan ./src .ts ``` ### 4. API 客户端 ```typescript // api-client.ts const API_URL = Deno.env.get("API_URL") || "https://api.example.com"; const API_KEY = Deno.env.get("API_KEY"); if (!API_KEY) { console.error("API_KEY environment variable is required"); Deno.exit(1); } async function fetchData(endpoint: string): Promise<any> { const response = await fetch(`${API_URL}${endpoint}`, { headers: { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json", }, }); if (!response.ok) { throw new Error(`API request failed: ${response.status}`); } return response.json(); } const endpoint = Deno.args[0] || "/users"; console.log(`Fetching data from: ${API_URL}${endpoint}`); try { const data = await fetchData(endpoint); console.log(JSON.stringify(data, null, 2)); } catch (error) { console.error(`Error: ${error.message}`); Deno.exit(1); } ``` 编译: ```bash deno compile --allow-net --allow-env --output=api-client api-client.ts API_KEY=your_key ./api-client /users ``` ## 高级用法 ### 1. 交叉编译 在 macOS 上编译 Linux 可执行文件: ```bash deno compile --target=x86_64-unknown-linux-gnu --output=myapp-linux app.ts ``` ### 2. 压缩可执行文件 使用 `upx` 压缩编译后的可执行文件: ```bash # 安装 upx brew install upx # macOS apt install upx # Linux # 压缩 upx --best myapp ``` ### 3. 创建图标(仅限 Windows) ```bash # 为 Windows 可执行文件添加图标 deno compile --icon=icon.ico --output=myapp.exe app.ts ``` ### 4. 设置元数据 ```bash # 设置可执行文件元数据 deno compile \ --output=myapp \ --app-name="My Application" \ --app-version="1.0.0" \ app.ts ``` ## 部署场景 ### 1. Docker 部署 ```dockerfile # 多阶段构建 FROM denoland/deno:1.38.0 AS builder WORKDIR /app COPY . . RUN deno compile --allow-net --allow-read --output=app server.ts FROM debian:bullseye-slim WORKDIR /app COPY --from=builder /app/app . EXPOSE 8000 CMD ["./app"] ``` ### 2. 云函数部署 ```typescript // cloud-function.ts export async function handler(event: any): Promise<any> { const name = event.name || "World"; return { statusCode: 200, body: JSON.stringify({ message: `Hello, ${name}!` }), }; } ``` 编译: ```bash deno compile --output=handler cloud-function.ts ``` ### 3. 独立应用分发 ```bash # 为不同平台编译 deno compile --target=x86_64-unknown-linux-gnu --output=myapp-linux app.ts deno compile --target=x86_64-apple-darwin --output=myapp-macos app.ts deno compile --target=x86_64-pc-windows-msvc --output=myapp-windows.exe app.ts # 创建发布包 mkdir release cp myapp-linux release/ cp myapp-macos release/ cp myapp-windows.exe release/ ``` ## 限制和注意事项 ### 1. 文件大小 编译后的可执行文件通常较大(约 50-100 MB),因为包含了整个 Deno 运行时和 V8 引擎。 ### 2. 动态导入 动态导入的模块会在运行时下载,不会被包含在可执行文件中: ```typescript // 这个模块不会被打包 const module = await import("https://example.com/module.ts"); ``` ### 3. 原生模块 使用 FFI 的原生模块需要确保目标平台上有相应的库。 ### 4. 权限 编译时指定的权限在运行时生效,无法在运行时更改。 ## 最佳实践 1. **指定目标平台**:明确指定编译目标,避免兼容性问题 2. **最小权限**:只授予必要的权限 3. **测试编译结果**:在目标平台上测试可执行文件 4. **版本控制**:记录编译命令和 Deno 版本 5. **文档化**:在 README 中说明如何编译和运行 6. **使用 CI/CD**:自动化编译和发布流程 ## 与其他工具对比 | 特性 | deno compile | pkg | nexe | |------|--------------|-----|------| | 原生支持 | 是 | 否 | 否 | | 跨平台 | 是 | 是 | 是 | | 文件大小 | 较大 | 中等 | 中等 | | 性能 | 好 | 好 | 好 | | 维护性 | 官方支持 | 社区维护 | 社区维护 | ## 故障排查 ### 1. 编译失败 ```bash # 查看详细错误信息 deno compile --log-level=debug app.ts ``` ### 2. 运行时错误 ```bash # 检查权限 deno compile --allow-net --allow-read app.ts # 检查环境变量 API_KEY=secret ./app ``` ### 3. 兼容性问题 ```bash # 检查目标平台 deno compile --target=x86_64-unknown-linux-gnu app.ts # 使用 Docker 测试 docker run --rm -v $(pwd):/app debian:bullseye-slim ./app ``` `deno compile` 为 Deno 应用程序提供了简单而强大的分发方式,特别适合需要独立部署的场景。通过合理使用这个功能,可以大大简化应用程序的部署和分发流程。
服务端 · 2月21日 16:08
Deno 的生态系统有哪些流行的库和工具?Deno 的生态系统虽然相对年轻,但发展迅速,提供了丰富的第三方库和工具。了解 Deno 的生态系统有助于开发者更好地利用现有资源。 ## 生态系统概述 Deno 的生态系统主要由以下几个部分组成: 1. **deno.land/x** - 第三方模块注册表 2. **deno.land/std** - 官方标准库 3. **nest.land** - 另一个流行的模块注册表 4. **Deno Deploy** - 官方边缘计算平台 5. **社区工具和框架** - 各种开发工具和框架 ## 流行的第三方库 ### 1. Web 框架 #### Oak ```typescript import { Application, Router } from "https://deno.land/x/oak@v12.6.1/mod.ts"; const app = new Application(); const router = new Router(); router.get("/", (ctx) => { ctx.response.body = "Hello, Oak!"; }); router.get("/users/:id", (ctx) => { const { id } = ctx.params; ctx.response.body = { id, name: `User ${id}` }; }); app.use(router.routes()); app.use(router.allowedMethods()); await app.listen({ port: 8000 }); ``` #### Fresh ```typescript import { Fresh } from "https://deno.land/x/fresh@1.4.0/server.ts"; import { handler } from "./routes/index.tsx"; const app = new Fresh(); app.get("/", handler); await app.listen({ port: 8000 }); ``` #### Hono ```typescript import { Hono } from "https://deno.land/x/hono@v3.11.0/mod.ts"; const app = new Hono(); app.get("/", (c) => c.text("Hello, Hono!")); app.get("/users/:id", (c) => { const id = c.req.param("id"); return c.json({ id, name: `User ${id}` }); }); Deno.serve(app.fetch); ``` ### 2. 数据库客户端 #### PostgreSQL (postgres) ```typescript import { Client } from "https://deno.land/x/postgres@v0.17.0/mod.ts"; const client = new Client({ user: "user", database: "database", hostname: "localhost", port: 5432, password: "password", }); await client.connect(); const result = await client.queryArray("SELECT * FROM users"); console.log(result.rows); await client.end(); ``` #### MySQL (mysql) ```typescript import { Client } from "https://deno.land/x/mysql@v2.12.0/mod.ts"; const client = await new Client().connect({ hostname: "127.0.0.1", username: "root", password: "password", db: "test", }); const users = await client.query("SELECT * FROM users"); console.log(users); await client.close(); ``` #### MongoDB (mongo) ```typescript import { MongoClient } from "https://deno.land/x/mongo@v0.31.0/mod.ts"; const client = new MongoClient(); await client.connect("mongodb://localhost:27017"); const db = client.database("test"); const users = db.collection<User>("users"); const user = await users.findOne({ name: "John" }); console.log(user); await client.close(); ``` #### Redis (redis) ```typescript import { connect } from "https://deno.land/x/redis@v0.31.0/mod.ts"; const redis = await connect({ hostname: "127.0.0.1", port: 6379, }); await redis.set("key", "value"); const value = await redis.get("key"); console.log(value); await redis.close(); ``` ### 3. ORM 和查询构建器 #### DenoDB ```typescript import { Database } from "https://deno.land/x/denodb@v1.0.0/mod.ts"; const db = new Database("postgres", { host: "127.0.0.1", username: "user", password: "password", database: "test", }); class User extends Model { static fields = { id: { primaryKey: true, autoIncrement: true }, name: { type: VARCHAR }, email: { type: VARCHAR, unique: true }, }; } db.link([User]); await db.sync(); const user = await User.create({ name: "John", email: "john@example.com" }); console.log(user); ``` #### Drizzle ORM ```typescript import { drizzle } from "https://deno.land/x/drizzle@v0.5.0/mod.ts"; import { pgTable, serial, text, varchar } from "https://deno.land/x/drizzle@v0.5.0/pg-core/mod.ts"; const users = pgTable("users", { id: serial("id").primaryKey(), name: text("name").notNull(), email: varchar("email", { length: 255 }).notNull().unique(), }); const db = drizzle("postgresql://user:password@localhost:5432/test"); const user = await db.insert(users).values({ name: "John", email: "john@example.com", }).returning().get(); console.log(user); ``` ### 4. 认证和授权 #### JWT (djwt) ```typescript import { create, verify, getNumericDate } from "https://deno.land/x/djwt@v3.0.1/mod.ts"; const key = await crypto.subtle.generateKey( { name: "HMAC", hash: "SHA-256" }, true, ["sign", "verify"] ); const payload = { iss: "my-app", exp: getNumericDate(60 * 60), // 1 hour data: { userId: 123 }, }; const token = await create({ alg: "HS256", typ: "JWT" }, payload, key); console.log("Token:", token); const verified = await verify(token, key); console.log("Verified:", verified); ``` #### OAuth (oauth2_client) ```typescript import { OAuth2Client } from "https://deno.land/x/oauth2_client@v1.0.2/mod.ts"; const oauth2Client = new OAuth2Client({ clientId: "your-client-id", clientSecret: "your-client-secret", authorizationEndpointUri: "https://github.com/login/oauth/authorize", tokenUri: "https://github.com/login/oauth/access_token", }); const authUrl = oauth2Client.code.getAuthorizationUri({ redirectUri: "http://localhost:8000/callback", scope: "read:user", }); console.log("Visit:", authUrl); ``` ### 5. 工具库 #### Lodash (lodash) ```typescript import _ from "https://deno.land/x/lodash@4.17.19-es/mod.ts"; const users = [ { name: "John", age: 30 }, { name: "Jane", age: 25 }, ]; const sorted = _.orderBy(users, ["age"], ["asc"]); console.log(sorted); const names = _.map(users, "name"); console.log(names); ``` #### Date-fns (date_fns) ```typescript import { format, addDays, isAfter } from "https://deno.land/x/date_fns@v2.29.3/mod.ts"; const now = new Date(); const future = addDays(now, 7); console.log(format(now, "yyyy-MM-dd")); console.log(format(future, "yyyy-MM-dd")); console.log(isAfter(future, now)); ``` #### Zod (zod) ```typescript import { z } from "https://deno.land/x/zod@v3.21.4/mod.ts"; const UserSchema = z.object({ id: z.number(), name: z.string().min(2), email: z.string().email(), age: z.number().min(0).max(120), }); const userData = { id: 1, name: "John", email: "john@example.com", age: 30, }; const user = UserSchema.parse(userData); console.log(user); ``` ### 6. 测试工具 #### Supabase (supabase) ```typescript import { createClient } from "https://deno.land/x/supabase@2.0.0/mod.ts"; const supabase = createClient( "your-project-url", "your-anon-key" ); const { data, error } = await supabase .from("users") .select("*") .eq("id", 1); console.log(data); ``` ## 开发工具 ### 1. 代码格式化 Deno 内置了格式化工具: ```bash deno fmt . ``` ### 2. 代码检查 ```bash deno lint . ``` ### 3. 文档生成 ```bash deno doc --html --output=./docs ./src ``` ### 4. 依赖检查 ```bash deno info ``` ## 包管理工具 ### 1. Denoify 将 Node.js 包转换为 Deno 兼容的包: ```bash denoify ``` ### 2. Deno 2 NPM (d2n) 将 npm 包转换为 Deno 模块: ```bash d2n express ``` ### 3. Import Map Generator 自动生成 import map: ```bash deno run --allow-read --allow-write https://deno.land/x/import_map_generator/main.ts ``` ## 部署平台 ### 1. Deno Deploy 官方边缘计算平台: ```typescript // main.ts import { serve } from "https://deno.land/std@0.208.0/http/server.ts"; serve((req) => { return new Response("Hello from Deno Deploy!"); }); ``` ### 2. Vercel 支持 Deno 部署: ```json { "builds": [ { "src": "main.ts", "use": "@vercel/deno" } ] } ``` ### 3. Railway 支持 Deno 部署: ```toml [build] builder = "NIXPACKS" [deploy] startCommand = "deno run --allow-net main.ts" ``` ## 社区资源 ### 1. 官方资源 - **官方网站**: https://deno.land - **文档**: https://deno.land/manual - **标准库**: https://deno.land/std - **第三方库**: https://deno.land/x ### 2. 社区论坛 - **GitHub Discussions**: https://github.com/denoland/deno/discussions - **Discord**: https://discord.gg/deno - **Twitter**: @deno_land ### 3. 学习资源 - **Deno by Example**: https://examples.deno.land - **Deno Tutorial**: https://deno-tutorial.com - **Awesome Deno**: https://github.com/denolib/awesome-deno ## 最佳实践 ### 1. 选择合适的库 - 优先使用标准库 - 选择活跃维护的第三方库 - 查看库的文档和示例 - 检查库的更新频率 ### 2. 版本管理 ```typescript // 指定具体版本 import { serve } from "https://deno.land/std@0.208.0/http/server.ts"; import { Application } from "https://deno.land/x/oak@v12.6.1/mod.ts"; ``` ### 3. 安全性 - 检查库的安全性 - 使用最小权限原则 - 定期更新依赖 ### 4. 性能 - 选择性能优化的库 - 避免不必要的依赖 - 使用缓存策略 ## 迁移指南 ### 1. 从 Node.js 迁移 ```typescript // Node.js const express = require('express'); const app = express(); // Deno import { Application } from "https://deno.land/x/oak@v12.6.1/mod.ts"; const app = new Application(); ``` ### 2. 使用兼容层 ```typescript import { polyfill } from "https://deno.land/x/node_polyfills@v0.1.48/main.ts"; polyfill(); // 现在可以使用 Node.js API const fs = require('fs'); ``` ## 未来发展 Deno 生态系统正在快速发展,以下是一些值得关注的趋势: 1. **更多第三方库**:生态系统持续扩大 2. **更好的工具支持**:开发工具不断完善 3. **企业级功能**:更多企业级特性 4. **性能优化**:持续的性能改进 5. **跨平台支持**:更好的跨平台兼容性 Deno 的生态系统虽然年轻,但发展迅速,提供了丰富的资源和工具。通过合理利用这些资源,开发者可以更高效地构建应用程序。
服务端 · 2月21日 16:08
Deno 的部署和运维有哪些最佳实践?Deno 的部署和运维是构建生产级应用的重要环节。了解如何正确部署和运维 Deno 应用程序可以确保应用的稳定性和可维护性。 ## 部署概述 Deno 应用可以部署到多种环境,包括传统服务器、容器化平台、云服务和边缘计算平台。 ## Docker 部署 ### 1. 基础 Dockerfile ```dockerfile # 使用官方 Deno 镜像 FROM denoland/deno:1.38.0 # 设置工作目录 WORKDIR /app # 复制依赖文件 COPY deno.json ./ # 缓存依赖 RUN deno cache src/main.ts # 复制源代码 COPY . . # 暴露端口 EXPOSE 8000 # 运行应用 CMD ["deno", "run", "--allow-net", "--allow-env", "src/main.ts"] ``` ### 2. 多阶段构建 ```dockerfile # 构建阶段 FROM denoland/deno:1.38.0 AS builder WORKDIR /app COPY . . # 编译为可执行文件 RUN deno compile --allow-net --allow-env --output=app src/main.ts # 运行阶段 FROM debian:bullseye-slim WORKDIR /app # 从构建阶段复制可执行文件 COPY --from=builder /app/app . # 暴露端口 EXPOSE 8000 # 运行应用 CMD ["./app"] ``` ### 3. 生产环境 Dockerfile ```dockerfile # 构建阶段 FROM denoland/deno:1.38.0 AS builder WORKDIR /app # 安装依赖 COPY deno.json ./ RUN deno cache src/main.ts # 复制源代码 COPY . . # 运行测试 RUN deno test --allow-all # 编译为可执行文件 RUN deno compile \ --allow-net \ --allow-env \ --allow-read \ --output=app \ src/main.ts # 运行阶段 FROM debian:bullseye-slim WORKDIR /app # 安装必要的运行时依赖 RUN apt-get update && \ apt-get install -y ca-certificates && \ rm -rf /var/lib/apt/lists/* # 创建非 root 用户 RUN useradd -m -u 1000 deno # 从构建阶段复制可执行文件 COPY --from=builder /app/app . # 更改所有者 RUN chown -R deno:deno /app # 切换到非 root 用户 USER deno # 暴露端口 EXPOSE 8000 # 健康检查 HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD curl -f http://localhost:8000/health || exit 1 # 运行应用 CMD ["./app"] ``` ## Kubernetes 部署 ### 1. Deployment 配置 ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: deno-app labels: app: deno-app spec: replicas: 3 selector: matchLabels: app: deno-app template: metadata: labels: app: deno-app spec: containers: - name: deno-app image: your-registry/deno-app:latest ports: - containerPort: 8000 env: - name: PORT value: "8000" - name: DATABASE_URL valueFrom: secretKeyRef: name: app-secrets key: database-url resources: requests: memory: "128Mi" cpu: "100m" limits: memory: "512Mi" cpu: "500m" livenessProbe: httpGet: path: /health port: 8000 initialDelaySeconds: 30 periodSeconds: 10 readinessProbe: httpGet: path: /ready port: 8000 initialDelaySeconds: 5 periodSeconds: 5 ``` ### 2. Service 配置 ```yaml apiVersion: v1 kind: Service metadata: name: deno-app-service spec: selector: app: deno-app ports: - protocol: TCP port: 80 targetPort: 8000 type: LoadBalancer ``` ### 3. ConfigMap 和 Secret ```yaml # ConfigMap apiVersion: v1 kind: ConfigMap metadata: name: app-config data: PORT: "8000" LOG_LEVEL: "info" --- # Secret apiVersion: v1 kind: Secret metadata: name: app-secrets type: Opaque data: database-url: <base64-encoded-url> api-key: <base64-encoded-key> ``` ## 云平台部署 ### 1. Deno Deploy Deno Deploy 是 Deno 官方的边缘计算平台。 ```typescript // main.ts import { serve } from "https://deno.land/std@0.208.0/http/server.ts"; const handler = async (req: Request): Promise<Response> => { const url = new URL(req.url); if (url.pathname === "/") { return new Response("Hello from Deno Deploy!", { headers: { "Content-Type": "text/plain" }, }); } return new Response("Not Found", { status: 404 }); }; await serve(handler, { port: 8000 }); ``` 部署步骤: 1. 创建 Deno Deploy 账户 2. 连接 GitHub 仓库 3. 配置部署设置 4. 自动部署 ### 2. Vercel 部署 ```json // vercel.json { "version": 2, "builds": [ { "src": "src/main.ts", "use": "@vercel/deno" } ], "routes": [ { "src": "/(.*)", "dest": "/src/main.ts" } ] } ``` ### 3. Railway 部署 ```toml # railway.toml [build] builder = "NIXPACKS" [deploy] startCommand = "deno run --allow-net --allow-env src/main.ts" [env] PORT = "8000" ``` ## 进程管理 ### 1. 使用 PM2 ```javascript // ecosystem.config.js module.exports = { apps: [{ name: 'deno-app', script: 'deno', args: 'run --allow-net --allow-env src/main.ts', instances: 'max', exec_mode: 'cluster', autorestart: true, watch: false, max_memory_restart: '1G', env: { NODE_ENV: 'production', PORT: 8000 } }] }; ``` ```bash # 安装 PM2 npm install -g pm2 # 启动应用 pm2 start ecosystem.config.js # 查看状态 pm2 status # 查看日志 pm2 logs # 重启应用 pm2 restart deno-app # 停止应用 pm2 stop deno-app ``` ### 2. 使用 Systemd ```ini # /etc/systemd/system/deno-app.service [Unit] Description=Deno Application After=network.target [Service] Type=simple User=deno WorkingDirectory=/app Environment="PORT=8000" Environment="DATABASE_URL=postgres://..." ExecStart=/usr/local/bin/deno run --allow-net --allow-env /app/src/main.ts Restart=always RestartSec=10 [Install] WantedBy=multi-user.target ``` ```bash # 启用服务 sudo systemctl enable deno-app # 启动服务 sudo systemctl start deno-app # 查看状态 sudo systemctl status deno-app # 查看日志 sudo journalctl -u deno-app -f # 重启服务 sudo systemctl restart deno-app ``` ## 监控和日志 ### 1. 日志管理 ```typescript // logger.ts import { getLogger, setup, handlers } from "https://deno.land/std@0.208.0/log/mod.ts"; await setup({ handlers: { console: new handlers.ConsoleHandler("INFO"), file: new handlers.FileHandler("INFO", { filename: "./logs/app.log", formatter: "{levelName} {datetime} {msg}", }), }, loggers: { default: { level: "INFO", handlers: ["console", "file"], }, }, }); export const logger = getLogger(); ``` ### 2. 健康检查 ```typescript // health.ts import { serve } from "https://deno.land/std@0.208.0/http/server.ts"; let isHealthy = true; let isReady = false; // 模拟启动时间 setTimeout(() => { isReady = true; }, 5000); const handler = async (req: Request): Promise<Response> => { const url = new URL(req.url); if (url.pathname === "/health") { return new Response(JSON.stringify({ status: isHealthy ? "ok" : "error" }), { headers: { "Content-Type": "application/json" }, status: isHealthy ? 200 : 503, }); } if (url.pathname === "/ready") { return new Response(JSON.stringify({ ready: isReady }), { headers: { "Content-Type": "application/json" }, status: isReady ? 200 : 503, }); } return new Response("Not Found", { status: 404 }); }; await serve(handler, { port: 8000 }); ``` ### 3. 指标收集 ```typescript // metrics.ts class MetricsCollector { private metrics: Map<string, number> = new Map(); private counters: Map<string, number> = new Map(); increment(name: string, value: number = 1) { const current = this.counters.get(name) || 0; this.counters.set(name, current + value); } gauge(name: string, value: number) { this.metrics.set(name, value); } timing(name: string, duration: number) { const timings = this.metrics.get(`${name}_timings`) || []; timings.push(duration); this.metrics.set(`${name}_timings`, timings); } getMetrics(): Record<string, any> { return { counters: Object.fromEntries(this.counters), gauges: Object.fromEntries(this.metrics), }; } } export const metrics = new MetricsCollector(); ``` ## 性能优化 ### 1. 连接池 ```typescript // connection-pool.ts class ConnectionPool<T> { private pool: T[] = []; private maxConnections: number; private factory: () => Promise<T>; constructor(maxConnections: number, factory: () => Promise<T>) { this.maxConnections = maxConnections; this.factory = factory; } async acquire(): Promise<T> { if (this.pool.length > 0) { return this.pool.pop()!; } return await this.factory(); } release(connection: T) { if (this.pool.length < this.maxConnections) { this.pool.push(connection); } } } ``` ### 2. 缓存策略 ```typescript // cache.ts class Cache { private cache: Map<string, { value: any; expires: number }> = new Map(); private ttl: number; constructor(ttl: number = 60000) { this.ttl = ttl; } set(key: string, value: any, ttl?: number) { const expires = Date.now() + (ttl || this.ttl); this.cache.set(key, { value, expires }); } get(key: string): any | null { const item = this.cache.get(key); if (!item) { return null; } if (Date.now() > item.expires) { this.cache.delete(key); return null; } return item.value; } clear() { this.cache.clear(); } cleanup() { const now = Date.now(); for (const [key, item] of this.cache.entries()) { if (now > item.expires) { this.cache.delete(key); } } } } ``` ## 安全最佳实践 ### 1. 环境变量管理 ```typescript // config.ts interface Config { port: number; databaseUrl: string; apiKey: string; logLevel: string; } function loadConfig(): Config { const port = parseInt(Deno.env.get("PORT") || "8000"); const databaseUrl = Deno.env.get("DATABASE_URL"); const apiKey = Deno.env.get("API_KEY"); const logLevel = Deno.env.get("LOG_LEVEL") || "info"; if (!databaseUrl) { throw new Error("DATABASE_URL environment variable is required"); } if (!apiKey) { throw new Error("API_KEY environment variable is required"); } return { port, databaseUrl, apiKey, logLevel, }; } export const config = loadConfig(); ``` ### 2. 权限最小化 ```bash # 只授予必要的权限 deno run --allow-net --allow-env src/main.ts # 限制网络访问范围 deno run --allow-net=api.example.com src/main.ts # 限制文件访问 deno run --allow-read=/app/data src/main.ts ``` ## CI/CD 集成 ### 1. GitHub Actions ```yaml # .github/workflows/deploy.yml name: Deploy on: push: branches: [main] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup Deno uses: denoland/setup-deno@v1 with: deno-version: v1.38.0 - name: Run tests run: deno test --allow-all - name: Lint run: deno lint - name: Format check run: deno fmt --check deploy: needs: test runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Build Docker image run: docker build -t deno-app:${{ github.sha }} . - name: Push to registry run: | echo ${{ secrets.REGISTRY_PASSWORD }} | docker login -u ${{ secrets.REGISTRY_USER }} --password-stdin docker push deno-app:${{ github.sha }} ``` ### 2. GitLab CI ```yaml # .gitlab-ci.yml stages: - test - build - deploy test: stage: test image: denoland/deno:1.38.0 script: - deno test --allow-all - deno lint - deno fmt --check build: stage: build image: docker:latest services: - docker:dind script: - docker build -t deno-app:$CI_COMMIT_SHA . - docker push deno-app:$CI_COMMIT_SHA deploy: stage: deploy image: alpine:latest script: - kubectl set image deployment/deno-app deno-app=deno-app:$CI_COMMIT_SHA only: - main ``` ## 故障排查 ### 1. 常见问题 **问题:应用启动失败** ```bash # 检查日志 deno run --log-level=debug src/main.ts # 检查权限 deno info src/main.ts ``` **问题:内存泄漏** ```typescript // 定期检查内存使用 setInterval(() => { const usage = Deno.memoryUsage(); console.log("Memory usage:", usage); }, 60000); ``` **问题:性能下降** ```typescript // 使用性能分析 import { performance } from "https://deno.land/std@0.208.0/node/performance.ts"; const start = performance.now(); // 执行操作 const duration = performance.now() - start; console.log(`Operation took ${duration}ms`); ``` ## 最佳实践 1. **容器化部署**:使用 Docker 确保环境一致性 2. **健康检查**:实现健康检查端点 3. **日志记录**:记录关键操作和错误 4. **监控指标**:收集和监控应用指标 5. **自动化部署**:使用 CI/CD 自动化部署流程 6. **权限最小化**:只授予必要的权限 7. **资源限制**:设置合理的资源限制 8. **备份策略**:定期备份重要数据 Deno 的部署和运维需要综合考虑多个方面,通过合理的规划和实施,可以构建稳定、可靠的生产级应用。
服务端 · 2月21日 16:06