Docker Compose 部署若依前后端分离版

准备一台服务器

本次使用虚拟机,虚拟机系统 Ubuntu20.04,内存 4G,4核。

确保虚拟机能连接互联网。

Ubuntu20.04 安装 Docker

添加 Docker 的官方 GPG key:

shell 复制代码
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

添加仓库到 apt 中:

shell 复制代码
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

  sudo apt-get update

安装最新版本的 Docker:

shell 复制代码
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

docker-compose-plugin 就是 Docker Compose,上面的命令就安装了 Docker Compose

配置 Docker 镜像仓库:

shell 复制代码
sudo mkdir -p /etc/docker

sudo tee /etc/docker/daemon.json <<-'EOF'
{
    "registry-mirrors": ["https://mirror.baidubce.com"]
}
EOF

sudo systemctl daemon-reload

sudo systemctl restart docker

# 使用 Docker info 命令查看配置的加速仓库
docker info

启动一个 Hello World 容器测试 Docker 是否安装成功:

shell 复制代码
sudo docker run hello-world

看到如下内容就是安装成功:

使用如下命令查看 Docker 的版本以及 Docker Compose 的版本:

shell 复制代码
docker version
docker compose version

配置 Docker 命令可以使用非 Root 用户使用:

shell 复制代码
# 创建名为 docker 的用户组
sudo groupadd docker

# 将当前用户添加到 docker 用户组中
sudo usermod -aG docker $USER

# 激活
newgrp docker

# 验证非 Root 用户是否能使用 Docker 命令
docker run hello-world

配置 Docker 开机自启:

shell 复制代码
sudo systemctl enable docker.service
sudo systemctl enable containerd.service

# 关闭 Docker 开机自启
sudo systemctl disable docker.service
sudo systemctl disable containerd.service

打 ruoyi 后端的镜像

做必要的配置修改


yaml 复制代码
master:
    url: jdbc:mysql://mysql-service:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&connectTimeout=1000&socketTimeout=30000&autoReconnect=true&failOverReadOnly=false
    username: ruoyi
    password: 123456

编写 Dockerfile 文件

shell 复制代码
# 使用官方的 OpenJDK 8 镜像作为基础镜像
FROM openjdk:8-jdk-alpine
# 创建存放上传文件的目录
RUN mkdir -p /opt/project/ruoyi/ruoyi-backend/upload-file-path
# 创建存放日志的目录
RUN mkdir -p /opt/project/ruoyi/ruoyi-backend/logs
# 安装字体文件
RUN mkdir -p /etc/apk/
RUN touch /etc/apk/repositories
RUN echo -e 'https://mirrors.aliyun.com/alpine/v3.6/main/\nhttps://mirrors.aliyun.com/alpine/v3.6/community/' > /etc/apk/repositories
RUN apk --no-cache add ttf-dejavu fontconfig
RUN set -xe && apk --no-cache add ttf-dejavu fontconfig
# 设置工作目录
WORKDIR /opt/project/ruoyi/ruoyi-backend
# 将构建好的 JAR 文件复制到容器中
COPY ./ruoyi-admin/target/ruoyi-admin.jar ruoyi-admin.jar
# 暴露应用程序端口
EXPOSE 8080
# 启动应用程序
CMD ["nohup","java","-jar","/opt/project/ruoyi/ruoyi-backend/ruoyi-admin.jar", ">", "/opt/project/ruoyi/ruoyi-backend/logs/nohup.log", "&"]

IDEA 连接虚拟机中的 Docker 并打镜像

首先,检查 IDEA 是否安装了 Docker 插件:

版本比较新的 IDEA 默认都安装了这个插件,如果没有安装,安装一下。

确保我们虚拟机上安装了 Docker 和 Docker-compose,并启动了 Docker。

找到 IDEA 下方的 Services tab 栏,按照如下操作新建一个 Docker 连接:

配置连接 Docker 的 SSH 连接:

如果按照下面通过 password 认证连接 docker,idea 会显示 "Only key-pair ssh auth type is supported for docker connectons":

所以需要改为 key-pair 的认证方式:

点击 OK。

移除路径映射后点击 OK:

右键我们创建的 Docker 连接,点击 "connection",就连接成功了:

找到我们的 Dockerfile,右键这个文件,点击"Modify Run Configuration":

这里我们只需要修改 Image tag,也就是镜像名称,再点击 OK:

在打镜像前将我们的项目打包:

在 Dockerfile 文件的头部左侧的箭头位置点击 "Build image for dockerfile",就会自动按照 Dockerfile 为我们打镜像:

打镜像完成后,就可以看到我们打的镜像了:

打 ruoyi 前端的镜像

编写 Dockerfile

