学成-部署总结
一、部署
1.DevOps
DevOps 是 Development(开发)+ Operations(运维) 的合成词,是一种文化理念 + 实践方法论 + 工具链的组合,核心目标是打破开发团队和运维团队之间的壁垒,让软件更快、更稳定地交付
核心理念
常见工具实现
1 2 3
| 代码提交 → 构建 → 测试 → 打包 → 部署 → 监控 Git Maven JUnit Docker K8s Prometheus GitLab npm pytest 镜像 Helm Grafana
|
2.手动部署
父工程聚合
1 2 3 4 5 6 7 8 9 10 11 12 13
| <modules> <module>../xuecheng-plus-base</module> <module>../xuecheng-plus-checkcode</module> <module>../xuecheng-plus-gateway</module> <module>../xuecheng-plus-auth</module> <module>../xuecheng-plus-content</module> <module>../xuecheng-plus-learning</module> <module>../xuecheng-plus-media</module> <module>../xuecheng-plus-orders</module> <module>../xuecheng-plus-message-sdk</module> <module>../xuecheng-plus-search</module> <module>../xuecheng-plus-system</module> </modules>
|
配置spring-boot-maven-plugin插件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <build> <finalName>${project.artifactId}-${project.version}</finalName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${spring-boot.version}</version> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
|
通 Maven 打出来的 jar 是不可执行的,只包含自己写的 class 文件,不含依赖
部署到 Linux Docker 化
1 2 3 4 5 6 7 8 9
| 本地jar包 ↓ 上传到Linux 编写 Dockerfile ↓ docker build 生成镜像 checkcode:1.0 ↓ docker run 运行容器,暴露端口 ↓ 外部访问 192.168.101.65:63075
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| FROM java:8u20
MAINTAINER docker_maven docker_maven@email.com
WORKDIR /ROOT
ADD xuecheng-plus-checkcode-0.0.1-SNAPSHOT.jar xuecheng-plus-checkcode.jar
CMD ["java", "-version"]
ENTRYPOINT ["java", "-Dfile.encoding=utf-8","-jar", "xuecheng-plus-checkcode.jar"]
EXPOSE 63075
|
1 2 3 4 5 6 7 8
| docker build -t checkcode:1.0 .
docker run --name xuecheng-plus-checkcode \ -p 63075:63075 \ -idt \ checkcode:1.0
|
3.自动部署
整体
1 2 3 4 5 6 7 8 9 10 11 12 13
| 开发者 push 代码 ↓ Gogs (Git仓库) 触发 Webhook ↓ Jenkins 收到通知,自动拉取代码 ↓ Maven 构建 + docker-maven-plugin 打镜像 ↓ 镜像推送到 Docker 私服 (192.168.101.65:5000) ↓ 自动创建/启动容器 ↓ 服务上线
|
1.docker-maven-plugin 插件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <imageName>192.168.101.65:5000/${project.artifactId}:${project.version}</imageName>
<baseImage>java:8u20</baseImage>
<dockerHost>http://192.168.101.65:2375</dockerHost>
<entryPoint>["java", "-Dfile.encoding=utf-8","-jar", "/root/${project.build.finalName}.jar"]</entryPoint>
<pushImage>true</pushImage>
<resources> <resource> <targetPath>/root</targetPath> <directory>${project.build.directory}</directory> <include>${project.build.finalName}.jar</include> </resource> </resources>
|
本质上就是用 pom.xml 的配置替代了手写 Dockerfile,插件会自动生成镜像并推送
2.Jenkins 流水线
4.对比

