扩展篇:基于Nginx的负载均衡部署

扩展篇:基于Nginx的负载均衡部署

随着业务增长,单个应用实例可能无法满足高并发需求。本章节介绍如何使用Nginx实现若依应用的负载均衡部署,提升系统的可用性和处理能力。

1 负载均衡架构说明

架构组件:

  • Nginx负载均衡器:作为入口,分发请求到后端应用实例
  • 多个若依应用实例:提供冗余和负载分担
  • 共享数据层:MySQL和Redis供所有应用实例共享

优势:

  • 提高系统吞吐量
  • 增强服务可用性
  • 支持滚动更新
  • 故障自动转移

前置步骤:使用Docker + Docker Compose部署若依(RuoYi)Java单体应用

2 Nginx配置文件

创建docker/nginx/nginx.conf

# Nginx主配置文件
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;

events {
    worker_connections 1024;
    use epoll;
    multi_accept on;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # 日志格式
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for" '
                    'upstream_addr=$upstream_addr upstream_status=$upstream_status '
                    'request_time=$request_time upstream_response_time=$upstream_response_time';

    access_log /var/log/nginx/access.log main;

    # 性能优化
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    client_max_body_size 50M;

    # Gzip压缩
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_types text/plain text/css text/xml text/javascript 
               application/javascript application/xml+rss application/json;

    # 若依应用负载均衡配置
    upstream ruoyi_backend {
        # 负载均衡策略:默认轮询
        # 可选策略:ip_hash, least_conn, random
        
        # 应用实例1
        server ruoyi-app-1:8080 weight=1 max_fails=3 fail_timeout=30s;
        # 应用实例2  
        server ruoyi-app-2:8080 weight=1 max_fails=3 fail_timeout=30s;
        
        # 健康检查配置
        keepalive 32;
    }

    server {
        listen 80;
        server_name localhost;
        
        # 访问日志
        access_log /var/log/nginx/ruoyi_access.log main;
        
        # 健康检查端点
        location /nginx-health {
            access_log off;
            return 200 "healthy\n";
            add_header Content-Type text/plain;
        }
        
        # 主应用代理
        location / {
            proxy_pass http://ruoyi_backend;
            
            # 代理头设置
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            
            # 超时设置
            proxy_connect_timeout 30s;
            proxy_send_timeout 60s;
            proxy_read_timeout 60s;
            
            # 缓冲设置
            proxy_buffering on;
            proxy_buffer_size 8k;
            proxy_buffers 8 8k;
            
            # 连接复用
            proxy_http_version 1.1;
            proxy_set_header Connection "";
        }
        
        # 静态资源缓存
        location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg)$ {
            proxy_pass http://ruoyi_backend;
            proxy_set_header Host $host;
            
            # 缓存配置
            expires 1d;
            add_header Cache-Control "public, immutable";
        }
        
        # API接口代理
        location /api/ {
            proxy_pass http://ruoyi_backend;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            
            # API特殊配置
            proxy_read_timeout 120s;
        }
    }

    # 负载均衡状态页面
    server {
        listen 8090;
        server_name localhost;
        
        location /nginx_status {
            stub_status on;
            access_log off;
            allow 172.16.0.0/12;  # 允许内网访问
            deny all;
        }
    }
}

3 负载均衡Docker Compose配置

创建docker-compose-loadbalancer.yml

version: '3.8'

services:
  # MySQL数据库(共享)
  mysql:
    image: mysql:8.0
    container_name: ruoyi-mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: 123456
      MYSQL_DATABASE: ry
      MYSQL_USER: ruoyi
      MYSQL_PASSWORD: 123456
      TZ: Asia/Shanghai
    ports:
      - "3306:3306"
    volumes:
      - mysql_data:/var/lib/mysql
      - ./docker/mysql/conf/my.cnf:/etc/mysql/conf.d/my.cnf
      - ./docker/mysql/init:/docker-entrypoint-initdb.d
      - mysql_logs:/var/log/mysql
    command: --default-authentication-plugin=mysql_native_password
    networks:
      - ruoyi-network

  # Redis缓存(共享)
  redis:
    image: redis:7-alpine
    container_name: ruoyi-redis
    restart: always
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
      - ./docker/redis/redis.conf:/etc/redis/redis.conf
    command: redis-server /etc/redis/redis.conf
    networks:
      - ruoyi-network

  # 若依应用实例1
  ruoyi-app-1:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: ruoyi-app-1
    restart: always
    ports:
      - "8080:8080"
    environment:
      - SPRING_PROFILES_ACTIVE=druid
      - TZ=Asia/Shanghai
      - SERVER_PORT=8080
      - INSTANCE_NAME=ruoyi-app-1
    volumes:
      - app1_logs:/app/logs
      - app_upload:/app/upload  # 共享上传目录
    depends_on:
      - mysql
      - redis
    networks:
      - ruoyi-network
    healthcheck:
      test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/actuator/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 60s

  # 若依应用实例2
  ruoyi-app-2:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: ruoyi-app-2
    restart: always
    ports:
      - "8081:8080"
    environment:
      - SPRING_PROFILES_ACTIVE=druid
      - TZ=Asia/Shanghai
      - SERVER_PORT=8080
      - INSTANCE_NAME=ruoyi-app-2
    volumes:
      - app2_logs:/app/logs
      - app_upload:/app/upload  # 共享上传目录
    depends_on:
      - mysql
      - redis
    networks:
      - ruoyi-network
    healthcheck:
      test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/actuator/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 60s

  # Nginx负载均衡器
  nginx:
    image: nginx:1.24-alpine
    container_name: ruoyi-nginx
    restart: always
    ports:
      - "80:80"
      - "8090:8090"  # 状态监控端口
    volumes:
      - ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf
      - nginx_logs:/var/log/nginx
    depends_on:
      - ruoyi-app-1
      - ruoyi-app-2
    networks:
      - ruoyi-network
    healthcheck:
      test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost/nginx-health"]
      interval: 30s
      timeout: 10s
      retries: 3