在项目的根目录下创建 nginx.conf 配置文件,这个配置文件会拷贝到 ruoyi 前端镜像中,作为 Nginx 的配置文件:

nginx 复制代码
user root;
worker_processes 1;

events {
    worker_connections 1024;
}

http {
    include mime.types;
    default_type application/octet-stream;
    sendfile on;
    keepalive_timeout 65;

    server {
        listen 80;
        server_name 127.0.0.1;
        charset utf-8;

        location / {
            root /opt/project/ruoyi/ruoyi-front-code;
            try_files $uri $uri/ /index.html;
            index index.html index.htm;
        }

        location /prod-api/ {
            proxy_set_header Host $http_host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header REMOTE-HOST $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_pass http://ruoyi-backend-service:8080/;
        }

        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
            root html;
        }
    }
}
shell 复制代码
# 使用 Nginx 作为基础镜像
FROM nginx:1.12.2
# 将 nginx.conf 拷贝到容器中
COPY nginx.conf /etc/nginx/nginx.conf
# 创建存放前端编译后代码的目录
RUN mkdir -p /opt/project/ruoyi/ruoyi-front-code
# 将构建好的应用拷贝到 Nginx 的默认 web 目录
COPY dist /opt/project/ruoyi/ruoyi-front-code
# Expose 端口
EXPOSE 80
# 启动 Nginx 服务器
CMD ["nginx", "-g", "daemon off;"]

WebStorm 连接虚拟机中的 Docker 打镜像

连接和打包步骤和 IDEA 连接虚拟机 Docker 的步骤一模一样。

准备 docker compose 启动的所有容器需要的文件以及文件夹

准备 Redis 容器需要的配置文件

找到本次部署的 Redis 7.2.0 的 redis.conf 文件,编辑其中的以下内容:

nginx 复制代码
# 修改连接 redis 的密码为 123456
requirepass 123456

# 注释掉 bind 127.0.0.1,bind 用于限制访问 Redis 的机器 ip,直接关掉
# bind 127.0.0.1

准备 MySQL 容器需要的初始化 SQL 文件

初始化的 SQL 文件在 Ruoyi 后端项目的如下位置:

容器首次启动的时候,我们需要执行这些初始化文件来建表、导入数据。

我们还需要一个创建数据库并创建一个用户的 sql 文件(create-database-user.sql):

sql 复制代码
-- 创建数据库
CREATE DATABASE  `ry-vue` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 创建普通用户
grant all PRIVILEGES on ry-vue.* to ruoyi@'%' identified by '123456';
flush privileges;
use ry-vue;

我们编写一个初始化的 sql 文件,在这个 sql 文件中再调用 create-database-user.sql、quartz.sql 和 ry_20231130.sql 这三个文件:

sql 复制代码
source /opt/mysql-service/create-database-user.sql;
use ry-vue;
source /opt/mysql-service/quartz.sql;
source /opt/mysql-service/ry_20231130.sql;

注意:这里的路径都是容器中的路径,就是容器首次启动时会调用这些命令

在宿主机中创建文件夹以及上传需要文件

/opt/project 目录的目录结构如下:

nginx 复制代码
project/
└── ruoyi
    ├── docker-compose.yml -- 下面我们要编写的 docker-compose 文件
    ├── mysql-service -- 存放 mysql-service 相关文件的目录
    │   ├── other
    │   │   ├── children-sql -- 子 sql 文件存放目录
    │   │   │   ├── create-database-user.sql
    │   │   │   ├── quartz.sql
    │   │   │   └── ry_20231130.sql
    │   │   └── init -- mysql 初始化执行目录,会挂载到 docker-entrypoint-initdb.d
    │   │       └── arrange-sql.sql -- sql 编排文件,会依次调用 children-sql 文件夹中的 sql 文件
    │   └── volumes -- mysql-service 挂载的容器卷
    │       └── mysql
    └── redis-service
        └── volumes -- redis-service 挂载的容器卷
            └── redis.conf

准备 SQL 文件

quartz.sql 和 ry_20231130.sql 在 ruoyi 后端代码根目录的 sql 文件夹中就有,直接拷贝即可,但是,需要在这两个 SQL 文件的第一行添加一行使用指定数据库的 SQL 语句:

sql 复制代码
use ry-vue;

create-database-user.sql 负责创建用户、数据库和为用户赋权:

sql 复制代码
-- 创建数据库
CREATE DATABASE  `ry-vue` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 应先创建新用户
create user 'ruoyi'@'%' identified by '123456';
-- 执行授权
GRANT ALL PRIVILEGES ON `ry-vue`.* TO 'ruoyi'@'%';
-- 刷新
flush privileges;
-- 授权远程
ALTER USER 'ruoyi'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
-- 刷新
flush privileges;

