SpringCloud微服务+UniApp多端统一——织码在线教育系统技术架构深度拆解

引言

在企业级在线教育系统的建设中,技术架构的选择往往决定了系统的天花板:单体架构可能在前几百门课程时就遇到瓶颈;前端各端独立开发会导致维护成本指数级增长;缺乏实时通信能力则无法支撑学习进度追踪和考试监考等关键场景。

本文将从技术架构层面深度拆解织码在线教育系统,重点分析五个核心技术方案:微服务拆分策略、多端统一 API 设计、SSE 实时通信方案、RSA+AES 混合加密传输、以及 Docker+Nginx 容器化部署。每个方案都会给出架构思路和关键代码实现。

在这里插入图片描述

一、微服务拆分策略

1.1 拆分原则

织码在线教育系统采用按业务域拆分的微服务策略。拆分遵循三个原则:

  1. 高内聚低耦合:每个服务对应一个明确的业务域,服务内部功能高度相关
  2. 独立部署独立扩容:高频服务(如课程服务)和低频服务(如配置服务)可以独立扩容
  3. 数据所有权独立:每个服务独占自己的数据库,跨服务通过 API 调用,不共享数据库

1.2 服务拆分全景

┌─────────────────────────────────────────────────────────────┐
│                     API Gateway (网关)                       │
│         路由 / 鉴权 / 限流 / 日志 / 请求聚合                   │
└────────────────────────────┬────────────────────────────────┘
                             │
    ┌────────────┬───────────┼───────────┬────────────┐
    │            │           │           │            │
    ▼            ▼           ▼           ▼            ▼
┌────────┐ ┌────────┐ ┌──────────┐ ┌────────┐ ┌────────┐
│用户服务│ │课程服务│ │考试服务  │ │任务服务│ │订单服务│
│User   │ │Course │ │Exam     │ │Task   │ │Order  │
│        │ │       │ │         │ │       │ │       │
│注册登录│ │课程管理│ │题库考试 │ │培训任务│ │订单支付│
│组织架构│ │章节管理│ │组卷判分 │ │学习路径│ │权限开通│
│权限RBAC│ │素材管理│ │防作弊   │ │进度追踪│ │       │
│学习档案│ │套餐管理│ │错题本   │ │问卷调研│ │       │
└────────┘ └────────┘ └──────────┘ └────────┘ └────────┘
    │            │           │           │            │
    ▼            ▼           ▼           ▼            ▼
┌────────┐ ┌────────┐ ┌──────────┐ ┌────────┐ ┌────────┐
│资源服务│ │直播服务│ │统计服务  │ │配置服务│ │消息服务│
│Resource│ │Live   │ │Stats    │ │Config │ │Message│
│        │ │       │ │         │ │       │ │       │
│素材入库│ │推拉流 │ │数据看板 │ │应用配置│ │站内信 │
│试卷归档│ │互动   │ │多维报表 │ │菜单管理│ │推送   │
│证书管理│ │       │ │订单统计 │ │微页面 │ │       │
│表单中心│ │       │ │         │ │资讯中心│ │       │
└────────┘ └────────┘ └──────────┘ └────────┘ └────────┘

1.3 服务注册与发现

使用 Nacos 作为注册中心和配置中心:

# bootstrap.yml - 各微服务统一配置
spring:
  application:
    name: course-service
  cloud:
    nacos:
      discovery:
        server-addr: ${NACOS_HOST:nacos}:8848
        namespace: ${NAMESPACE:prod}
      config:
        server-addr: ${NACOS_HOST:nacos}:8848
        namespace: ${NAMESPACE:prod}
        file-extension: yaml
        shared-configs:
          - data-id: common-db.yaml    # 共享数据库配置
          - data-id: common-redis.yaml # 共享缓存配置
          - data-id: common-oss.yaml   # 共享存储配置

1.4 服务间调用:OpenFeign

// 考试服务需要调用用户服务获取学员信息
@FeignClient(name = "user-service", contextId = "userClient")
public interface UserFeignClient {

    @GetMapping("/api/user/internal/{id}")
    UserDTO getUserById(@PathVariable("id") Long id);

    @GetMapping("/api/user/internal/{id}/permissions")
    List<String> getUserPermissions(@PathVariable("id") Long id);
}

