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

面试题手册

OffscreenCanvas 如何在 Web Worker 中进行渲染?

OffscreenCanvas 是 HTML5 提供的一个功能,允许在 Web Worker 中进行 Canvas 渲染,从而将复杂的图形计算从主线程移到后台线程。OffscreenCanvas 的核心概念特点可以在 Worker 中进行 Canvas 绘图操作支持大部分 Canvas 2D API 和 WebGL API通过 transferControlToOffscreen() 方法将 Canvas 控制权转移适用于复杂的图形渲染和动画基本使用主线程设置// 获取 Canvas 元素const canvas = document.getElementById('myCanvas');// 将 Canvas 控制权转移到 OffscreenCanvasconst offscreen = canvas.transferControlToOffscreen();// 创建 Workerconst worker = new Worker('canvas-worker.js');// 将 OffscreenCanvas 发送给 Workerworker.postMessage({ canvas: offscreen }, [offscreen]);Worker 中渲染// canvas-worker.jsself.onmessage = function(e) { const canvas = e.data.canvas; const ctx = canvas.getContext('2d'); // 在 Worker 中进行绘图 function render() { ctx.clearRect(0, 0, canvas.width, canvas.height); // 绘制图形 ctx.fillStyle = 'blue'; ctx.fillRect(50, 50, 100, 100); // 继续动画 requestAnimationFrame(render); } render();};实际应用场景1. 复杂动画渲染// 主线程const canvas = document.getElementById('canvas');const offscreen = canvas.transferControlToOffscreen();const worker = new Worker('animation-worker.js');worker.postMessage({ canvas: offscreen }, [offscreen]);// animation-worker.jsself.onmessage = function(e) { const canvas = e.data.canvas; const ctx = canvas.getContext('2d'); let particles = []; function initParticles() { for (let i = 0; i < 1000; i++) { particles.push({ x: Math.random() * canvas.width, y: Math.random() * canvas.height, vx: (Math.random() - 0.5) * 2, vy: (Math.random() - 0.5) * 2, size: Math.random() * 3 + 1 }); } } function updateParticles() { particles.forEach(p => { p.x += p.vx; p.y += p.vy; if (p.x < 0 || p.x > canvas.width) p.vx *= -1; if (p.y < 0 || p.y > canvas.height) p.vy *= -1; }); } function render() { ctx.clearRect(0, 0, canvas.width, canvas.height); particles.forEach(p => { ctx.beginPath(); ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2); ctx.fillStyle = `rgba(100, 150, 255, 0.7)`; ctx.fill(); }); updateParticles(); requestAnimationFrame(render); } initParticles(); render();};2. 图像处理// 主线程const canvas = document.getElementById('canvas');const offscreen = canvas.transferControlToOffscreen();const worker = new Worker('image-worker.js');// 加载图像const img = new Image();img.onload = function() { worker.postMessage({ canvas: offscreen, image: img }, [offscreen]);};img.src = 'image.jpg';// image-worker.jsself.onmessage = function(e) { const canvas = e.data.canvas; const ctx = canvas.getContext('2d'); const img = e.data.image; // 设置 Canvas 大小 canvas.width = img.width; canvas.height = img.height; // 绘制原始图像 ctx.drawImage(img, 0, 0); // 获取图像数据 const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); const data = imageData.data; // 图像处理:灰度化 for (let i = 0; i < data.length; i += 4) { const avg = (data[i] + data[i + 1] + data[i + 2]) / 3; data[i] = avg; // R data[i + 1] = avg; // G data[i + 2] = avg; // B } // 放回处理后的图像 ctx.putImageData(imageData, 0, 0);};3. WebGL 渲染// 主线程const canvas = document.getElementById('glCanvas');const offscreen = canvas.transferControlToOffscreen();const worker = new Worker('webgl-worker.js');worker.postMessage({ canvas: offscreen }, [offscreen]);// webgl-worker.jsself.onmessage = function(e) { const canvas = e.data.canvas; const gl = canvas.getContext('webgl'); // WebGL 初始化代码 const vertexShaderSource = ` attribute vec4 aVertexPosition; void main() { gl_Position = aVertexPosition; } `; const fragmentShaderSource = ` void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); } `; // 编译着色器 const vertexShader = compileShader(gl, gl.VERTEX_SHADER, vertexShaderSource); const fragmentShader = compileShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource); // 创建程序 const shaderProgram = gl.createProgram(); gl.attachShader(shaderProgram, vertexShader); gl.attachShader(shaderProgram, fragmentShader); gl.linkProgram(shaderProgram); // 渲染 function render() { gl.clearColor(0.0, 0.0, 0.0, 1.0); gl.clear(gl.COLOR_BUFFER_BIT); gl.useProgram(shaderProgram); gl.drawArrays(gl.TRIANGLES, 0, 3); requestAnimationFrame(render); } render();};function compileShader(gl, type, source) { const shader = gl.createShader(type); gl.shaderSource(shader, source); gl.compileShader(shader); return shader;}与主线程交互动态调整 Canvas 大小// 主线程const canvas = document.getElementById('canvas');const offscreen = canvas.transferControlToOffscreen();const worker = new Worker('canvas-worker.js');worker.postMessage({ canvas: offscreen }, [offscreen]);// 监听窗口大小变化window.addEventListener('resize', function() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; worker.postMessage({ type: 'resize', width: canvas.width, height: canvas.height });});// canvas-worker.jsself.onmessage = function(e) { if (e.data.type === 'resize') { canvas.width = e.data.width; canvas.height = e.data.height; }};接收用户输入// 主线程const canvas = document.getElementById('canvas');const offscreen = canvas.transferControlToOffscreen();const worker = new Worker('canvas-worker.js');worker.postMessage({ canvas: offscreen }, [offscreen]);// 发送鼠标位置canvas.addEventListener('mousemove', function(e) { const rect = canvas.getBoundingClientRect(); worker.postMessage({ type: 'mousemove', x: e.clientX - rect.left, y: e.clientY - rect.top });});// canvas-worker.jslet mouseX = 0, mouseY = 0;self.onmessage = function(e) { if (e.data.type === 'mousemove') { mouseX = e.data.x; mouseY = e.data.y; }};注意事项1. Canvas 控制权只能转移一次// ❌ 错误:多次转移const offscreen1 = canvas.transferControlToOffscreen();const offscreen2 = canvas.transferControlToOffscreen(); // 报错// ✅ 正确:只转移一次const offscreen = canvas.transferControlToOffscreen();2. OffscreenCanvas 不支持所有 Canvas API// ❌ 不支持canvas.toDataURL(); // 在 Worker 中不可用canvas.toBlob(); // 在 Worker 中不可用// ✅ 使用 ImageBitmap 代替const bitmap = await createImageBitmap(canvas);3. 浏览器兼容性// 检查浏览器支持if ('transferControlToOffscreen' in HTMLCanvasElement.prototype) { // 支持 OffscreenCanvas} else { // 不支持,使用回退方案}性能优化1. 批量绘制// ❌ 频繁调用绘制方法for (let i = 0; i < 1000; i++) { ctx.beginPath(); ctx.arc(particles[i].x, particles[i].y, particles[i].size, 0, Math.PI * 2); ctx.fill();}// ✅ 批量绘制ctx.beginPath();for (let i = 0; i < 1000; i++) { ctx.moveTo(particles[i].x, particles[i].y); ctx.arc(particles[i].x, particles[i].y, particles[i].size, 0, Math.PI * 2);}ctx.fill();2. 使用 ImageBitmap// 加载图像为 ImageBitmapconst bitmap = await createImageBitmap(image);// 在 Worker 中绘制ctx.drawImage(bitmap, 0, 0);3. 降低渲染频率let lastRenderTime = 0;const targetFPS = 30;const frameInterval = 1000 / targetFPS;function render(timestamp) { if (timestamp - lastRenderTime >= frameInterval) { // 执行渲染 ctx.clearRect(0, 0, canvas.width, canvas.height); // ... 绘制代码 lastRenderTime = timestamp; } requestAnimationFrame(render);}最佳实践复杂渲染使用 OffscreenCanvas:将计算密集型图形渲染移到 Worker合理控制渲染频率:避免不必要的重绘批量处理:减少绘制调用次数使用 ImageBitmap:提高图像加载和渲染性能检查浏览器兼容性:提供回退方案及时释放资源:使用完毕后清理资源
阅读 0·2月21日 15:44