arrange-sql.sql 负责调用其他 SQL 文件,起到对 SQL 文件进行编排的作用:

sql 复制代码
source /opt/mysql-service/create-database-user.sql;
SET NAMES utf8;
source /opt/mysql-service/quartz.sql;
source /opt/mysql-service/ry_20231130.sql;

编写 docker-compose.yml 文件

yaml 复制代码
version: '3.8'
services:
  mysql-service:
    image: mysql:8.0
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: 123456
    ports:
      - "3306:3306"
    volumes:
      - /opt/project/ruoyi/mysql-service/volumes/mysql:/var/lib/mysql
      - /opt/project/ruoyi/mysql-service/other/init:/docker-entrypoint-initdb.d/
      - /opt/project/ruoyi/mysql-service/other/children-sql:/opt/mysql-service/

  redis-service:
    image: redis:7.2.0-alpine3.18
    restart: always
    ports:
      - "6379:6379"
    volumes:
      - /opt/project/ruoyi/redis-service/volumes/redis.conf:/usr/local/etc/redis/redis.conf
    command: [ "redis-server", "/usr/local/etc/redis/redis.conf" ]

  ruoyi-backend-service:
    image: ruoyi-backend:1.0
    networks:
      - ruoyi-network
    ports:
      - "8080:8080"
    depends_on:
      - mysql-service
      - redis-service

  ruoyi-frontend-service:
    image: ruoyi-frontend:1.0
    networks:
      - ruoyi-network
    ports:
      - "80:80"
    depends_on:
      - ruoyi-backend-service

networks:
  ruoyi-network:
    driver: bridge

使用如下命令启动 docker-compose 文件中的容器:

shell 复制代码
docker compose up -d

遇到的问题

后端连接到 MySQL 错误

13:55:01.882 [main] ERROR c.a.d.p.DruidDataSource - [init,942] - init datasource error, url: jdbc:mysql://mysql-service:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT&connectTimeout=1000&socketTimeout=30000
com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure

The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
        at com.mysql.cj.jdbc.exceptions.SQLError.createCommunicationsException(SQLError.java:175)
        at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:64)
        at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:825)
        at com.mysql.cj.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:446)
        at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:239)
        at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:188)
        at com.alibaba.druid.filter.FilterChainImpl.connection_connect(FilterChainImpl.java:118)
        at com.alibaba.druid.filter.stat.StatFilter.connection_connect(StatFilter.java:232)
        at com.alibaba.druid.filter.FilterChainImpl.connection_connect(FilterChainImpl.java:112)
        at com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1706)
        at com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1801)
        at com.alibaba.druid.pool.DruidDataSource.init(DruidDataSource.java:938)
        at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:1462)
        at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:1458)
        at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:83)
        at org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource.getConnection(AbstractRoutingDataSource.java:194)
        at org.springframework.jdbc.datasource.DataSourceUtils.fetchConnection(DataSourceUtils.java:159)
        at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:117)
        at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:80)
        at org.mybatis.spring.transaction.SpringManagedTransaction.openConnection(SpringManagedTransaction.java:80)
        at org.mybatis.spring.transaction.SpringManagedTransaction.getConnection(SpringManagedTransaction.java:67)
        at org.apache.ibatis.executor.BaseExecutor.getConnection(BaseExecutor.java:345)
        at org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor.java:89)
        at org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:64)
        at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:333)
        at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:158)
        at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:110)
        at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:90)
        at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:154)
        at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:147)
        at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:142)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:425)
        at com.sun.proxy.$Proxy100.selectList(Unknown Source)
        at org.mybatis.spring.SqlSessionTemplate.selectList(SqlSessionTemplate.java:224)
        at org.apache.ibatis.binding.MapperMethod.executeForMany(MapperMethod.java:147)
        at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:80)
        at org.apache.ibatis.binding.MapperProxy$PlainMethodInvoker.invoke(MapperProxy.java:142)
        at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:86)
        at com.sun.proxy.$Proxy101.selectConfigList(Unknown Source)
        at com.ruoyi.system.service.impl.SysConfigServiceImpl.loadingConfigCache(SysConfigServiceImpl.java:177)
        at com.ruoyi.system.service.impl.SysConfigServiceImpl.init(SysConfigServiceImpl.java:40)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:389)
        at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:333)
        at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:157)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:440)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1796)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:620)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
        at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1391)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1311)
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:657)
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640)
        at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119)
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1431)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:619)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955)
        at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:920)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583)
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145)
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:780)
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:453)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:343)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1370)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1359)
        at com.ruoyi.RuoYiApplication.main(RuoYiApplication.java:18)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49)
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:108)
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:58)
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:88)
Caused by: com.mysql.cj.exceptions.CJCommunicationsException: Communications link failure