二、技术点
1.接口的定义
接口路径
1 2
| 好的例子: /api/v1/users (指向用户集合)或 /api/v1/users/123 (指向特定用户) 坏的例子: /api/getUser 或 /api/deleteUser
|
请求方法
1 2 3 4 5 6
| 使用不同的 HTTP 动词来表示对资源执行的不同操作: GET:获取资源(例如:获取用户列表) POST:创建新资源(例如:注册新用户) PUT:更新资源的全部内容(例如:修改用户的全部个人信息) PATCH:更新资源的部分内容(例如:仅修改用户密码) DELETE:删除资源(例如:注销用户)
|
请求参数
1 2 3 4 5
| 明确调用者需要传递什么数据给你。参数通常分为三种类型: 路径参数 (Path Variables): 包含在 URL 中,用于定位特定资源。如 /users/{id} 中的 id。 查询参数 (Query Parameters): 附加在 URL 问号之后,多用于过滤、排序或分页。如 /users?role=admin&page=1。 请求体 (Request Body): 通常用于 POST/PUT 请求,携带较复杂的数据(通常为 JSON 格式)。 请求头 (Request Headers): 传递元数据或鉴权信息。如 Authorization: Bearer <token>。
|
响应内容
1 2 3 4 5
| 接口处理完成后,需要告诉调用者结果如何。 HTTP 状态码 (Status Code): * 200 OK (成功) / 201 Created (创建成功) 400 Bad Request (客户端参数错误) / 401 Unauthorized (未登录) / 404 Not Found (资源不存在) 500 Internal Server Error (服务器内部错误) 响应体 (Response Body): 返回给客户端的数据,通常是统一格式的 JSON。
|
持久层 mapper –> 服务层 service –> 响应层 controller
2.异常处理
全局异常处理器
在代码实现上,绝对不要在每一个 Controller 或业务方法里写满 try-catch。现代 Web 框架都提供了“全局异常处理”的机制
只需要在最外层定义一个拦截器,拦截所有未被捕获的异常,然后将其转换为上述的统一 JSON 格式
Java (Spring Boot): 使用 @RestControllerAdvice 和 @ExceptionHandler 注解
自定义业务异常
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @Getter public class BusinessException extends RuntimeException { private final int code;
public BusinessException(ErrorCode errorCode) { super(errorCode.getMessage()); this.code = errorCode.getCode(); } }
public enum ErrorCode { USER_NOT_FOUND(40401, "用户不存在"), PARAM_ERROR(40001, "参数错误"), TOKEN_EXPIRED(40101, "Token 已过期");
private final int code; private final String message; }
|
可预知异常使用自定义异常类
3.跨域问题
3.1、跨域
浏览器的**同源策略(Same-Origin Policy)**规定:协议 + 域名 + 端口三者必须完全一致,否则请求被拦截
1 2 3 4 5
| http://a.com:8080 → http://a.com:8081 ❌ 端口不同 http://a.com → https://a.com ❌ 协议不同 http://a.com → http://api.a.com ❌ 子域名不同 http://a.com → http://b.com ❌ 域名不同 http://a.com:8080 → http://a.com:8080 ✅ 同源
|
⚠️ 跨域是浏览器行为,请求实际已到达服务器,是响应被浏览器拦截
3.2、CORS 请求类型
1 2 3 4 5 6 7 8 9 10 11 12
| ┌─────────────────────────────────┐ │ 跨域请求 │ └────────────┬────────────────────┘ │ ┌──────────────────┴──────────────────┐ ▼ ▼ 简单请求 预检请求(Preflight) (GET/POST + 普通Header) (PUT/DELETE/自定义Header/JSON Body) │ │ │ 先发 OPTIONS 请求 ▼ ▼ 直接发送请求 服务器返回允许后再发真实请求
|
简单请求条件: Method 为 GET/POST/HEAD,且 Content-Type 为 text/plain / multipart/form-data / application/x-www-form-urlencoded
3.3、解决方案
后端设置 CORS Header
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| @Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") .allowedOrigins("https://your-frontend.com") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .allowCredentials(true) .maxAge(3600); } }
@Component public class CorsFilter implements Filter { @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletResponse response = (HttpServletResponse) res; HttpServletRequest request = (HttpServletRequest) req;
response.setHeader("Access-Control-Allow-Origin", "https://your-frontend.com"); response.setHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS"); response.setHeader("Access-Control-Allow-Headers", "Authorization,Content-Type"); response.setHeader("Access-Control-Allow-Credentials", "true"); response.setHeader("Access-Control-Max-Age", "3600");
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { response.setStatus(HttpServletResponse.SC_OK); return; } chain.doFilter(req, res); } }
|
Nginx 反向代理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| server { listen 80; server_name your-frontend.com;
location / { root /var/www/html; try_files $uri $uri/ /index.html; }
location /api/ { proxy_pass http://backend-server:8080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr;
add_header Access-Control-Allow-Origin $http_origin always; add_header Access-Control-Allow-Methods 'GET,POST,PUT,DELETE,OPTIONS' always; add_header Access-Control-Allow-Headers 'Authorization,Content-Type' always; add_header Access-Control-Allow-Credentials 'true' always;
if ($request_method = 'OPTIONS') { return 204; } } }
|
3.4、常见问题
| 现象 |
原因 |
解决 |
No 'Access-Control-Allow-Origin' header |
后端未设置 CORS |
添加响应头 |
allowCredentials=true 时 * 通配不生效 |
携带凭证时不能用 * |
改为指定具体域名 |
| 预检请求返回 401/403 |
Spring Security 拦截了 OPTIONS |
放行 OPTIONS 请求 |
| CORS 已配置但仍跨域 |
响应头被重复设置冲突 |
检查是否 Nginx 和后端都设置了 |
| Cookie 无法携带 |
前端未设 withCredentials |
axios.defaults.withCredentials = true |
4.微服务之间调用
调用方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| ┌─────────────────────────────────────────────────────┐ │ 微服务调用方式 │ └──────────────────┬──────────────────────────────────┘ │ ┌───────────┴───────────┐ ▼ ▼ 同步调用 异步调用 (HTTP/RPC) (消息队列) │ │ ┌────┴────┐ ┌──────┴──────┐ │ Feign │ │ RabbitMQ │ │ RestTpl │ │ RocketMQ │ │ gRPC │ │ Kafka │ └─────────┘ └─────────────┘
|
Feign 调用(主流方案)
1.引入依赖
1
| spring-cloud-starter-openfeign
|
2.启动类开启 Feign
1 2 3
| @SpringBootApplication @EnableFeignClients public class OrderApplication { ... }
|
3.定义 Feign 接口(order-service 调用 user-service)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @FeignClient( name = "user-service", // 注册中心的服务名 path = "/api/user", fallback = UserFeignFallback.class // 降级实现类 ) public interface UserFeignClient {
@GetMapping("/{id}") ApiResponse<UserDTO> getUserById(@PathVariable Long id);
@PostMapping("/batch") ApiResponse<List<UserDTO>> batchGetUsers(@RequestBody List<Long> ids); }
|
三、业务相关
1.内容管理
课程发布分布式任务调度

freemarker 实现页面静态化
2.媒资管理
文件分块上传
文件访问
通过 nginx 访问 minio 服务
3.认证授权
授权流程
网关认证,微服务授权,jwt存在客户端
OUTH2协议
验证码服务
spring security 统一认证入口
4.选课学习
订单支付