# 网络配置
networks:
  ruoyi-network:
    driver: bridge
    ipam:
      config:
        - subnet: 172.20.0.0/16

# 数据卷配置
volumes:
  mysql_data:
    driver: local
  mysql_logs:
    driver: local
  redis_data:
    driver: local
  app1_logs:
    driver: local
  app2_logs:
    driver: local
  app_upload:
    driver: local
  nginx_logs:
    driver: local

4 负载均衡部署步骤

4.1 创建目录结构

# 创建Nginx配置目录
mkdir -p docker/nginx

# 复制配置文件
# 将上述nginx.conf内容保存到docker/nginx/nginx.conf

4.2 修改Dockerfile支持健康检查

更新Dockerfile添加wget支持:

# 使用OpenJDK 8作为基础镜像
FROM openjdk:8-jre-alpine

# 设置维护者信息
LABEL maintainer="ruoyi@example.com"

# 安装wget用于健康检查
RUN apk add --no-cache wget tzdata && \
    ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
    echo "Asia/Shanghai" > /etc/timezone

# 创建应用目录
WORKDIR /app

# 复制jar包到容器
COPY target/ruoyi-admin.jar app.jar

# 暴露端口
EXPOSE 8080

# 设置JVM参数
ENV JAVA_OPTS="-Xmx512m -Xms512m"

# 启动应用
ENTRYPOINT exec java $JAVA_OPTS -jar app.jar

4.3 启动负载均衡集群

# 清理并打包项目
mvn clean package -DskipTests

# 使用负载均衡配置启动服务
docker-compose -f docker-compose-loadbalancer.yml up -d

# 查看所有服务状态
docker-compose -f docker-compose-loadbalancer.yml ps

# 查看Nginx日志
docker-compose -f docker-compose-loadbalancer.yml logs -f nginx

5 验证负载均衡效果

5.1 基础连通性测试

# 测试Nginx代理
curl -I http://localhost/

# 测试健康检查
curl http://localhost/nginx-health

# 查看Nginx状态
curl http://localhost:8090/nginx_status

5.2 负载分发测试

创建测试脚本test_loadbalance.sh

#!/bin/bash

echo "开始负载均衡测试..."

# 发送10个请求,观察负载分发情况
for i in {1..10}; do
    echo "请求 $i:"
    curl -s -H "Cache-Control: no-cache" http://localhost/ | grep -o "实例.*" || echo "  请求处理完成"
    sleep 1
done

echo "查看Nginx访问日志(最后10条):"
docker exec ruoyi-nginx tail -10 /var/log/nginx/ruoyi_access.log

5.3 故障转移测试

# 停止一个应用实例
docker stop ruoyi-app-1

# 测试服务是否仍可用
curl http://localhost/

# 查看Nginx错误日志
docker exec ruoyi-nginx tail -20 /var/log/nginx/error.log

# 重启停止的实例
docker start ruoyi-app-1

6 监控和调优

6.1 实时监控脚本

创建monitor_cluster.sh

#!/bin/bash

echo "=== 若依集群监控面板 ==="

while true; do
    clear
    echo "时间: $(date)"
    echo "================================="
    
    # 容器状态
    echo "容器状态:"
    docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | grep ruoyi
    echo ""
    
    # Nginx状态
    echo "Nginx状态:"
    curl -s http://localhost:8090/nginx_status 2>/dev/null || echo "Nginx状态页面不可用"
    echo ""
    
    # 系统资源
    echo "资源使用:"
    docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}" | grep ruoyi
    echo ""
    
    echo "按Ctrl+C退出监控"
    sleep 5
done

6.2 性能调优建议

Nginx调优参数:

# 在nginx.conf的http块中添加
worker_processes auto;
worker_connections 2048;

# 优化upstream配置
upstream ruoyi_backend {
    least_conn;  # 使用最少连接负载均衡
    server ruoyi-app-1:8080 weight=1 max_fails=2 fail_timeout=10s;
    server ruoyi-app-2:8080 weight=1 max_fails=2 fail_timeout=10s;
    keepalive 64;
}

应用实例调优:

# 在docker-compose中调整应用资源
ruoyi-app-1:
  deploy:
    resources:
      limits:
        memory: 1G
        cpus: '0.5'
      reservations:
        memory: 512M
        cpus: '0.25'
posted @ 2025-06-02 21:18  生命博爱  阅读(69)  评论(0)    收藏  举报