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

面试题手册

GraphQL 客户端开发有哪些关键要点

GraphQL 客户端开发指南GraphQL 客户端开发是构建现代前端应用的关键部分。以下是使用 Apollo Client 和其他 GraphQL 客户端的全面指南。1. Apollo Client 配置基本配置import { ApolloClient, InMemoryCache, HttpLink } from '@apollo/client';const httpLink = new HttpLink({ uri: 'https://api.example.com/graphql', credentials: 'include'});const client = new ApolloClient({ link: httpLink, cache: new InMemoryCache(), defaultOptions: { watchQuery: { fetchPolicy: 'cache-and-network', errorPolicy: 'all' }, query: { fetchPolicy: 'network-only', errorPolicy: 'all' }, mutate: { errorPolicy: 'all' } }});带认证的配置import { ApolloClient, InMemoryCache, createHttpLink, ApolloLink } from '@apollo/client';import { setContext } from '@apollo/client/link/context';const httpLink = createHttpLink({ uri: 'https://api.example.com/graphql'});const authLink = setContext((_, { headers }) => { const token = localStorage.getItem('token'); return { headers: { ...headers, authorization: token ? `Bearer ${token}` : '' } };});const client = new ApolloClient({ link: authLink.concat(httpLink), cache: new InMemoryCache()});2. 查询数据使用 useQuery Hookimport { useQuery, gql } from '@apollo/client';const GET_USERS = gql` query GetUsers($limit: Int, $offset: Int) { users(limit: $limit, offset: $offset) { id name email createdAt } }`;function UserList() { const { loading, error, data, fetchMore } = useQuery(GET_USERS, { variables: { limit: 10, offset: 0 }, notifyOnNetworkStatusChange: true }); if (loading) return <div>Loading...</div>; if (error) return <div>Error: {error.message}</div>; return ( <div> {data.users.map(user => ( <div key={user.id}> <h3>{user.name}</h3> <p>{user.email}</p> </div> ))} <button onClick={() => fetchMore({ variables: { offset: data.users.length }, updateQuery: (prev, { fetchMoreResult }) => { if (!fetchMoreResult) return prev; return { users: [...prev.users, ...fetchMoreResult.users] }; } })} > Load More </button> </div> );}使用 lazy queryimport { useLazyQuery, gql } from '@apollo/client';const GET_USER = gql` query GetUser($id: ID!) { user(id: $id) { id name email } }`;function UserSearch() { const [getUser, { loading, error, data }] = useLazyQuery(GET_USER); const [userId, setUserId] = useState(''); return ( <div> <input value={userId} onChange={(e) => setUserId(e.target.value)} placeholder="Enter user ID" /> <button onClick={() => getUser({ variables: { id: userId } })}> Search </button> {loading && <div>Loading...</div>} {error && <div>Error: {error.message}</div>} {data && ( <div> <h3>{data.user.name}</h3> <p>{data.user.email}</p> </div> )} </div> );}3. 修改数据使用 useMutation Hookimport { useMutation, gql } from '@apollo/client';const CREATE_USER = gql` mutation CreateUser($input: CreateUserInput!) { createUser(input: $input) { id name email } }`;function CreateUserForm() { const [createUser, { loading, error }] = useMutation(CREATE_USER, { update(cache, { data: { createUser } }) { cache.modify({ fields: { users(existingUsers = []) { const newUserRef = cache.writeFragment({ data: createUser, fragment: gql` fragment NewUser on User { id name email } ` }); return [...existingUsers, newUserRef]; } } }); } }); const [name, setName] = useState(''); const [email, setEmail] = useState(''); const handleSubmit = (e) => { e.preventDefault(); createUser({ variables: { input: { name, email } } }); }; return ( <form onSubmit={handleSubmit}> <input value={name} onChange={(e) => setName(e.target.value)} placeholder="Name" /> <input value={email} onChange={(e) => setEmail(e.target.value)} placeholder="Email" /> <button type="submit" disabled={loading}> {loading ? 'Creating...' : 'Create User'} </button> {error && <div>Error: {error.message}</div>} </form> );}乐观更新const [updateUser, { loading }] = useMutation(UPDATE_USER, { optimisticResponse: (variables) => ({ updateUser: { __typename: 'User', id: variables.id, name: variables.input.name, email: variables.input.email } }), update(cache, { data: { updateUser } }) { cache.writeFragment({ id: `User:${updateUser.id}`, fragment: gql` fragment UpdateUser on User { name email } `, data: updateUser }); }});4. 缓存管理配置缓存策略const cache = new InMemoryCache({ typePolicies: { Query: { fields: { users: { keyArgs: ['filter', 'sort'], merge(existing = [], incoming) { return [...existing, ...incoming]; } }, read(existing, { args: { offset, limit } }) { return existing && existing.slice(offset, offset + limit); } }, user: { read(_, { args, toReference }) { return toReference({ __typename: 'User', id: args.id }); } } } }, User: { keyFields: ['id', 'email'] } }});手动更新缓存import { useApolloClient } from '@apollo/client';function UpdateUserButton({ userId, newData }) { const client = useApolloClient(); const handleClick = () => { client.writeFragment({ id: `User:${userId}`, fragment: gql` fragment UserFragment on User { name email } `, data: newData }); }; return <button onClick={handleClick}>Update User</button>;}清除缓存function ClearCacheButton() { const client = useApolloClient(); const handleClick = () => { client.clearStore(); }; return <button onClick={handleClick}>Clear Cache</button>;}5. 分页基于偏移的分页const GET_POSTS = gql` query GetPosts($offset: Int, $limit: Int) { posts(offset: $offset, limit: $limit) { id title content author { name } } }`;function PostList() { const { loading, data, fetchMore } = useQuery(GET_POSTS, { variables: { offset: 0, limit: 10 } }); return ( <div> {data?.posts.map(post => ( <div key={post.id}> <h3>{post.title}</h3> <p>{post.content}</p> </div> ))} <button onClick={() => fetchMore({ variables: { offset: data.posts.length }, updateQuery: (prev, { fetchMoreResult }) => { if (!fetchMoreResult) return prev; return { posts: [...prev.posts, ...fetchMoreResult.posts] }; } })} > Load More </button> </div> );}基于游标的分页const GET_POSTS = gql` query GetPosts($after: String, $first: Int) { posts(after: $after, first: $first) { edges { node { id title content } cursor } pageInfo { hasNextPage endCursor } } }`;function PostList() { const { loading, data, fetchMore } = useQuery(GET_POSTS, { variables: { first: 10 } }); return ( <div> {data?.posts.edges.map(({ node }) => ( <div key={node.id}> <h3>{node.title}</h3> <p>{node.content}</p> </div> ))} {data?.posts.pageInfo.hasNextPage && ( <button onClick={() => fetchMore({ variables: { after: data.posts.pageInfo.endCursor }, updateQuery: (prev, { fetchMoreResult }) => { if (!fetchMoreResult) return prev; return { posts: { ...fetchMoreResult.posts, edges: [ ...prev.posts.edges, ...fetchMoreResult.posts.edges ] } }; } })} > Load More </button> )} </div> );}6. 错误处理处理 GraphQL 错误function UserList() { const { loading, error, data } = useQuery(GET_USERS); if (loading) return <div>Loading...</div>; if (error) { if (error.graphQLErrors) { return ( <div> GraphQL Errors: {error.graphQLErrors.map((err, i) => ( <div key={i}>{err.message}</div> ))} </div> ); } if (error.networkError) { return <div>Network Error: {error.networkError.message}</div>; } return <div>Error: {error.message}</div>; } return <div>{/* render data */}</div>;}全局错误处理import { ApolloClient, InMemoryCache, ApolloLink, HttpLink } from '@apollo/client';import { onError } from '@apollo/client/link/error';const errorLink = onError(({ graphQLErrors, networkError, operation, forward }) => { if (graphQLErrors) { graphQLErrors.forEach(({ message, locations, path }) => { console.error( `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}` ); }); } if (networkError) { console.error(`[Network error]: ${networkError}`); }});const httpLink = new HttpLink({ uri: 'https://api.example.com/graphql'});const client = new ApolloClient({ link: errorLink.concat(httpLink), cache: new InMemoryCache()});7. 订阅使用 useSubscription Hookimport { useSubscription, gql } from '@apollo/client';const MESSAGE_ADDED = gql` subscription OnMessageAdded($roomId: ID!) { messageAdded(roomId: $roomId) { id text author { name } createdAt } }`;function ChatRoom({ roomId }) { const { data, loading, error } = useSubscription(MESSAGE_ADDED, { variables: { roomId } }); if (loading) return <div>Connecting...</div>; if (error) return <div>Error: {error.message}</div>; return ( <div> <h3>New Message:</h3> <p>{data.messageAdded.text}</p> <small>By {data.messageAdded.author.name}</small> </div> );}8. 性能优化使用 persisted queriesimport { createPersistedQueryLink } from '@apollo/client/link/persisted-queries';import { sha256 } from 'crypto-hash';const persistedQueryLink = createPersistedQueryLink({ sha256, useGETForHashedQueries: true});const client = new ApolloClient({ link: persistedQueryLink.concat(httpLink), cache: new InMemoryCache()});批量查询import { useQuery, gql } from '@apollo/client';const GET_MULTIPLE_USERS = gql` query GetMultipleUsers($ids: [ID!]!) { users(ids: $ids) { id name email } }`;function UserList({ userIds }) { const { loading, data } = useQuery(GET_MULTIPLE_USERS, { variables: { ids: userIds } }); if (loading) return <div>Loading...</div>; return ( <div> {data.users.map(user => ( <div key={user.id}>{user.name}</div> ))} </div> );}9. 客户端开发最佳实践| 实践 | 说明 ||------|------|| 使用缓存 | 减少网络请求,提高性能 || 实现乐观更新 | 提升用户体验 || 处理错误 | 提供友好的错误提示 || 使用分页 | 处理大量数据 || 实现加载状态 | 改善用户体验 || 使用订阅 | 实现实时更新 || 优化查询 | 只请求需要的字段 || 使用 persisted queries | 减少网络传输 || 实现重试机制 | 提高可靠性 || 监控性能 | 及时发现性能问题 |10. 常见问题及解决方案| 问题 | 原因 | 解决方案 ||------|------|----------|| 缓存未更新 | 缓存策略配置不当 | 调整缓存策略,手动更新缓存 || 查询慢 | 请求过多数据 | 优化查询,使用字段选择 || 内存占用高 | 缓存数据过多 | 定期清理缓存,限制缓存大小 || 订阅断开 | 网络不稳定 | 实现自动重连机制 || 错误处理不当 | 未正确处理错误 | 实现全局错误处理 |
阅读 0·2月21日 17:00