// 考试服务需要调用课程服务校验课程权限
@FeignClient(name = "course-service", contextId = "courseClient")
public interface CourseFeignClient {

    @GetMapping("/api/course/internal/{id}/permission/{userId}")
    Boolean checkPermission(@PathVariable("id") Long courseId,
                           @PathVariable("userId") Long userId);
}

1.5 熔断与降级

// Feign 降级处理
@Component
public class UserFeignFallback implements UserFeignClient {

    @Override
    public UserDTO getUserById(Long id) {
        // 降级策略:返回缓存数据或默认值
        return UserDTO.builder()
            .id(id)
            .name("未知用户")
            .build();
    }

    @Override
    public List<String> getUserPermissions(Long id) {
        // 降级策略:返回空权限列表,拒绝访问(安全优先)
        return Collections.emptyList();
    }
}

在这里插入图片描述

二、多端统一 API 设计

2.1 设计理念

织码在线教育系统的四端(Admin/Web/App/小程序)共享同一套后端 API。核心设计原则:

设计原则实现方式
URL 统一所有端调用相同的 RESTful API
响应格式统一统一 Result 包装
鉴权统一JWT Token,四端通用
分页统一游标分页,兼容小程序无限滚动
差异处理通过请求头 X-Client-Type 标识客户端类型

2.2 统一响应格式

@Data
@Schema(description = "统一响应结构")
public class Result<T> {
    private int code;       // 业务状态码:0成功,非0失败
    private String message; // 提示信息
    private T data;         // 业务数据
    private long timestamp; // 时间戳

    public static <T> Result<T> success(T data) {
        Result<T> r = new Result<>();
        r.code = 0;
        r.message = "success";
        r.data = data;
        r.timestamp = System.currentTimeMillis();
        return r;
    }

    public static <T> Result<T> error(int code, String message) {
        Result<T> r = new Result<>();
        r.code = code;
        r.message = message;
        r.timestamp = System.currentTimeMillis();
        return r;
    }
}

2.3 多端分页适配

// 统一分页请求参数
@Data
public class PageRequest {
    private Integer page = 1;
    private Integer size = 20;
    private Long lastId;   // 游标:上一页最后一条记录ID(小程序用)
    private String sort;   // 排序字段
    private String order;  // asc/desc
}

// 分页响应
@Data
public class PageResult<T> {
    private List<T> list;
    private Long total;
    private Integer page;
    private Integer size;
    private Boolean hasMore;  // 是否还有更多(小程序无限滚动用)
    private Long lastId;      // 当前页最后一条ID(下一页游标)
}

2.4 客户端类型处理

不同端在某些场景下有差异化需求,通过拦截器统一处理:

@Component
public class ClientTypeInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) {
        String clientType = request.getHeader("X-Client-Type");

        // 记录客户端类型到请求上下文
        RequestContext.setClientType(clientType);

        // 小程序端:限制单次请求数据量,避免大响应导致卡顿
        if ("MINIAPP".equals(clientType)) {
            String size = request.getParameter("size");
            if (size == null || Integer.parseInt(size) > 10) {
                request.getParameterMap().put("size", new String[]{"10"});
            }
        }

        return true;
    }
}

2.5 文件上传统一方案

四端文件上传统一走 OSS 直传 + 服务端签名:

上传流程(以图片上传为例):

  前端(任意端)          后端签名服务          阿里云OSS
      │                    │                    │
      │ 1.请求上传签名      │                    │
      │───────────────────▶│                    │
      │                    │ 2.生成STS临时凭证   │
      │                    │───────────────────▶│
      │                    │◀───────────────────│
      │ 3.返回签名/上传URL  │                    │
      │◀───────────────────│                    │
      │                    │                    │
      │ 4.直传文件到OSS     │                    │
      │────────────────────────────────────────▶│
      │                    │                    │
      │ 5.上传成功回调       │                    │
      │◀────────────────────────────────────────│
      │                    │                    │
      │ 6.通知后端文件已上传  │                    │
      │───────────────────▶│                    │
      │                    │ 7.记录文件元数据     │
      │ 8.返回文件访问URL    │                    │
      │◀───────────────────│                    │
@RestController
@RequestMapping("/api/media")
public class MediaController {

