若依高可用架构

若依官网

一、环境准备

1.1 规划

序号 主机名 IP地址 用途
1 nginx-proxy-01 192.168.10.111 (VIP: 110) 静态文件 / 集群入口 (Master)
2 nginx-proxy-02 192.168.10.112 (VIP: 110) 静态文件 / 集群入口 (Backup)
3 java-backend 192.168.10.115 Java 后端服务
4 mysql-proxy 192.168.10.120 数据库读写分离入口
5 mysql-master 192.168.10.121 MySQL 主库 (写)
6 mysql-slave-01 192.168.10.122 MySQL 从库 (读)
7 redis-01 192.168.10.131 Redis 分片集群节点
8 redis-02 192.168.10.132 Redis 分片集群节点
9 redis-03 192.168.10.133 Redis 分片集群节点
10 compile-machine 192.168.10.130 编译机 / 运维中控
1
2
3
4
5
6
7
8
9
10
11
cat >> /etc/hosts <<EOF
192.168.10.111 nginx-proxy-01
192.168.10.112 nginx-proxy-02
192.168.10.115 java-backend
192.168.10.120 mysql-proxy
192.168.10.121 mysql-master
192.168.10.122 mysql-slave-01
192.168.10.131 redis-01
192.168.10.132 redis-02
192.168.10.133 redis-03
EOF
graph TD
    %% 1. 样式定义 (放在最前面)
    classDef user fill:#fff9c4,stroke:#fbc02d,stroke-width:2px;
    classDef vip fill:#ffecb3,stroke:#ff6f00,stroke-width:2px,stroke-dasharray: 5 5;
    classDef web fill:#bbdefb,stroke:#1565c0,stroke-width:2px;
    classDef app fill:#c8e6c9,stroke:#2e7d32,stroke-width:2px;
    classDef db fill:#ffccbc,stroke:#d84315,stroke-width:2px;
    classDef cache fill:#e1bee7,stroke:#6a1b9a,stroke-width:2px;

    %% 2. 节点定义
    User(👤 用户):::user
    VIP(💎 VIP: 192.168.10.110):::vip

    subgraph LB [接入层 / Keepalived]
        N1[nginx-01
192.168.10.111]:::web N2[nginx-02
192.168.10.112]:::web end subgraph APP [应用层] Java[java-backend
192.168.10.115]:::app end subgraph MYSQL [MySQL 集群] Proxy[mysql-proxy
192.168.10.120]:::db Master[Master 写
192.168.10.121]:::db Slave[Slave 读
192.168.10.122]:::db end subgraph REDIS [Redis 集群] R1[redis-01]:::cache R2[redis-02]:::cache R3[redis-03]:::cache end %% 3. 连线关系 User --> VIP VIP -.-> N1 VIP -.-> N2 N1 --> Java N2 --> Java Java -- JDBC连接 --> Proxy Proxy -- 写 --> Master Proxy -- 读 --> Slave Master -. 同步 .-> Slave Java -- 缓存连接 --> R1 Java

1.2 安装软件

1.2.1 Nginx

  • nginx-proxy-01
  • nginx-proxy-02
1
vim /etc/yum.repos.d/nginx.repo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[nginx-stable]
name=nginx stable repo
baseurl=https://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

[nginx-mainline]
name=nginx mainline repo
baseurl=https://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
1
yum install -y keepalived nginx

1.2.2 数据库

  • mysql-master 192.168.10.121
  • mysql-slave-01 192.168.10.122
1
2
wget https://dev.mysql.com/get/mysql80-community-release-el7-11.noarch.rpm
yum localinstall mysql80-community-release-el7-11.noarch.rpm -y
1
yum install mysql-community-server -y
1
systemctl enable --now mysqld
1
grep 'temporary password' /var/log/mysqld.log
1
mysql -uroot -p''
1
ALTER USER 'root'@'localhost' IDENTIFIED BY 'Root21..';
1
mysql -u root -p'Root21..'
  • mysql-proxy
1
2
3
4
5
6
7
cat <<EOF | sudo tee /etc/yum.repos.d/proxysql.repo
[proxysql]
name=ProxySQL YUM repository
baseurl=https://repo.proxysql.com/ProxySQL/proxysql-2.7.x/centos/7
gpgcheck=1
gpgkey=https://repo.proxysql.com/ProxySQL/proxysql-2.7.x/repo_pub_key
EOF
1
yum install -y proxysql
1
systemctl enable --now proxysql
1
yum -y install mysql

