电商导购类平台的前后端分离架构:Vue.js+Spring Boot的RESTful API设计规范与性能调优

大家好,我是阿可,微赚淘客系统及省赚客APP创始人,是个冬天不穿秋裤,天冷也要风度的程序猿!

一、业务背景:导购场景对前后端分离的硬性需求
省赚客导购平台日均千万级请求,业务模块20+(搜索、领券、下单、分佣、埋点)。
旧项目采用JSP+Struts,前后端耦合,发版需全量重启,静态资源流量占带宽35%。
目标:

  1. 前端独立部署,支持CDN缓存与灰度;
  2. 后端纯接口,QPS 3万RT<50ms;
  3. 多端复用:H5、小程序、App同一套API。

二、技术栈总览
前端:Vue3 + Vite + Pinia + Axios
网关:Nginx + KONG
后端:SpringBoot 3.2 + JDK21 + Netty IO
安全:Spring Security + JWT + RSA
文档:SpringDoc OpenAPI 3
部署:Docker + K8s + Jenkins


电商导购类平台

三、RESTful API设计规范

3.1 URL命名
全小写、中线分隔、名词复数

GET   /v1/coupons?keyword=手机&page=1&size=20
POST  /v1/coupons/{id}/receive
PUT   /v1/orders/{id}/status
DELETE /v1/users/{id}/addresses/{addrId}

3.2 请求头

Content-Type: application/json
Accept-Language: zh-CN
X-Request-ID: 7ac0b2c1f1   // 链路追踪
X-Client-Version: 2.1.0

3.3 统一响应格式

package cn.juwatech.common.core;

public class R<T> {
    private int code;
    private String msg;
    private T data;
    private long ts;

    public static <T> R<T> ok(T data){
        return new R<>(0,"ok",data,System.currentTimeMillis());
    }
    public static <T> R<T> fail(int code,String msg){
        return new R<>(code,msg,null,System.currentTimeMillis());
    }
}

3.4 状态码
200 正常 | 201 创建成功 | 400 参数错误 | 401 未授权 | 403 无权限 | 404 资源不存在 | 500 服务器异常


四、SpringBoot分层骨架

cn.juwatech
├── controller
│   └── CouponController.java
├── service
│   └── CouponService.java
├── repository
│   └── CouponMapper.java
├── domain
│   └── Coupon.java
└── config
    ├── WebMvcConfig.java
    └── SecurityConfig.java

五、Controller示例:优惠券列表接口

package cn.juwatech.controller;

import cn.juwatech.common.core.R;
import cn.juwatech.domain.Coupon;
import cn.juwatech.service.CouponService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/v1/coupons")
@RequiredArgsConstructor
@Tag(name = "CouponAPI")
public class CouponController {

    private final CouponService couponService;

    @GetMapping
    @Operation(summary = "查询优惠券列表")
    public R<Page<Coupon>> list(@RequestParam(defaultValue = "") String keyword,
                                @RequestParam(defaultValue = "1") @Min(1) int page,
                                @RequestParam(defaultValue = "20") @Min(5) @Max(100) int size) {
        Page<Coupon> data = couponService.list(keyword, PageRequest.of(page - 1, size));
        return R.ok(data);
    }
}

六、全局跨域与静态资源加速

package cn.juwatech.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/v1/**")
                .allowedOriginPatterns("*")
                .allowedMethods("GET","POST","PUT","DELETE","OPTIONS")
                .allowedHeaders("*")
                .exposedHeaders("X-Total-Count","X-Request-ID")
                .allowCredentials(true)
                .maxAge(3600);
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        // 本地图片前缀
        registry.addResourceHandler("/upload/**")
                .addResourceLocations("file:/data/upload/");
    }
}

静态文件走CDN,URL带?v=hash,Nginx设置Cache-Control: max-age=31536000, immutable


七、性能调优:从50ms到20ms

7.1 开启GZIP

server:
  compression:
    enabled: true
    mime-types: application/json,application/xml,text/html,text/xml,text/plain

传输体积减少75%。

7.2 Jackson优化

@Bean
public ObjectMapper objectMapper() {
    return JsonMapper.builder()
            .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
            .enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT)
            .build();
}

7.3 连接池调优

spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    hikari:
      maximum-pool-size: 32
      minimum-idle: 16
      idle-timeout: 300000
      connection-timeout: 20000

7.4 热数据缓存

package cn.juwatech.service;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
public class CouponCache {

    private final Cache<String, Coupon> cache = Caffeine.newBuilder()
            .maximumSize(10_000)
            .expireAfterWrite(5, TimeUnit.MINUTES)
            .build();

    public Coupon get(String key) {
        return cache.get(key, k -> couponMapper.selectById(k));
    }
}

7.5 分页limit深优化
采用“上一页最大值”游标,避免OFFSET大页慢查询:

SELECT * FROM t_coupon
WHERE id < #{lastId}
ORDER BY id DESC
LIMIT 20

八、Vue3前端请求封装

// src/utils/http.ts
import axios, { AxiosInstance } from 'axios'

const http: AxiosInstance = axios.create({
  baseURL: import.meta.env.VITE_API_BASE,
  timeout: 8000,
  headers: { 'Content-Type': 'application/json' }
})

http.interceptors.request.use(config => {
  config.headers['Authorization'] = `Bearer ${useUserStore().token}`
  return config
})

http.interceptors.response.use(
  response => {
    const { code, msg, data } = response.data
    if (code !== 0) {
      ElMessage.error(msg)
      return Promise.reject(new Error(msg))
    }
    return data
  },
  error => {
    ElMessage.error('网络异常')
    return Promise.reject(error)
  }
)

export default http

组件调用:

import http from '@/utils/http'

export function listCoupon(params: any) {
  return http.get('/v1/coupons', { params })
}

九、JWT+RSA安全模型
后端登录接口:

@PostMapping("/v1/sessions")
public R<LoginVO> login(@Valid @RequestBody LoginDTO dto){
    User user = userService.verify(dto);
    String token = jwtHelper.generate(user.getId());
    return R.ok(new LoginVO(token));
}

JWT payload仅含uidexp,敏感数据放Redis。
公钥下发给前端,私钥留服务端,杜绝伪造。


十、链路追踪与监控
引入micrometer-tracing+Zipkin:

management:
  tracing:
    sampling:
      probability: 0.1
  zipkin:
    endpoint: http://zipkin:9411/api/v2/spans

前端在Axios拦截器写入X-Request-ID,后端MDC打印,全链路串联。
Grafana大盘:QPS、P99、错误率、带宽,阈值告警接入飞书。


十一、灰度与版本控制
Nginx按X-Client-Version分流:

map $http_x_client_version $backend_pool {
  ~^2\.1\..+      kube-svc-v210;
  default         kube-svc-v200;
}

后端接口兼容策略:URL路径带版本,同一服务支持多版本共存,字段增不改,废弃标@Deprecated


十二、压测结果

环境:8C16G×20容器,JDK21+G1
工具:PTS 1万并发

指标 调优前 调优后
平均RT 105ms 22ms
P99 680ms 58ms
CPU占用 68% 31%
带宽节省 35%(GZIP+304)

本文著作权归聚娃科技省赚客app开发者团队,转载请注明出处!

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