    /**
     * 获取 OSS 上传签名(所有端通用)
     */
    @PostMapping("/upload-sign")
    public Result<UploadSignVO> getUploadSign(@RequestBody UploadSignDTO dto) {
        // 1. 生成唯一文件Key
        String fileKey = String.format("edu/%s/%s/%s",
            dto.getType(),                    // course/cover/question/...
            LocalDate.now(),                  // 按日期分目录
            UUID.randomUUID() + "_" + dto.getFileName()
        );

        // 2. 生成 POST 签名策略
        String policy = ossService.generatePolicy(dto.getContentType(), 50); // 50MB限制
        String signature = ossService.signPolicy(policy);

        return Result.success(UploadSignVO.builder()
            .host(ossConfig.getEndpoint())
            .key(fileKey)
            .policy(policy)
            .accessKeyId(ossConfig.getAccessKeyId())
            .signature(signature)
            .expire(ossConfig.getExpireSeconds())
            .build());
    }
}

三、SSE 实时通信方案

3.1 为什么用 SSE 而不是 WebSocket

在在线教育场景中,大部分实时需求是服务端推送(学习进度更新、考试倒计时同步、直播状态推送),客户端不需要频繁向服务端发消息。SSE(Server-Sent Events)相比 WebSocket 有以下优势:

维度SSEWebSocket
通信方向服务端→客户端(单向)双向
协议HTTP独立协议
断线重连浏览器自动重连需手动实现
兼容性除IE外全支持全支持
复杂度
适用场景推送通知、进度更新聊天、协同编辑

3.2 SSE 服务端实现

@RestController
@RequestMapping("/api/sse")
public class SSEController {

    private final ConcurrentHashMap<Long, SseEmitter> emitters = new ConcurrentHashMap<>();

    /**
     * 建立 SSE 连接
     */
    @GetMapping(value = "/connect", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public SseEmitter connect(@AuthenticationPrincipal UserPrincipal user) {
        // 超时时间设为30分钟(覆盖一节课程的学习时长)
        SseEmitter emitter = new SseEmitter(30 * 60 * 1000L);

        emitter.onCompletion(() -> emitters.remove(user.getId()));
        emitter.onTimeout(() -> emitters.remove(user.getId()));
        emitter.onError(e -> emitters.remove(user.getId()));

        emitters.put(user.getId(), emitter);

        // 发送连接成功事件
        try {
            emitter.send(SseEmitter.event()
                .name("connected")
                .data("{\"status\":\"connected\"}")
                .id(String.valueOf(System.currentTimeMillis())));
        } catch (IOException e) {
            emitter.completeWithError(e);
        }

        return emitter;
    }

    /**
     * 推送学习进度更新
     */
    public void pushProgressUpdate(Long userId, ProgressUpdateDTO dto) {
        SseEmitter emitter = emitters.get(userId);
        if (emitter != null) {
            try {
                emitter.send(SseEmitter.event()
                    .name("progress")
                    .data(JsonUtils.toJson(dto))
                    .reconnectTime(3000));  // 断线后3秒重连
            } catch (IOException e) {
                emitter.completeWithError(e);
                emitters.remove(userId);
            }
        }
    }

    /**
     * 推送考试时间提醒
     */
    public void pushExamReminder(Long userId, ExamReminderDTO dto) {
        SseEmitter emitter = emitters.get(userId);
        if (emitter != null) {
            try {
                emitter.send(SseEmitter.event()
                    .name("exam-reminder")
                    .data(JsonUtils.toJson(dto)));
            } catch (IOException e) {
                emitter.completeWithError(e);
                emitters.remove(userId);
            }
        }
    }
}

3.3 SSE 前端实现

// Vue 3 Composition API - SSE 连接 Hook
export function useSSE() {
  const eventSource = ref(null)
  const connected = ref(false)

  const connect = (token) => {
    // 注意:SSE 不支持自定义请求头,通过 URL 参数传递 Token
    eventSource.value = new EventSource(
      `/api/sse/connect?token=${token}`
    )

    eventSource.value.addEventListener('connected', (e) => {
      connected.value = true
      console.log('SSE 已连接')
    })

    // 学习进度推送
    eventSource.value.addEventListener('progress', (e) => {
      const data = JSON.parse(e.data)
      // 更新学习进度
      progressStore.update(data)
    })

    // 考试提醒推送
    eventSource.value.addEventListener('exam-reminder', (e) => {
      const data = JSON.parse(e.data)
      ElNotification({
        title: '考试提醒',
        message: `${data.examTitle}」将在${data.minutes}分钟后开始`,
        type: 'warning',
        duration: 0  // 不自动关闭
      })
    })

    eventSource.value.onerror = () => {
      connected.value = false
      // 浏览器会自动重连,无需手动处理
    }
  }

  const disconnect = () => {
    eventSource.value?.close()
    connected.value = false
  }

  onUnmounted(() => disconnect())

  return { connected, connect, disconnect }
}

3.4 学习进度实时同步

@Service
public class ProgressSyncService {