如何优化Expo应用的性能?有哪些常见的性能问题?

Expo应用的性能优化是确保良好用户体验的关键。Expo基于React Native,因此许多React Native的性能优化技巧同样适用,同时Expo也提供了一些特定的优化工具和策略。性能优化策略:组件渲染优化使用React.memo、useMemo和useCallback减少不必要的渲染:// 使用React.memo避免不必要的重新渲染const MyComponent = React.memo(({ data }) => { return <Text>{data.value}</Text>;});// 使用useMemo缓存计算结果function ExpensiveComponent({ items }) { const sortedItems = useMemo(() => { return items.sort((a, b) => a.id - b.id); }, [items]); return <FlatList data={sortedItems} />;}// 使用useCallback缓存函数function ParentComponent() { const handleClick = useCallback(() => { console.log('Clicked'); }, []); return <ChildComponent onClick={handleClick} />;}列表优化使用FlatList而不是ScrollView处理长列表:<FlatList data={items} renderItem={({ item }) => <Item item={item} />} keyExtractor={(item) => item.id} removeClippedSubviews={true} maxToRenderPerBatch={10} windowSize={10} initialNumToRender={10} getItemLayout={(data, index) => ({ length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index, })}/>关键属性说明:removeClippedSubviews:移除屏幕外的视图maxToRenderPerBatch:每批渲染的项目数windowSize:渲染窗口大小initialNumToRender:初始渲染项目数getItemLayout:提供项目布局信息图片优化使用expo-image和图片缓存策略:import { Image } from 'expo-image';<Image source={{ uri: 'https://example.com/image.jpg' }} style={{ width: 200, height: 200 }} cachePolicy="memory-disk" contentFit="cover" transition={200}/>优化技巧:使用适当的图片尺寸启用缓存策略使用WebP格式懒加载图片网络请求优化使用缓存和请求去重:import { useQuery } from '@tanstack/react-query';function UserProfile({ userId }) { const { data, isLoading } = useQuery({ queryKey: ['user', userId], queryFn: () => fetchUser(userId), staleTime: 5 * 60 * 1000, // 5分钟 cacheTime: 10 * 60 * 1000, // 10分钟 }); if (isLoading) return <Loading />; return <Text>{data.name}</Text>;}动画优化使用react-native-reanimated进行高性能动画:import Animated, { useSharedValue, useAnimatedStyle, withTiming,} from 'react-native-reanimated';function AnimatedBox() { const opacity = useSharedValue(0); const animatedStyle = useAnimatedStyle(() => { return { opacity: withTiming(opacity.value, { duration: 500 }), }; }); useEffect(() => { opacity.value = 1; }, []); return <Animated.View style={animatedStyle} />;}内存管理及时释放资源:// 取消网络请求useEffect(() => { const controller = new AbortController(); fetchData(controller.signal); return () => controller.abort();}, []);// 清理定时器useEffect(() => { const timer = setInterval(() => { console.log('Tick'); }, 1000); return () => clearInterval(timer);}, []);// 清理订阅useEffect(() => { const subscription = someEvent.subscribe(); return () => subscription.unsubscribe();}, []);Bundle优化减少应用包大小:// 代码分割const LazyComponent = React.lazy(() => import('./LazyComponent'));// 动态导入const loadModule = async () => { const module = await import('./heavyModule'); module.doSomething();};// 移除未使用的依赖npm prune使用Expo的性能工具Expo提供了一些性能监控工具:import { Performance } from 'react-native';// 记录性能标记Performance.mark('component-start');// 组件渲染完成后Performance.mark('component-end');// 测量性能Performance.measure('component-render', 'component-start', 'component-end');性能分析工具:React DevTools Profiler分析组件渲染性能识别性能瓶颈优化渲染次数Flipper网络请求监控布局检查内存分析Expo DevTools实时性能监控Bundle大小分析加载时间追踪常见性能问题及解决方案:列表滚动卡顿使用FlatList而不是ScrollView启用removeClippedSubviews提供正确的getItemLayout图片加载慢使用expo-image启用缓存使用适当的图片尺寸应用启动慢优化初始化代码延迟加载非关键模块使用启动画面内存泄漏及时清理订阅和定时器使用WeakMap和WeakSet定期检查内存使用最佳实践:性能监控:定期使用性能工具分析应用渐进优化:先优化关键路径,再优化次要功能测试覆盖:在不同设备上测试性能持续优化:将性能优化作为持续过程用户体验:平衡性能和功能,优先保证核心体验通过系统性的性能优化,可以显著提升Expo应用的用户体验和竞争力。
阅读 0·2月21日 15:43

