【架构实战】CDN架构设计与加速策略
一、CDN概述
CDN(Content Delivery Network)内容分发网络,通过将内容缓存到离用户最近的节点来加速访问:
CDN的核心价值:
- 加速访问速度(减少物理距离)
- 减轻源站压力(扛住流量高峰)
- 提升用户体验(减少等待时间)
- 节省带宽成本(边缘节点分流)
二、CDN架构
1. 架构组件
用户请求 → DNS解析 → CDN边缘节点 → 缓存命中? │ ┌───────────────┼───────────────┐ ▼ ▼ ▼ 边缘节点 区域节点 源站 (Last Mile) (Middle Mile) (Origin) └───────────────┴───────────────┘ │ 缓存未命中时回源2. 工作流程
1. 用户访问 www.example.com/logo.png 2. 本地DNS解析到CDN 3. CDN智能调度(选择最近节点) 4. 边缘节点检查缓存 ├── 命中 → 直接返回 └── 未命中 → 回源获取 → 缓存 → 返回三、主流CDN服务商
| 服务商 | 特点 | 价格 |
|---|---|---|
| 阿里云CDN | 节点多,生态完善 | 中等 |
| 腾讯云CDN | 国内访问快 | 中等 |
| Cloudflare | 全球覆盖,免费套餐 | 便宜 |
| Akamai | 全球第一 | 昂贵 |
| CloudFront | AWS生态集成 | 按量付费 |
四、CDN配置实战
1. 阿里云CDN配置
@ConfigurationpublicclassAliyunCDNConfig{@Value("${aliyun.cdn.domain}")privateStringcdnDomain;@Value("${aliyun.cdn.accessKeyId}")privateStringaccessKeyId;@Value("${aliyun.cdn.accessKeySecret}")privateStringaccessKeySecret;@BeanpublicCdnClientcdnClient(){returnnewCdnClientBuilder().build(accessKeyId,accessKeySecret);}}@ServicepublicclassCDNService{@AutowiredprivateCdnClientcdnClient;// 刷新缓存publicvoidrefreshCache(Stringurl){RefreshObjectCachesRequestrequest=newRefreshObjectCachesRequest();request.setObjectPath(url);request.setObjectType("File");cdnClient.refreshObjectCaches(request);}// 预热缓存publicvoidpreloadCache(Stringurl){PushObjectCacheRequestrequest=newPushObjectCacheRequest();request.setObjectPath(url);cdnClient.pushObjectCache(request);}}2. Nginx配置CDN回源
# nginx.conf upstream origin { server origin.example.com:8080; } server { listen 80; server_name cdn.example.com; # 缓存配置 proxy_cache my_cache; proxy_cache_key "$scheme$host$request_uri"; proxy_cache_valid 200 10m; proxy_cache_valid 404 1m; proxy_cache_use_stale error timeout updating; # 回源配置 proxy_pass http://origin; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; # 缓存控制 add_header X-Cache-Status $upstream_cache_status; }五、缓存策略
1. 缓存Key设计
// 合理的缓存Key设计publicclassCacheKeyBuilder{// 基础KeypublicstaticStringbuildProductKey(LongproductId){return"product:"+productId;}// 带版本的Key(版本更新时自动失效旧缓存)publicstaticStringbuildProductKeyV(LongproductId,Stringversion){return"product:"+productId+":v"+version;}// 带用户分组的Key(不同用户看到不同价格)publicstaticStringbuildProductPriceKey(LongproductId,LonguserGroupId){return"product:price:"+productId+":g"+userGroupId;}}2. 缓存失效策略
@ServicepublicclassProductCacheService{@AutowiredprivateCdnClientcdnClient;publicvoidupdateProduct(Productproduct){// 1. 更新数据库productMapper.updateById(product);// 2. 删除Redis缓存redisTemplate.delete("product:"+product.getId());// 3. 刷新CDN缓存StringcdnUrl="https://cdn.example.com/product/"+product.getId()+".html";cdnClient.refreshObjectCaches(newRefreshObjectCachesRequest(cdnUrl));// 4. 如果是大面积更新,使用刷新目录if(isBigUpdate()){cdnClient.refreshObjectCaches(newRefreshObjectCachesRequest("https://cdn.example.com/product/list/"));}}}3. 缓存控制头
@ConfigurationpublicclassCacheControlFilterimplementsFilter{@OverridepublicvoiddoFilter(ServletRequestreq,ServletResponseres,FilterChainchain)throwsIOException,ServletException{HttpServletRequestrequest=(HttpServletRequest)req;HttpServletResponseresponse=(HttpServletResponse)res;Stringpath=request.getRequestURI();// 静态资源:长时间缓存if(isStaticResource(path)){response.setHeader("Cache-Control","public, max-age=31536000");response.setHeader("Expires",LocalDateTime.now().plusYears(1).format(DateTimeFormatter.RFC_1123_DATE_TIME));}// API接口:短时间缓存或不缓存elseif(isAPI(path)){response.setHeader("Cache-Control","no-cache, no-store, must-revalidate");}// 动态页面:短时间缓存else{response.setHeader("Cache-Control","public, max-age=300");}chain.doFilter(req,res);}}六、CDN安全
1. 防盗链
# Nginx防盗链配置 server { location /images/ { # 允许的域名 valid_referers none blocked server_names ~\.google\. ~\.baidu\. ~\.example\.com; if ($invalid_referer) { return 403; } } }2. IP限流
// CDN IP限流配置publicvoidconfigureIPLimit(CdnClientclient){CreateIpFcRequestrequest=newCreateIpFcRequest();request.setDomainName("cdn.example.com");request.setConfigId("ip_limit_config");// 限制单IP请求频率request.setRules(Arrays.asList(newIpAclRule().setIpList(Arrays.asList("1.2.3.4","5.6.7.8")).setType("allow")));client.createIpFc(request);}3. HTTPS配置
// CDN HTTPS配置publicvoidconfigureHTTPS(CdnClientclient,Stringdomain){// 上传证书SetCdnDomainSSLCertificateRequestcertRequest=newSetCdnDomainSSLCertificateRequest();certRequest.setDomainName(domain);certRequest.setCertType("free");// 免费证书certRequest.setSSLProtocol("TLSv1.2");client.setCdnDomainSSLCertificate(certRequest);}七、性能优化
1. 预热策略
@ServicepublicclassPreloadService{@AutowiredprivateCdnClientcdnClient;// 秒杀开始前预热publicvoidpreloadSeckillProducts(List<Long>productIds){for(LongproductId:productIds){Stringurl="https://cdn.example.com/product/"+productId+".html";PushObjectCacheRequestrequest=newPushObjectCacheRequest();request.setObjectPath(url);cdnClient.pushObjectCache(request);log.info("预热CDN: {}",url);}}// 批量预热publicvoidbatchPreload(Stringpattern){// 预热整个目录PushObjectCacheRequestrequest=newPushObjectCacheRequest();request.setObjectPath("https://cdn.example.com/products/");cdnClient.pushObjectCache(request);}}2. 访问日志分析
@ServicepublicclassCDNLogAnalysis{// 分析CDN访问日志publicvoidanalyzeLogs(){// 获取热门资源StringlogPath="/var/log/cdn/access.log";try(Stream<String>lines=Files.lines(Paths.get(logPath))){Map<String,Long>topResources=lines.map(this::parseLogLine).filter(Objects::nonNull).collect(Collectors.groupingBy(LogEntry::getUrl,Collectors.counting())).entrySet().stream().sorted(Map.Entry.<String,Long>comparingByValue().reversed()).limit(100).collect(Collectors.toMap(Map.Entry::getKey,Map.Entry::getValue,(a,b)->a,LinkedHashMap::new));log.info("热门资源TOP10: {}",topResources);}catch(IOExceptione){log.error("日志分析失败",e);}}}八、总结
CDN是提升访问速度的重要手段:
- 架构理解:边缘节点→区域节点→源站
- 缓存策略:合理设置缓存Key和失效策略
- 安全防护:防盗链、IP限流、HTTPS
- 性能优化:预热、缓存控制头
最佳实践:
- 静态资源使用CDN
- 合理设置缓存时间
- 使用版本号实现缓存更新
- 监控CDN命中率
个人观点,仅供参考