Kafka 与 RabbitMQ、RocketMQ 有什么区别?

Kafka 与其他消息队列的对比Kafka 作为分布式流处理平台,与传统消息队列(如 RabbitMQ、RocketMQ、ActiveMQ)相比,在设计理念、性能特性和应用场景上都有显著差异。理解这些差异对于技术选型和系统架构设计非常重要。Kafka vs RabbitMQ架构设计Kafka:分布式架构,支持水平扩展基于日志存储,消息持久化到磁盘采用 Pull 模式,Consumer 主动拉取消息无状态 Broker,消息存储在文件系统RabbitMQ:集中式架构,支持集群模式基于内存存储,消息存储在内存或磁盘采用 Push 模式,Broker 主动推送消息有状态 Broker,消息存储在内部数据库性能特性Kafka:高吞吐量:单机可达百万级 TPS低延迟:毫秒级延迟高并发:支持大量并发连接顺序读写:利用磁盘顺序读写优势RabbitMQ:中等吞吐量:单机万级 TPS低延迟:微秒级延迟中等并发:并发连接数有限随机读写:内存访问速度快消息可靠性Kafka:消息持久化到磁盘支持副本机制保证数据不丢失支持消息回溯消息保留时间可配置RabbitMQ:消息可持久化到磁盘支持消息确认机制支持死信队列消息默认不持久化功能特性Kafka:支持消息回溯支持消息压缩支持事务消息支持流处理(Kafka Streams)RabbitMQ:支持消息路由(Exchange、Binding)支持消息优先级支持延迟消息支持消息 TTLKafka vs RocketMQ架构设计Kafka:纯分布式架构无中心化设计基于 ZooKeeper 协调简单的存储模型RocketMQ:分布式架构支持 NameServer 协调支持主从架构复杂的存储模型性能特性Kafka:吞吐量更高延迟稍高批量处理能力强零拷贝技术优化RocketMQ:吞吐量较高延迟较低单条消息处理快事务消息性能好消息可靠性Kafka:副本机制保证可靠性支持同步和异步复制数据持久化到磁盘支持消息回溯RocketMQ:主从同步保证可靠性支持同步双写和异步复制支持消息刷盘策略支持消息重试功能特性Kafka:流处理能力强生态丰富(Kafka Connect、Kafka Streams)社区活跃文档完善RocketMQ:事务消息支持完善支持消息过滤支持定时消息支持消息轨迹Kafka vs ActiveMQ架构设计Kafka:现代分布式架构无状态设计水平扩展能力强存储与计算分离ActiveMQ:传统消息队列架构有状态设计垂直扩展为主存储与计算耦合性能特性Kafka:高吞吐量低延迟高并发顺序读写优化ActiveMQ:中等吞吐量中等延迟低并发传统数据库存储消息可靠性Kafka:副本机制持久化存储消息回溯高可用性ActiveMQ:持久化存储消息确认事务支持主从复制技术选型建议选择 Kafka 的场景大数据场景日志收集实时数据分析流式处理数据管道高吞吐量场景每秒百万级消息批量数据处理大规模数据传输消息回溯需求需要重新消费历史消息需要多订阅者消费需要消息持久化流处理场景实时计算事件驱动架构复杂事件处理选择 RabbitMQ 的场景复杂路由场景需要灵活的消息路由需要消息过滤需要多条件匹配低延迟场景微秒级延迟要求实时性要求高消息量适中企业应用场景企业级消息中间件传统的消息队列需求需要丰富的管理功能选择 RocketMQ 的场景金融场景事务消息需求高可靠性要求消息顺序要求电商场景订单处理库存同步消息轨迹追踪阿里生态使用阿里云服务需要 Spring Cloud 集成需要完善的技术支持选择 ActiveMQ 的场景传统应用JMS 规范要求传统企业应用简单消息队列需求小规模应用消息量不大部署简单维护成本低性能对比总结| 特性 | Kafka | RabbitMQ | RocketMQ | ActiveMQ ||------|-------|----------|----------|----------|| 吞吐量 | 极高 | 中等 | 高 | 低 || 延迟 | 毫秒级 | 微秒级 | 毫秒级 | 中等 || 可靠性 | 高 | 高 | 高 | 中等 || 扩展性 | 极强 | 中等 | 强 | 弱 || 复杂度 | 中等 | 高 | 高 | 中等 || 生态 | 丰富 | 丰富 | 一般 | 一般 |最佳实践根据业务场景选择大数据场景优先选择 Kafka复杂路由场景优先选择 RabbitMQ金融场景优先选择 RocketMQ考虑团队能力选择团队熟悉的技术栈考虑学习和维护成本评估技术支持能力评估长期规划考虑业务增长需求评估技术发展趋势规划技术演进路线通过对比不同消息队列的特性和适用场景,可以做出更合理的技术选型决策。
阅读 0·2月21日 17:00

Kafka 的 Consumer Group Rebalance 机制是什么?

Kafka Consumer Group Rebalance 机制Consumer Group Rebalance 是 Kafka 中一个重要的机制,用于在 Consumer Group 成员变化时重新分配 Partition。理解 Rebalance 机制对于保证 Kafka 消费的稳定性和高可用性至关重要。Rebalance 触发条件1. Consumer 成员变化新 Consumer 加入:新的 Consumer 实例加入 Consumer GroupConsumer 退出:Consumer 实例正常退出或异常退出Consumer 故障:Consumer 宕机或网络中断Consumer 超时:Consumer 超过 session.timeout.ms 未发送心跳2. Partition 数量变化Topic Partition 增加:Topic 的 Partition 数量增加Topic Partition 减少:Topic 的 Partition 数量减少Topic 删除:Topic 被删除3. 订阅变化Consumer 订阅新 Topic:Consumer 开始订阅新的 TopicConsumer 取消订阅:Consumer 取消订阅某个 TopicRebalance 过程1. Rebalance 触发触发条件满足后,Controller 检测到需要 RebalanceController 通知 Group Coordinator 启动 Rebalance2. Join Group 阶段所有 Consumer 向 Group Coordinator 发送 JoinGroup 请求Group Coordinator 选择一个 Consumer 作为 LeaderLeader Consumer 负责制定 Partition 分配方案3. Sync Group 阶段Leader Consumer 将分配方案发送给 Group CoordinatorGroup Coordinator 将分配方案发送给所有 ConsumerConsumer 接收分配方案并开始消费4. 完成阶段Consumer 开始消费分配到的 PartitionRebalance 过程完成Rebalance 策略Range 策略(默认)原理:按照 Partition 的顺序和 Consumer 的顺序进行分配分配规则:将 Partition 按照数字顺序排序将 Consumer 按照名称排序每个 Consumer 分配连续的 Partition 范围示例:Topic: test-topic, Partitions: 0,1,2,3,4,5Consumers: C1, C2, C3分配结果:C1: 0,1C2: 2,3C3: 4,5特点:分配相对均匀可能导致分配不均衡(当 Partition 数不能被 Consumer 数整除时)RoundRobin 策略原理:轮询分配 Partition分配规则:将所有 Topic 的 Partition 合并按照轮询方式分配给 Consumer示例:Topic1: 0,1,2Topic2: 0,1Consumers: C1, C2分配结果:C1: Topic1-0, Topic1-2, Topic2-1C2: Topic1-1, Topic2-0特点:分配更均匀适用于多个 Topic 的情况Sticky 策略原理:在保证分配均匀的前提下,尽量保持原有分配特点:减少 Partition 在 Consumer 之间的移动降低 Rebalance 的影响提高消费的连续性CooperativeSticky 策略原理:增量式 Rebalance,只重新分配受影响的 Partition特点:减少 Stop-the-world 时间提高系统可用性适用于对连续性要求高的场景Rebalance 配置关键参数# 会话超时时间session.timeout.ms=30000# 心跳间隔时间heartbeat.interval.ms=3000# 最大 poll 间隔时间max.poll.interval.ms=300000# Rebalance 超时时间max.poll.records=500参数说明session.timeout.ms:Consumer 超过此时间未发送心跳,将被认为失效heartbeat.interval.ms:Consumer 发送心跳的间隔时间max.poll.interval.ms:Consumer 两次 poll 之间的最大间隔时间max.poll.records:每次 poll 最多返回的消息数Rebalance 问题及解决方案1. Rebalance 频繁触发原因:Consumer 频繁上下线网络不稳定导致心跳超时消费处理时间过长解决方案:# 增加会话超时时间session.timeout.ms=60000# 增加心跳间隔heartbeat.interval.ms=5000# 增加 poll 间隔max.poll.interval.ms=6000002. Rebalance 时间过长原因:Consumer 数量过多Partition 数量过多网络延迟高解决方案:使用 CooperativeSticky 策略减少 Consumer 数量优化网络配置3. Rebalance 导致消息重复消费原因:Rebalance 期间 Consumer 可能重复消费消息Offset 提交不及时解决方案:# 禁用自动提交enable.auto.commit=false# 手动提交 Offsetconsumer.commitSync();4. Rebalance 导致消费中断原因:Rebalance 期间 Consumer 停止消费Stop-the-world 时间过长解决方案:使用 CooperativeSticky 策略减少 Rebalance 触发频率优化 Rebalance 配置最佳实践1. 合理配置参数# 推荐配置session.timeout.ms=30000heartbeat.interval.ms=3000max.poll.interval.ms=300000max.poll.records=5002. 选择合适的 Rebalance 策略一般场景:使用 Range 或 RoundRobin需要连续性:使用 Sticky高可用要求:使用 CooperativeSticky3. 监控 Rebalance# 查看 Consumer Group 状态kafka-consumer-groups --bootstrap-server localhost:9092 \ --describe --group my-group# 查看 Rebalance 日志tail -f /path/to/kafka/logs/server.log | grep Rebalance4. 优化消费逻辑避免长时间处理单条消息使用异步处理提高效率合理设置批量处理大小5. 预防 Rebalance保持 Consumer 稳定运行避免频繁启停 Consumer监控 Consumer 健康状态Rebalance 监控指标RebalanceRatePerSec:每秒 Rebalance 次数RebalanceTotal:Rebalance 总次数FailedRebalanceRate:失败的 Rebalance 比例SuccessfulRebalanceRate:成功的 Rebalance 比例通过合理配置和优化 Rebalance 机制,可以有效减少 Rebalance 对系统的影响,提高 Kafka 消费的稳定性和可用性。
阅读 0·2月21日 17:00

