OJ 快速上线-笔记 (微信云托管+vercel)

Cx-CS OJ

image.png image.png

后端部署

微信云托管平台

准备工作

  1. 在idea中初始化 远程数据库

    连接成功后, 执行数据库sql文件 初始化 cx-csoj 数据库

    text
    复制代码
    username: root password: 密码
    text
    复制代码
    jdbc:mysql://sh-cynosdbmysql-xxxx.com:port
    部署-微信云托管-云端mysql信息 部署-微信云托管-idea初始化远程数据库
  2. 设置 application-prod.yml 远程环境配置

    ==切记修改为线上远程数据库==

    yml
    复制代码
    # 线上配置文件 # @author Cx-CS # @Version: 1.0 server: port: 8101 servlet: context-path: /api # cookie 30 天过期 session: cookie: max-age: 2592000 # todo 解决跨域问题(需https证书) same-site: none secure: true spring: application: name: cx-csoj-backend-remote # 支持 swagger3 mvc: pathmatch: matching-strategy: ant_path_matcher # session 配置 session: # todo 取消注释开启分布式 session(须先配置 Redis) #store-type: redis # 30 天过期 timeout: 2592000 # 数据库配置 # todo 需替换配置 datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: 数据库路径 username: root password: 密码 # Redis 配置 # todo 需替换配置,然后取消注释 # redis: # database: 1 # host: localhost # port: 6379 # timeout: 5000 # password: 123456 # 文件上传 servlet: multipart: # 大小限制 max-file-size: 10MB #mybatis-plus: # configuration: # # 生产环境关闭日志 # log-impl: '' mybatis-plus: configuration: map-underscore-to-camel-case: false log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-field: isDelete # 全局逻辑删除的实体字段名 logic-delete-value: 1 # 逻辑已删除值(默认为 1) logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) # 代码沙箱配置 codesandbox: type: remote url: 公网路径 # 微信相关 wx: # 微信公众平台 # todo 需替换配置 mp: token: xxx aesKey: xxx appId: xxx secret: xxx config-storage: http-client-type: HttpClient key-prefix: wx redis: host: 127.0.0.1 port: 6379 type: Memory # 微信开放平台 # todo 需替换配置 open: appId: xxx appSecret: xxx # 对象存储 # todo 需替换配置 cos: client: accessKey: xxx secretKey: xxx region: xxx bucket: xxx
  3. 切记修改 要使用的配置文件

    yml
    复制代码
    spring: profiles: active: prod #需要使用的配置文件的后缀 开发为dev
  4. 准备 Dockerfile 文件

    端口号8101

    text
    复制代码
    # Docker 镜像构建 # @author Cx-CS # @Version: 1.0 FROM maven:3.8.1-jdk-8-slim as builder # Copy local code to the container image. WORKDIR /app COPY pom.xml . COPY src ./src # Build a release artifact. RUN mvn package -DskipTests EXPOSE 8101 # 开发端口 !!!!!! 需要与微信云一致 # Run the web service on container startup. CMD ["java","-jar","/app/target/cx-csoj-backend-remote-0.0.1-SNAPSHOT.jar","--spring.profiles.active=prod"]
  5. 上传到 Github 设置private权限 C-XCS/cx-csoj-backend-remote

上线

==注意: 部署端口需与 项目yml 中端口一致!!!!!==

访问

https://cx-cs-oj-backend-xxxxxxxx.com/

要点

需要加上Cookie解决跨域问题

yml
复制代码
servlet: context-path: /api # 总路径加上 /api # cookie 30 天过期 session: cookie: max-age: 2592000 # todo 解决Cookie跨域问题(需https证书) same-site: none secure: true

前端部署

Vercel

  1. 修改 OpenAPI 请求地址为 后端的公网地址

    ts
    复制代码
    export const OpenAPI: OpenAPIConfig = { BASE: '后端公网地址', VERSION: '1.0', WITH_CREDENTIALS: true, CREDENTIALS: 'include', TOKEN: undefined, USERNAME: undefined, PASSWORD: undefined, HEADERS: undefined, ENCODE_PATH: undefined, };
  2. push GitHub 私有仓库

  3. vervel 绑定 gihub 上传的仓库, 一键打包运行

访问

https://cx-cs-oj-frontend.vercel.app/ 需要魔法 vercel.app国内被墙

绑定域名

https://cx-cs.cn

  1. vercel 绑定域名, 获得其提供的 DNS服务器 地址

cx-cs-oj-frontend – Vercel - 自定义域名

