图片上传优化与7层负载均衡实践:解决高并发场景下的带宽瓶颈
写在前面
最近在做一个充电桩安装业务系统,安装师傅每天安装完充电桩后需要上传大量的现场图片(每单可能10-20张,每张2-5MB)。随着业务量增长,每天上传的图片量达到数万张,很快就遇到了两个严重问题:
- 服务器带宽占用过高:所有图片都先上传到应用服务器,再转发到OSS,导致服务器带宽被打满
- 请求超时:大文件上传占用连接时间长,导致其他正常业务请求超时
我们的技术栈是:阿里云OSS存储、ACK(容器服务Kubernetes)部署的微服务架构。这篇文章我会详细分析问题根源,介绍7层负载均衡的原理和应用场景,并给出完整的优化方案。
问题分析
当前架构的问题
让我们先看看当前的架构流程:
安装师傅手机端 ↓ (上传图片,每张2-5MB) 应用服务器(ACK Pod) ↓ (占用服务器带宽,转发图片) 阿里云OSS问题1:带宽瓶颈
假设每天有1000个安装师傅,每人上传15张图片,每张平均3MB:
- 每天总流量:1000 × 15 × 3MB = 45GB
- 如果集中在8小时内上传:45GB ÷ 8小时 = 5.625GB/小时 ≈ 12.5Mbps
- 但实际是并发上传,峰值可能达到100-200Mbps
如果应用服务器带宽只有100Mbps,很容易被打满,导致:
- 图片上传慢,用户体验差
- 其他业务请求被阻塞
- 服务器响应超时
问题2:成本浪费
当前架构下,图片流量走了两次:
- 手机端 → 应用服务器(占用服务器带宽,产生费用)
- 应用服务器 → OSS(占用服务器带宽,产生费用)
实际上,图片可以直接上传到OSS,不需要经过应用服务器。
问题3:连接占用
大文件上传通常需要较长时间(几秒到几十秒),在这期间会占用服务器的连接资源。如果并发上传量大,可能导致:
- 连接池耗尽
- 新请求无法建立连接
- 服务超时
解决方案:OSS直传 + CDN + 负载均衡
方案架构
优化后的架构应该是这样的:
安装师傅手机端 ↓ (获取上传凭证) 应用服务器(只返回签名,不处理文件) ↓ (直接上传,不走服务器带宽) 阿里云OSS ↓ (CDN加速) CDN节点(就近访问)核心优化点
1. OSS直传(PostObject)
原理:客户端直接上传到OSS,不经过应用服务器。
实现步骤:
- 服务端生成签名(Java示例)
@ServicepublicclassOssService{@Value("${aliyun.oss.endpoint}")privateStringendpoint;@Value("${aliyun.oss.bucket}")privateStringbucket;@Value("${aliyun.oss.accessKeyId}")privateStringaccessKeyId;@Value("${aliyun.oss.accessKeySecret}")privateStringaccessKeySecret;/** * 生成OSS直传签名 * @param fileName 文件名 * @param contentType 文件类型 * @return 上传凭证 */publicOssUploadTokengenerateUploadToken(StringfileName,StringcontentType){// 生成唯一文件名(避免覆盖)StringobjectKey="install-images/"+LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"))+"/"+UUID.randomUUID().toString()+"_"+fileName;// 设置过期时间(1小时)Dateexpiration=newDate(System.currentTimeMillis()+3600*1000);// 构建PolicyJSONObjectpolicy=newJSONObject();policy.put("expiration",newSimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").format(expiration));JSONArrayconditions=newJSONArray();// 限制bucketJSONArraybucketCondition=newJSONArray();bucketCondition.add("eq");bucketCondition.add("$bucket");bucketCondition.add(bucket);conditions.add(bucketCondition);// 限制文件大小(最大10MB)JSONArraysizeCondition=newJSONArray();sizeCondition.add("content-length-range");sizeCondition.add(0);sizeCondition.add(10485760);conditions.add(sizeCondition);// 限制文件类型JSONArraycontentTypeCondition=newJSONArray();contentTypeCondition.add("starts-with");contentTypeCondition.add("$content-type");contentTypeCondition.add("image/");conditions.add(contentTypeCondition);policy.put("conditions",conditions);// Base64编码PolicyStringpolicyBase64=Base64.getEncoder().encodeToString(policy.toJSONString().getBytes(StandardCharsets.UTF_8));// 生成签名Machmac=Mac.getInstance("HmacSHA1");SecretKeySpecsecretKeySpec=newSecretKeySpec(accessKeySecret.getBytes(StandardCharsets.UTF_8),"HmacSHA1");hmac.init(secretKeySpec);byte[]signData=hmac.doFinal(policyBase64.getBytes(StandardCharsets.UTF_8));Stringsignature=Base64.getEncoder().encodeToString(signData);OssUploadTokentoken=newOssUploadToken();token.setPolicy(policyBase64);token.setSignature(signature);token.setAccessKeyId(accessKeyId);token.setObjectKey(objectKey);token.setHost("https://"+bucket+"."+endpoint);token.setExpireTime(expiration.getTime());returntoken;}}- 客户端直接上传(前端示例)
// 1. 获取上传凭证asyncfunctiongetUploadToken(fileName,fileType){constresponse=awaitfetch('/api/oss/upload-token',{method:'POST',headers:{'Content-Type':'application/json',},body:JSON.stringify({fileName:fileName,contentType:fileType})});returnawaitresponse.json();}// 2. 直接上传到OSSasyncfunctionuploadToOss(file){// 获取上传凭证consttoken=awaitgetUploadToken(file.name,file.type);// 构建FormDataconstformData=newFormData();formData.append('key',token.objectKey);formData.append('policy',token.policy);formData.append('OSSAccessKeyId',token.accessKeyId);formData.append('signature',token.signature);formData.append('Content-Type',file.type);formData.append('file',file);// 直接上传到OSS(不走应用服务器)constresponse=awaitfetch(token.host,{method:'POST',body:formData});if(response.ok){// 上传成功,返回OSS文件URLreturn`${token.host}/${token.objectKey}`;}else{thrownewError('上传失败');}}优势:
- 图片不经过应用服务器,节省服务器带宽
- 上传速度快(直接到OSS,减少一跳)
- 降低服务器负载
- 节省流量费用
2. CDN加速
原理:将OSS中的图片通过CDN分发,用户访问时从就近节点获取。
配置步骤:
OSS绑定CDN域名
- 在OSS控制台,为Bucket绑定CDN加速域名
- 配置HTTPS证书
- 开启CDN缓存(图片建议缓存7-30天)
CDN回源配置
- 回源Host:OSS的Bucket域名
- 回源协议:HTTPS
- 缓存规则:根据文件类型和路径设置
访问URL
- 原来:
https://bucket.oss-cn-hangzhou.aliyuncs.com/image.jpg - 现在:
https://cdn.example.com/image.jpg(通过CDN访问)
- 原来:
优势:
- 用户访问图片更快(就近节点)
- 减少OSS的访问压力
- CDN流量费用通常比OSS外网流量费用低
3. 图片压缩与格式优化
客户端压缩:
// 压缩图片(保持质量的同时减小体积)functioncompressImage(file,maxWidth=1920,quality=0.8){returnnewPromise((resolve)=>{constreader=newFileReader();reader.onload=(e)=>{constimg=newImage();img.onload=()=>{constcanvas=document.createElement('canvas');letwidth=img.width;letheight=img.height;// 按比例缩放if(width>maxWidth){height=(height*maxWidth)/width;width=maxWidth;}canvas.width=width;canvas.height=height;constctx=canvas.getContext('2d');ctx.drawImage(img,0,0,width,height);// 转换为Blobcanvas.toBlob((blob)=>{resolve(blob);},'image/jpeg',quality);};img.src=e.target.result;};reader.readAsDataURL(file);});}// 使用constcompressedFile=awaitcompressImage(originalFile);awaituploadToOss(compressedFile);服务端处理(可选):
- 使用OSS的图片处理服务(IMG)
- 自动生成缩略图
- 格式转换(WebP等)
7层负载均衡详解
什么是7层负载均衡?
负载均衡分为4层(L4)和7层(L7):
- 4层负载均衡:基于IP和端口进行转发(TCP/UDP层)
- 7层负载均衡:基于HTTP/HTTPS协议内容进行转发(应用层)
OSI模型与负载均衡
OSI 7层模型: ┌─────────────────────────────────────┐ │ 7. 应用层 (Application) │ ← 7层负载均衡(HTTP/HTTPS) │ 6. 表示层 (Presentation) │ │ 5. 会话层 (Session) │ ├─────────────────────────────────────┤ │ 4. 传输层 (Transport) - TCP/UDP │ ← 4层负载均衡 ├─────────────────────────────────────┤ │ 3. 网络层 (Network) - IP │ │ 2. 数据链路层 (Data Link) │ │ 1. 物理层 (Physical) │ └─────────────────────────────────────┘7层负载均衡的工作原理
7层负载均衡器(如Nginx、ALB)会:
- 解析HTTP请求:读取HTTP头部信息(Host、URL、Cookie等)
- 根据规则路由:基于请求内容决定转发到哪个后端服务器
- 建立新连接:与后端服务器建立新的TCP连接
- 转发请求:将完整的HTTP请求转发给后端
- 返回响应:将后端响应返回给客户端
客户端请求 ↓ 7层负载均衡器(解析HTTP) ├─ 根据Host: api.example.com → 转发到API服务 ├─ 根据URL: /static/ → 转发到静态资源服务 └─ 根据Cookie: session_id → 转发到特定服务器(会话保持) ↓ 后端服务器7层负载均衡的核心功能
1. 基于域名的路由(Virtual Host)
场景:多个域名指向同一个负载均衡器,需要路由到不同的后端服务。
请求:Host: api.example.com → 后端:API服务集群 请求:Host: admin.example.com → 后端:管理后台集群 请求:Host: static.example.com → 后端:静态资源服务Nginx配置示例:
# API服务 server { listen 80; server_name api.example.com; location / { proxy_pass http://api_backend; } } # 管理后台 server { listen 80; server_name admin.example.com; location / { proxy_pass http://admin_backend; } }2. 基于URL路径的路由
场景:同一个域名下,不同路径路由到不同服务。
请求:/api/user → 用户服务 请求:/api/order → 订单服务 请求:/api/payment → 支付服务 请求:/static/ → 静态资源(CDN或Nginx直接返回)Nginx配置示例:
server { listen 80; server_name example.com; # 用户服务 location /api/user { proxy_pass http://user_service; } # 订单服务 location /api/order { proxy_pass http://order_service; } # 静态资源 location /static/ { root /var/www/static; expires 30d; } }3. 基于HTTP头部的路由
场景:根据请求头(如User-Agent、Cookie)进行路由。
移动端请求(User-Agent包含Mobile) → 移动端专用服务 PC端请求 → PC端服务Nginx配置示例:
server { listen 80; # 移动端 if ($http_user_agent ~* "mobile|android|iphone") { proxy_pass http://mobile_backend; break; } # PC端 proxy_pass http://pc_backend; }4. 会话保持(Session Affinity)
场景:确保同一用户的请求总是转发到同一台服务器(用于有状态服务)。
实现方式:
- Cookie方式:负载均衡器插入Cookie,记录后端服务器信息
- IP Hash:根据客户端IP的Hash值选择后端服务器
Nginx配置示例:
upstream backend { # IP Hash方式 ip_hash; server 192.168.1.10:8080; server 192.168.1.11:8080; server 192.168.1.12:8080; }5. 内容重写与重定向
场景:URL重写、HTTPS重定向等。
# HTTP重定向到HTTPS server { listen 80; server_name example.com; return 301 https://$server_name$request_uri; } # URL重写 location /old-path { rewrite ^/old-path/(.*)$ /new-path/$1 permanent; }6. SSL终止(SSL Termination)
场景:在负载均衡器上处理SSL/TLS,减轻后端服务器压力。
客户端 → [HTTPS] → 负载均衡器(SSL终止) → [HTTP] → 后端服务器优势:
- 后端服务器不需要处理SSL,性能更好
- 集中管理证书,更安全
- 可以统一做SSL优化
7层负载均衡的应用场景
场景1:微服务网关
需求:多个微服务需要统一入口,根据路径路由到不同服务。
客户端 ↓ API网关(7层负载均衡) ├─ /user/* → 用户服务 ├─ /order/* → 订单服务 ├─ /payment/* → 支付服务 └─ /install/* → 安装服务(你的场景)阿里云ALB配置:
- 创建应用型负载均衡(ALB)
- 配置监听规则:基于路径转发
- 后端服务器组:指向ACK的Service
场景2:动静分离
需求:静态资源(图片、CSS、JS)和动态API分开处理。
请求:/api/* → 应用服务器(动态内容) 请求:/static/* → CDN或Nginx(静态资源,直接返回,不走应用服务器)优势:
- 静态资源不占用应用服务器资源
- 可以单独对静态资源做CDN加速
- 应用服务器专注处理业务逻辑
场景3:A/B测试与灰度发布
需求:根据用户特征(Cookie、Header)将流量分发到不同版本。
新用户 → 新版本服务(10%流量) 老用户 → 旧版本服务(90%流量)Nginx配置示例:
# 根据Cookie中的version字段路由 map $cookie_version $backend { default "v1_backend"; "v2" "v2_backend"; } server { location / { proxy_pass http://$backend; } }场景4:限流与防护
需求:在负载均衡层做限流,保护后端服务。
Nginx限流配置:
# 限制每个IP的请求速率 limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s; server { location /api/ { limit_req zone=api_limit burst=20; proxy_pass http://backend; } }场景5:请求日志与分析
需求:在负载均衡层记录访问日志,用于分析。
Nginx日志配置:
log_format detailed '$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"'; access_log /var/log/nginx/access.log detailed;4层 vs 7层负载均衡对比
| 特性 | 4层负载均衡(L4) | 7层负载均衡(L7) |
|---|---|---|
| 工作层级 | TCP/UDP层 | HTTP/HTTPS层 |
| 转发依据 | IP + 端口 | HTTP头部、URL、Cookie等 |
| 性能 | 更高(不需要解析HTTP) | 相对较低(需要解析HTTP) |
| 功能 | 简单转发 | 丰富的路由规则、内容处理 |
| 适用场景 | 高并发、简单转发 | 微服务网关、复杂路由 |
| 典型产品 | 阿里云SLB(4层)、F5 | 阿里云ALB、Nginx、HAProxy |
阿里云负载均衡产品选择
1. 传统型负载均衡CLB(原SLB)
- 支持4层和7层
- 适用场景:传统应用、简单转发
- 特点:功能基础,性能稳定
2. 应用型负载均衡ALB
- 仅支持7层(HTTP/HTTPS)
- 适用场景:微服务、API网关、复杂路由
- 特点:
- 基于内容的路由(域名、路径、Header)
- 支持Serverless后端(函数计算)
- 自动弹性伸缩
- 更丰富的监控指标
3. 网络型负载均衡NLB
- 仅支持4层(TCP/UDP)
- 适用场景:超高性能、低延迟场景
- 特点:
- 超高性能(支持百万级并发)
- 低延迟
- 适用于游戏、金融等对性能要求极高的场景
针对我的场景的完整方案
架构设计
安装师傅手机端 ↓ ├─ 1. 获取上传凭证(小请求,走应用服务器) │ /api/oss/upload-token │ └─ 2. 直接上传图片到OSS(大文件,不走应用服务器) https://bucket.oss-cn-hangzhou.aliyuncs.com ↓ OSS存储 ↓ CDN加速(可选) https://cdn.example.com/image.jpg具体实施步骤
步骤1:实现OSS直传
后端服务(Spring Boot):
@RestController@RequestMapping("/api/oss")publicclassOssController{@AutowiredprivateOssServiceossService;/** * 获取上传凭证 */@PostMapping("/upload-token")publicResult<OssUploadToken>getUploadToken(@RequestBodyUploadTokenRequestrequest){OssUploadTokentoken=ossService.generateUploadToken(request.getFileName(),request.getContentType());returnResult.success(token);}/** * 上传完成回调(可选,用于记录上传记录) */@PostMapping("/upload-callback")publicResult<String>uploadCallback(@RequestBodyOssCallbackcallback){// 验证OSS回调签名if(!ossService.verifyCallback(callback)){returnResult.error("签名验证失败");}// 记录上传信息到数据库installImageService.recordUpload(callback.getObjectKey(),callback.getSize(),callback.getUserId());returnResult.success("上传记录成功");}}步骤2:配置CDN加速(可选但推荐)
在OSS控制台绑定CDN域名
- 进入OSS Bucket → 传输管理 → CDN加速
- 绑定自定义域名(如:cdn.example.com)
- 配置HTTPS证书
CDN缓存配置
- 缓存时间:图片文件缓存30天
- 回源协议:HTTPS
- 开启Gzip压缩
修改上传后的URL
@ServicepublicclassOssService{@Value("${aliyun.cdn.domain:}")privateStringcdnDomain;publicStringgetImageUrl(StringobjectKey){if(StringUtils.isNotBlank(cdnDomain)){// 使用CDN域名return"https://"+cdnDomain+"/"+objectKey;}else{// 使用OSS域名return"https://"+bucket+"."+endpoint+"/"+objectKey;}}}步骤3:配置7层负载均衡(ALB)- 完整实施流程
场景:如果你的应用有多个服务,需要统一入口,通过7层负载均衡实现灵活的请求路由。
3.1 准备工作
在开始配置之前,需要确认以下信息:
ACK集群信息
- 集群ID
- VPC ID
- 可用区(至少2个)
- 子网ID
域名和证书
- API域名(如:api.example.com)
- SSL证书(可在阿里云SSL证书服务申请,或上传自有证书)
服务信息
- 各个微服务的Service名称
- 服务的健康检查路径(如:/health、/actuator/health)
3.2 创建ALB实例
步骤1:进入负载均衡控制台
- 登录阿里云控制台
- 进入「产品与服务」→「网络」→「负载均衡SLB」
- 点击「创建负载均衡」
步骤2:选择负载均衡类型
- 负载均衡类型:选择「应用型负载均衡(ALB)」
- 地域:选择与ACK集群相同的地域(如:华东1-杭州)
- 可用区:至少选择2个可用区(如:可用区H、可用区I),实现高可用
步骤3:配置基本信息
实例名称:alb-api-gateway 付费类型:按量付费(或包年包月) 规格:标准型(根据业务量选择,支持弹性扩容)步骤4:配置网络
网络类型:专有网络(VPC) VPC:选择ACK集群所在的VPC 交换机:在每个可用区选择至少一个交换机 - 可用区H:选择交换机1(如:vsw-xxx1) - 可用区I:选择交换机2(如:vsw-xxx2)重要提示:
- ALB必须与ACK集群在同一个VPC内
- 交换机必须有足够的IP地址(建议至少预留10个IP)
步骤5:创建并获取ALB地址
创建完成后,记录ALB的IP地址(如:47.xxx.xxx.xxx),后续配置DNS会用到。
3.3 配置ACK Service(后端服务)
在配置ALB之前,需要确保ACK中的服务已经正确配置。
步骤1:检查现有Service
# 查看所有Servicekubectl get svc -n<namespace># 查看Service详情kubectl describe svc<service-name>-n<namespace>步骤2:创建或修改Service(如果需要)
假设你有以下微服务:
install-service:安装服务(处理安装相关API)oss-service:OSS服务(处理上传凭证等)user-service:用户服务
为每个服务创建Service:
# install-service.yamlapiVersion:v1kind:Servicemetadata:name:install-servicenamespace:defaultlabels:app:install-servicespec:type:ClusterIP# 内部服务,不对外暴露ports:-port:8080# Service端口targetPort:8080# Pod端口protocol:TCPselector:app:install-service# 选择器,匹配Pod标签---# oss-service.yamlapiVersion:v1kind:Servicemetadata:name:oss-servicenamespace:defaultlabels:app:oss-servicespec:type:ClusterIPports:-port:8080targetPort:8080protocol:TCPselector:app:oss-service应用配置:
kubectl apply -f install-service.yaml kubectl apply -f oss-service.yaml步骤3:添加健康检查端点
确保每个服务都有健康检查接口:
// Spring Boot示例@RestControllerpublicclassHealthController{@GetMapping("/health")publicMap<String,String>health(){Map<String,String>status=newHashMap<>();status.put("status","UP");status.put("timestamp",Instant.now().toString());returnstatus;}// 或者使用Spring Boot Actuator// 在application.yml中配置:// management:// endpoints:// web:// exposure:// include: health,info}3.4 配置ALB监听(HTTPS)
步骤1:添加监听
- 在ALB实例详情页,点击「监听」标签
- 点击「添加监听」
- 选择协议和端口:
- 协议:HTTPS
- 端口:443
步骤2:配置SSL证书
证书来源:
- 如果已有证书:选择「上传证书」,上传证书文件(.pem格式)和私钥(.key格式)
- 如果没有证书:可以在「SSL证书」服务中申请免费证书(DV证书)
证书配置:
证书:选择或上传SSL证书 TLS版本:TLSv1.2、TLSv1.3(建议都勾选)
步骤3:配置监听高级设置
调度算法:加权轮询(WRR)或加权最小连接数(WLC) - WRR:按权重轮询,适合请求处理时间相近的场景 - WLC:优先分配给连接数少的后端,适合请求处理时间差异大的场景 连接超时时间:60秒(根据实际情况调整) 请求超时时间:60秒 Gzip压缩:开启(可以压缩响应内容,节省带宽)3.5 配置后端服务器组
步骤1:创建服务器组
- 在监听配置页面,点击「后端服务器组」
- 点击「创建服务器组」
步骤2:配置服务器组基本信息
服务器组名称:backend-install-service 服务器组类型:IP类型 或 服务器类型 - IP类型:直接指定Pod IP(不推荐,Pod重启IP会变) - 服务器类型:选择ACK集群的节点(需要安装ALB Ingress Controller) - 函数计算类型:如果使用Serverless后端 协议:HTTP(ALB到后端使用HTTP,SSL在ALB终止) 端口:8080(Service的端口)重要:对于ACK场景,推荐使用ALB Ingress Controller,这样可以直接关联Kubernetes Service。
步骤3:安装ALB Ingress Controller(推荐方式)
这是连接ALB和ACK的最佳实践:
# 1. 添加ALB Ingress Controller的Helm仓库helm repoaddalibaba https://aliacs-app-catalog.oss-cn-hangzhou.aliyuncs.com/charts-incubator/ helm repo update# 2. 安装ALB Ingress Controllerhelminstallalibaba/alb-ingress-controller\--namespace kube-system\--setregionId=cn-hangzhou\--setclusterId=<你的ACK集群ID>\--setvpcId=<你的VPC ID>\--setalbId=<你的ALB实例ID>步骤4:使用Ingress配置路由(推荐)
创建Ingress资源,自动关联ALB:
# ingress.yamlapiVersion:networking.k8s.io/v1kind:Ingressmetadata:name:api-ingressnamespace:defaultannotations:# 指定使用ALBalb.ingress.kubernetes.io/load-balancer-id:<ALB实例ID># 监听配置alb.ingress.kubernetes.io/listen-ports:'[{"HTTPS":443}]'# SSL证书alb.ingress.kubernetes.io/certificate-id:<证书ID># 健康检查alb.ingress.kubernetes.io/healthcheck-path:/healthalb.ingress.kubernetes.io/healthcheck-protocol:HTTPalb.ingress.kubernetes.io/healthcheck-interval-seconds:'5'alb.ingress.kubernetes.io/healthcheck-timeout-seconds:'3'alb.ingress.kubernetes.io/healthy-threshold-count:'2'alb.ingress.kubernetes.io/unhealthy-threshold-count:'3'spec:ingressClassName:albrules:# 安装服务路由-host:api.example.comhttp:paths:-path:/api/installpathType:Prefixbackend:service:name:install-serviceport:number:8080# OSS服务路由-path:/api/osspathType:Prefixbackend:service:name:oss-serviceport:number:8080# 其他API路由-path:/apipathType:Prefixbackend:service:name:api-gateway-serviceport:number:8080应用Ingress配置:
kubectl apply -f ingress.yaml步骤5:手动配置后端服务器组(如果不使用Ingress)
如果不想使用Ingress,可以手动配置:
创建服务器组
- 服务器组名称:
backend-install-service - 协议:HTTP
- 端口:8080
- 服务器组名称:
添加后端服务器
- 方式1:添加ECS实例(ACK节点)
- 选择ACK集群的节点ECS
- 端口:8080(Service的NodePort,如果使用NodePort类型)
- 方式2:添加IP地址(Pod IP,不推荐)
- 方式1:添加ECS实例(ACK节点)
配置健康检查
检查协议:HTTP 检查路径:/health 检查端口:8080 检查间隔:5秒 超时时间:3秒 健康阈值:2次(连续2次成功认为健康) 不健康阈值:3次(连续3次失败认为不健康) HTTP状态码:200(健康检查成功的状态码)配置权重
- 如果后端服务器性能不同,可以设置不同权重
- 权重越高,分配的流量越多
3.6 配置转发规则
步骤1:添加转发规则
在监听配置页面,添加转发规则:
规则1:安装服务路由
规则名称:rule-install-service 域名:api.example.com(或留空,匹配所有域名) 路径:/api/install/* - 匹配方式:前缀匹配 - 说明:所有以 /api/install 开头的请求 转发至:backend-install-service(后端服务器组) 优先级:1(数字越小优先级越高)规则2:OSS服务路由
规则名称:rule-oss-service 域名:api.example.com 路径:/api/oss/* 转发至:backend-oss-service 优先级:2规则3:默认路由
规则名称:rule-default 域名:api.example.com 路径:/* 转发至:backend-default-service 优先级:100(最低优先级,作为默认规则)规则匹配顺序:
- 先匹配域名(如果配置了)
- 再匹配路径(按优先级从高到低)
- 匹配到第一个规则后停止
步骤2:配置高级路由规则(可选)
如果需要更复杂的路由,可以使用基于Header的路由:
条件:HTTP Header Header名称:X-Service-Version Header值:v2 匹配方式:等于 转发至:backend-service-v23.7 配置DNS解析
步骤1:获取ALB的IP地址
在ALB实例详情页,记录「服务地址」(公网IP或内网IP)。
步骤2:配置DNS解析
- 进入「云解析DNS」控制台
- 找到你的域名(example.com)
- 添加解析记录:
记录类型:A记录 主机记录:api(完整域名为 api.example.com) 记录值:ALB的服务地址(IP) TTL:600秒(10分钟)步骤3:验证DNS解析
# 使用dig命令验证digapi.example.com# 或使用nslookupnslookupapi.example.com# 应该返回ALB的IP地址3.8 配置会话保持(如需要)
如果你的服务是有状态的,需要配置会话保持:
步骤1:在服务器组中配置
会话保持:开启 会话保持方式:基于Cookie - 植入Cookie:ALB自动插入Cookie - 重写Cookie:重写应用返回的Cookie Cookie名称:ALB_SESSION(可自定义) Cookie超时时间:86400秒(24小时)步骤2:在代码中处理(如果使用重写Cookie)
// Spring Boot示例@RestControllerpublicclassApiController{@GetMapping("/api/test")publicResulttest(HttpServletResponseresponse){// 如果使用重写Cookie方式,需要在响应中设置CookieCookiecookie=newCookie("ALB_SESSION",sessionId);cookie.setPath("/");cookie.setMaxAge(86400);response.addCookie(cookie);returnResult.success();}}3.9 配置访问控制(安全加固)
步骤1:配置访问控制列表(ACL)
- 在ALB实例中,进入「访问控制」
- 创建访问控制策略组
- 添加IP白名单或黑名单:
策略组名称:api-whitelist 类型:白名单(只允许列表中的IP访问) IP地址: - 192.168.1.0/24(内网段) - 10.0.0.0/8(VPC网段)- 在监听中关联访问控制策略组
步骤2:配置WAF(Web应用防火墙)
如果需要更高级的安全防护:
- 在ALB监听中开启「WAF防护」
- 配置WAF规则:
- 防SQL注入
- 防XSS攻击
- 防CC攻击
- 自定义规则
3.10 测试验证
步骤1:测试健康检查
# 直接访问后端服务(通过Service)kubectl port-forward svc/install-service8080:8080 -n default# 在另一个终端测试curlhttp://localhost:8080/health# 应该返回:# {"status":"UP","timestamp":"2024-01-01T12:00:00Z"}步骤2:测试ALB路由
# 测试安装服务路由curl-H"Host: api.example.com"https://<ALB-IP>/api/install/health# 测试OSS服务路由curl-H"Host: api.example.com"https://<ALB-IP>/api/oss/upload-token# 测试默认路由curl-H"Host: api.example.com"https://<ALB-IP>/api/health步骤3:测试HTTPS
# 测试HTTPS(忽略证书验证,仅测试连通性)curl-k https://api.example.com/api/health# 正常测试(验证证书)curlhttps://api.example.com/api/health步骤4:测试负载均衡
# 连续发送多个请求,观察是否分发到不同后端foriin{1..10};docurlhttps://api.example.com/api/healthecho""done# 查看后端服务器的访问日志,确认请求被分发3.11 监控与告警配置
步骤1:查看监控指标
在ALB控制台,可以查看:
- 请求数:QPS、总请求数
- 流量:入流量、出流量
- 延迟:平均响应时间、P99延迟
- 错误率:4xx、5xx错误率
- 后端健康:健康检查成功率
步骤2:配置告警
- 进入「云监控」控制台
- 创建告警规则:
指标:ALB请求错误率 阈值:> 5%(错误率超过5%告警) 通知方式:邮件、短信、钉钉指标:ALB后端健康检查失败 阈值:连续失败3次 通知方式:邮件、短信3.12 故障排查
问题1:502 Bad Gateway
可能原因:
- 后端服务不可用
- 健康检查失败
- 网络不通
排查步骤:
# 1. 检查后端服务状态kubectl get pods -n default kubectl logs<pod-name>-n default# 2. 检查Servicekubectl get svc -n default kubectl describe svc<service-name>-n default# 3. 检查健康检查端点kubectl port-forward svc/<service-name>8080:8080curlhttp://localhost:8080/health# 4. 检查ALB后端服务器组状态# 在ALB控制台查看后端服务器健康状态问题2:路由不生效
可能原因:
- 转发规则配置错误
- 路径匹配不正确
- 优先级设置错误
排查步骤:
# 1. 检查Ingress配置kubectl get ingress -n default kubectl describe ingress<ingress-name>-n default# 2. 检查ALB转发规则# 在ALB控制台查看监听规则,确认路径和优先级# 3. 测试不同路径curlhttps://api.example.com/api/install/testcurlhttps://api.example.com/api/oss/test问题3:SSL证书问题
可能原因:
- 证书过期
- 证书域名不匹配
- 证书格式错误
排查步骤:
# 1. 检查证书有效期openssl x509 -in certificate.pem -noout -dates# 2. 检查证书域名openssl x509 -in certificate.pem -noout -text|grepDNS# 3. 测试SSL连接openssl s_client -connect api.example.com:443 -servername api.example.com问题4:DNS解析问题
排查步骤:
# 1. 检查DNS解析digapi.example.comnslookupapi.example.com# 2. 检查本地DNS缓存# Windows: ipconfig /flushdns# Linux: systemd-resolve --flush-caches# 3. 直接使用IP测试curl-H"Host: api.example.com"https://<ALB-IP>/api/health3.13 性能优化建议
1. 开启HTTP/2
在ALB监听中开启HTTP/2支持,可以提升性能:
协议版本:HTTP/2(自动协商,支持HTTP/1.1和HTTP/2)2. 配置连接复用
在应用代码中配置HTTP客户端连接池:
// Spring Boot RestTemplate配置@ConfigurationpublicclassRestTemplateConfig{@BeanpublicRestTemplaterestTemplate(){HttpComponentsClientHttpRequestFactoryfactory=newHttpComponentsClientHttpRequestFactory();factory.setConnectionRequestTimeout(5000);factory.setConnectTimeout(5000);factory.setReadTimeout(10000);// 配置连接池PoolingHttpClientConnectionManagerconnectionManager=newPoolingHttpClientConnectionManager();connectionManager.setMaxTotal(200);connectionManager.setDefaultMaxPerRoute(50);CloseableHttpClienthttpClient=HttpClients.custom().setConnectionManager(connectionManager).build();factory.setHttpClient(httpClient);returnnewRestTemplate(factory);}}3. 配置超时时间
根据业务需求合理设置超时:
连接超时:5秒(建立连接的最大时间) 请求超时:30秒(处理请求的最大时间,根据接口响应时间调整)4. 开启Gzip压缩
在ALB监听中开启Gzip压缩,可以减少传输数据量:
Gzip压缩:开启 压缩类型:text/html, text/css, application/json, application/javascript3.14 完整配置示例总结
架构图:
客户端 ↓ HTTPS (api.example.com) DNS解析 ↓ ALB (47.xxx.xxx.xxx:443) ├─ 规则1: /api/install/* → backend-install-service ├─ 规则2: /api/oss/* → backend-oss-service └─ 规则3: /api/* → backend-default-service ↓ HTTP (内网) ACK Service (ClusterIP) ↓ Pod (应用服务)配置清单:
- ✅ ALB实例创建(VPC、可用区、规格)
- ✅ SSL证书配置(HTTPS监听)
- ✅ ACK Service配置(ClusterIP类型)
- ✅ Ingress配置(自动关联ALB)或手动配置后端服务器组
- ✅ 转发规则配置(域名、路径、优先级)
- ✅ 健康检查配置(路径、间隔、阈值)
- ✅ DNS解析配置(A记录指向ALB)
- ✅ 监控告警配置(错误率、健康检查)
- ✅ 安全配置(ACL、WAF,可选)
验证清单:
- DNS解析正确(dig/nslookup)
- HTTPS访问正常(curl测试)
- 路由规则生效(不同路径访问不同服务)
- 健康检查正常(后端服务健康状态为正常)
- 负载均衡生效(请求分发到多个后端)
- 监控指标正常(请求数、错误率等)
步骤4:客户端优化
图片压缩与上传:
classImageUploader{constructor(){this.maxWidth=1920;this.quality=0.8;}// 压缩图片asynccompressImage(file){returnnewPromise((resolve,reject)=>{constreader=newFileReader();reader.onload=(e)=>{constimg=newImage();img.onload=()=>{constcanvas=document.createElement('canvas');let{width,height}=img;if(width>this.maxWidth){height=(height*this.maxWidth)/width;width=this.maxWidth;}canvas.width=width;canvas.height=height;constctx=canvas.getContext('2d');ctx.drawImage(img,0,0,width,height);canvas.toBlob((blob)=>resolve(blob),'image/jpeg',this.quality);};img.onerror=reject;img.src=e.target.result;};reader.onerror=reject;reader.readAsDataURL(file);});}// 上传到OSSasyncupload(file){try{// 1. 压缩图片constcompressedFile=awaitthis.compressImage(file);// 2. 获取上传凭证(小请求)consttoken=awaitthis.getUploadToken(file.name,compressedFile.type);// 3. 直接上传到OSS(大文件,不走服务器)constformData=newFormData();formData.append('key',token.objectKey);formData.append('policy',token.policy);formData.append('OSSAccessKeyId',token.accessKeyId);formData.append('signature',token.signature);formData.append('Content-Type',compressedFile.type);formData.append('file',compressedFile);constresponse=awaitfetch(token.host,{method:'POST',body:formData});if(!response.ok){thrownewError('上传失败');}// 4. 返回CDN URL(如果配置了CDN)returnthis.getImageUrl(token.objectKey);}catch(error){console.error('上传失败:',error);throwerror;}}asyncgetUploadToken(fileName,contentType){constresponse=awaitfetch('/api/oss/upload-token',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({fileName,contentType})});returnawaitresponse.json();}getImageUrl(objectKey){// 如果配置了CDN,返回CDN URLconstcdnDomain='cdn.example.com';return`https://${cdnDomain}/${objectKey}`;}}// 使用constuploader=newImageUploader();constimageUrl=awaituploader.upload(file);成本优化分析
优化前(当前方案)
每天流量:45GB - 手机端 → 应用服务器:45GB(服务器带宽费用) - 应用服务器 → OSS:45GB(服务器带宽费用) - 用户访问图片:假设50GB(OSS外网流量费用) 总成本:服务器带宽 + OSS流量费用优化后(OSS直传 + CDN)
每天流量:45GB - 手机端 → OSS:45GB(OSS内网流量,如果同区域几乎免费) - 用户访问图片:50GB(CDN流量,通常比OSS外网流量便宜30-50%) 节省: 1. 服务器带宽费用:45GB × 2 = 90GB(完全节省) 2. OSS外网流量费用:50GB(替换为更便宜的CDN流量)预计节省成本:50-70%(取决于具体定价)
重要澄清:OSS流量费用详解
常见误解:通过SLB上传OSS,就不需要购买OSS流量资源包了?
答案:❌不是的。SLB和OSS流量费用是两个不同的概念。
OSS流量费用构成
OSS的流量费用主要分为以下几种:
外网上行流量(上传):通常是免费的
- 从客户端上传文件到OSS
- 无论是否经过SLB,上传流量都是免费的
- 例如:安装师傅上传图片到OSS,这部分流量免费
外网下行流量(下载):需要付费
- 从OSS下载文件到客户端(用户查看图片)
- 这是产生OSS流量费用的主要来源
- 例如:用户访问、查看图片时产生的流量
内网流量:几乎免费
- 同地域内(如都在华东1-杭州)的流量
- 如果OSS和应用服务器在同一地域,内网流量几乎免费
跨区域流量:需要付费
- 不同地域之间的流量传输
SLB的作用
重要理解:SLB(负载均衡)不会减少OSS的流量费用。
错误理解: 客户端 → SLB → OSS(以为这样就不收OSS流量费了) 实际情况: 客户端 → OSS(直接上传,不走SLB) 客户端 ← OSS(直接下载,不走SLB)SLB的真正作用:
- SLB用于API请求的负载均衡(如获取上传凭证的接口)
- 不用于文件上传/下载的转发
- 文件上传/下载是客户端直接与OSS通信
正确的架构理解
场景1:获取上传凭证(小请求,走SLB) 客户端 → SLB → 应用服务器 → 返回签名 (这个请求很小,几KB,不涉及文件传输) 场景2:上传图片(大文件,直接到OSS) 客户端 → OSS(直接上传,不走SLB,不走应用服务器) (上传流量免费) 场景3:查看图片(大文件,直接到OSS或CDN) 客户端 ← OSS/CDN(直接下载,不走SLB) (下载流量收费,这是需要流量资源包的地方)什么时候需要OSS流量资源包?
需要购买流量资源包的情况:
用户访问图片量大
- 每天有大量用户查看图片
- 例如:每天50GB的图片访问量
- 这种情况下,购买流量资源包比按量付费更便宜
外网下载流量
- 用户通过外网访问OSS中的图片
- 跨地域访问OSS
不需要购买流量资源包的情况:
只有上传,没有下载
- 如果图片只上传,用户不访问(很少见)
- 上传流量本身是免费的
使用CDN加速
- 如果图片通过CDN访问,流量走CDN,不走OSS外网
- CDN流量费用通常比OSS外网流量便宜30-50%
成本优化策略
策略1:使用CDN替代OSS外网流量
方案A(不推荐): 用户访问图片 → OSS外网 → 用户 流量费用:按OSS外网流量计费(较贵) 方案B(推荐): 用户访问图片 → CDN → OSS(回源)→ CDN → 用户 流量费用:按CDN流量计费(较便宜,通常便宜30-50%)配置CDN后:
- 首次访问:CDN从OSS回源(产生OSS内网流量,几乎免费)
- 后续访问:CDN直接返回(不产生OSS流量)
- 用户流量:走CDN,按CDN流量计费
策略2:购买流量资源包(如果必须用OSS外网)
如果必须使用OSS外网流量(不使用CDN),可以:
- 预估流量:根据历史数据预估每月流量
- 购买资源包:在阿里云购买OSS流量资源包
- 成本对比:
按量付费:0.5元/GB(示例价格,实际以阿里云为准) 资源包:100GB = 40元(0.4元/GB,更便宜)
策略3:内网访问(同地域)
如果应用服务器和OSS在同一地域:
应用服务器(华东1) → OSS(华东1)→ 内网流量(几乎免费)实际成本对比示例
假设场景:
- 每天上传:45GB(免费)
- 每天用户访问:50GB
方案对比:
| 方案 | 上传流量费用 | 下载流量费用 | 总费用(月) |
|---|---|---|---|
| 方案1:直接OSS外网 | 免费 | 50GB/天 × 30天 × 0.5元/GB = 750元 | 750元 |
| 方案2:OSS + CDN | 免费 | 50GB/天 × 30天 × 0.3元/GB = 450元 | 450元 |
| 方案3:OSS流量资源包 | 免费 | 1500GB资源包 ≈ 600元 | 600元 |
推荐方案:方案2(OSS + CDN),既省钱又提升访问速度。
总结
SLB不减少OSS流量费用
- SLB用于API请求负载均衡
- 文件上传/下载不走SLB
上传流量免费
- 无论是否经过SLB,上传到OSS都是免费的
下载流量需要付费
- 用户访问图片产生的下载流量需要付费
- 这是需要流量资源包的地方
最佳实践
- 使用CDN加速,降低流量成本
- 如果必须用OSS外网,考虑购买流量资源包
- 尽量使用内网访问(同地域)
性能优化建议
1. 批量上传优化
如果一次需要上传多张图片,可以:
// 并发上传(但限制并发数,避免过载)asyncfunctionuploadMultiple(files,maxConcurrent=3){constresults=[];constqueue=[...files];while(queue.length>0){constbatch=queue.splice(0,maxConcurrent);constbatchResults=awaitPromise.all(batch.map(file=>uploader.upload(file)));results.push(...batchResults);}returnresults;}2. 断点续传(大文件场景)
如果单张图片很大(>10MB),可以考虑断点续传:
- 使用OSS的分片上传(Multipart Upload)
- 前端记录上传进度
- 失败后可以续传
3. 图片预处理
- 服务端:使用OSS的图片处理服务自动生成缩略图
- 客户端:上传前压缩,减少上传时间
4. 监控与告警
// 添加上传监控@ComponentpublicclassUploadMonitor{@AutowiredprivateMeterRegistrymeterRegistry;publicvoidrecordUpload(StringuserId,longsize,longduration){// 记录上传次数meterRegistry.counter("upload.count","user",userId).increment();// 记录上传大小meterRegistry.summary("upload.size","user",userId).record(size);// 记录上传耗时meterRegistry.timer("upload.duration","user",userId).record(duration,TimeUnit.MILLISECONDS);}}总结
核心优化点
- OSS直传:图片直接上传到OSS,不经过应用服务器,节省服务器带宽
- CDN加速:图片通过CDN分发,提升访问速度,降低流量成本
- 7层负载均衡:用于API网关,统一入口,灵活路由
- 客户端压缩:上传前压缩图片,减少上传时间和流量
7层负载均衡适用场景
- ✅ 微服务网关(根据路径路由到不同服务)
- ✅ 动静分离(静态资源直接返回,不走应用服务器)
- ✅ 多域名统一入口(根据Host路由)
- ✅ A/B测试、灰度发布(根据Header/Cookie路由)
- ✅ SSL终止(在负载均衡层处理HTTPS)
不适用场景
- ❌ 纯TCP/UDP协议(需要用4层负载均衡)
- ❌ 超高性能、低延迟场景(4层性能更好)
- ❌ 简单的IP+端口转发(4层更简单高效)
最终效果
- ✅服务器带宽占用:从100-200Mbps降低到几乎为0(只有获取凭证的小请求)
- ✅上传速度:提升30-50%(减少一跳,直接到OSS)
- ✅成本:节省50-70%(服务器带宽费用 + CDN替代OSS外网流量)
- ✅用户体验:上传更快,访问图片更快(CDN加速)
- ✅系统稳定性:服务器不再被大文件上传阻塞,其他业务正常
希望这篇文章能帮你解决图片上传的带宽瓶颈问题。如果还有其他问题,欢迎继续讨论!