    private final SSEController sseController;

    /**
     * 学员观看视频时,前端定时上报学习进度
     * 服务端收到后通过 SSE 推送给管理端实时看板
     */
    @Async
    public void syncLearnProgress(Long userId, Long courseId, Integer progress) {
        // 1. 更新数据库学习进度
        userCourseService.updateProgress(userId, courseId, progress);

        // 2. 通过 SSE 推送实时进度
        sseController.pushProgressUpdate(userId, ProgressUpdateDTO.builder()
            .userId(userId)
            .courseId(courseId)
            .progress(progress)
            .timestamp(System.currentTimeMillis())
            .build());

        // 3. 进度100%时触发完成事件
        if (progress >= 100) {
            eventPublisher.publishEvent(new CourseCompletedEvent(userId, courseId));
        }
    }
}

四、RSA+AES 混合加密传输

4.1 加密方案设计

系统对敏感接口(登录、支付、考试提交等)采用 RSA+AES 混合加密传输,兼顾安全性与性能:

混合加密流程:

  客户端                                    服务端
    │                                         │
    │  1. 请求获取 RSA 公钥                     │
    │────────────────────────────────────────▶│
    │  2. 返回 RSA 公钥                        │
    │◀────────────────────────────────────────│
    │                                         │
    │  3. 生成随机 AES 密钥                     │
    │  4. 用 RSA 公钥加密 AES 密钥              │
    │  5. 用 AES 密钥加密请求体                  │
    │  6. 发送:加密的AES密钥 + 加密的请求体      │
    │────────────────────────────────────────▶│
    │                                         │ 7. 用 RSA 私钥解密获取 AES 密钥
    │                                         │ 8. 用 AES 密钥解密请求体
    │                                         │ 9. 业务处理
    │  10. 返回:AES 加密的响应体                │
    │◀────────────────────────────────────────│
    │                                         │
    │  11. 用 AES 密钥解密响应体                 │

4.2 服务端实现

@Component
public class CryptoService {

    private final KeyPair rsaKeyPair;
    private final ConcurrentHashMap<String, String> aesKeyCache = new ConcurrentHashMap<>();

    public CryptoService() {
        // 启动时生成 RSA 密钥对
        this.rsaKeyPair = RSAUtil.generateKeyPair(2048);
    }

    /**
     * 获取 RSA 公钥(Base64 编码)
     */
    public String getPublicKey() {
        return Base64.getEncoder().encodeToString(
            rsaKeyPair.getPublic().getEncoded()
        );
    }

    /**
     * 解密客户端发来的加密数据
     */
    public String decrypt(String encryptedAesKey, String encryptedData) {
        // 1. 用 RSA 私钥解密 AES 密钥
        String aesKey = RSAUtil.decrypt(encryptedAesKey, rsaKeyPair.getPrivate());

        // 2. 用 AES 密钥解密数据
        return AESUtil.decrypt(encryptedData, aesKey);
    }

    /**
     * 加密响应数据
     */
    public String encrypt(String aesKey, String data) {
        return AESUtil.encrypt(data, aesKey);
    }
}

// 加密请求解密过滤器
@Component
public class DecryptFilter implements Filter {

    @Autowired
    private CryptoService cryptoService;

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;

        // 检查是否是加密接口
        String encrypted = req.getHeader("X-Encrypted");
        if ("true".equals(encrypted)) {
            String aesKey = req.getHeader("X-Aes-Key");
            String body = StreamUtils.copyToString(
                req.getInputStream(), StandardCharsets.UTF_8
            );

            // 解密
            String decrypted = cryptoService.decrypt(aesKey, body);

            // 包装为新的请求对象
            request = new DecryptRequestWrapper(req, decrypted);
        }