1.2.3 Java

  • java-backend
1
tar xf jdk-8u461-linux-x64.tar.gz -C /usr/local/
1
ln -s /usr/local/jdk1.8.0_461/ /usr/local/java
1
2
3
4
5
6
7
cat >> /etc/profile.d/jdk.sh <<-EOF
#!/bin/bash
# 指定java安装目录
export JAVA_HOME=/usr/local/java
# 用于指定java系统查找命令的路径
export PATH=\$JAVA_HOME/bin:\$PATH
EOF
1
source /etc/profile.d/jdk.sh
1
java -version

1.2.4 redis

编译机编译,然后 scp 到目标机器。

  • compile-machine
1
yum install gcc make -y
1
wget https://download.redis.io/releases/redis-7.4.7.tar.gz
1
tar xf redis-7.4.7.tar.gz -C /usr/local
1
2
cd /usr/local/redis-7.4.7
make
1
cd /usr/local/redis-7.4.7/src
1
scp redis-server redis-cli redis-sentinel redis-benchmark redis-check-aof redis-check-rdb root@redis-01:/usr/local/bin/
1
scp redis-server redis-cli redis-sentinel redis-benchmark redis-check-aof redis-check-rdb root@redis-02:/usr/local/bin/
1
scp redis-server redis-cli redis-sentinel redis-benchmark redis-check-aof redis-check-rdb root@redis-03:/usr/local/bin/
  • redis-01
  • redis-02
  • redis-03

1.2.5 java、node、maven、mysql

  • compile-machine java
1
tar xf jdk-8u461-linux-x64.tar.gz -C /usr/local/
1
ln -s /usr/local/jdk1.8.0_461/ /usr/local/java
1
2
3
4
5
6
7
cat >> /etc/profile.d/jdk.sh <<-EOF
#!/bin/bash
# 指定java安装目录
export JAVA_HOME=/usr/local/java
# 用于指定java系统查找命令的路径
export PATH=\$JAVA_HOME/bin:\$PATH
EOF
1
source /etc/profile.d/jdk.sh
1
java -version

  • compile-machine node
  • 注意:尽量使用 16 的长期版本,若伊前端的加密算法没有更新。
1
wget https://nodejs.org/download/release/v17.9.1/node-v17.9.1-linux-x64.tar.gz
1
tar xf node-v17.9.1-linux-x64.tar.gz -C /usr/local/
1
ln -sv /usr/local/node-v17.9.1-linux-x64 /usr/local/node
1
2
3
4
5
6
cat >> /etc/profile.d/node.sh <<-EOF
#!/bin/bash
export NODE_HOME=/usr/local/node
export PATH=\$PATH:\$NODE_HOME/bin
export NODE_PATH=\$NODE_HOME/lib/node_modules
EOF
1
source  /etc/profile.d/node.sh
1
2
node -v
npm -v
  • compile-machine maven
1
tar xf apache-maven-3.9.12-bin.tar.gz -C /usr/local/
1
ln -s /usr/local/apache-maven-3.9.12/ /usr/local/maven
1
2
3
4
5
cat >> /etc/profile.d/mvn.sh <<EOF
#!/bin/bash
export MAVEN_HOME=/usr/local/maven/
export PATH=\$MAVEN_HOME/bin:\$PATH
EOF
1
source /etc/profile.d/mvn.sh
1
mvn -v
  • compile-machine mysql

    使用mysql连接工具

1
yum -y install mysql

1.2.6 拉取代码

1
yum install git -y
1
2
3
cd ~
git clone https://gitee.com/y_project/RuoYi-Vue
cd RuoYi-Vue

二、构建前的配置

2.1 mysql相关

2.1.1 设置主从

  • mysql-master