CDN 如何实现视频加速?有哪些关键技术?

CDN 视频加速的重要性视频内容是互联网流量消耗的主要来源,占据了全球互联网流量的 60% 以上。CDN 视频加速通过优化视频传输和播放体验,显著提升用户满意度,降低带宽成本。视频加速的核心技术1. 自适应码率(ABR)工作原理自适应码率根据用户的网络状况和设备能力,动态调整视频的码率和分辨率:流程:客户端检测网络带宽选择合适的码率档位下载对应码率的视频片段持续监控网络状况动态调整码率码率档位示例:{ "streams": [ { "bitrate": 500000, "resolution": "640x360", "fps": 30, "codec": "h264" }, { "bitrate": 1000000, "resolution": "854x480", "fps": 30, "codec": "h264" }, { "bitrate": 2000000, "resolution": "1280x720", "fps": 30, "codec": "h264" }, { "bitrate": 4000000, "resolution": "1920x1080", "fps": 30, "codec": "h264" }, { "bitrate": 8000000, "resolution": "1920x1080", "fps": 60, "codec": "h265" } ]}ABR 算法常见算法:1. 基于缓冲区的算法function selectBitrate(bufferLevel, bitrates) { if (bufferLevel < 5) { // 缓冲区低,选择最低码率 return bitrates[0]; } else if (bufferLevel < 10) { // 缓冲区中等,选择中等码率 return bitrates[Math.floor(bitrates.length / 2)]; } else { // 缓冲区充足,选择最高码率 return bitrates[bitrates.length - 1]; }}2. 基于吞吐量的算法function selectBitrate(throughput, bitrates) { // 选择不超过当前吞吐量的最高码率 return bitrates.filter(b => b.bitrate <= throughput) .sort((a, b) => b.bitrate - a.bitrate)[0] || bitrates[0];}3. 混合算法function selectBitrate(bufferLevel, throughput, bitrates) { // 结合缓冲区和吞吐量 const bufferFactor = Math.min(bufferLevel / 30, 1); const throughputFactor = Math.min(throughput / 5000000, 1); const combinedFactor = (bufferFactor + throughputFactor) / 2; const index = Math.floor(combinedFactor * (bitrates.length - 1)); return bitrates[index];}2. 视频编码优化编码格式选择主流编码格式:H.264/AVC优点:兼容性好,广泛支持缺点:压缩效率相对较低适用场景:需要广泛兼容性的场景H.265/HEVC优点:比 H.264 小 50%缺点:编码计算量大,部分旧设备不支持适用场景:高清视频,带宽受限场景VP9优点:开源,比 H.264 小 40%缺点:编码时间长,兼容性一般适用场景:Web 视频播放AV1优点:最新标准,比 H.264 小 60%缺点:编码计算量极大,支持度有限适用场景:未来视频,超高清视频编码参数优化关键参数:1. 码率控制# 使用 FFmpeg 编码ffmpeg -i input.mp4 \ -c:v libx264 \ -b:v 2000k \ -maxrate 2500k \ -bufsize 5000k \ -c:a aac \ -b:a 128k \ output.mp42. 分辨率适配# 生成多分辨率版本ffmpeg -i input.mp4 \ -vf "scale=640:360" \ -c:v libx264 \ -b:v 500k \ output_360p.mp4ffmpeg -i input.mp4 \ -vf "scale=1280:720" \ -c:v libx264 \ -b:v 2000k \ output_720p.mp43. 帧率优化# 降低帧率以减少码率ffmpeg -i input.mp4 \ -r 24 \ -c:v libx264 \ -b:v 1500k \ output_24fps.mp43. 视频分段和流媒体协议HLS(HTTP Live Streaming)特点:Apple 开发,广泛支持基于 HTTP,易于部署支持 AES 加密文件结构:video.m3u8 (主播放列表)├── stream0.m3u8 (子播放列表)│ ├── segment0.ts│ ├── segment1.ts│ └── ...├── stream1.m3u8│ ├── segment0.ts│ ├── segment1.ts│ └── ...└── ...M3U8 播放列表示例:#EXTM3U#EXT-X-VERSION:3#EXT-X-TARGETDURATION:10#EXT-X-MEDIA-SEQUENCE:0#EXTINF:10.0,segment0.ts#EXTINF:10.0,segment1.ts#EXTINF:10.0,segment2.ts#EXT-X-ENDLISTDASH(Dynamic Adaptive Streaming over HTTP)特点:国际标准,跨平台支持基于 XML 描述支持多种编码格式MPD 文件示例:<?xml version="1.0"?><MPD xmlns="urn:mpeg:dash:schema:mpd:2011" type="static"> <Period> <AdaptationSet mimeType="video/mp4"> <Representation id="1" bandwidth="500000" width="640" height="360"> <BaseURL>video_360p.mp4</BaseURL> </Representation> <Representation id="2" bandwidth="2000000" width="1280" height="720"> <BaseURL>video_720p.mp4</BaseURL> </Representation> </AdaptationSet> </Period></MPD>4. CDN 视频缓存优化分段缓存策略策略:按视频片段缓存热门片段优先缓存预加载后续片段配置示例:# 缓存视频片段location ~* \.ts$ { proxy_cache video_cache; proxy_cache_valid 200 1h; proxy_cache_key "$scheme$request_method$host$uri"; # 缓存锁,防止缓存击穿 proxy_cache_lock on; proxy_cache_lock_timeout 5s;}# 缓存播放列表location ~* \.m3u8$ { proxy_cache video_cache; proxy_cache_valid 200 5m; proxy_no_cache $http_pragma $http_authorization;}智能预加载策略:预加载下一片段预加载下一码率档位基于用户行为预测实现示例:// 预加载下一片段function preloadNextSegment(currentSegment, nextSegmentUrl) { const preloadLink = document.createElement('link'); preloadLink.rel = 'preload'; preloadLink.href = nextSegmentUrl; preloadLink.as = 'video'; document.head.appendChild(preloadLink);}// 预加载多个片段function preloadSegments(segments, count = 3) { segments.slice(0, count).forEach(segment => { preloadNextSegment(null, segment.url); });}5. 视频播放优化首屏时间优化优化策略:1. 使用关键帧# 增加关键帧频率ffmpeg -i input.mp4 \ -c:v libx264 \ -g 30 \ -keyint_min 30 \ output.mp42. 优化首帧# 从关键帧开始编码ffmpeg -i input.mp4 \ -c:v libx264 \ -force_key_frames "expr:gte(t,n_forced*2)" \ output.mp43. 预加载首屏// 预加载首屏内容video.addEventListener('loadedmetadata', () => { const preloadTime = 5; // 预加载 5 秒 video.currentTime = Math.min(video.duration, preloadTime); video.currentTime = 0; // 回到开头});拖动优化优化策略:1. 快速定位// 快速定位到指定时间function seekToTime(video, time) { const segmentIndex = Math.floor(time / segmentDuration); const segmentUrl = getSegmentUrl(segmentIndex); // 直接加载目标片段 video.src = segmentUrl; video.currentTime = time % segmentDuration;}2. 预加载拖动位置// 预加载拖动位置周围的片段video.addEventListener('seeking', () => { const currentTime = video.currentTime; const segmentIndex = Math.floor(currentTime / segmentDuration); // 预加载前后各 2 个片段 for (let i = segmentIndex - 2; i <= segmentIndex + 2; i++) { if (i >= 0 && i < totalSegments) { preloadSegment(i); } }});6. 视频传输优化协议优化HTTP/2 优势:多路复用:减少连接数头部压缩:减少传输开销服务器推送:主动推送资源HTTP/3 优势:基于 UDP:减少连接建立时间改进的拥塞控制:更好的网络适应性连接迁移:支持网络切换配置示例:listen 443 ssl http2;listen 443 ssl http3;传输协议选择TCP vs UDP:TCP:优点:可靠传输,广泛支持缺点:延迟较高,不适合实时直播UDP:优点:低延迟,适合实时直播缺点:不可靠,需要应用层重传选择建议:点播:使用 TCP(HTTP)实时直播:使用 UDP(如 WebRTC)7. 视频质量监控关键指标播放质量指标:启动时间:从点击播放到开始播放的时间缓冲次数:播放过程中的缓冲次数缓冲时长:每次缓冲的持续时间码率切换次数:自适应码率切换的频率用户体验指标:卡顿率:卡顿时间占总播放时间的比例平均码率:播放期间的平均码率分辨率:播放期间的平均分辨率监控实现示例:// 视频播放监控const videoMetrics = { startTime: null, bufferEvents: [], bitrateChanges: [], currentBitrate: null};video.addEventListener('play', () => { videoMetrics.startTime = Date.now();});video.addEventListener('waiting', () => { videoMetrics.bufferEvents.push({ time: Date.now(), duration: null });});video.addEventListener('playing', () => { const lastBufferEvent = videoMetrics.bufferEvents[videoMetrics.bufferEvents.length - 1]; if (lastBufferEvent && lastBufferEvent.duration === null) { lastBufferEvent.duration = Date.now() - lastBufferEvent.time; }});function reportMetrics() { const metrics = { startupTime: videoMetrics.startTime ? Date.now() - videoMetrics.startTime : 0, bufferCount: videoMetrics.bufferEvents.length, totalBufferTime: videoMetrics.bufferEvents.reduce((sum, event) => sum + (event.duration || 0), 0), bitrateChanges: videoMetrics.bitrateChanges.length }; // 发送到监控服务器 fetch('/api/video-metrics', { method: 'POST', body: JSON.stringify(metrics) });}// 定期上报setInterval(reportMetrics, 30000);CDN 视频加速最佳实践1. 内容准备使用现代编码格式(H.265、AV1)生成多码率档位优化关键帧间隔压缩音频2. 缓存策略按片段缓存热门内容优先缓存设置合理的 TTL使用缓存预热3. 传输优化使用 HTTP/2 或 HTTP/3启用压缩优化 TCP 参数使用 CDN 边缘节点4. 播放优化优化首屏时间实现智能预加载优化拖动体验提供降级方案5. 监控和分析实时监控播放质量分析用户行为优化码率切换算法持续改进常见问题及解决方案问题 1:首屏加载慢原因:码率过高网络延迟高缓存未命中解决方案:降低初始码率使用 CDN 加速预热热门内容问题 2:频繁缓冲原因:网络不稳定码率切换不及时缓存策略不当解决方案:优化 ABR 算法增加缓冲区大小优化缓存策略问题 3:画质模糊原因:码率过低编码质量差分辨率不匹配解决方案:提高码率优化编码参数自适应分辨率面试要点回答这个问题时应该强调:理解视频加速的核心技术掌握自适应码率的实现原理了解主流流媒体协议的优缺点有实际的视频加速优化经验能够分析和解决视频播放问题
阅读 0·2月21日 16:59

CDN 故障排查的流程是什么?有哪些常用工具?

CDN 故障排查的重要性CDN 作为网站和应用的流量入口,其故障会直接影响用户体验和业务可用性。掌握 CDN 故障排查的方法和技巧,能够快速定位和解决问题,最小化故障影响。常见 CDN 故障类型1. 访问失败症状:用户无法访问网站返回 5xx 错误连接超时可能原因:CDN 节点故障DNS 解析问题源站故障网络连接问题2. 性能下降症状:响应时间变慢频繁缓冲(视频)加载时间延长可能原因:缓存命中率低网络拥塞源站负载高CDN 节点过载3. 内容不一致症状:用户看到旧内容不同地区看到不同内容更新后未生效可能原因:缓存未刷新TTL 设置过长缓存键配置错误多 CDN 配置不一致4. 安全问题症状:遭受 DDoS 攻击恶意爬虫访问数据泄露可能原因:安全配置不当防护策略不足漏洞未修复故障排查流程1. 确认故障范围检查步骤:1. 确认用户范围# 检查是否有大量用户报告# 查看监控数据# 分析错误日志2. 确认地理范围# 检查是否特定地区受影响# 使用地理位置工具# 分析访问日志3. 确认时间范围# 检查故障开始时间# 查看时间序列数据# 对比历史数据2. 检查 CDN 状态检查项:1. CDN 节点状态# 检查节点健康状态curl -I https://cdn.example.com/health# 检查多个节点for node in node1 node2 node3; do curl -I https://$node.example.com/healthdone2. CDN 控制台查看节点状态检查告警信息分析流量图表3. CDN API// 使用 CDN API 检查状态const response = await fetch('https://api.cdn.com/status', { headers: { 'Authorization': 'Bearer {api_token}' }})const status = await response.json()console.log(status)3. 检查 DNS 解析检查步骤:1. 检查 DNS 解析# 检查域名解析dig example.com# 检查特定 DNS 服务器dig @8.8.8.8 example.com# 检查 CNAME 记录dig CNAME cdn.example.com2. 检查 DNS 传播# 检查多个 DNS 服务器for dns in 8.8.8.8 1.1.1.1 114.114.114.114; do echo "DNS: $dns" dig @$dns example.comdone3. 检查 DNS 缓存# 清除本地 DNS 缓存# macOSsudo dscacheutil -flushcachesudo killall -HUP mDNSResponder# Linuxsudo systemctl restart nscd4. 检查网络连接检查步骤:1. 检查网络延迟# Ping 测试ping cdn.example.com# Traceroute 测试traceroute cdn.example.com# MTR 测试(结合 ping 和 traceroute)mtr cdn.example.com2. 检查端口连接# 检查 HTTP 端口telnet cdn.example.com 80# 检查 HTTPS 端口telnet cdn.example.com 443# 使用 nc 测试nc -zv cdn.example.com 4433. 检查 SSL/TLS# 检查 SSL 证书openssl s_client -connect cdn.example.com:443 -servername cdn.example.com# 检查 SSL 证书有效期echo | openssl s_client -connect cdn.example.com:443 2>/dev/null | openssl x509 -noout -dates5. 检查缓存状态检查步骤:1. 检查缓存命中率# 分析访问日志grep "HIT" access.log | wc -lgrep "MISS" access.log | wc -l# 计算缓存命中率hit_count=$(grep "HIT" access.log | wc -l)total_count=$(wc -l < access.log)hit_rate=$((hit_count * 100 / total_count))echo "Cache hit rate: $hit_rate%"2. 检查缓存键# 检查缓存键配置nginx -T | grep proxy_cache_key# 分析缓存键差异grep "cache_key" access.log | sort | uniq -c3. 检查缓存过期# 检查缓存 TTLcurl -I https://cdn.example.com/file.jpg | grep -i cache-control# 检查缓存过期时间curl -I https://cdn.example.com/file.jpg | grep -i expires6. 检查源站状态检查步骤:1. 直接访问源站# 直接访问源站测试curl -I https://origin.example.com/file.jpg# 检查源站响应时间time curl https://origin.example.com/file.jpg2. 检查源站负载# 检查 CPU 使用率top# 检查内存使用率free -h# 检查磁盘使用率df -h# 检查网络连接netstat -an | grep ESTABLISHED | wc -l3. 检查源站日志# 检查错误日志tail -f /var/log/nginx/error.log# 检查访问日志tail -f /var/log/nginx/access.log# 检查慢查询tail -f /var/log/mysql/slow.log常用排查工具1. 网络诊断工具Ping# 基本使用ping cdn.example.com# 指定次数ping -c 10 cdn.example.com# 指定包大小ping -s 1024 cdn.example.comTraceroute# 基本使用traceroute cdn.example.com# 使用 ICMPtraceroute -I cdn.example.com# 指定端口traceroute -p 443 cdn.example.comMTR# 基本使用mtr cdn.example.com# 指定报告模式mtr -r -c 10 cdn.example.com# 保存到文件mtr -r -c 10 cdn.example.com > mtr_report.txt2. HTTP 调试工具Curl# 基本请求curl https://cdn.example.com/file.jpg# 查看响应头curl -I https://cdn.example.com/file.jpg# 查看详细信息curl -v https://cdn.example.com/file.jpg# 查看请求和响应头curl -i https://cdn.example.com/file.jpg# 指定请求头curl -H "User-Agent: Mozilla/5.0" https://cdn.example.com/file.jpg# 查看响应时间curl -w "@curl-format.txt" -o /dev/null -s https://cdn.example.com/file.jpgcurl-format.txt: time_namelookup: %{time_namelookup}\n time_connect: %{time_connect}\n time_appconnect: %{time_appconnect}\n time_pretransfer: %{time_pretransfer}\n time_redirect: %{time_redirect}\n time_starttransfer: %{time_starttransfer}\n ----------\n time_total: %{time_total}\nWget# 基本下载wget https://cdn.example.com/file.jpg# 查看详细信息wget -d https://cdn.example.com/file.jpg# 保存响应头wget -S https://cdn.example.com/file.jpg# 指定超时wget -T 10 https://cdn.example.com/file.jpg3. 浏览器开发者工具Network 面板查看请求详情:请求 URL请求方法请求头响应头响应时间状态码查看瀑布图:请求时间线等待时间下载时间总时间Console 面板查看错误信息:JavaScript 错误网络错误资源加载错误4. 日志分析工具ELK StackElasticsearch 查询:// 查询特定错误{ "query": { "match": { "status": 502 } }}// 查询特定时间段{ "query": { "range": { "@timestamp": { "gte": "2026-02-19T00:00:00", "lte": "2026-02-19T23:59:59" } } }}Kibana 可视化:请求量趋势图错误率分布图响应时间分布图AWStats分析访问日志:# 生成报告awstats.pl -config=cdn -update# 查看报告awstats.pl -config=cdn -output故障排查案例案例 1:网站访问缓慢问题描述:用户反馈网站加载缓慢排查步骤:1. 检查 CDN 节点# Ping 测试ping cdn.example.com# 检查响应时间curl -w "@curl-format.txt" -o /dev/null -s https://cdn.example.com/2. 检查缓存命中率# 分析访问日志grep "MISS" access.log | wc -l3. 检查源站# 直接访问源站curl -w "@curl-format.txt" -o /dev/null -s https://origin.example.com/4. 解决方案:提高缓存命中率优化源站性能增加 CDN 节点案例 2:内容更新未生效问题描述:更新内容后,用户仍看到旧内容排查步骤:1. 检查缓存 TTL# 检查缓存控制头curl -I https://cdn.example.com/file.jpg | grep -i cache-control2. 检查缓存键# 检查缓存键配置nginx -T | grep proxy_cache_key3. 检查缓存状态# 检查缓存是否命中curl -I https://cdn.example.com/file.jpg | grep -i x-cache4. 解决方案:刷新 CDN 缓存使用版本控制调整 TTL 设置案例 3:HTTPS 证书错误问题描述:浏览器提示证书错误排查步骤:1. 检查 SSL 证书# 检查证书信息openssl s_client -connect cdn.example.com:443 -servername cdn.example.com# 检查证书有效期echo | openssl s_client -connect cdn.example.com:443 2>/dev/null | openssl x509 -noout -dates2. 检查证书链# 检查证书链完整性openssl s_client -connect cdn.example.com:443 -showcerts3. 解决方案:更新 SSL 证书配置完整的证书链检查证书配置故障预防措施1. 监控告警关键指标:节点可用性响应时间错误率缓存命中率告警配置:# Prometheus 告警规则groups: - name: cdn_alerts rules: - alert: HighErrorRate expr: cdn_errors_total / cdn_requests_total * 100 > 1 for: 5m labels: severity: critical annotations: summary: "High error rate detected"2. 健康检查定期检查:# 健康检查脚本#!/bin/bashwhile true; do status=$(curl -s -o /dev/null -w "%{http_code}" https://cdn.example.com/health) if [ $status -ne 200 ]; then echo "Health check failed: $status" # 发送告警 fi sleep 60done3. 备份和容灾备份策略:定期备份配置备份 SSL 证书备份 DNS 记录容灾方案:多 CDN 策略源站冗余自动故障转移面试要点回答这个问题时应该强调:掌握系统化的故障排查流程熟练使用各种排查工具有实际的故障排查经验能够快速定位和解决问题有故障预防和容灾的意识
阅读 0·2月21日 16:59

CDN 如何防御 DDoS 攻击?有哪些安全防护机制?

CDN 安全防护的重要性随着网络攻击日益频繁和复杂,CDN 不仅是内容分发工具,更是重要的安全防护层。CDN 安全防护能够有效防御各种网络攻击,保护源站和用户数据安全。主要安全威胁1. DDoS 攻击分布式拒绝服务攻击是 CDN 面临的最大威胁:攻击类型:** volumetric attacks**:通过大量流量耗尽带宽Protocol attacks:利用协议漏洞耗尽连接资源Application layer attacks:针对应用层的攻击(如 HTTP Flood)攻击规模:小型攻击:<1 Gbps中型攻击:1-10 Gbps大型攻击:10-100 Gbps超大型攻击:>100 Gbps2. Web 应用攻击针对 Web 应用的各种攻击:常见攻击:SQL 注入:通过输入恶意 SQL 语句获取数据XSS(跨站脚本):注入恶意脚本窃取用户信息CSRF(跨站请求伪造):伪造用户请求执行未授权操作文件包含攻击:包含恶意文件执行代码3. 恶意爬虫和抓取自动化程序大量抓取网站内容:危害:消耗大量带宽和服务器资源窃取网站内容和数据影响正常用户体验4. 数据泄露敏感数据被非法获取:泄露途径:未加密的传输数据配置错误的访问控制漏洞利用CDN 安全防护机制1. DDoS 防护流量清洗CDN 通过分布式网络清洗恶意流量:工作原理:检测异常流量模式识别攻击特征过滤恶意流量转发正常流量清洗能力:边缘清洗:在边缘节点直接过滤中心清洗:汇聚到清洗中心处理混合清洗:边缘和中心结合流量限流限制单个 IP 或用户的请求频率:限流策略:# 限制单个 IP 每秒最多 10 个请求limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;location / { limit_req zone=one burst=20 nodelay;}限流级别:IP 级限流:基于源 IP用户级限流:基于用户标识URL 级限流:基于特定 URL智能识别使用 AI/ML 技术识别攻击:识别方法:行为分析:分析访问模式特征匹配:匹配已知攻击特征机器学习:训练模型识别新型攻击2. WAF(Web Application Firewall)Web 应用防火墙保护应用层安全:WAF 功能核心功能:输入验证:验证和过滤用户输入输出编码:编码输出防止 XSS访问控制:控制访问权限攻击检测:检测和阻止攻击规则示例:# 防止 SQL 注入if ($args ~* "union.*select.*from") { return 403;}# 防止 XSSif ($args ~* "<script|javascript:") { return 403;}WAF 部署模式部署方式:反向代理模式:CDN 作为反向代理透明代理模式:透明拦截流量DNS 模式:通过 DNS 重定向流量3. 访问控制IP 白名单/黑名单控制允许或拒绝访问的 IP:# 白名单allow 192.168.1.0/24;allow 10.0.0.0/8;deny all;# 黑名单deny 1.2.3.4;deny 5.6.7.0/24;allow all;地理位置限制基于地理位置限制访问:应用场景:仅允许特定国家/地区访问阻止来自高风险地区的访问符合地区法规要求配置示例:# 仅允许中国访问geo $allowed_country { default no; CN yes;}if ($allowed_country = no) { return 403;}Referer 检查防止盗链和非法引用:# 检查 Referervalid_referers none blocked example.com *.example.com;if ($invalid_referer) { return 403;}4. 加密传输HTTPS/TLS 加密保护数据传输安全:配置要点:使用强加密套件启用 HSTS定期更新证书ssl_protocols TLSv1.2 TLSv1.3;ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;Token 认证使用 Token 验证请求合法性:Token 生成:import hashlibimport timedef generate_token(secret_key, path, timestamp): data = f"{secret_key}{path}{timestamp}" return hashlib.sha256(data.encode()).hexdigest()Token 验证:# 验证 Tokenif ($arg_token != $expected_token) { return 403;}5. 爬虫防护识别爬虫识别合法和非法爬虫:识别方法:User-Agent 分析行为模式识别访问频率分析爬虫管理管理策略:白名单:允许合法爬虫(如 Googlebot)黑名单:阻止恶意爬虫限流:限制爬虫访问频率# 限制爬虫访问频率if ($http_user_agent ~* "bot|spider|crawler") { limit_req zone=crawler_zone rate=5r/s;}安全监控和告警1. 实时监控监控指标:流量异常:流量突然增加或减少请求异常:异常请求模式错误率:错误率突然升高响应时间:响应时间异常2. 告警机制告警级别:P1(紧急):正在遭受攻击P2(重要):检测到可疑活动P3(一般):安全配置问题告警方式:邮件短信即时通讯工具监控大屏3. 日志分析日志内容:访问日志:记录所有请求安全日志:记录安全事件错误日志:记录错误信息分析工具:ELK Stack(Elasticsearch, Logstash, Kibana)SplunkGraylog安全最佳实践1. 分层防护实施多层安全防护:用户 → CDN 边缘节点 → WAF → 源站各层职责:边缘节点:基础防护、流量清洗WAF:应用层防护源站:深度防护2. 最小权限原则仅授予必要的权限:最小化公开的端口和服务限制访问权限定期审查权限3. 定期安全审计审计内容:安全配置检查漏洞扫描渗透测试安全策略审查4. 应急响应计划响应流程:检测攻击隔离受影响系统分析攻击来源修复漏洞恢复服务总结经验常见安全问题及解决方案问题 1:CC 攻击问题描述:大量 HTTP 请求耗尽服务器资源解决方案:启用 WAF实施限流策略使用验证码IP 黑名单问题 2:盗链问题描述:其他网站引用你的资源解决方案:配置 Referer 检查使用 Token 认证启用防盗链功能问题 3:数据泄露问题描述:敏感数据被非法获取解决方案:启用 HTTPS加密敏感数据实施访问控制定期安全审计面试要点回答这个问题时应该强调:了解 CDN 面临的主要安全威胁掌握 CDN 的各种安全防护机制理解安全防护的分层策略有实际的安全配置和防护经验能够分析和应对安全事件
阅读 0·2月21日 16:59

CDN 的性能监控指标有哪些?如何监控 CDN 的性能?

CDN 性能监控的重要性CDN 性能监控是确保 CDN 服务质量和用户体验的关键环节。通过实时监控 CDN 的各项性能指标,可以及时发现和解决问题,优化 CDN 配置,提升整体性能。核心监控指标1. 延迟指标响应时间定义:从用户发起请求到收到完整响应的时间关键指标:TTFB(Time to First Byte):首字节时间TTLB(Time to Last Byte):末字节时间总响应时间:完整请求响应时间目标值:静态内容:<100ms动态内容:<500msAPI 请求:<200ms网络延迟定义:数据在网络中传输的时间测量方法:# 使用 ping 测量延迟ping cdn.example.com# 使用 traceroute 测量路径延迟traceroute cdn.example.com2. 吞吐量指标带宽使用率定义:实际使用的带宽占总带宽的比例计算公式:带宽使用率 = (当前带宽 / 总带宽) × 100%监控维度:边缘节点带宽回源带宽总带宽使用率请求量关键指标:QPS(Queries Per Second):每秒请求数RPS(Requests Per Second):每秒请求数(同 QPS)峰值 QPS:最高每秒请求数监控示例:// 计算每秒请求数let requestCount = 0setInterval(() => { console.log(`QPS: ${requestCount}`) requestCount = 0}, 1000)// 每个请求增加计数function handleRequest(request) { requestCount++ // 处理请求...}3. 可用性指标节点可用性定义:节点正常提供服务的时间比例计算公式:节点可用性 = (正常运行时间 / 总时间) × 100%目标值:单个节点:>99.9%整体 CDN:>99.99%故障转移时间定义:从节点故障到流量切换到其他节点的时间目标值:故障检测:<5 秒流量切换:<10 秒总故障转移:<15 秒4. 缓存指标缓存命中率定义:从 CDN 缓存返回的请求占总请求的比例计算公式:缓存命中率 = (缓存命中请求数 / 总请求数) × 100%目标值:静态内容:>95%动态内容:>70%整体:>90%优化策略:# 设置合理的缓存时间location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ { expires 1y; add_header Cache-Control "public, immutable";}回源率定义:需要回源的请求占总请求的比例计算公式:回源率 = (回源请求数 / 总请求数) × 100%目标值:<10%5. 错误指标HTTP 错误率定义:返回 4xx/5xx 状态码的请求比例关键错误码:4xx:客户端错误(如 404 Not Found)5xx:服务器错误(如 502 Bad Gateway)目标值:<1%超时率定义:请求超时的比例目标值:<0.1%监控工具和平台1. CDN 自带监控主流 CDN 服务商提供的监控:Cloudflare Analytics功能:实时流量监控请求分析威胁检测性能报告使用示例:// 通过 API 获取监控数据const response = await fetch('https://api.cloudflare.com/client/v4/zones/{zone_id}/analytics/dashboard', { headers: { 'Authorization': 'Bearer {api_token}' }})const data = await response.json()console.log(data)AWS CloudFront Metrics功能:请求量统计字节传输统计错误率监控延迟监控CloudWatch 集成:# 使用 AWS CLI 获取 CloudFront 指标aws cloudwatch get-metric-statistics \ --namespace AWS/CloudFront \ --metric-name Requests \ --dimensions Name=DistributionId,Value={distribution_id} \ --start-time 2026-02-19T00:00:00Z \ --end-time 2026-02-19T23:59:59Z \ --period 3600 \ --statistics Sum2. 第三方监控工具Pingdom功能:网站性能监控可用性监控页面速度测试报警通知特点:全球监控节点详细性能报告易于使用New Relic功能:应用性能监控(APM)基础设施监控用户体验监控错误追踪特点:全栈监控实时数据强大的分析能力Datadog功能:基础设施监控应用性能监控日志管理安全监控特点:统一平台强大的集成能力灵活的告警3. 自建监控系统Prometheus + Grafana架构:CDN → Exporter → Prometheus → Grafana配置示例:Prometheus 配置(prometheus.yml):global: scrape_interval: 15sscrape_configs: - job_name: 'cdn' static_configs: - targets: ['cdn-exporter:9090']Grafana 仪表板:{ "dashboard": { "title": "CDN Performance Dashboard", "panels": [ { "title": "Request Rate", "targets": [ { "expr": "rate(cdn_requests_total[5m])" } ] }, { "title": "Cache Hit Rate", "targets": [ { "expr": "cdn_cache_hits / cdn_requests_total * 100" } ] } ] }}ELK Stack(Elasticsearch, Logstash, Kibana)用途:日志收集和分析性能监控错误追踪配置示例:Logstash 配置(logstash.conf):input { file { path => "/var/log/cdn/access.log" start_position => "beginning" }}filter { grok { match => { "message" => "%{COMBINEDAPACHELOG}" } }}output { elasticsearch { hosts => ["localhost:9200"] index => "cdn-logs-%{+YYYY.MM.dd}" }}监控数据采集1. 日志采集访问日志格式:log_format cdn '$remote_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent" ' 'rt=$request_time uct="$upstream_connect_time" ' 'uht="$upstream_header_time" urt="$upstream_response_time" ' 'cache=$upstream_cache_status';关键字段:request_time:总请求时间upstream_connect_time:连接上游时间upstream_header_time:接收上游响应头时间upstream_response_time:接收上游响应时间upstream_cache_status:缓存状态(HIT/MISS/BYPASS)2. 指标采集自定义指标采集:// 使用 Prometheus client 库const client = require('prom-client');// 创建指标const httpRequestDuration = new client.Histogram({ name: 'cdn_http_request_duration_seconds', help: 'Duration of HTTP requests in seconds', labelNames: ['method', 'route', 'code']});// 记录指标const end = httpRequestDuration.startTimer();// 处理请求...end({ method: 'GET', route: '/api/data', code: 200 });3. 实时监控WebSocket 实时推送:// 使用 WebSocket 实时推送监控数据const WebSocket = require('ws');const wss = new WebSocket.Server({ port: 8080 });wss.on('connection', (ws) => { // 定期发送监控数据 const interval = setInterval(() => { const metrics = { qps: getCurrentQPS(), latency: getAverageLatency(), cacheHitRate: getCacheHitRate() }; ws.send(JSON.stringify(metrics)); }, 1000); ws.on('close', () => { clearInterval(interval); });});告警机制1. 告警规则常见告警规则:高延迟告警# Prometheus 告警规则groups: - name: cdn_alerts rules: - alert: HighLatency expr: cdn_request_duration_seconds{quantile="0.95"} > 0.5 for: 5m labels: severity: warning annotations: summary: "High latency detected" description: "95th percentile latency is {{ $value }}s"低缓存命中率告警- alert: LowCacheHitRate expr: cdn_cache_hits / cdn_requests_total * 100 < 80 for: 10m labels: severity: warning annotations: summary: "Low cache hit rate" description: "Cache hit rate is {{ $value }}%"高错误率告警- alert: HighErrorRate expr: cdn_errors_total / cdn_requests_total * 100 > 1 for: 5m labels: severity: critical annotations: summary: "High error rate detected" description: "Error rate is {{ $value }}%"2. 告警通知通知渠道:邮件通知# Alertmanager 配置receivers: - name: 'email' email_configs: - to: 'team@example.com' from: 'alertmanager@example.com' smarthost: 'smtp.example.com:587' auth_username: 'alertmanager' auth_password: 'password'短信通知receivers: - name: 'sms' webhook_configs: - url: 'https://sms.example.com/send' send_resolved: true即时通讯工具receivers: - name: 'slack' slack_configs: - api_url: 'https://hooks.slack.com/services/xxx' channel: '#cdn-alerts' username: 'CDN Alert Bot'性能优化建议1. 基于监控数据的优化延迟优化分析高延迟的请求路径优化缓存策略调整 CDN 节点配置缓存优化识别低缓存命中率的内容调整 TTL 设置优化缓存键配置带宽优化分析高带宽消耗的内容启用压缩优化图片和视频2. A/B 测试测试不同配置:// A/B 测试不同缓存策略function getCacheStrategy(userId) { const hash = hashUserId(userId); if (hash % 2 === 0) { return 'strategy-a'; // 长缓存 } else { return 'strategy-b'; // 短缓存 }}3. 容量规划基于历史数据预测:# 使用时间序列预测import pandas as pdfrom statsmodels.tsa.arima.model import ARIMA# 加载历史数据data = pd.read_csv('cdn_metrics.csv')# 训练模型model = ARIMA(data['requests'], order=(5,1,0))model_fit = model.fit()# 预测未来 7 天forecast = model_fit.forecast(steps=7)print(forecast)面试要点回答这个问题时应该强调:了解 CDN 的核心监控指标及其目标值掌握主流的监控工具和平台能够设计监控数据采集方案理解告警机制的重要性有基于监控数据进行性能优化的经验
阅读 0·2月21日 16:59

CDN 如何配置 HTTPS?有哪些 HTTPS 模式?

CDN HTTPS 配置的重要性HTTPS(Hypertext Transfer Protocol Secure)是 HTTP 的安全版本,通过 SSL/TLS 协议加密通信内容。在 CDN 上配置 HTTPS 不仅可以保护数据传输安全,还能提升 SEO 排名和用户信任度。CDN HTTPS 配置方式1. 自定义证书上传将自有 SSL 证书上传到 CDN:步骤:购买或申请 SSL 证书下载证书文件(.crt)和私钥文件(.key)在 CDN 控制台上传证书和私钥配置 HTTPS 监听端口(通常是 443)优点:完全控制证书支持通配符证书和 EV 证书适合企业级应用缺点:需要手动管理证书续期需要购买证书(部分 CDN 提供免费证书)2. CDN 提供的免费证书使用 CDN 服务商提供的免费 SSL 证书:特点:Let's Encrypt:大多数主流 CDN 支持自动续期:无需手动操作通配符支持:部分 CDN 支持 *.example.com配置示例(Cloudflare):# 通过 API 启用 Universal SSLcurl -X PATCH "https://api.cloudflare.com/client/v4/zones/{zone_id}/settings/ssl" \ -H "Authorization: Bearer {api_token}" \ -H "Content-Type: application/json" \ -d '{"value":"flexible"}'3. SNI(Server Name Indication)支持SNI 允许在同一 IP 地址上托管多个 HTTPS 网站:工作原理:客户端在 SSL 握手时发送目标域名服务器根据域名返回对应的证书CDN 自动处理 SNI 请求兼容性:现代浏览器完全支持旧版浏览器(如 IE6)不支持HTTPS 模式选择1. Flexible 模式流程:用户 → CDN(HTTPS)→ 源站(HTTP)优点:配置简单,源站无需 SSL 证书适合快速部署缺点:CDN 到源站不加密,存在安全风险不适合敏感数据传输适用场景:静态内容分发测试环境临时解决方案2. Full 模式流程:用户 → CDN(HTTPS)→ 源站(HTTPS)优点:端到端加密安全性高缺点:源站需要配置 SSL 证书证书必须与 CDN 配置匹配适用场景:生产环境敏感数据传输符合合规要求3. Full (Strict) 模式流程:用户 → CDN(HTTPS)→ 源站(HTTPS,严格验证)特点:验证源站证书的有效性检查证书链完整性验证证书与域名匹配优点:最高安全性防止中间人攻击缺点:配置复杂证书问题会导致连接失败适用场景:金融、医疗等高安全要求行业政府机构企业核心业务HTTPS 配置最佳实践1. 强制 HTTPS 重定向自动将 HTTP 请求重定向到 HTTPS:# 配置示例HTTP/1.1 301 Moved PermanentlyLocation: https://example.com$request_uri实现方式:CDN 页面规则源站配置HSTS(HTTP Strict Transport Security)2. HSTS 配置HSTS 强制浏览器使用 HTTPS:Strict-Transport-Security: max-age=31536000; includeSubDomains参数说明:max-age:HSTS 有效期(秒)includeSubDomains:包含所有子域名preload:加入 HSTS 预加载列表注意事项:一旦启用,短期内无法禁用确保所有子域名都支持 HTTPS3. 优化 SSL/TLS 配置选择合适的加密套件和协议:推荐的协议和加密套件:# 仅支持 TLS 1.2 和 TLS 1.3ssl_protocols TLSv1.2 TLSv1.3;# 优先使用强加密套件ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;# 启用会话复用ssl_session_cache shared:SSL:10m;ssl_session_timeout 10m;4. OCSP StaplingOCSP Stapling 提高 SSL 握手性能:ssl_stapling on;ssl_stapling_verify on;ssl_trusted_certificate /path/to/chain.pem;优点:减少 SSL 握手时间减轻 CA 服务器负载提高用户隐私保护5. 证书自动续期使用 Let's Encrypt 等自动续期服务:# 使用 certbot 自动续期certbot renew --quiet --post-hook "systemctl reload nginx"定时任务:# 每天凌晨 2 点检查证书续期0 2 * * * certbot renew --quiet --post-hook "systemctl reload nginx"HTTPS 性能优化1. 启用 HTTP/2HTTP/2 在 HTTPS 基础上提供性能提升:listen 443 ssl http2;优势:多路复用:减少连接数头部压缩:减少传输数据量服务器推送:主动推送资源2. 会话缓存和复用减少 SSL 握手次数:ssl_session_cache shared:SSL:50m;ssl_session_timeout 1d;ssl_session_tickets off;3. 优化证书链使用完整的证书链:主证书:域名证书中间证书:CA 中间证书根证书:CA 根证书(通常不需要)验证证书链:openssl s_client -connect example.com:443 -showcerts常见问题及解决方案问题 1:混合内容警告原因:HTTPS 页面包含 HTTP 资源解决方案:将所有资源改为 HTTPS使用相对路径使用 CSP(Content Security Policy)Content-Security-Policy: upgrade-insecure-requests问题 2:证书链不完整原因:缺少中间证书解决方案:上传完整的证书链使用正确的证书顺序证书链顺序:域名证书 → 中间证书 → 根证书问题 3:SSL 握手慢原因:未启用会话缓存未启用 OCSP Stapling使用了弱加密套件解决方案:启用会话缓存和复用启用 OCSP Stapling使用强加密套件问题 4:证书过期原因:忘记续期证书解决方案:使用自动续期工具设置过期提醒使用 CDN 的免费证书(自动续期)HTTPS 监控指标1. 证书状态证书有效期:距离过期时间证书链完整性:是否完整证书匹配度:是否与域名匹配2. SSL 握手性能握手时间:平均 SSL 握手耗时握手成功率:SSL 握手成功比例会话复用率:会话复用的比例3. 加密套件使用加密套件分布:各加密套件的使用比例协议版本分布:TLS 1.2 vs TLS 1.3弱加密套件:是否使用了弱加密套件面试要点回答这个问题时应该强调:了解不同的 HTTPS 配置方式及其优缺点能够根据业务需求选择合适的 HTTPS 模式掌握 HTTPS 配置的最佳实践了解 HTTPS 性能优化方法有实际配置和故障排查经验
阅读 0·2月21日 16:59

RxJS 中 Hot Observable 和 Cold Observable 有什么区别?

Hot Observable vs Cold ObservableCold Observable(冷 Observable)定义: Cold Observable 是惰性的,每个订阅者都会独立执行 Observable 的逻辑。特点:每个订阅者都会获得独立的数据流订阅时才开始执行不共享数据生产者不会主动推送数据示例:import { Observable } from 'rxjs';const cold$ = new Observable(subscriber => { console.log('Observable executed'); subscriber.next(Math.random()); subscriber.complete();});cold$.subscribe(value => console.log('Subscriber 1:', value));// Observable executed// Subscriber 1: 0.123456cold$.subscribe(value => console.log('Subscriber 2:', value));// Observable executed// Subscriber 2: 0.789012// 注意:每次订阅都重新执行,产生不同的随机数常见的 Cold Observable:of()from()interval()timer()ajax()http.get() (Angular)大多数创建操作符Hot Observable(热 Observable)定义: Hot Observable 是主动的,多个订阅者共享同一个数据流。特点:所有订阅者共享同一个数据流即使没有订阅者也会执行共享数据生产者主动推送数据示例:import { Observable, Subject } from 'rxjs';const hot$ = new Observable(subscriber => { console.log('Observable executed'); subscriber.next(Math.random()); subscriber.complete();});const subject = new Subject();hot$.subscribe(subject);subject.subscribe(value => console.log('Subscriber 1:', value));// Observable executed// Subscriber 1: 0.123456subject.subscribe(value => console.log('Subscriber 2:', value));// Subscriber 2: 0.123456// 注意:两个订阅者收到相同的值常见的 Hot Observable:Subject 及其变体BehaviorSubjectReplaySubjectAsyncSubjectDOM 事件(通过 fromEvent)WebSocket 连接share() 转换后的 Observable转换方法1. 使用 share() 将 Cold 转换为 Hotimport { interval } from 'rxjs';import { share, take } from 'rxjs/operators';const cold$ = interval(1000).pipe( take(5));const hot$ = cold$.pipe( share() // 转换为 Hot Observable);hot$.subscribe(value => console.log('Subscriber 1:', value));hot$.subscribe(value => console.log('Subscriber 2:', value));// 两个订阅者共享同一个数据流2. 使用 shareReplay() 缓存值import { interval } from 'rxjs';import { shareReplay, take } from 'rxjs/operators';const hot$ = interval(1000).pipe( take(5), shareReplay(1) // 缓存最后一个值);hot$.subscribe(value => console.log('Subscriber 1:', value));setTimeout(() => { hot$.subscribe(value => console.log('Subscriber 2:', value)); // 新订阅者会立即收到缓存的值}, 3000);3. 使用 publish() 和 connect()import { interval } from 'rxjs';import { publish, take } from 'rxjs/operators';const cold$ = interval(1000).pipe( take(5));const hot$ = cold$.pipe( publish() // 转换为 Hot Observable);hot$.subscribe(value => console.log('Subscriber 1:', value));hot$.subscribe(value => console.log('Subscriber 2:', value));hot$.connect(); // 开始执行实际应用场景Cold Observable 适用场景HTTP 请求// 每次订阅都会发起新的请求http.get('/api/data').subscribe(data => { console.log('Request 1:', data);});http.get('/api/data').subscribe(data => { console.log('Request 2:', data);});独立的数据处理// 每个订阅者需要独立的数据流of(1, 2, 3).pipe( map(x => x * 2)).subscribe(value => console.log(value));需要重新执行的场景// 每次订阅都重新计算const calculation$ = new Observable(subscriber => { const result = expensiveCalculation(); subscriber.next(result); subscriber.complete();});Hot Observable 适用场景共享数据// 多个组件共享同一个数据流const userData$ = http.get('/api/user').pipe( share());component1.userData$.subscribe(user => { console.log('Component 1:', user);});component2.userData$.subscribe(user => { console.log('Component 2:', user);});// 只发起一次请求,两个组件共享结果事件流// 多个订阅者监听同一个事件const click$ = fromEvent(document, 'click').pipe( share());click$.subscribe(event => { console.log('Handler 1:', event);});click$.subscribe(event => { console.log('Handler 2:', event);});WebSocket 连接// 多个订阅者共享同一个 WebSocket 连接const socket$ = webSocket('ws://localhost:8080').pipe( share());socket$.subscribe(message => { console.log('Handler 1:', message);});socket$.subscribe(message => { console.log('Handler 2:', message);});性能对比Cold Observable 性能特点优点:每个订阅者获得独立的数据流不会相互影响适合需要独立处理的场景缺点:可能重复执行相同的操作浪费资源(如重复的 HTTP 请求)内存占用可能更高Hot Observable 性能特点优点:共享数据流,避免重复执行节省资源(如只发起一次 HTTP 请求)内存占用更低缺点:订阅者可能错过之前的数据需要管理订阅时机可能出现竞态条件选择指南使用 Cold Observable 当:每个订阅者需要独立的数据流需要重新执行操作订阅者之间不应该相互影响数据源是按需生成的使用 Hot Observable 当:多个订阅者需要共享数据需要避免重复执行(如 HTTP 请求)数据源是主动推送的(如事件、WebSocket)需要缓存数据供后续订阅者使用最佳实践1. HTTP 请求共享// ❌ 错误:每次订阅都发起请求class UserService { getUser(id: string) { return http.get(`/api/users/${id}`); }}// ✅ 正确:共享请求结果class UserService { private cache = new Map<string, Observable<User>>(); getUser(id: string) { if (!this.cache.has(id)) { this.cache.set(id, http.get(`/api/users/${id}`).pipe( shareReplay(1) )); } return this.cache.get(id)!; }}2. 事件处理// 使用 share() 共享事件流const resize$ = fromEvent(window, 'resize').pipe( debounceTime(200), share());resize$.subscribe(event => { updateLayout1(event);});resize$.subscribe(event => { updateLayout2(event);});3. 状态管理// 使用 BehaviorSubject 管理状态const state$ = new BehaviorSubject(initialState);state$.subscribe(state => { console.log('Listener 1:', state);});state$.subscribe(state => { console.log('Listener 2:', state);});// 更新状态state$.next(newState);常见陷阱1. 忘记共享导致重复请求// ❌ 错误示例const data$ = http.get('/api/data');data$.subscribe(data => console.log('Component 1:', data));data$.subscribe(data => console.log('Component 2:', data));// 发起两次请求// ✅ 正确示例const data$ = http.get('/api/data').pipe( share());data$.subscribe(data => console.log('Component 1:', data));data$.subscribe(data => console.log('Component 2:', data));// 只发起一次请求2. 错误的共享时机// ❌ 错误示例const data$ = http.get('/api/data').pipe( share());// 立即订阅触发请求data$.subscribe();// 后续订阅者可能错过数据setTimeout(() => { data$.subscribe(data => console.log(data));}, 2000);// ✅ 正确示例const data$ = http.get('/api/data').pipe( shareReplay(1) // 缓存数据);3. 不当使用 shareReplay// ❌ 错误示例:缓存过多数据const data$ = interval(1000).pipe( shareReplay(1000) // 缓存1000个值,占用大量内存);// ✅ 正确示例:合理设置缓存大小const data$ = interval(1000).pipe( shareReplay(1) // 只缓存最后一个值);总结理解 Hot 和 Cold Observable 的区别对于编写高效的 RxJS 代码至关重要:Cold Observable: 惰性、独立执行、适合按需生成的数据Hot Observable: 主动、共享执行、适合主动推送的数据转换方法: 使用 share()、shareReplay() 等操作符进行转换性能考虑: Hot Observable 可以避免重复执行,提高性能选择原则: 根据场景选择合适的类型,避免不必要的资源浪费正确使用这两种 Observable 类型,可以显著提升应用的性能和可维护性。
阅读 0·2月21日 16:59

Nginx 如何优化静态资源?有哪些优化策略?

Nginx 如何优化静态资源?有哪些优化策略?Nginx 在提供静态资源方面表现优异,通过合理的配置可以显著提升静态资源的加载速度和用户体验。启用高效文件传输:http { # 启用 sendfile sendfile on; # 启用 tcp_nopush tcp_nopush on; # 启用 tcp_nodelay tcp_nodelay on; server { listen 80; server_name example.com; root /var/www/html; location / { try_files $uri $uri/ =404; } }}Gzip 压缩:http { # 启用 Gzip gzip on; gzip_vary on; gzip_min_length 1024; gzip_comp_level 6; gzip_buffers 16 8k; gzip_http_version 1.1; gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/rss+xml application/atom+xml image/svg+xml; gzip_disable "msie6"; # 静态资源预压缩 gzip_static on; server { listen 80; server_name example.com; root /var/www/html; location / { try_files $uri $uri/ =404; } }}浏览器缓存:server { listen 80; server_name example.com; root /var/www/html; # 静态资源长期缓存 location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2|ttf|eot)$ { expires 1y; add_header Cache-Control "public, immutable"; access_log off; } # HTML 文件短期缓存 location ~* \.html$ { expires 1h; add_header Cache-Control "public, must-revalidate"; } # 不缓存动态内容 location ~* \.php$ { expires off; add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0"; } location / { try_files $uri $uri/ =404; }}文件缓存:http { # 打开文件缓存 open_file_cache max=100000 inactive=20s; open_file_cache_valid 30s; open_file_cache_min_uses 2; open_file_cache_errors on; server { listen 80; server_name example.com; root /var/www/html; location / { try_files $uri $uri/ =404; } }}静态资源分离:server { listen 80; server_name example.com; # 主站点 location / { root /var/www/html; try_files $uri $uri/ =404; } # 静态资源 location /static/ { root /var/www/static; expires 1y; add_header Cache-Control "public, immutable"; access_log off; } # 图片资源 location /images/ { root /var/www/images; expires 1y; add_header Cache-Control "public, immutable"; access_log off; } # 字体文件 location /fonts/ { root /var/www/fonts; expires 1y; add_header Cache-Control "public, immutable"; access_log off; add_header Access-Control-Allow-Origin *; }}CDN 集成:server { listen 80; server_name example.com; root /var/www/html; # 重写静态资源 URL 到 CDN location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2)$ { return 301 https://cdn.example.com$request_uri; } location / { try_files $uri $uri/ =404; }}图片优化:server { listen 80; server_name example.com; root /var/www/html; # 图片缓存 location ~* \.(jpg|jpeg|png|gif|ico|svg|webp)$ { expires 1y; add_header Cache-Control "public, immutable"; access_log off; # WebP 支持 if ($http_accept ~* "webp") { rewrite ^(.+)\.(jpg|png)$ $1.webp last; } } # 图片防盗链 location ~* \.(jpg|jpeg|png|gif)$ { valid_referers none blocked example.com *.example.com; if ($invalid_referer) { return 403; } } location / { try_files $uri $uri/ =404; }}字体文件优化:server { listen 80; server_name example.com; root /var/www/html; # 字体文件 location ~* \.(woff|woff2|ttf|otf|eot)$ { expires 1y; add_header Cache-Control "public, immutable"; access_log off; # CORS 支持 add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods "GET, OPTIONS"; add_header Access-Control-Allow-Headers "Origin, Content-Type"; } location / { try_files $uri $uri/ =404; }}静态资源预加载:server { listen 80; server_name example.com; root /var/www/html; location = / { add_header Link "</style.css>; rel=preload; as=style, </script.js>; rel=preload; as=script, </image.jpg>; rel=preload; as=image"; try_files $uri $uri/ =404; } location / { try_files $uri $uri/ =404; }}HTTP/2 推送:server { listen 443 ssl http2; server_name example.com; ssl_certificate /etc/nginx/ssl/example.com.crt; ssl_certificate_key /etc/nginx/ssl/example.com.key; root /var/www/html; # HTTP/2 推送 location = / { http2_push /style.css; http2_push /script.js; http2_push /image.jpg; try_files $uri $uri/ =404; } location / { try_files $uri $uri/ =404; }}静态资源合并:# 使用第三方模块 ngx_http_concat_moduleserver { listen 80; server_name example.com; root /var/www/html; # CSS 合并 location /static/css/ { concat on; concat_types text/css; concat_unique on; concat_max_files 10; } # JS 合并 location /static/js/ { concat on; concat_types application/javascript; concat_unique on; concat_max_files 10; } location / { try_files $uri $uri/ =404; }}完整静态资源优化配置:user nginx;worker_processes auto;worker_rlimit_nofile 65535;events { worker_connections 10240; use epoll; multi_accept on;}http { # 基础优化 sendfile on; tcp_nopush on; tcp_nodelay on; # Gzip 压缩 gzip on; gzip_vary on; gzip_min_length 1024; gzip_comp_level 6; gzip_buffers 16 8k; gzip_http_version 1.1; gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/rss+xml application/atom+xml image/svg+xml; gzip_disable "msie6"; gzip_static on; # 文件缓存 open_file_cache max=100000 inactive=20s; open_file_cache_valid 30s; open_file_cache_min_uses 2; open_file_cache_errors on; # 静态资源长期缓存 map $sent_http_content_type $expires { default off; text/html 1h; text/css 1y; application/javascript 1y; ~image/ 1y; ~font/ 1y; } server { listen 80; server_name example.com; root /var/www/html; index index.html; # 静态资源优化 location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2|ttf|eot)$ { expires $expires; add_header Cache-Control "public, immutable"; access_log off; } # 字体文件 CORS location ~* \.(woff|woff2|ttf|otf|eot)$ { add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Methods "GET, OPTIONS"; add_header Access-Control-Allow-Headers "Origin, Content-Type"; } # 图片防盗链 location ~* \.(jpg|jpeg|png|gif)$ { valid_referers none blocked example.com *.example.com; if ($invalid_referer) { return 403; } } # 主路由 location / { try_files $uri $uri/ =404; } # 禁止访问隐藏文件 location ~ /\. { deny all; access_log off; log_not_found off; } }}静态资源优化最佳实践:启用压缩:使用 Gzip 压缩文本资源合理缓存:根据资源类型设置缓存时间文件分离:将静态资源分离到独立域名或 CDN预压缩:使用 gzip_static 预压缩静态文件HTTP/2:启用 HTTP/2 提升加载速度图片优化:使用 WebP 格式,启用图片压缩字体优化:使用 WOFF2 格式,启用 CORS监控性能:使用 Lighthouse 等工具监控性能定期清理:清理未使用的静态资源版本控制:使用文件名哈希实现缓存更新
阅读 0·2月21日 16:58