Linux部署 CentOs8安装docker出现冲突。 解决方法 出现以错误,根据提示在命令结尾加上–allowerasing或–nobest后再次执行即可
1 2 yum install -y docker-ce --nobest yum install -y docker-ce --allowerasing
启动docker并设置开机自启
1 systemctl start docker && systemctl enable docker
查看Docker是否安装成功
安装docker-compose 安装docker-compose
方法一:
docker-compose版本选择
1 2 3 4 curl -L https://github.com/docker/compose/releases/download/1.26.2/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose chmod +x /usr/local/bin/docker-compose 123
查看docker-compose
版本 1 2 docker-compose version 1
方法二:
1、安装python-pip
1 2 3 4 yum -y install epel-release dnf install -y python3 dnf install python3-paramiko 123
2、安装docker -compose
1 pip3 install docker-compose
Redis安装 Docker搜索redis镜像
命令:docker search <镜像名称>
可以看到有很多redis的镜像,此处因没有指定版本,所以下载的就是默认的最新版本 。redis latest.
Docker拉取镜像
命令::docker pull <镜像名称>:<版本号>
Docker挂载配置文件 接下来就是要将redis 的配置文件进行挂载,以配置文件方式启动redis 容器 。(挂载:即将宿主的文件和容器内部目录相关联,相互绑定,在宿主机内修改文件的话也随之修改容器内部文件)
1)、挂载 redis 的配置文件
2)、挂载 redis 的持久化文件(为了数据的持久化)。
本人的配置文件是放在
1 2 liunx` 下redis.conf文件位置: `/home/redis/myredis/redis.conf liunx` 下redis的data文件位置 : `/home/redis/myredis/data
位置可以自己随便选择哈
mkdir -p /home/redis/myredis 命令 是不存在就直接创建/home/redis/myredis 文件夹
myredis.conf
是我手动上传的。 (文件在文末,redis.conf 的标准文件在redis官网也可以找到)
1 2 docker run --restart=always --log-opt max-size=100m --log-opt max-file=2 -p 6479:6379 --name myredis -v /home/redis/myredis/myredis.conf:/etc/redis/redis.conf -v /home/redis/myredis/data:/data -d redis redis-server /etc/redis/redis.conf --appendonly yes --requirepass 181393 1
--restart=always
总是开机启动
--log
是日志方面的
-p 6379:6379
将6379端口挂载出去
--name
给这个容器取一个名字
-v
数据卷挂载- /home/redis/myredis/myredis.conf:/etc/redis/redis.conf
这里是将 liunx 路径下的myredis.conf 和redis下的redis.conf 挂载在一起。- /home/redis/myredis/data:/data
这个同上
-d redis
表示后台启动redis
redis-server /etc/redis/redis.conf
以配置文件启动redis,加载容器内的conf文件,最终找到的是挂载的目录 /etc/redis/redis.conf
也就是liunx下的/home/redis/myredis/myredis.conf
–appendonly yes 开启redis 持久化
–requirepass 000415 设置密码 (如果你是通过docker 容器内部连接的话,就随意,可设可不设。但是如果想向外开放的话,一定要设置,我被搞过,可以看这篇文章“阿里云服务器中毒‘Kirito666’经历 ”)
成功界面
测试 通过docker ps指令查看启动状态 1 2 docker ps -a |grep myredis 1
查看容器运行日志
命令:docker logs –since 30m <容器名>
此处 --since 30m
是查看此容器30分钟之内的日志情况。
1 2 docker logs --since 30m myredis 1
容器内部连接进行测试 进入容器
命令:docker exec -it <容器名> /bin/bash
此处跟着的 redis-cli 是直接将命令输在上面了。
1 2 docker exec -it myredis redis-cli 1
进入之后,我直接输入查看命令:
error是没有权限验证。(因为设置了密码的。)
验证密码:
查看当前redis有没有设置密码: (得验证通过了才能输入的)
1 2 config get requirepass 1
配置文件 myredis.conf
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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 protected-mode no port 6379 tcp-backlog 511 requirepass 000415 timeout 0tcp-keepalive 300 daemonize no supervised no pidfile /var/run/redis_6379.pid loglevel notice logfile "" databases 30 always-show-logo yes save 900 1 save 300 10 save 60 10000 stop-writes-on-bgsave-error yes rdbcompression yes rdbchecksum yes dbfilename dump.rdb dir ./replica-serve-stale-data yes replica-read-only yes repl-diskless-sync no repl-disable-tcp-nodelay no replica-priority 100 lazyfree-lazy-eviction no lazyfree-lazy-expire no lazyfree-lazy-server-del no replica-lazy-flush no appendonly yes appendfilename "appendonly.aof" no-appendfsync-on-rewrite no auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb aof-load-truncated yes aof-use-rdb-preamble yes lua-time-limit 5000 slowlog-max-len 128 notify-keyspace-events "" hash-max-ziplist-entries 512 hash-max-ziplist-value 64 list-max-ziplist-size -2 list-compress-depth 0 set-max-intset-entries 512 zset-max-ziplist-entries 128 zset-max-ziplist-value 64 hll-sparse-max-bytes 3000 stream-node-max-bytes 4096 stream-node-max-entries 100 activerehashing yes hz 10 dynamic-hz yes aof-rewrite-incremental-fsync yes rdb-save-incremental-fsync yes
nacos安装 1 2 3 4 5 6 7 mkdir -p /home/txy/cloud/docker/nacos/data/ mkdir -p /home/txy/cloud/docker/nacos/logs/ mkdir -p /home/txy/cloud/docker/nacos/init.d/ vim /usr/local/docker/nacos/init.d/custom.properties docker run -dit --name mynacos -p 8948:8848 --privileged=true -e JVM_XMS=256m -e JVM_XMX=256m -e MODE=standalone -e PREFER_HOST_MODE=hostname -v /usr/local/docker/nacos/logs:/home/nacos/logs -v /usr/local/docker/nacos/data:/home/nacos/data -v /usr/local/docker/nacos/init.d/custom.properties:/home/nacos/init.d/custom.properties nacos/nacos-server
custom.properties内容 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 server.contextPath=/nacos server.servlet.contextPath=/nacos server.port=8848 // 端口号 spring.datasource.platform=mysql // mysql连接 db.num=1 db.url.0=jdbc:mysql://192.168.136.1:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true db.user=root db.password=root nacos.cmdb.dumpTaskInterval=3600 nacos.cmdb.eventTaskInterval=10 nacos.cmdb.labelTaskInterval=300 nacos.cmdb.loadDataAtStart=false management.metrics.export.elastic.enabled=false management.metrics.export.influx.enabled=false server.tomcat.accesslog.enabled=true server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D %{User-Agent}i nacos.security.ignore.urls=/,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/v1/auth/login,/v1/console/health/**,/v1/cs/**,/v1/ns/**,/v1/cmdb/**,/actuator/**,/v1/console/server/** nacos.naming.distro.taskDispatchThreadCount=1 nacos.naming.distro.taskDispatchPeriod=200 nacos.naming.distro.batchSyncKeyCount=1000 nacos.naming.distro.initDataRatio=0.9 nacos.naming.distro.syncRetryDelay=5000 nacos.naming.data.warmup=true nacos.naming.expireInstance=true
Docker界面管理工具 1 2 docker run -d -p 9000:9000 --name portainer --restart=always --privileged=true -v /home/txy/cloud/docker/dockerGUI/docker.sock:/var/run/docker.sock -v /home/txy/cloud/docker/dockerGUI/portainer_data:/data portainer/portainer
本地部署 问题一 1 2 java: java.lang.ExceptionInInitializerError com.sun.tools.javac.code.TypeTags
方案一:在对应的依赖下加上版本号
方案二:lombok的版本太低,更换高版本即可
问题二 1 java: 程序包javax.validation不存在
起因
SpringBoot 2.3.0版本之后就没有引入validation对应的包
解决方法
导入Spring Boot Starter Validation,附上Maven Repository的链接
根据自己项目所使用的版本号导入
1 2 3 4 5 6 <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-validation --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>
Docker中的nacos本地连接不上。
查看Linux中的端口是否开放。
删除浏览器缓存或禁用浏览器缓存。
通过通配符关闭相关容器 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 1. 根据容器名称查询容器ID并删除 # 第一种写法 docker stop `docker ps -a| grep oes-live-manage | awk '{print $1}' ` docker rm `docker ps -a| grep oes-live-manage | awk '{print $1}' ` # 第二种写法 docker stop `docker ps -aq --filter name=oes-live-manage` docker rm `docker ps -aq --filter name=oes-live-manage` 2. 根据镜像名称查询容器ID并删除 # 第一种写法 docker stop `docker ps -a| grep ygsama/test-project:1.0.2 | awk '{print $1}' ` docker rm `docker ps -a| grep ygsama/test-project:1.0.2 | awk '{print $1}' ` # 第二种写法--filter,reference 后跟镜像名称 docker stop `docker ps -aq --filter oes-live-manage:1.0.2` docker rm `docker ps -aq --filter oes-live-manage:1.0.2` 3. 根据镜像名称查询镜像ID并删除 docker images -q --filter reference=oes-live-manage*:* docker image rm `docker images -q --filter reference=oes-live-manage*:*` 注意:grep后面填写匹配的数据
BUG 前端启动 npm : 无法将“npm”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确,然后再试一次。
后端启动 Error creating bean with name 'scopedTarget.configVO'
出现原因:nacos中的中文编码有问题,将乱码更改即可。
[http-nio-8088-exec-1] ERROR o.a.c.c.C.[.[localhost].[/].[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed; nested exception is java.lang.NoClassDefFoundError: com/sun/jna/platform/win32/VersionHelpers] with root cause
数据库 tag_classification表
列名
实体属性类型
键
备注
id
Long
primary key
name
String
标签分类名称
oj
String
标签分类所属oj
gmt_modifie
datetime
修改时间
gmt_create
datetime
创建时间
rank
int
标签分类优先级 越小越高
tag表:
列名
实体属性类型
键
备注
id
Long
primary key
name
String
标签名字
color
String
标签颜色
oj
String
标签所属oj
gid
ilong
外键
group id
tcid
long
外键
tag_classification id
gmt_modifie
datetime
修改时间
gmt_create
datetime
创建时间
rank
int
标签分类优先级 越小越高
group表:
列名
实体属性类型
键
备注
id
long
primary id
avatar
String
头像地址
name
String
团队名称
short_name
String
团队简称,创建题目时题目前缀
brief
String
团队简介
description
String
团队介绍
owner
String
团队拥有者用户名
uid
String
外键
团队拥有者id
auth
int
0为Public,1为Protected,2为Private
visible
Boolean
是否可见
status
Boolean
是否封禁
code
String
邀请码
gmt_create
datetime
创建时间
gmt_modified
datetime
修改时间
group_member表:
列名
实体属性类型
键
备注
id
long
primary key
gid
long
外键
group_id
uid
String
外键
user_id
auth
int
1未审批,2拒绝,3普通成员,4团队管理员,5团队拥有者
reason
String
申请理由
gmt_create
datetime
创建时间
gmt_modified
datetime
修改时间
后端代码 top.hcode.hoj config shiroConfig 开启Shiro注解相关知识:
AOP:(53条消息) 细说Spring——AOP详解(AOP概览)_spring aop看哪个_Jivan2233的博客-CSDN博客
shiro权限管理:Shiro权限管理框架(一):Shiro的基本使用 - 知乎 (zhihu.com)
判题服务
后端接受判题请求:
进入JudgeController
,判断是否拥有权限进行判题。按照路径进入方法。
在Service层调用JudgeManager
的判题方法。
在JudgeManager
中对用户提交进行条件判断,判断是否多次频繁提交
进入JudgeDispatcher
将题目数据加入判题等待队列
处理队列中的判题任务
1 2 3 // 优先处理比赛的提交任务 // 其次处理普通提交的提交任务 // 最后处理在线调试的任务
进入Dispatcher,向判断机发送判题请求,获取判题的结果返回。
以上过程中一但抛出异常则表明判题失败或提交结果不正确
前端 Vue前端使用的是https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/vue/2.6.11/vue.min.js
体积较小的压缩版,应该改为开发环境版https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/vue/2.6.11/vue.js
前往vue.config.js
文件修改
路由
HOJ标志 点击之后在中文与英文之间切换。
1 2 3 4 // 调用方法 changeWebLanguage() { this.$store.commit('changeWebLanguage', {language: this.webLanguage == 'zh-CN' ? 'en-US' : 'zh-CN'}); },
首页 跳转至首页。
路由:/home
1 2 3 4 5 6 { path: '/', redirect: '/home', component: Home, meta: {title: 'Home'} },
首页初始化时的操作:
获取首页轮播图:URL:/api/home-carousel
获取用户最近14天的通过的题目榜单:/api/get-recent-contest
获取用户最近一周的提交统计:/api/get-recent-seven-ac-rank
获取最新的10个题目:/api/get-recent-updated-problem
题目 跳转至题目列表页。
路由: /problem
1 2 3 4 5 6 { path: '/problem', name: 'ProblemList', component: ProblemLIst, meta: {title: 'Problem'} },
条件搜索 训练 跳转至训练列表页。
路由:/training
1 2 3 4 5 6 { name: 'TrainingFullProblemDetails', path: '/training/:trainingID/problem/:problemID/full-screen', component: Problem, meta: {title: 'Training Problem Details', fullScreenSource: 'training'} },
比赛 跳转至比赛列表
路由:/contest
1 2 3 4 5 6 { name: 'ContestFullProblemDetails', path: '/contest/:contestID/problem/:problemID/full-screen', component: Problem, meta: {title: 'Contest Problem Details', fullScreenSource: 'contest'} },
测评 跳转至网页的用户测评页。
路由:/status
1 2 3 4 5 6 { path: '/status', name: 'SubmissionList', component: SubmissionList, meta: {title: 'Status'} },
排名 排名分为ACM排名和OI排名。
路由分别为:/acm-rank
,/oi-rank
1 2 3 4 5 6 7 8 9 10 11 12 { path: '/acm-rank', name: 'ACM Rank', component: ACMRank, meta: {title: 'ACM Rank'} }, { path: '/oi-rank', name: 'OI Rank', component: OIRank, meta: {title: 'OI Rank'} },
讨论 路由:/discussion
团队 路由:/group
1 2 3 4 5 6 { path: '/discussion', name: 'AllDiscussion', meta: {title: 'Discussion', access: 'discussion'}, component: DiscussionList },
关于 关于拥有简介和开发者。
路由分别为:/introduction
,/developer
1 2 3 4 5 6 7 8 9 10 { path: '/introduction', meta: {title: 'Introduction'}, component: Introduction, }, { path: '/developer', meta: {title: 'Developer'}, component: Developer, },
未登录时 登录 绑定事件:
1 2 3 4 5 6 handleBtnClick(mode) { this.changeModalStatus({ mode, visible: true, }); },
注册 绑定事件:
1 2 3 4 5 6 handleBtnClick(mode) { this.changeModalStatus({ mode, visible: true, }); },
登录注册 用户点击登录触发按钮,执行hanleBtnClick(‘Login’).
调用共享数据中的changeModalStatus
方法
在NavBar.vue组件页面的最后,由于mode的值被修改为Login,modalVisible修改为true。调用Login组件。
1 <component :is="modalStatus.mode" v-if="modalVisible"></component>
输入账号密码之后判断登录失败的次数决定登录是否需要进行人机验证。
1 2 3 4 5 6 7 8 9 watch: { loginFailNum(newVal, oldVal) { if (newVal >= 5) { this.needVerify = true; } else { this.needVerify = false; } }, },
调用Login方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 api.login(formData).then( (res) => { this.btnLoginLoading = false; this.changeModalStatus({visible: false}); const jwt = res.headers['authorization']; this.$store.commit('changeUserToken', jwt); this.$store.dispatch('setUserInfo', res.data.data); this.$store.dispatch('incrLoginFailNum', true); mMessage.success(this.$i18n.t('m.Welcome_Back')); }, (_) => { this.$store.dispatch('incrLoginFailNum', false); this.btnLoginLoading = false; } );
进入axios全局请求拦截器:
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 axios.interceptors.request.use( config => { // NProgress.start(); // 每次发送请求之前判断vuex中是否存在token // 如果存在,则统一在http请求的header都加上token,这样后台根据token判断你的登录情况 // 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断 const token = localStorage.getItem('token') if (config.url != '/api/login' && config.url != '/api/admin/login') { token && (config.headers.Authorization = token); } let type = config.url.split("/")[2]; if (type === 'admin') { // 携带请求区别是否为admin config.headers['Url-Type'] = type } else { config.headers['Url-Type'] = 'general' } return config; }, error => { // NProgress.done(); mMessage.error(error.response.data.msg); if (!isMobile) { Vue.prototype.$notify.error({ title: i18n.t('m.Error'), message: error.response.data.msg, duration: 5000, offset: 50 }); } return Promise.error(error); })
发送ajax请求获取登录结果,进入全局响应拦截
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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 / 响应拦截器 axios.interceptors.response.use( response => { // NProgress.done(); if (response.headers['refresh-token']) { // token续约! store.commit('changeUserToken', response.headers['authorization']) } if (response.data.status === 200 || response.data.status == undefined) { return Promise.resolve(response); } else { mMessage.error(response.data.msg); if (!isMobile) { Vue.prototype.$notify.error({ title: i18n.t('m.Error'), message: response.data.msg, duration: 5000, offset: 50 }); } return Promise.reject(response); } }, // 服务器状态码不是200的情况 error => { // NProgress.done(); if (error.response) { if (error.response.headers['refresh-token']) { // token续约!! store.commit('changeUserToken', error.response.headers['authorization']) } if (error.response.data instanceof Blob) { // 如果是文件操作的返回,由后续进行处理 return Promise.resolve(error.response); } switch (error.response.status) { // 401: 未登录 token过期 // 未登录则跳转登录页面,并携带当前页面的路径 // 在登录成功后返回当前页面,这一步需要在登录页操作。 case 401: if (error.response.data.msg) { mMessage.error(error.response.data.msg); if (!isMobile) { Vue.prototype.$notify.error({ title: i18n.t('m.Error'), message: error.response.data.msg, duration: 5000, offset: 50 }); } } if (error.response.config.headers['Url-Type'] === 'admin') { router.push("/admin/login") } else { store.commit('changeModalStatus', {mode: 'Login', visible: true}); } store.commit('clearUserInfoAndToken'); break; // 403 // 无权限访问或操作的请求 case 403: if (error.response.data.msg) { mMessage.error(error.response.data.msg); if (!isMobile) { Vue.prototype.$notify.error({ title: i18n.t('m.Error'), message: error.response.data.msg, duration: 5000, offset: 50 }); } } let isAdminApi = error.response.config.url.startsWith('/api/admin'); store.dispatch('refreshUserAuthInfo').then((res) => { if (isAdminApi) { router.push("/admin") } }) break; // 404请求不存在 case 404: mMessage.error(i18n.t('m.Query_error_unable_to_find_the_resource_to_request')); break; // 其他错误,直接抛出错误提示 default: if (error.response.data) { if (error.response.data.msg) { mMessage.error(error.response.data.msg); if (!isMobile) { Vue.prototype.$notify.error({ title: i18n.t('m.Error'), message: error.response.data.msg, duration: 5000, offset: 50 }); } } else { mMessage.error(i18n.t('m.Server_error_please_refresh_again')); } } break; } return Promise.reject(error); } else { //处理断网或请求超时,请求没响应 if (error.code == 'ECONNABORTED' || error.message.includes('timeout')) { mMessage.error(i18n.t('m.Request_timed_out_please_try_again_later')); } else { mMessage.error(i18n.t('m.Network_error_abnormal_link_with_server_please_try_again_later')); } return Promise.reject(error); } } );
支持的远程评测平台 为各个远程评测平台的链接。