1
vim /etc/my.cnf
1
2
3
4
5
6
7
8
9
10
11
12
13
[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
server-id=121
port=3306
gtid_mode=ON
enforce_gtid_consistency=ON
log_bin=mysql-bin
binlog_format=ROW
expire_logs_days=7
log_slave_updates=ON
1
systemctl restart mysqld
1
mysql -u root -p'Root21..'
1
2
3
4
5
-- 创建复制专用用户
CREATE USER 'repl'@'%' IDENTIFIED WITH mysql_native_password BY 'Repl21..';
-- 赋予复制权限
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
FLUSH PRIVILEGES;
  • mysql-slave-01
  • ….
1
vim /etc/my.cnf
1
2
3
4
5
6
7
8
9
10
11
12
[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
server-id = 122
port = 3306
gtid_mode = ON
enforce_gtid_consistency = ON
log_bin = mysql-bin
binlog_format = ROW
read_only = 1
1
systemctl restart mysqld
1
mysql -u root -p'Root21..'
1
2
3
4
5
6
7
CHANGE MASTER TO
MASTER_HOST='192.168.10.121',
MASTER_USER='repl',
MASTER_PASSWORD='Repl21..',
MASTER_PORT=3306,
MASTER_AUTO_POSITION=1; -- 【核心】这句话告诉它:用 GTID 自动找位置
START SLAVE;
1
SHOW SLAVE STATUS\G;

2.1.2 读写分离

  • mysql-master

注意:这里顺带创建若伊数据库,mysql8 创建用户需携带 WITH mysql_native_password

1
mysql -u root -p'Root21..'
1
2
CREATE USER 'monitor'@'%' IDENTIFIED WITH mysql_native_password BY 'Monitor21..';
GRANT REPLICATION CLIENT ON *.* TO 'monitor'@'%';
1
create database ruoyi character set utf8mb4;
1
2
create user ruoyi@'192.168.10.%' identified WITH mysql_native_password by 'Ruoyi21..';
grant all privileges on ruoyi.* to ruoyi@'192.168.10.%';
1
FLUSH PRIVILEGES;
  • mysql-proxy
1
mysql -u admin -padmin -h 127.0.0.1 -P 6032 --prompt='ProxySQLAdmin> '
1
INSERT INTO mysql_servers (hostgroup_id, hostname, port) VALUES (10, '192.168.10.121', 3306);
1
INSERT INTO mysql_servers (hostgroup_id, hostname, port) VALUES (20, '192.168.10.122', 3306);
1
2
LOAD MYSQL SERVERS TO RUNTIME;
SAVE MYSQL SERVERS TO DISK;
1
2
3
4
5
UPDATE global_variables SET variable_value='monitor' WHERE variable_name='mysql-monitor_username';
UPDATE global_variables SET variable_value='Monitor21..' WHERE variable_name='mysql-monitor_password';

LOAD MYSQL VARIABLES TO RUNTIME;
SAVE MYSQL VARIABLES TO DISK;
1
2
3
4
5
-- 这里的 default_hostgroup 是默认路由,万一没匹配到规则,就去这个组(通常设为写组10)
INSERT INTO mysql_users (username, password, default_hostgroup) VALUES ('ruoyi', 'Ruoyi21..', 10);

LOAD MYSQL USERS TO RUNTIME;
SAVE MYSQL USERS TO DISK;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-- 先清空旧规则,防止干扰
DELETE FROM mysql_query_rules;

-- 规则 1:特殊读 -> 走主库 (HG 10)
INSERT INTO mysql_query_rules
(rule_id, active, match_digest, destination_hostgroup, apply)
VALUES (1, 1, '^SELECT.*FOR UPDATE$', 10, 1);

-- 规则 2:普通读 -> 走从库 (HG 20)
INSERT INTO mysql_query_rules
(rule_id, active, match_digest, destination_hostgroup, apply)
VALUES (2, 1, '^SELECT', 20, 1);

-- 加载生效
LOAD MYSQL QUERY RULES TO RUNTIME;
SAVE MYSQL QUERY RULES TO DISK;
1
SELECT hostgroup_id, hostname, status FROM runtime_mysql_servers;

2.1.3 导入数据

  • compile-machine
1
cd ~/RuoYi-Vue/sql
1
2
mysql -h mysql-proxy -P 6033 -u ruoyi -p'Ruoyi21..' ruoyi < quartz.sql
mysql -h mysql-proxy -P 6033 -u ruoyi -p'Ruoyi21..' ruoyi < ry_20250522.sql

2.1.4 连接配置

1
mysql -h 192.168.10.120 -P 6033 -u ruoyi -p'Ruoyi21..'
1
2
3
4
192.168.10.120
6033
ruoyi
Ruoyi21..

2.1.5 补充配置

  1. 不要伪装成 mysql5,否则后续连接不成功。
  • mysql-proxy
1
mysql -u admin -padmin -h 127.0.0.1 -P 6032 --prompt='ProxySQLAdmin> '
1
2
3
4
5
6
7
8
-- 1. 修改全局变量,伪装成 8.0 版本
UPDATE global_variables SET variable_value='8.0.30' WHERE variable_name='mysql-server_version';

-- 2. 让配置在运行时生效
LOAD MYSQL VARIABLES TO RUNTIME;

-- 3. 保存配置到磁盘(重启不丢失)
SAVE MYSQL VARIABLES TO DISK;

关键报错

1
2
16:18:04.666 [Druid-ConnectionPool-Create-1121401953] ERROR c.a.d.p.DruidDataSource - [run,2795] - create connection SQLException, url: jdbc:mysql://192.168.10.120:6033/ruoyi?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8, errorCode 1193, state HY000
java.sql.SQLException: Unknown system variable 'query_cache_size'

2.2 redis

2.2.1 生产环境调优

  • redis-01
  • redis-02
  • redis-03
1
vim /etc/security/limits.conf
1
* - nofile 65535
1
ulimit -n
1
vim /etc/sysctl.conf
1
2
net.core.somaxconn = 10240
vm.overcommit_memory = 1
1
sysctl -p

2.2.3 配置启动

  • redis-01 7001、7002
  • redis-02 7001、7002
  • redis-03 7001、7002
1
2
3
4
5
6
7
8
9
10
11
12
13
14
mkdir -pv /data/redis/{7001,7002}
cat > /data/redis/7001/redis.conf <<EOF
port 7001
bind 0.0.0.0
requirepass "redispwd"
protected-mode no
daemonize yes
pidfile /var/run/redis_7001.pid
dir /data/redis/7001
appendonly yes
cluster-enabled yes
cluster-config-file nodes-7001.conf
cluster-node-timeout 5000
EOF
1
2
cp /data/redis/7001/redis.conf /data/redis/7002/redis.conf
sed -i 's/7001/7002/g' /data/redis/7002/redis.conf
1
2
redis-server /data/redis/7001/redis.conf
redis-server /data/redis/7002/redis.conf
1
ps -ef | grep redis
1
2
3
4
5
6
# 注意:IP 请换成你实际的 IP,确保 node3 是 103
redis-cli -a redispwd --cluster create \
192.168.10.131:7001 192.168.10.131:7002 \
192.168.10.132:7001 192.168.10.132:7002 \
192.168.10.133:7001 192.168.10.133:7002 \
--cluster-replicas 1
1
redis-cli -a redispwd -c -p 7002 cluster nodes

2.3 修改 Ruoyi 项目配置信息

  1. mysql 连接信息
  • compile-machine
1
vim ~/RuoYi-Vue/ruoyi-admin/src/main/resources/application-druid.yml
1
2
3
4
5
6
7
8
9
10
11
# 数据源配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
druid:
# 主库数据源
master:
url: jdbc:mysql://192.168.10.120:6033/ruoyi?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: ruoyi
password: Ruoyi21..
  1. redis 连接配置
  • compile-machine
  • 注意:集群和单机配置稍有不同
1
vim ~/RuoYi-Vue/ruoyi-admin/src/main/resources/application.yml
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
redis:
# 1. 【必须】集群模式不支持 select db,只能用 0
database: 0
# 2. 【必须】你的 Redis 密码(如果有)
password: redispwd
# 3. 连接超时
timeout: 10s

# 4. 【核心变化】这里一旦出现,host 和 port 就失效了
cluster:
# 最大重定向次数
max-redirects: 3
# 【关键】填入你 6 个节点的 IP:Port
nodes:
- 192.168.10.131:7001
- 192.168.10.131:7002
- 192.168.10.132:7001
- 192.168.10.132:7002
- 192.168.10.133:7001
- 192.168.10.133:7002

# 5. 连接池配置(Lettuce)可以保留,通用的
lettuce:
pool:
min-idle: 0
max-idle: 8
max-active: 8
max-wait: -1ms

三、编译

3.1 编译后端

  • compile-machine
1
cd ~/RuoYi-Vue/
1
mvn install
1
cd ~/RuoYi-Vue/ruoyi-admin/
1
mvn clean package
1
ls /root/RuoYi-Vue/ruoyi-admin/target/

验证

1
java -jar /root/RuoYi-Vue/ruoyi-admin/target/ruoyi-admin.jar

3.2 编译前端

  • compile-machine
1
cd ~/RuoYi-Vue/ruoyi-ui/
1
npm install -registry=http://registry.npmmirror.com

(可选,17版本及以上需要)高版本临时降级使用加密算法

1
export NODE_OPTIONS=--openssl-legacy-provider
1
npm run build:prod
1
ll /root/RuoYi-Vue/ruoyi-ui/dist/

3.3 移动构建产物到目标机器

前端静态资源

1
cd ~/RuoYi-Vue/ruoyi-ui
1
tar zcvf front-end.tar.gz dist
1
scp front-end.tar.gz root@nginx-proxy-01:/root
1
scp front-end.tar.gz root@nginx-proxy-02:/root

后端 jar

1
scp /root/RuoYi-Vue/ruoyi-admin/target/ruoyi-admin.jar root@java-backend:/root

四、部署

4.1 启动后端

  • java-backend
1
java -jar /root/ruoyi-admin.jar

后台启动,8080

1
nohup java -jar /root/ruoyi-admin.jar > ruoyi.log 2>&1 &

4.2 启动前端

  • nginx-proxy-01
  • nginx-proxy-02

修改配置

1
mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bak
1
vim /etc/nginx/conf.d/ruoyi.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
server {
listen 80;
server_name localhost;
access_log /var/log/nginx/ruoyi-access.log main;
location / {
root /www/wwwroot/ruoyi;
try_files $uri $uri/ /index.html;
index index.html;
}

location /prod-api/ {
proxy_set_header Host $http_host; # 传递原始请求的Host头。
proxy_set_header X-Real-IP $remote_addr; # 记录客户端真实IP地址。
proxy_set_header REMOTE-HOST $remote_addr; # 记录客户端真实IP地址。
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 记录经过代理链的完整IP链。
proxy_pass http://192.168.10.115:8080/;
}
}

验证配置

1
nginx -t

设置站点目录

1
2
3
4
5
mkdir -pv /www/wwwroot/ruoyi
cd ~
tar xf front-end.tar.gz -C /www/wwwroot/ruoyi
cd /www/wwwroot/ruoyi
mv dist/* .

启动

1
systemctl enable --now nginx
1
systemctl restart nginx

4.3 设置nginx高可用

  • nginx-proxy-01
1
cp /etc/keepalived/keepalived.conf{,.bak}
1
vim /etc/keepalived/keepalived.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
global_defs {
router_id nginx-proxy-01
}

# 脚本:每 2 秒检查一次 Nginx 活着没
vrrp_script check_nginx {
script "/usr/bin/killall -0 nginx"
interval 2
weight -20
}

vrrp_instance VI_1 {
state MASTER # 【核心】我是老大
interface ens33 # 【注意】改这里!你的网卡名,用 ip addr 看
virtual_router_id 51 # 组号,老大老二必须一样
priority 100 # 【核心】权重,老大要高
advert_int 1

authentication {
auth_type PASS
auth_pass 1111
}

virtual_ipaddress {
192.168.10.110 # 【核心】这就是那个漂移的 VIP
}

track_script {
check_nginx # 引用上面的检查脚本
}
}

  • ``nginx-proxy-02`
1
cp /etc/keepalived/keepalived.conf{,.bak}
1
vim /etc/keepalived/keepalived.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
global_defs {
router_id nginx-proxy-02
}

vrrp_script check_nginx {
script "/usr/bin/killall -0 nginx"
interval 2
weight -20
}

vrrp_instance VI_1 {
state BACKUP # 【核心】我是备胎
interface ens33 # 【注意】改这里!
virtual_router_id 51 # 必须和老大一样
priority 90 # 【核心】权重,备胎要低
advert_int 1

authentication {
auth_type PASS
auth_pass 1111
}

virtual_ipaddress {
192.168.10.110 # 同样的 VIP
}

track_script {
check_nginx
}
}

1
systemctl enable --now keepalived

五、访问

http://192.168.10.110