text
复制代码
76.76.21.21 cname.vercel-dns.com.

部署-vercel-vercel绑定域名

  1. 在阿里云 域名控制台 域名控制台 (aliyun.com)

沙箱部署

Dockerfile

text
复制代码
# Docker 镜像构建 # @author Cx-CS # @Version: 1.0 FROM maven:3.8.1-jdk-8-slim as builder # Copy local code to the container image. WORKDIR /app COPY pom.xml . COPY src ./src # Build a release artifact. RUN mvn package -DskipTests EXPOSE 8090 # Run the web service on container startup. CMD ["java","-jar","/app/target/cx-cs-oj-codesandbox-0.0.1-SNAPSHOT.jar","--spring.profiles.active=prod"]

yml文件

yml
复制代码
server: port: 8090 spring: # 默认 prod 环境 profiles: active: prod

报错

  1. 部署时 找不到指定主类 mainClass
text
复制代码
no main manifest attribute, in /app/target/cx-csoj-codesandbox-remote-0.0.1-SNAPSHOT.jar

在pom.xml 中添加 <mainClass>com.cx-cs.cx-csojcodesandbox.Cx-csOjCodesandboxApplication</mainClass>

查看pom时发现, 初始化项目时就已存在, 求问ai得知 需要注释掉 <skip> 标签

确保Maven构建中会进行repackge

xml
复制代码
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${spring-boot.version}</version> <configuration> <mainClass>com.cx-cs.cx-csojcodesandbox.Cx-csOjCodesandboxApplication</mainClass> <!-- <skip>true</skip> 需要注释掉,确保上线 Maven构建中会进行repackge --> </configuration> <executions> <execution> <id>repackage</id> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin>
  1. Docker 容器找不到 jar 文件
text
复制代码
Error: Unable to access jarfile /app/target/cx-csoj-codesandbox-remote-0.0.1-SNAPSHOT.jar

仔细查看Dockerfile 发现其执行jar包的命令, jar包名称与 项目名称不同

修改即可

  • Dockerfile

  • text
    复制代码
    CMD ["java","-jar","/app/target/cx-cs-oj-codesandbox-0.0.1-SNAPSHOT.jar","--spring.profiles.active=prod"]
  • pom.xml 中项目名称如下

  • xml
    复制代码
    <artifactId>cx-cs-oj-codesandbox</artifactId> <version>0.0.1-SNAPSHOT</version> <name>cx-cs-oj-codesandbox</name> <description>cx-cs-oj-codesandbox</description>

==请求报错 415==

请求沙箱接口时, 返回 415 错误码

请求接口: https://cx-cs-oj-codesandbox-xxx.com/code/execute

  • 报错内容如下: 客户端发送的请求的 Content-Type 与服务端预期的不匹配。
text
复制代码
content-type: application/json date: Mon, 16 Sep 2024 09:36:32 GMT server: cbrgw x-cloudbase-request-id: e171ff7a-920c-4598-9cec-5f5ef66c52f1 x-cloudbase-upstream-status-code: 415 x-cloudbase-upstream-timecost: 11 x-cloudbase-upstream-type: Tencent-CBR x-request-id: e171ff7a-920c-4598-9cec-5f5ef66c52f1 x-upstream-status-code: 415 x-upstream-timecost: 11 transfer-encoding: chunked

找好多解决方案 等等 都不是问题所在

  • 请求方 加上 content-type
  • 接收方 限制只接收 content-type
  • 接收方加上跨域请求

最后使用 postman请求 发现能够顺利接收 沙箱响应

text
复制代码
curl --location 'https://cx-cs-oj-codesandbox-xxx.com/code/execute' \ --header 'Content-Type: application/json' \ --header 'auth: secretKey' \ --data '{"inputList":["4 5 1 2 2 4 3 4 4 5"],"code":"public class main {public static void main(String[] args) {} }","language":"java"}'

==问题所在==

调用远程沙箱类 需要识别yml配置的请求地址, idea显示识别到了地址

但其实 @Value 注解不能识别 static 和 final 属性, 会失败为空

​ 当时贪图方便将, url 属性写死, 一直没有发现 请求的是本地沙箱项目

简单粗暴解决: 注解将写死的 url 改为沙箱公网地址

![部署-沙箱-请求沙箱415 代码](部署-沙箱-请求沙箱415 代码.png)

优化解决方案