Ollama 与其他 LLM 部署方案(如 vLLM、LM Studio)相比有什么优缺点?

Ollama 与其他 LLM 部署方案相比,各有优劣:1. Ollama vs. vLLM:Ollama 优势:安装简单,一行命令即可部署跨平台支持(Linux/macOS/Windows)内置模型管理和 API 服务适合个人开发和小型应用vLLM 优势:更高的推理性能和吞吐量支持 PagedAttention 技术更适合大规模生产环境支持更多模型格式2. Ollama vs. LM Studio:Ollama 优势:命令行和 API 友好更适合服务器部署开源且免费更好的自动化集成LM Studio 优势:图形化界面,用户体验好内置模型市场适合桌面用户可视化配置选项3. Ollama vs. OpenAI API:Ollama 优势:完全本地运行,数据隐私保护无 API 调用费用可自定义模型无网络依赖OpenAI API 优势:模型性能更强(GPT-4 等)无需本地硬件持续更新和优化更好的多语言支持4. Ollama vs. LocalAI:Ollama 优势:更轻量级更简单的配置更好的性能优化活跃的社区支持LocalAI 优势:OpenAI API 兼容性更好支持更多模型类型更灵活的配置选项支持多模型并行5. Ollama vs. Text Generation WebUI:Ollama 优势:更适合 API 集成更简单的部署更好的性能命令行友好Text Generation WebUI 优势:功能丰富的 Web 界面支持更多高级功能可视化参数调整更好的交互体验选择建议:个人开发/学习:Ollama 或 LM Studio生产环境 API 服务:Ollama 或 vLLM需要 OpenAI 兼容性:LocalAI桌面用户:LM Studio需要最高性能:vLLM需要图形界面:LM Studio 或 Text Generation WebUI
阅读 0·2月21日 15:43

