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

面试题手册

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

Nginx 如何处理动态内容?有哪些配置方式?

Nginx 如何处理动态内容?有哪些配置方式?Nginx 本身不处理动态内容,而是通过 FastCGI、uWSGI、SCGI 等协议将请求转发给后端应用服务器处理。FastCGI 配置(PHP):server { listen 80; server_name example.com; root /var/www/html; index index.php index.html; # PHP 文件处理 location ~ \.php$ { try_files $uri =404; fastcgi_pass unix:/var/run/php/php8.0-fpm.sock; fastcgi_index index.php; # FastCGI 参数 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param QUERY_STRING $query_string; fastcgi_param REQUEST_METHOD $request_method; fastcgi_param CONTENT_TYPE $content_type; fastcgi_param CONTENT_LENGTH $content_length; include fastcgi_params; # 超时设置 fastcgi_connect_timeout 60s; fastcgi_send_timeout 60s; fastcgi_read_timeout 60s; # 缓冲区设置 fastcgi_buffer_size 4k; fastcgi_buffers 8 4k; } location / { try_files $uri $uri/ /index.php?$query_string; }}uWSGI 配置(Python):upstream django_backend { server unix:/var/run/uwsgi/app.sock; server 127.0.0.1:8000;}server { listen 80; server_name example.com; root /var/www/html; location / { uwsgi_pass django_backend; include uwsgi_params; # uWSGI 参数 uwsgi_param UWSGI_SCHEME $scheme; uwsgi_param SERVER_NAME $server_name; uwsgi_param REMOTE_ADDR $remote_addr; # 超时设置 uwsgi_connect_timeout 60s; uwsgi_send_timeout 60s; uwsgi_read_timeout 60s; # 缓冲区设置 uwsgi_buffering on; uwsgi_buffer_size 4k; uwsgi_buffers 8 4k; } # 静态文件 location /static/ { alias /var/www/html/static/; expires 1y; add_header Cache-Control "public, immutable"; }}SCGI 配置(Ruby):upstream rails_backend { server unix:/var/run/scgi/rails.sock; server 127.0.0.1:9000;}server { listen 80; server_name example.com; root /var/www/html; location / { scgi_pass rails_backend; include scgi_params; # SCGI 参数 scgi_param SCGI 1; scgi_param SERVER_SOFTWARE nginx; # 超时设置 scgi_connect_timeout 60s; scgi_send_timeout 60s; scgi_read_timeout 60s; }}HTTP 代理配置:upstream nodejs_backend { server 127.0.0.1:3000; server 127.0.0.1:3001;}server { listen 80; server_name example.com; location / { proxy_pass http://nodejs_backend; # 代理头设置 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 超时设置 proxy_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 60s; # 缓冲区设置 proxy_buffering on; proxy_buffer_size 4k; proxy_buffers 8 4k; # WebSocket 支持 proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; }}FastCGI 缓存:# 定义 FastCGI 缓存fastcgi_cache_path /var/cache/nginx/fastcgi levels=1:2 keys_zone=fastcgi_cache:10m max_size=1g inactive=60m;server { listen 80; server_name example.com; root /var/www/html; location ~ \.php$ { try_files $uri =404; # 启用 FastCGI 缓存 fastcgi_cache fastcgi_cache; fastcgi_cache_valid 200 60m; fastcgi_cache_valid 404 1m; fastcgi_cache_key "$scheme$request_method$host$request_uri"; # 缓存跳过条件 fastcgi_cache_bypass $skip_cache; fastcgi_no_cache $skip_cache; fastcgi_pass unix:/var/run/php/php8.0-fpm.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; # 添加缓存状态头 add_header X-Cache-Status $upstream_cache_status; }}动态内容负载均衡:upstream php_backend { least_conn; server 192.168.1.100:9000 weight=3; server 192.168.1.101:9000 weight=2; server 192.168.1.102:9000 weight=1; keepalive 32;}server { listen 80; server_name example.com; root /var/www/html; location ~ \.php$ { fastcgi_pass php_backend; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; }}动态内容压缩:http { # 启用 Gzip 压缩 gzip on; gzip_vary on; gzip_min_length 1024; gzip_comp_level 6; gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml application/rss+xml; server { listen 80; server_name example.com; root /var/www/html; location ~ \.php$ { fastcgi_pass unix:/var/run/php/php8.0-fpm.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } }}动态内容安全:server { listen 80; server_name example.com; root /var/www/html; # 禁止访问敏感文件 location ~* \.(htaccess|htpasswd|ini|log|sh|sql|bak|old|swp|tmp)$ { deny all; access_log off; } # PHP 文件处理 location ~ \.php$ { # 防止 PHP 文件上传执行 if ($request_filename ~* \.(jpg|jpeg|png|gif|ico)$) { return 403; } fastcgi_pass unix:/var/run/php/php8.0-fpm.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; # 限制请求大小 client_max_body_size 10m; } location / { try_files $uri $uri/ /index.php?$query_string; }}动态内容监控:# 自定义日志格式log_format dynamic '$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';server { listen 80; server_name example.com; root /var/www/html; access_log /var/log/nginx/dynamic.log dynamic; location ~ \.php$ { fastcgi_pass unix:/var/run/php/php8.0-fpm.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; # 添加性能头 add_header X-Response-Time $request_time; add_header X-Upstream-Time $upstream_response_time; }}完整动态内容配置示例:user nginx;worker_processes auto;http { # FastCGI 缓存 fastcgi_cache_path /var/cache/nginx/fastcgi levels=1:2 keys_zone=fastcgi_cache:10m max_size=1g inactive=60m; # Gzip 压缩 gzip on; gzip_vary on; gzip_min_length 1024; gzip_comp_level 6; gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml application/rss+xml; # 日志格式 log_format dynamic '$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'; # PHP 后端 upstream php_backend { least_conn; server 192.168.1.100:9000; server 192.168.1.101:9000; keepalive 32; } server { listen 80; server_name example.com; root /var/www/html; index index.php index.html; access_log /var/log/nginx/example.com.access.log dynamic; error_log /var/log/nginx/example.com.error.log warn; # 禁止访问敏感文件 location ~* \.(htaccess|htpasswd|ini|log|sh|sql|bak|old|swp|tmp)$ { deny all; access_log off; } # 静态资源 location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2)$ { expires 1y; add_header Cache-Control "public, immutable"; access_log off; } # PHP 文件处理 location ~ \.php$ { try_files $uri =404; # 启用缓存 fastcgi_cache fastcgi_cache; fastcgi_cache_valid 200 60m; fastcgi_cache_valid 404 1m; fastcgi_cache_key "$scheme$request_method$host$request_uri"; fastcgi_pass php_backend; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; # 超时设置 fastcgi_connect_timeout 60s; fastcgi_send_timeout 60s; fastcgi_read_timeout 60s; # 缓冲区设置 fastcgi_buffer_size 4k; fastcgi_buffers 8 4k; # 添加缓存状态头 add_header X-Cache-Status $upstream_cache_status; add_header X-Response-Time $request_time; add_header X-Upstream-Time $upstream_response_time; } # 主路由 location / { try_files $uri $uri/ /index.php?$query_string; } }}动态内容处理最佳实践:使用缓存:启用 FastCGI 缓存减少后端负载负载均衡:使用 upstream 实现负载均衡合理超时:根据业务需求设置超时时间缓冲区优化:调整缓冲区大小提升性能压缩响应:启用 Gzip 压缩减少传输数据安全配置:防止文件上传执行和敏感文件访问监控性能:记录响应时间和缓存命中率静态分离:将静态资源分离到独立路径连接保持:使用 keepalive 减少连接开销错误处理:配置友好的错误页面
阅读 0·2月21日 16:58

如何优化 CDN 成本?有哪些成本优化策略?

CDN 成本优化的必要性随着业务增长,CDN 成本可能成为企业的重要开支。通过合理的成本优化策略,可以在保证服务质量的前提下,显著降低 CDN 使用成本。CDN 成本构成1. 流量费用计费方式:按流量计费:根据实际使用的流量计费按带宽计费:根据峰值带宽计费混合计费:结合流量和带宽计费计费周期:月度计费季度计费年度计费(通常有折扣)2. 请求数费用计费对象:HTTP 请求HTTPS 请求API 请求计费方式:按请求次数计费通常有免费额度3. 存储费用计费对象:边缘存储源站存储备份存储计费方式:按存储容量计费(GB/月)按存储类型计费(标准、低频、归档)4. 功能费用计费功能:HTTPS 证书WAF 防护DDoS 防护边缘计算视频处理5. 其他费用数据传输费(跨区域)技术支持费定制开发费成本优化策略1. 缓存优化提高缓存命中率目标:缓存命中率 >95%优化方法:1. 合理设置 TTL# 静态资源:长 TTLCache-Control: public, max-age=31536000, immutable# 动态内容:短 TTLCache-Control: public, max-age=60# 不缓存的内容Cache-Control: no-store2. 优化缓存键# 忽略不影响内容的查询参数proxy_cache_key "$scheme$request_method$host$uri";3. 使用版本控制# 不推荐:更新后需要清除缓存style.css# 推荐:更新时改变 URLstyle.v1.cssstyle.v2.css效果:减少回源流量降低源站负载节省带宽成本缓存预热策略:在内容发布前预热预热热门内容定期预热更新内容示例:# 预热多个 URLfor url in $(cat urls.txt); do curl -X POST "https://api.cdn.com/prefetch" \ -H "Content-Type: application/json" \ -d "{\"urls\": [\"$url\"]}"done2. 内容优化图片优化优化方法:1. 选择合适的格式JPEG:适合照片PNG:适合透明图片WebP:比 JPEG/PNG 小 30-50%AVIF:比 WebP 小 20-30%2. 压缩图片# 使用 ImageMagick 压缩convert input.jpg -quality 85 output.jpg# 使用 pngquant 压缩 PNGpngquant --quality=65-80 input.png3. 响应式图片<picture> <source srcset="image-800w.webp" type="image/webp" media="(max-width: 800px)"> <source srcset="image-1200w.webp" type="image/webp"> <img src="image-1200w.jpg" alt="Description"></picture>效果:减少 50-70% 的图片流量视频优化优化方法:1. 选择合适的编码格式H.264:兼容性好H.265/HEVC:比 H.264 小 50%VP9:开源,比 H.264 小 40%AV1:最新标准,比 H.264 小 60%2. 自适应码率(ABR){ "streams": [ {"bitrate": 500000, "resolution": "640x360"}, {"bitrate": 1000000, "resolution": "854x480"}, {"bitrate": 2000000, "resolution": "1280x720"}, {"bitrate": 4000000, "resolution": "1920x1080"} ]}3. 使用 CDN 视频处理// 使用 CDN 边缘处理视频const processedVideo = await cdn.processVideo({ input: 'original.mp4', output: 'compressed.mp4', codec: 'h265', bitrate: '2000k'})效果:减少 40-60% 的视频流量文本压缩启用压缩:# Gzip 压缩gzip on;gzip_types text/plain text/css application/json application/javascript;# Brotli 压缩(比 Gzip 小 20-30%)brotli on;brotli_types text/plain text/css application/json application/javascript;效果:文本内容减少 60-80%3. 流量优化减少不必要的请求方法:1. 合并资源<!-- 不推荐:多个 CSS 文件 --><link rel="stylesheet" href="style1.css"><link rel="stylesheet" href="style2.css"><link rel="stylesheet" href="style3.css"><!-- 推荐:合并为一个 CSS 文件 --><link rel="stylesheet" href="styles.css">2. 使用雪碧图.sprite { background-image: url('sprite.png'); background-repeat: no-repeat;}.icon1 { background-position: 0 0; width: 32px; height: 32px;}.icon2 { background-position: -32px 0; width: 32px; height: 32px;}3. 内联关键 CSS<style> /* 关键 CSS 内联 */ .critical { ... }</style>使用 HTTP/2 或 HTTP/3优势:多路复用:减少连接数头部压缩:减少传输数据量服务器推送:主动推送资源配置示例:listen 443 ssl http2;4. 智能路由优化地理位置路由策略:将用户路由到最近的节点配置示例:geo $geo { default default; 1.0.0.0/8 us-east; 2.0.0.0/8 us-west; 3.0.0.0/8 eu-west;}upstream cdn_us_east { server cdn-us-east-1.example.com;}upstream cdn_us_west { server cdn-us-west-1.example.com;}效果:减少跨区域流量成本成本优化路由策略:优先使用成本较低的节点实现:分析各节点的成本配置节点权重动态调整路由5. 预算和配额管理设置预算上限方法:设置月度预算上限配置超额告警自动降级策略配置示例:// 设置预算告警const budget = { monthly: 10000, // $10,000 alertThreshold: 0.8, // 80% 时告警 stopThreshold: 1.0 // 100% 时停止服务}function checkBudget(currentSpend) { const ratio = currentSpend / budget.monthly if (ratio >= budget.stopThreshold) { // 停止服务或降级 enableDegradationMode() } else if (ratio >= budget.alertThreshold) { // 发送告警 sendAlert(`Budget usage: ${ratio * 100}%`) }}使用预留实例策略:预留带宽或流量获得折扣价格适合稳定业务示例:# 购买预留实例aws cloudfront create-reserved-instance \ --reserved-instance-offering-id xxx \ --instance-count 10效果:节省 20-40% 的成本6. 多 CDN 策略使用多个 CDN 服务商优势:降低单一供应商风险利用各 CDN 的优势获得更好的价格实现方式:1. DNS 负载均衡# 配置多个 CDN 的 CNAMEexample.com. IN CNAME cdn1.example.comexample.com. IN CNAME cdn2.example.com2. 智能路由// 根据成本和性能选择 CDNfunction selectCDN(userLocation, content) { const cdns = [ { name: 'cdn1', cost: 0.01, performance: 0.8 }, { name: 'cdn2', cost: 0.015, performance: 0.9 }, { name: 'cdn3', cost: 0.008, performance: 0.7 } ] // 根据业务需求选择最优 CDN return cdns.reduce((best, cdn) => { const score = calculateScore(cdn, userLocation, content) return score > best.score ? { ...cdn, score } : best }, { score: 0 })}按内容类型分配 CDN策略:静态内容:使用低成本 CDN动态内容:使用高性能 CDN视频:使用视频优化 CDN示例:// 根据内容类型选择 CDNfunction selectCDNByContentType(contentType) { const cdnMapping = { 'image/jpeg': 'low-cost-cdn', 'video/mp4': 'video-optimized-cdn', 'application/json': 'high-performance-cdn' } return cdnMapping[contentType] || 'default-cdn'}成本监控和分析1. 成本分析工具CDN 自带分析Cloudflare Analytics:流量统计请求分析成本报告AWS Cost Explorer:成本趋势分析成本预测成本优化建议2. 自定义成本监控实现示例:// 记录 CDN 成本const costTracker = { traffic: 0, requests: 0, storage: 0, addTrafficCost(bytes) { this.traffic += bytes * 0.00001 // $0.01 per GB }, addRequestCost(count) { this.requests += count * 0.000001 // $0.001 per 1000 requests }, addStorageCost(gb) { this.storage += gb * 0.02 // $0.02 per GB per month }, getTotalCost() { return this.traffic + this.requests + this.storage }}// 使用示例costTracker.addTrafficCost(1024 * 1024 * 1024) // 1 GBcostTracker.addRequestCost(1000) // 1000 requestscostTracker.addStorageCost(100) // 100 GBconsole.log(`Total cost: $${costTracker.getTotalCost()}`)3. 成本优化建议基于数据分析的建议:1. 识别高成本内容-- 查询流量最大的内容SELECT url, SUM(bytes) as total_bytesFROM cdn_logsWHERE date >= '2026-02-01'GROUP BY urlORDER BY total_bytes DESCLIMIT 10;2. 分析缓存命中率-- 查询缓存命中率低的内容SELECT url, COUNT(*) as total_requests, SUM(CASE WHEN cache_status = 'HIT' THEN 1 ELSE 0 END) as hits, SUM(CASE WHEN cache_status = 'HIT' THEN 1 ELSE 0 END) / COUNT(*) * 100 as hit_rateFROM cdn_logsWHERE date >= '2026-02-01'GROUP BY urlHAVING hit_rate < 80ORDER BY hit_rate ASCLIMIT 10;3. 优化建议对高成本内容实施压缩对低缓存命中率内容优化缓存策略对不常访问的内容使用低成本存储成本优化最佳实践1. 定期审查成本审查内容:月度成本报告成本趋势分析优化机会识别2. A/B 测试测试不同策略:不同的缓存策略不同的压缩算法不同的 CDN 配置3. 持续优化优化流程:监控成本数据分析成本构成实施优化措施评估优化效果持续改进面试要点回答这个问题时应该强调:了解 CDN 成本的构成要素掌握多种成本优化策略有实际的成本优化经验能够分析和监控 CDN 成本理解成本与性能的平衡
阅读 0·2月21日 16:58

RxJS 中的 Marble Testing 是什么?如何使用?

Marble Testing 的概念Marble Testing 是 RxJS 中一种基于字符串的可视化测试方法,它使用特殊的语法来表示 Observable 的时间流和事件。这种方法让异步测试变得直观和易于理解。Marble 语法基本符号| 符号 | 含义 ||------|------|| - | 时间流逝(1帧,约10ms)|| a, b, c | 发出的值 || | | 完成 || # | 错误 || () | 同步发出 || ^ | 订阅点(hot Observable)|| ! | 取消订阅 |示例// 基本示例const source$ = cold('-a-b-c-|');// 含义:10ms后发出a,20ms后发出b,30ms后发出c,40ms后完成// 错误示例const error$ = cold('-a-b-#');// 含义:10ms后发出a,20ms后发出b,30ms后出错// 同步示例const sync$ = cold('(abc|)');// 含义:同步发出a、b、c,然后完成// Hot Observableconst hot$ = hot('^-a-b-c-|');// 含义:从订阅点开始,10ms后发出a,20ms后发出b,30ms后发出c,40ms后完成TestScheduler 的使用基本设置import { TestScheduler } from 'rxjs/testing';describe('My Observable Tests', () => { let testScheduler: TestScheduler; beforeEach(() => { testScheduler = new TestScheduler((actual, expected) => { expect(actual).toEqual(expected); }); });});测试基本操作符import { of } from 'rxjs';import { map, filter } from 'rxjs/operators';it('should map values', () => { testScheduler.run(({ cold, expectObservable }) => { const source$ = cold('-a-b-c-|'); const expected = '-A-B-C-|'; const result$ = source$.pipe( map(x => x.toUpperCase()) ); expectObservable(result$).toBe(expected, { a: 'a', b: 'b', c: 'c' }); });});it('should filter values', () => { testScheduler.run(({ cold, expectObservable }) => { const source$ = cold('-a-b-c-d-|'); const expected = '-a-c---|'; const result$ = source$.pipe( filter(x => ['a', 'c'].includes(x)) ); expectObservable(result$).toBe(expected); });});测试时间相关操作符import { of } from 'rxjs';import { delay, debounceTime, throttleTime } from 'rxjs/operators';it('should delay emissions', () => { testScheduler.run(({ cold, expectObservable }) => { const source$ = cold('-a-b-c-|'); const expected = '---a-b-c-|'; // 延迟30ms const result$ = source$.pipe( delay(30, testScheduler) ); expectObservable(result$).toBe(expected); });});it('should debounce', () => { testScheduler.run(({ cold, expectObservable }) => { const source$ = cold('-a--b---c-|'); const expected = '-----b---c-|'; const result$ = source$.pipe( debounceTime(20, testScheduler) ); expectObservable(result$).toBe(expected); });});it('should throttle', () => { testScheduler.run(({ cold, expectObservable }) => { const source$ = cold('-a-b-c-d-|'); const expected = '-a---c---|'; const result$ = source$.pipe( throttleTime(30, testScheduler) ); expectObservable(result$).toBe(expected); });});测试组合操作符import { of, merge, concat, combineLatest } from 'rxjs';it('should merge observables', () => { testScheduler.run(({ cold, expectObservable }) => { const source1$ = cold('-a---b-|'); const source2$ = cold('--c-d---|'); const expected = '-a-c-b-d-|'; const result$ = merge(source1$, source2$); expectObservable(result$).toBe(expected); });});it('should concatenate observables', () => { testScheduler.run(({ cold, expectObservable }) => { const source1$ = cold('-a-b-|'); const source2$ = cold('--c-d-|'); const expected = '-a-b--c-d-|'; const result$ = concat(source1$, source2$); expectObservable(result$).toBe(expected); });});it('should combine latest', () => { testScheduler.run(({ cold, expectObservable }) => { const source1$ = cold('-a---b-|'); const source2$ = cold('--c-d---|'); const expected = '----ab-bd-|'; const result$ = combineLatest([source1$, source2$]); expectObservable(result$).toBe(expected); });});测试错误处理import { of, throwError } from 'rxjs';import { catchError, retry } from 'rxjs/operators';it('should catch errors', () => { testScheduler.run(({ cold, expectObservable }) => { const source$ = cold('-a-b-#'); const expected = '-a-b-(d|)'; const result$ = source$.pipe( catchError(() => of('d')) ); expectObservable(result$).toBe(expected); });});it('should retry on error', () => { testScheduler.run(({ cold, expectObservable }) => { const source$ = cold('-a-#'); const expected = '-a-a-#'; const result$ = source$.pipe( retry(1) ); expectObservable(result$).toBe(expected); });});测试订阅和取消订阅import { interval } from 'rxjs';import { take } from 'rxjs/operators';it('should handle subscription', () => { testScheduler.run(({ cold, hot, expectObservable, expectSubscriptions }) => { const source$ = cold('-a-b-c-|'); const subs = '^------!'; const result$ = source$.pipe(take(2)); expectObservable(result$).toBe('-a-b-|'); expectSubscriptions(source$.subscriptions).toBe(subs); });});it('should handle unsubscription', () => { testScheduler.run(({ cold, hot, expectObservable, expectSubscriptions }) => { const source$ = cold('-a-b-c-d-|'); const subs = '^---!'; const result$ = source$.pipe(take(2)); expectObservable(result$).toBe('-a-b-|'); expectSubscriptions(source$.subscriptions).toBe(subs); });});实际应用示例1. 测试搜索功能import { of } from 'rxjs';import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators';function search(query: string) { return of(`Results for ${query}`);}it('should search with debounce', () => { testScheduler.run(({ cold, expectObservable }) => { const input$ = cold('-a--b---c-|'); const expected = '-----b---c-|'; const result$ = input$.pipe( debounceTime(20, testScheduler), distinctUntilChanged(), switchMap(query => search(query)) ); expectObservable(result$).toBe(expected); });});2. 测试自动保存import { of } from 'rxjs';import { debounceTime, switchMap } from 'rxjs/operators';function save(data: any) { return of('Saved');}it('should auto-save with debounce', () => { testScheduler.run(({ cold, expectObservable }) => { const changes$ = cold('-a--b---c-|'); const expected = '-----b---c-|'; const result$ = changes$.pipe( debounceTime(20, testScheduler), switchMap(data => save(data)) ); expectObservable(result$).toBe(expected); });});3. 测试轮询功能import { interval } from 'rxjs';import { take, map } from 'rxjs/operators';it('should poll at intervals', () => { testScheduler.run(({ cold, expectObservable }) => { const expected = '-a-b-c-d-e-|'; const result$ = interval(10, testScheduler).pipe( take(5), map(x => String.fromCharCode(97 + x)) ); expectObservable(result$).toBe(expected); });});4. 测试缓存功能import { of } from 'rxjs';import { shareReplay } from 'rxjs/operators';it('should cache values', () => { testScheduler.run(({ cold, expectObservable, expectSubscriptions }) => { const source$ = cold('-a-b-c-|'); const expected = '-a-b-c-|'; const subs = ['^------!', ' ^-!']; const cached$ = source$.pipe(shareReplay(1)); expectObservable(cached$).toBe(expected); expectObservable(cached$).toBe('--c-|'); expectSubscriptions(source$.subscriptions).toBe(subs); });});高级用法1. 测试 Hot Observableit('should handle hot observable', () => { testScheduler.run(({ hot, expectObservable }) => { const source$ = hot('--a--b--c--|'); const sub = '---^--------!'; const expected = '--b--c--|'; const result$ = source$.pipe(take(2)); expectObservable(result$, sub).toBe(expected); });});2. 测试多播import { of } from 'rxjs';import { share, multicast } from 'rxjs/operators';import { Subject } from 'rxjs';it('should multicast correctly', () => { testScheduler.run(({ cold, expectObservable }) => { const source$ = cold('-a-b-c-|'); const expected = '-a-b-c-|'; const shared$ = source$.pipe(share()); expectObservable(shared$).toBe(expected); expectObservable(shared$).toBe(expected); });});3. 测试自定义操作符import { Observable } from 'rxjs';import { OperatorFunction } from 'rxjs';function customMap<T, R>(project: (value: T) => R): OperatorFunction<T, R> { return (source$) => new Observable(subscriber => { return source$.subscribe({ next: value => { try { subscriber.next(project(value)); } catch (error) { subscriber.error(error); } }, error: error => subscriber.error(error), complete: () => subscriber.complete() }); });}it('should use custom operator', () => { testScheduler.run(({ cold, expectObservable }) => { const source$ = cold('-a-b-c-|'); const expected = '-A-B-C-|'; const result$ = source$.pipe( customMap(x => x.toUpperCase()) ); expectObservable(result$).toBe(expected); });});最佳实践1. 使用有意义的值// ✅ 好的做法it('should map values', () => { testScheduler.run(({ cold, expectObservable }) => { const source$ = cold('-a-b-c-|'); const expected = '-A-B-C-|'; const result$ = source$.pipe(map(x => x.toUpperCase())); expectObservable(result$).toBe(expected, { a: 'a', b: 'b', c: 'c', A: 'A', B: 'B', C: 'C' }); });});// ❌ 不好的做法it('should map values', () => { testScheduler.run(({ cold, expectObservable }) => { const source$ = cold('-a-b-c-|'); const expected = '-d-e-f-|'; const result$ = source$.pipe(map(x => x.toUpperCase())); expectObservable(result$).toBe(expected); });});2. 测试边界情况it('should handle empty observable', () => { testScheduler.run(({ cold, expectObservable }) => { const source$ = cold('|'); const expected = '|'; const result$ = source$.pipe(map(x => x.toUpperCase())); expectObservable(result$).toBe(expected); });});it('should handle error observable', () => { testScheduler.run(({ cold, expectObservable }) => { const source$ = cold('-#'); const expected = '-#'; const result$ = source$.pipe(map(x => x.toUpperCase())); expectObservable(result$).toBe(expected); });});3. 使用 expectSubscriptionsit('should subscribe and unsubscribe correctly', () => { testScheduler.run(({ cold, expectObservable, expectSubscriptions }) => { const source$ = cold('-a-b-c-|'); const subs = '^------!'; const result$ = source$.pipe(take(3)); expectObservable(result$).toBe('-a-b-c-|'); expectSubscriptions(source$.subscriptions).toBe(subs); });});总结Marble Testing 是 RxJS 中强大的测试工具,它提供了:可视化测试: 使用字符串表示时间流,直观易懂时间控制: 精确控制异步操作的时序易于维护: 清晰的语法和结构全面覆盖: 可以测试各种操作符和场景掌握 Marble Testing 可以显著提升 RxJS 代码的测试质量和开发效率。
阅读 0·2月21日 16:58

Kafka 为什么能够实现高吞吐量?

Kafka 高吞吐量原理Kafka 之所以能够实现高吞吐量,主要得益于其独特的设计和架构优化。理解这些原理对于性能调优和系统设计非常重要。核心设计原理1. 顺序读写Kafka 采用顺序读写磁盘的方式,这是其高吞吐量的关键因素。优势:顺序读写速度远高于随机读写(可达 100MB/s 以上)减少磁盘磁头移动,降低 I/O 延迟充分利用操作系统的 Page Cache实现:消息以追加方式写入日志文件Consumer 顺序读取日志文件避免随机访问带来的性能损耗2. 零拷贝技术Kafka 使用零拷贝技术减少数据在内核空间和用户空间之间的拷贝次数。传统方式:磁盘 → 内核缓冲区内核缓冲区 → 用户缓冲区用户缓冲区 → Socket 缓冲区Socket 缓冲区 → 网卡零拷贝方式:磁盘 → 内核缓冲区内核缓冲区 → 网卡(直接通过 sendfile 系统调用)优势:减少数据拷贝次数(从 4 次减少到 2 次)减少 CPU 上下文切换提高数据传输效率3. 批量发送Kafka 支持批量发送消息,减少网络请求次数。配置参数:# 批量发送大小batch.size=16384# 批量发送等待时间linger.ms=5优势:减少网络请求次数提高网络利用率降低网络开销4. 页缓存Kafka 充分利用操作系统的页缓存机制。原理:消息写入时先写入页缓存读取时优先从页缓存读取操作系统负责刷盘优势:减少磁盘 I/O提高读取速度利用操作系统的缓存优化5. 分区机制Kafka 通过分区实现并行处理,提高整体吞吐量。优势:不同分区可以并行读写提高并发处理能力分散负载到不同 Broker配置:# Topic 分区数num.partitions=10性能优化配置Producer 配置# 压缩类型compression.type=snappy# 批量发送大小batch.size=32768# 批量发送等待时间linger.ms=10# 缓冲区大小buffer.memory=67108864# 最大请求大小max.request.size=1048576Broker 配置# 网络线程数num.network.threads=8# I/O 线程数num.io.threads=16# 日志刷新间隔log.flush.interval.messages=10000# 日志刷新时间间隔log.flush.interval.ms=1000# 页缓存大小log.dirs=/data/kafka-logsConsumer 配置# 每次拉取最小字节数fetch.min.bytes=1024# 每次拉取最大字节数fetch.max.bytes=52428800# 每次拉取最大等待时间fetch.max.wait.ms=500# 每次拉取消息数max.poll.records=500性能监控指标Producer 指标record-send-rate:消息发送速率record-queue-time-avg:消息在缓冲区平均等待时间request-latency-avg:请求平均延迟batch-size-avg:平均批量大小Broker 指标BytesInPerSec:每秒接收字节数BytesOutPerSec:每秒发送字节数MessagesInPerSec:每秒接收消息数RequestHandlerAvgIdlePercent:请求处理器空闲比例Consumer 指标records-consumed-rate:消息消费速率records-lag-max:最大消费延迟fetch-rate:拉取速率fetch-latency-avg:平均拉取延迟性能调优建议合理设置分区数分区数过多会增加管理开销分区数过少会限制并发能力一般设置为 Broker 数量的倍数优化批量发送根据消息大小调整 batch.size合理设置 linger.ms 平衡延迟和吞吐量监控批量发送效果使用压缩对于文本消息使用 Snappy 或 Gzip对于二进制消息使用 LZ4权衡 CPU 消耗和压缩率监控和调优持续监控性能指标根据监控数据调整配置进行压力测试验证效果硬件优化使用 SSD 提高磁盘性能增加内存提高缓存命中率优化网络配置性能与可靠性的权衡高吞吐量配置可能降低可靠性需要根据业务场景选择合适的配置在关键业务中优先保证可靠性在非关键业务中可以追求更高吞吐量通过理解 Kafka 高吞吐量的原理并进行合理的配置优化,可以在大多数场景下获得优秀的性能表现。
阅读 0·2月21日 16:58

Kafka 如何保证消息的顺序性?

Kafka 消息顺序性保证Kafka 在 Partition 级别保证消息的顺序性,这是 Kafka 设计的一个重要特性。分区内有序性保证机制:Kafka 保证同一个 Partition 内的消息按照发送顺序被消费实现原理:每个 Partition 内部维护一个有序的消息队列,消息按照追加顺序写入消费顺序:Consumer 从 Partition 读取消息时,严格按照写入顺序消费跨分区无序性Topic 级别:如果 Topic 有多个 Partition,则无法保证 Topic 级别的消息顺序原因:不同 Partition 之间的消息是并行处理的,无法保证全局顺序影响:相关消息可能被分配到不同 Partition,导致消费顺序不一致保证顺序性的方法单分区策略将需要保证顺序的消息发送到同一个 Partition使用相同的 Key,Kafka 会根据 Key 进行 Hash 分配到同一 Partition适用于顺序性要求高的场景自定义分区器实现 Partitioner 接口根据业务逻辑自定义分区规则确保相关消息路由到同一 Partition单 Consumer 消费在 Consumer Group 中只有一个 Consumer 消费该 Topic避免多 Consumer 并行消费导致乱序会降低消费性能实践建议对于需要严格顺序的场景,使用单 Partition对于可以容忍部分乱序的场景,使用多 Partition 提高性能合理设置消息 Key,确保相关消息在同一 Partition监控 Consumer 的消费进度,避免消息积压性能与顺序的权衡单分区保证顺序但性能受限多分区提高性能但牺牲顺序性需要根据业务需求在两者之间找到平衡点在实际应用中,大多数场景不需要全局顺序,只需要保证相关消息的顺序即可,此时通过合理的 Key 设计和分区策略可以在性能和顺序性之间取得良好平衡。
阅读 0·2月21日 16:58