由于@Value注解 在new对象的属性中不能生效, 所以考虑外部传入

  1. 将沙箱的调用方法 传入 url 地址 (即修改其接口 CodeSandBox)

    text
    复制代码
    public interface CodeSandBox { ExecuteCodeResponse executeCode(ExecuteCodeRequest executeCodeRequest, String url); }
  2. 将其对应实现类 修改

  3. RemoteCodeSandBox 方法内直接使用 传入的沙箱

  4. JudgeServiceImpl.java 判题服务中识别 yml 的沙箱配置

    在其代码调用时 传入 url

    java
    复制代码
    // 读取 yml 文件 的代码沙箱配置 @Value("${codesandbox.type:example}") private String type; // 选择沙箱 @Value("${codesandbox.url:http://localhost:8090/code/execute}") private String url; // 请求地址
    java
    复制代码
    // 该实体类使用Builder注解 ExecuteCodeRequest executeCodeRequest = ExecuteCodeRequest.builder() .code(code) .language(language) .inputList(inputList) .build(); ExecuteCodeResponse executeCodeResponse = codeSandBox.executeCode(executeCodeRequest, url); // 沙箱执行响应信息

沙箱运行java程序 错误

启动 java 安全管理器失败

shell
复制代码
2024-09-17 16:44:07.707 INFO 15800 --- [onPool-worker-9] c.c.c.j.codesandbox.CodeSandBoxProxy : 代码沙箱响应信息:ExecuteCodeResponse(outputList=[Error occurred during initialization of VM java.lang.InternalError: Could not create SecurityManager: DefaultSecurityManager at sun.misc.Launcher.<init>(Launcher.java:106) at sun.misc.Launcher.<clinit>(Launcher.java:54) at java.lang.ClassLoader.initSystemClassLoader(ClassLoader.java:1444) at java.lang.ClassLoader.getSystemClassLoader(ClassLoader.java:1429) , Error occurred during initialization of VM java.lang.InternalError: Could not create SecurityManager: DefaultSecurityManager at sun.misc.Launcher.<init>(Launcher.java:106) at sun.misc.Launcher.<clinit>(Launcher.java:54) at java.lang.ClassLoader.initSystemClassLoader(ClassLoader.java:1444) at java.lang.ClassLoader.getSystemClassLoader(ClassLoader.java:1429) ], message=null, status=2, judgeInfo=JudgeInfo(message=null, memory=null, time=73))

原因

  1. windows分隔符 \ linux(docker)分隔符 /

    java
    复制代码
    private static final String SECURITY_MANAGER_PATH = System.getProperty("user.dir") + "\\src\\main\\resources\\security" ;
  2. 执行的命令

    在 linux中命令间分隔是 ==冒号:== 而windows分隔是 ==分号;==

    linux命令

    text
    复制代码
    String runCmd = String.format("java -Xmx256m -Dfile.encoding=UTF-8 -cp %s:%s -Djava.security.manager=%s Main %s",userCodeParenPath, SECURITY_MANAGER_PATH, SECURITY_MANAGER_CLASS_NAME, inputArgs);

    windows 命令

    text
    复制代码
    String runCmd = String.format("java -Xmx256m -Dfile.encoding=UTF-8 -cp %s;%s -Djava.security.manager=%s Main %s",userCodeParenPath, SECURITY_MANAGER_PATH, SECURITY_MANAGER_CLASS_NAME, inputArgs);

解决方案

  • File.separator java跨平台分隔符

    java
    复制代码
    private static final String SECURITY_MANAGER_PATH = System.getProperty("user.dir") + File.separator + "src" + File.separator + "main" + File.separator + "resources" + File.separator + "security";
  • 对应win和linux的命令 不同分隔号问题

    ==优化==: 使用 java 获取当前系统分隔符

    java
    复制代码
    // 获取java 跨平台分隔符 linux冒号 win分号 String pathSeparator = System.getProperty("path.separator"); // 运行文件时 启动 recourses/security/DefaultSecurityManager.class 下的安全管理 String runCmd = String.format("java -Xmx256m -Dfile.encoding=UTF-8 -cp %s%s%s -Djava.security.manager=%s Main %s",userCodeParenPath, pathSeparator, SECURITY_MANAGER_PATH, SECURITY_MANAGER_CLASS_NAME, inputArgs);

API

公网域名: https://cx-cs-oj-codesandbox-xxxxxxxxxxxx.com/

text
复制代码
codesandbox: type: remote # url: http://localhost:8090/code/execute # wx云托管部署沙箱 公网路径 url: https://cx-cs-oj-codesandbox-xxxxx.com/code/execute
0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
下载 APP