Gin 框架的性能优化技巧和最佳实践有哪些?

Gin 框架的性能优化技巧和最佳实践如下:1. 路由优化1.1 路由分组// 合理使用路由组,减少重复前缀api := r.Group("/api/v1"){ users := api.Group("/users") { users.GET("", getUsers) users.GET("/:id", getUser) users.POST("", createUser) }}1.2 路由顺序将高频路由放在前面静态路由优先于动态路由避免路由冲突1.3 减少路由嵌套避免过深的路由层级合理规划路由结构2. 中间件优化2.1 中间件选择// 只在需要的路由上添加中间件r.GET("/public/data", getData) // 不需要认证r.GET("/private/data", authMiddleware(), getPrivateData) // 需要认证2.2 中间件逻辑优化保持中间件逻辑轻量避免在中间件中进行阻塞操作使用缓存减少重复计算2.3 中间件顺序将性能影响小的中间件放在前面将可能中断请求的中间件放在前面3. 数据绑定优化3.1 使用明确的绑定方法// 推荐:使用明确的绑定方法c.ShouldBindJSON(&obj)// 不推荐:使用通用绑定方法c.ShouldBind(&obj)3.2 避免过度验证只验证必要的字段使用合理的验证规则4. 数据库优化4.1 连接池配置db.SetMaxOpenConns(100)db.SetMaxIdleConns(10)db.SetConnMaxLifetime(time.Hour)4.2 查询优化使用索引避免 N+1 查询合理使用缓存5. 响应优化5.1 启用压缩import "github.com/gin-contrib/gzip"r.Use(gzip.Gzip(gzip.DefaultCompression))5.2 流式响应// 对于大数据量,使用流式响应c.Stream(func(w io.Writer) bool { // 写入数据 w.Write(data) return true // 继续写入})5.3 合理设置缓存头c.Header("Cache-Control", "public, max-age=3600")6. 并发优化6.1 使用 goroutine 池// 使用 worker pool 处理并发任务type WorkerPool struct { tasks chan func()}func (p *WorkerPool) Submit(task func()) { p.tasks <- task}6.2 避免阻塞操作将阻塞操作放到 goroutine 中使用 context 控制超时7. 内存优化7.1 对象复用// 使用 sync.Pool 复用对象var bufferPool = sync.Pool{ New: func() interface{} { return new(bytes.Buffer) },}7.2 避免内存泄漏及时释放资源避免在 Context 中存储大量数据使用 defer 确保资源释放8. 日志优化8.1 异步日志// 使用异步日志记录logger := log.New(os.Stdout, "", log.LstdFlags)go func() { for entry := range logChannel { logger.Println(entry) }}()8.2 合理的日志级别生产环境使用 INFO 或 WARN 级别开发环境使用 DEBUG 级别9. 监控和性能分析9.1 使用 pprofimport _ "net/http/pprof"go func() { log.Println(http.ListenAndServe("localhost:6060", nil))}()9.2 添加性能指标// 使用 Prometheus 等工具收集指标import "github.com/prometheus/client_golang/prometheus"var requestDuration = prometheus.NewHistogramVec( prometheus.HistogramOpts{ Name: "http_request_duration_seconds", Help: "HTTP request duration in seconds", }, []string{"method", "path"},)10. 最佳实践总结合理使用路由组和中间件启用 gzip 压缩配置数据库连接池使用缓存减少重复计算避免阻塞操作使用对象池减少内存分配异步日志记录添加性能监控定期进行性能测试使用 pprof 分析性能瓶颈通过以上优化技巧,可以显著提升 Gin 应用的性能和稳定性。
阅读 0·2月21日 15:43