        chain.doFilter(request, response);
    }
}

4.3 前端加密实现

// 前端加密工具
import JSEncrypt from 'jsencrypt'
import CryptoJS from 'crypto-js'

// 获取 RSA 公钥并加密请求
export async function encryptedRequest(url, data) {
  // 1. 获取 RSA 公钥
  const { data: pubKey } = await api.get('/api/crypto/public-key')

  // 2. 生成随机 AES 密钥
  const aesKey = CryptoJS.lib.WordArray.random(16).toString()

  // 3. RSA 加密 AES 密钥
  const encryptor = new JSEncrypt()
  encryptor.setPublicKey(pubKey)
  const encryptedAesKey = encryptor.encrypt(aesKey)

  // 4. AES 加密请求数据
  const encryptedData = CryptoJS.AES.encrypt(
    JSON.stringify(data),
    CryptoJS.enc.Utf8.parse(aesKey),
    { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 }
  ).toString()

  // 5. 发送加密请求
  const response = await api.post(url, encryptedData, {
    headers: {
      'X-Encrypted': 'true',
      'X-Aes-Key': encryptedAesKey,
      'Content-Type': 'text/plain'
    }
  })

  // 6. 解密响应
  const decrypted = CryptoJS.AES.decrypt(
    response.data,
    CryptoJS.enc.Utf8.parse(aesKey),
    { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 }
  ).toString(CryptoJS.enc.Utf8)

  return JSON.parse(decrypted)
}

五、容器化部署方案

5.1 部署架构

┌──────────────────────────────────────────────────────┐
│                    互联网用户                           │
└──────────────────────┬───────────────────────────────┘
                       │
                       ▼