修改 application-durid.yml 中的如下配置:

yaml 复制代码
url: jdbc:mysql://mysql-service:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT&connectTimeout=1000&socketTimeout=30000&autoReconnect=true&failOverReadOnly=false

testOnBorrow: true

开启代理软件会导致访问虚拟机前端页面出现问题

访问后端接口报错

后端的报错日志:

15:38:29.831 [http-nio-8080-exec-8] ERROR c.r.f.w.e.GlobalExceptionHandler - [handleRuntimeException,93] - 请求地址'/captchaImage',发生未知异常.
java.lang.NullPointerException: null
        at sun.awt.FontConfiguration.getVersion(FontConfiguration.java:1264)
        at sun.awt.FontConfiguration.readFontConfigFile(FontConfiguration.java:219)
        at sun.awt.FontConfiguration.init(FontConfiguration.java:107)
        at sun.awt.X11FontManager.createFontConfiguration(X11FontManager.java:774)
        at sun.font.SunFontManager$2.run(SunFontManager.java:431)
        at java.security.AccessController.doPrivileged(Native Method)
        at sun.font.SunFontManager.<init>(SunFontManager.java:376)
        at sun.awt.FcFontManager.<init>(FcFontManager.java:35)
        at sun.awt.X11FontManager.<init>(X11FontManager.java:57)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
        at java.lang.Class.newInstance(Class.java:442)
        at sun.font.FontManagerFactory$1.run(FontManagerFactory.java:83)
        at java.security.AccessController.doPrivileged(Native Method)
        at sun.font.FontManagerFactory.getInstance(FontManagerFactory.java:74)
        at java.awt.Font.getFont2D(Font.java:491)
        at java.awt.Font.access$000(Font.java:224)
        at java.awt.Font$FontAccessImpl.getFont2D(Font.java:228)
        at sun.font.FontUtilities.getFont2D(FontUtilities.java:180)
        at sun.font.StandardGlyphVector.initFontData(StandardGlyphVector.java:1126)
        at sun.font.StandardGlyphVector.init(StandardGlyphVector.java:1115)
        at sun.font.StandardGlyphVector.<init>(StandardGlyphVector.java:167)
        at java.awt.Font.createGlyphVector(Font.java:2545)
        at com.google.code.kaptcha.text.impl.DefaultWordRenderer.renderWord(DefaultWordRenderer.java:67)
        at com.google.code.kaptcha.impl.DefaultKaptcha.createImage(DefaultKaptcha.java:43)
        at com.ruoyi.web.controller.common.CaptchaController.getCode(CaptchaController.java:70)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150)
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1072)
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:965)
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
        at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:529)
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:623)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:209)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:111)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:111)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
        at com.alibaba.druid.support.http.WebStatFilter.doFilter(WebStatFilter.java:114)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
        at com.ruoyi.common.filter.RepeatableFilter.doFilter(RepeatableFilter.java:39)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:327)
        at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:115)
        at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:81)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
        at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:121)
        at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:115)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
        at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:126)
        at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:81)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
        at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:105)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
        at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:149)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
        at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
        at com.ruoyi.framework.security.filter.JwtAuthenticationTokenFilter.doFilterInternal(JwtAuthenticationTokenFilter.java:42)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:111)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
        at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:103)
        at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:89)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
        at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
        at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90)
        at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
        at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:110)
        at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:80)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
        at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:55)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
        at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
        at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:211)
        at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:183)
        at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354)
        at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
        at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
        at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
        at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:481)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:130)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:390)
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:926)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1791)
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:748)

http://doc.ruoyi.vip/ruoyi/other/faq.html#linux系统验证码乱码解决方法

相关推荐
pk_xz12345637 分钟前
Shell 脚本中变量和字符串的入门介绍
linux·运维·服务器
小珑也要变强40 分钟前
Linux之sed命令详解
linux·运维·服务器
Lary_Rock3 小时前
RK3576 LINUX RKNN SDK 测试
linux·运维·服务器
昌sit!5 小时前
K8S node节点没有相应的pod镜像运行故障处理办法
云原生·容器·kubernetes
追风林6 小时前
mac 本地docker-mysql主从复制部署
mysql·macos·docker
一坨阿亮6 小时前
Linux 使用中的问题
linux·运维
A ?Charis8 小时前
Gitlab-runner running on Kubernetes - hostAliases
容器·kubernetes·gitlab
城南vision8 小时前
Docker学习—Docker核心概念总结
java·学习·docker
wclass-zhengge8 小时前
Docker篇(Docker Compose)
运维·docker·容器