Expo应用中如何管理权限?有哪些最佳实践?

Expo应用的权限管理是开发过程中的重要环节,特别是在处理敏感功能如相机、位置、麦克风等时。Expo提供了统一的权限管理API,简化了跨平台权限请求流程。权限管理基础:Expo使用expo-permissions和各个模块的权限API来管理应用权限。安装权限模块:npx expo install expo-permissions基本权限请求流程:import * as Permissions from 'expo-permissions';import { Camera } from 'expo-camera';async function requestCameraPermission() { // 请求相机权限 const { status } = await Camera.requestCameraPermissionsAsync(); if (status === 'granted') { console.log('Camera permission granted'); } else { console.log('Camera permission denied'); }}常用权限类型:相机权限import { Camera } from 'expo-camera';// 请求相机权限const { status } = await Camera.requestCameraPermissionsAsync();// 检查权限状态const { status: currentStatus } = await Camera.getCameraPermissionsAsync();// 请求麦克风权限(用于视频录制)const { status: audioStatus } = await Camera.requestMicrophonePermissionsAsync();位置权限import * as Location from 'expo-location';// 请求前台位置权限const { status } = await Location.requestForegroundPermissionsAsync();// 请求后台位置权限const { status: backgroundStatus } = await Location.requestBackgroundPermissionsAsync();// 获取当前位置const location = await Location.getCurrentPositionAsync({});通知权限import * as Notifications from 'expo-notifications';// 请求通知权限const { status } = await Notifications.requestPermissionsAsync();// 配置通知处理程序Notifications.setNotificationHandler({ handleNotification: async () => ({ shouldShowAlert: true, shouldPlaySound: false, shouldSetBadge: false, }),});媒体库权限import * as MediaLibrary from 'expo-media-library';// 请求媒体库权限const { status } = await MediaLibrary.requestPermissionsAsync();// 保存图片到媒体库const asset = await MediaLibrary.createAssetAsync(uri);联系人权限import * as Contacts from 'expo-contacts';// 请求联系人权限const { status } = await Contacts.requestPermissionsAsync();// 获取联系人const { data } = await Contacts.getContactsAsync();日历权限import * as Calendar from 'expo-calendar';// 请求日历权限const { status } = await Calendar.requestCalendarPermissionsAsync();// 创建日历事件const eventId = await Calendar.createEventAsync(calendarId, eventDetails);权限状态:权限请求返回的状态包括:granted:权限已授予denied:权限被拒绝undetermined:用户尚未做出选择limited:部分权限已授予(iOS特有)权限配置:在app.json中声明权限:{ "expo": { "ios": { "infoPlist": { "NSCameraUsageDescription": "需要相机权限来拍照", "NSLocationWhenInUseUsageDescription": "需要位置权限来显示附近信息", "NSMicrophoneUsageDescription": "需要麦克风权限来录制音频" } }, "android": { "permissions": [ "CAMERA", "ACCESS_FINE_LOCATION", "RECORD_AUDIO", "READ_EXTERNAL_STORAGE", "WRITE_EXTERNAL_STORAGE" ] } }}最佳实践:适时请求权限// 在用户需要使用功能时才请求权限function CameraButton() { const [hasPermission, setHasPermission] = useState(null); useEffect(() => { (async () => { const { status } = await Camera.requestCameraPermissionsAsync(); setHasPermission(status === 'granted'); })(); }, []); if (hasPermission === null) { return <Text>请求权限中...</Text>; } if (hasPermission === false) { return <Text>没有相机权限</Text>; } return <Button title="打开相机" onPress={openCamera} />;}提供清晰的权限说明async function requestPermissionWithExplanation() { const { status } = await Location.requestForegroundPermissionsAsync(); if (status !== 'granted') { Alert.alert( '需要位置权限', '应用需要位置权限来显示附近的信息,请在设置中授予权限。', [ { text: '取消', style: 'cancel' }, { text: '打开设置', onPress: () => Linking.openSettings() } ] ); }}处理权限被拒绝的情况async function handlePermissionDenied() { const { status } = await Camera.requestCameraPermissionsAsync(); if (status !== 'granted') { // 检查是否可以再次请求 const { canAskAgain } = await Camera.getCameraPermissionsAsync(); if (canAskAgain) { Alert.alert( '需要相机权限', '应用需要相机权限来拍照功能', [ { text: '取消' }, { text: '授予权限', onPress: () => requestCameraPermission() } ] ); } else { Alert.alert( '权限被永久拒绝', '请在系统设置中手动授予权限', [ { text: '取消' }, { text: '打开设置', onPress: () => Linking.openSettings() } ] ); } }}权限状态缓存import { useState, useEffect } from 'react';function usePermission(permissionGetter) { const [status, setStatus] = useState(null); useEffect(() => { (async () => { const { status } = await permissionGetter(); setStatus(status); })(); }, [permissionGetter]); return status;}// 使用const cameraStatus = usePermission(() => Camera.getCameraPermissionsAsync());平台差异:iOS权限需要在Info.plist中声明使用目的某些权限只能请求一次用户可以在设置中随时更改权限Android权限需要在AndroidManifest.xml中声明可以多次请求权限运行时权限从Android 6.0开始常见问题:权限请求失败检查权限是否在配置文件中声明确保使用正确的权限API处理用户拒绝权限的情况权限状态不一致缓存权限状态在需要时重新检查权限处理权限状态变化后台权限后台位置权限需要特殊处理通知后台权限需要额外配置遵循平台特定的后台权限规则安全考虑:最小权限原则:只请求必要的权限透明度:清晰解释为什么需要权限用户控制:允许用户撤销权限数据保护:妥善处理敏感数据良好的权限管理不仅能提升用户体验,还能确保应用符合各个平台的隐私政策和法律法规要求。
阅读 0·2月21日 15:43

Prometheus 的 Recording Rules 和 Alerting Rules 有什么区别?

Prometheus Recording Rules 和 Alerting Rules 的区别和使用:Recording Rules(记录规则):预先计算并存储常用的查询结果提高查询性能,减少计算开销不会触发告警配置示例:groups: - name: api_recording_rules interval: 30s rules: - record: job:http_requests:rate5m expr: sum by (job) (rate(http_requests_total[5m])) - record: job:request_errors:rate5m expr: sum by (job) (rate(http_requests_total{status=~"5.."}[5m]))使用场景:频繁查询的复杂表达式需要聚合多个指标的计算提高仪表盘加载速度减少实时查询压力Alerting Rules(告警规则):监控指标并触发告警支持告警分组、抑制、静默发送通知到 Alertmanager配置示例:groups: - name: api_alerting_rules rules: - alert: HighErrorRate expr: job:request_errors:rate5m / job:http_requests:rate5m > 0.05 for: 5m labels: severity: critical annotations: summary: "High error rate on {{ $labels.job }}" description: "Error rate is {{ $value | humanizePercentage }}"关键区别:| 特性 | Recording Rules | Alerting Rules ||------|----------------|----------------|| 目的 | 预计算查询结果 | 触发告警通知 || 存储 | 生成新的时间序列 | 不存储新序列 || 性能 | 提高查询性能 | 可能增加评估开销 || 使用 | 仪表盘、查询 | 监控、告警 |最佳实践:Recording Rules:使用有意义的命名规范合理设置评估间隔定期审查和清理无用规则使用 by 子句进行分组Alerting Rules:合理设置 for 参数避免误报使用分级告警(info、warning、critical)添加清晰的描述信息使用标签便于分组和路由规则管理:使用版本控制管理规则文件使用 promtool 检查规则语法测试规则后再部署监控规则评估性能验证规则:promtool check rules /path/to/rules.yml
阅读 0·2月21日 15:41