┌──────────────────────────────────────────────────────┐
│              Nginx (反向代理 + SSL)                   │
│  ┌─────────┐  ┌──────────┐  ┌─────────────────────┐ │
│  │ /admin  │  │ /        │  │ /api/*              │ │
│  │ Admin端 │  │ Web学习端│  │ Gateway网关         │ │
│  │ 静态资源│  │ 静态资源 │  │                     │ │
│  └─────────┘  └──────────┘  └──────────┬──────────┘ │
└─────────────────────────────────────────┼────────────┘
                                          │
                    ┌─────────────────────┼──────────────┐
                    │                     │              │
                    ▼                     ▼              ▼
              ┌──────────┐         ┌──────────┐   ┌──────────┐
              │user-svc  │         │course-svc│   │exam-svc  │
              │Container │         │Container │   │Container │
              │:8081     │         │:8082     │   │:8083     │
              └────┬─────┘         └────┬─────┘   └────┬─────┘
                   │                    │              │
                   └────────────┬───────┴──────────────┘
                                │
                    ┌───────────┼───────────┐
                    │           │           │
                    ▼           ▼           ▼
              ┌────────┐  ┌────────┐  ┌────────┐
              │ MySQL  │  │ Redis  │  │ Nacos  │
              │        │  │        │  │        │
              └────────┘  └────────┘  └────────┘

5.2 Nginx 配置

# nginx.conf
upstream gateway {
    server gateway:8080;
    keepalive 32;
}

server {
    listen 443 ssl http2;
    server_name edu.example.com;

    ssl_certificate     /etc/nginx/ssl/server.crt;
    ssl_certificate_key /etc/nginx/ssl/server.key;
    ssl_protocols       TLSv1.2 TLSv1.3;

    # Admin 管理端
    location /admin {
        alias /usr/share/nginx/html/admin;
        try_files $uri $uri/ /admin/index.html;
    }

    # Web 学习端 (Nuxt SSR)
    location / {
        proxy_pass http://web-ssr:3000;
        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;
    }

    # API 网关
    location /api {
        proxy_pass http://gateway;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # SSE 支持
        proxy_buffering off;
        proxy_cache off;
        proxy_read_timeout 300s;

        # WebSocket 支持(直播)
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

    # 静态资源缓存
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    # Gzip 压缩
    gzip on;
    gzip_types text/plain text/css application/json application/javascript;
    gzip_min_length 1024;
}

# HTTP 重定向到 HTTPS
server {
    listen 80;
    server_name edu.example.com;
    return 301 https://$server_name$request_uri;
}

5.3 Docker Compose 完整编排

version: '3.8'

services:
  # ============ 基础设施 ============
  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: edu_system
    volumes:
      - mysql-data:/var/lib/mysql
      - ./sql/init:/docker-entrypoint-initdb.d
    ports:
      - "3306:3306"

  redis:
    image: redis:7-alpine
    command: redis-server --requirepass ${REDIS_PASSWORD} --maxmemory 512mb
    volumes:
      - redis-data:/data

  nacos:
    image: nacos/nacos-server:v2.3.0
    environment:
      MODE: standalone
      SPRING_DATASOURCE_PLATFORM: mysql
      MYSQL_SERVICE_HOST: mysql
      MYSQL_SERVICE_DB_NAME: nacos_config
    depends_on:
      - mysql

  # ============ 后端微服务 ============
  gateway:
    build: ./services/gateway
    environment:
      NACOS_SERVER_ADDR: nacos:8848
    depends_on:
      - nacos
    restart: always

  user-service:
    build: ./services/user-service
    environment:
      NACOS_SERVER_ADDR: nacos:8848
      MYSQL_HOST: mysql
      REDIS_HOST: redis
    depends_on:
      - mysql
      - redis
      - nacos
    restart: always

  course-service:
    build: ./services/course-service
    environment:
      NACOS_SERVER_ADDR: nacos:8848
      MYSQL_HOST: mysql
      REDIS_HOST: redis
    depends_on:
      - mysql
      - redis
      - nacos
    restart: always

  exam-service:
    build: ./services/exam-service
    environment:
      NACOS_SERVER_ADDR: nacos:8848
      MYSQL_HOST: mysql
      REDIS_HOST: redis
    depends_on:
      - mysql
      - redis
      - nacos
    restart: always

  # ============ 前端服务 ============
  admin-web:
    build: ./frontend/admin
    # 构建后为静态文件,由 Nginx 托管

  web-ssr:
    build: ./frontend/web
    environment:
      NUXT_PUBLIC_API_BASE: https://edu.example.com/api
    restart: always

  # ============ 反向代理 ============
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/ssl:/etc/nginx/ssl
      - admin-build:/usr/share/nginx/html/admin
    depends_on:
      - gateway
      - web-ssr
    restart: always

volumes:
  mysql-data:
  redis-data:
  admin-build:

5.4 后端 Dockerfile 多阶段构建

# 阶段1:构建
FROM maven:3.9-openjdk-17 AS builder
WORKDIR /build
COPY pom.xml .
COPY src ./src
RUN mvn package -DskipTests -q

# 阶段2:运行(使用精简镜像)
FROM openjdk:17-jdk-slim
WORKDIR /app

# 安装时区
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

COPY --from=builder /build/target/*.jar app.jar

# JVM 参数优化
ENV JAVA_OPTS="-Xms256m -Xmx512m -XX:+UseG1GC -XX:MaxGCPauseMillis=200"

EXPOSE 8082
ENTRYPOINT exec java $JAVA_OPTS -jar app.jar

六、架构总结

回顾织码在线教育系统的技术架构,五个核心方案各有侧重:

技术方案解决的核心问题关键收益
Spring Cloud 微服务系统复杂度高、需独立扩容服务解耦、独立部署、弹性扩容
多端统一 API四端数据一致性、维护成本一套 API 驱动四端,降低 60%+ 后端维护成本
SSE 实时通信学习进度推送、考试提醒轻量级实时推送,浏览器自动重连
RSA+AES 混合加密敏感数据传输安全RSA 保证密钥安全,AES 保证传输性能
Docker+Nginx 容器化部署一致性、运维效率一键部署、环境隔离、快速扩缩容

这套架构已在企业新员工培训、合规安全培训、技能认证考试、知识付费等多种场景中稳定运行,支撑了课程管理、在线考试、培训任务、直播培训等全流程业务。

技术架构没有银弹,但务实的选择能让团队走得更远。如果你正在规划类似的企业级教育平台,希望本文的拆解能为你提供有价值的参考。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值