如何在阿里云ECS上同时运行多个SpringBoot应用?

在阿里云ECS上同时运行多个Spring Boot应用是完全可行的,以下是详细的步骤和最佳实践:

1. 基础环境准备

# 更新系统
sudo yum update -y

# 安装Java(以OpenJDK为例)
sudo yum install -y java-1.8.0-openjdk-devel

# 验证安装
java -version

2. 应用配置 – 端口分离

每个Spring Boot应用必须使用不同的端口:

应用1 (application1) – application.yml

server:
  port: 8080
spring:
  application:
    name: app1-service

应用2 (application2) – application.yml

server:
  port: 8081
spring:
  application:
    name: app2-service

应用3 (application3) – application.yml

server:
  port: 8082
spring:
  application:
    name: app3-service

3. 目录结构规划

/home/apps/
├── app1/
│   ├── app1.jar
│   ├── application.yml
│   └── start.sh
├── app2/
│   ├── app2.jar
│   ├── application.yml
│   └── start.sh
├── app3/
│   ├── app3.jar
│   ├── application.yml
│   └── start.sh
└── logs/
    ├── app1.log
    ├── app2.log
    └── app3.log

4. 启动脚本示例

start.sh 示例

#!/bin/bash

APP_NAME="app1"
JAR_NAME="app1.jar"
LOG_DIR="/home/apps/logs"
LOG_FILE="${LOG_DIR}/${APP_NAME}.log"

# 创建日志目录
mkdir -p $LOG_DIR

# JVM参数优化
JVM_OPTS="-Xms512m -Xmx1024m -XX:+UseG1GC -Dspring.profiles.active=prod"

case "$1" in
    start)
        echo "Starting $APP_NAME..."
        nohup java $JVM_OPTS -jar $JAR_NAME > $LOG_FILE 2>&1 &
        echo $! > ${APP_NAME}.pid
        echo "$APP_NAME started with PID $(cat ${APP_NAME}.pid)"
        ;;
    stop)
        if [ -f "${APP_NAME}.pid" ]; then
            PID=$(cat ${APP_NAME}.pid)
            echo "Stopping $APP_NAME (PID: $PID)..."
            kill $PID
            rm -f ${APP_NAME}.pid
            echo "$APP_NAME stopped"
        else
            echo "$APP_NAME is not running"
        fi
        ;;
    restart)
        $0 stop
        sleep 2
        $0 start
        ;;
    status)
        if [ -f "${APP_NAME}.pid" ]; then
            PID=$(cat ${APP_NAME}.pid)
            if ps -p $PID > /dev/null; then
                echo "$APP_NAME is running (PID: $PID)"
            else
                echo "$APP_NAME is not running (stale PID file)"
                rm -f ${APP_NAME}.pid
            fi
        else
            echo "$APP_NAME is not running"
        fi
        ;;
    *)
        echo "Usage: $0 {start|stop|restart|status}"
        exit 1
        ;;
esac

5. 使用Systemd服务管理

创建systemd服务文件

# /etc/systemd/system/app1.service
[Unit]
Description=App1 Spring Boot Application
After=network.target

[Service]
Type=simple
User=apps
WorkingDirectory=/home/apps/app1
ExecStart=/usr/bin/java -Xms512m -Xmx1024m -jar app1.jar
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

启用和管理服务

# 重新加载systemd配置
sudo systemctl daemon-reload

# 启用开机自启
sudo systemctl enable app1.service
sudo systemctl enable app2.service
sudo systemctl enable app3.service

# 启动服务
sudo systemctl start app1.service
sudo systemctl start app2.service
sudo systemctl start app3.service

# 查看状态
sudo systemctl status app1.service

6. Nginx反向X_X配置

# /etc/nginx/conf.d/apps.conf
upstream app1 {
    server 127.0.0.1:8080;
}

upstream app2 {
    server 127.0.0.1:8081;
}

upstream app3 {
    server 127.0.0.1:8082;
}

server {
    listen 80;
    server_name your-domain.com;

    location /app1/ {
        proxy_pass http://app1/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    location /app2/ {
        proxy_pass http://app2/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    location /app3/ {
        proxy_pass http://app3/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

7. 资源监控和优化

检查端口占用

# 查看所有监听端口
netstat -tlnp | grep java

# 检查特定端口
lsof -i :8080

内存监控脚本

#!/bin/bash
# monitor_apps.sh
echo "=== Spring Boot Apps Status ==="
for pid_file in /home/apps/*/app*.pid; do
    if [ -f "$pid_file" ]; then
        APP_NAME=$(basename $(dirname $pid_file))
        PID=$(cat $pid_file)
        if ps -p $PID > /dev/null; then
            MEM_USAGE=$(ps -o pid,ppid,cmd,%mem --no-headers -p $PID | awk '{print $4}')
            echo "$APP_NAME: PID=$PID, Memory=${MEM_USAGE}%"
        else
            echo "$APP_NAME: Not running (PID file exists but process dead)"
        fi
    fi
done

8. 安全建议

# 防火墙配置
sudo firewall-cmd --permanent --add-port=8080/tcp
sudo firewall-cmd --permanent --add-port=8081/tcp
sudo firewall-cmd --permanent --add-port=8082/tcp
sudo firewall-cmd --reload

# 或者只开放Nginx端口
sudo firewall-cmd --permanent --remove-port=8080-8082/tcp
sudo firewall-cmd --permanent --add-port=80/tcp
sudo firewall-cmd --reload

9. 性能调优建议

  • 内存分配:根据ECS实例规格合理分配JVM内存
  • 线程池:配置合适的线程池大小
  • 数据库连接池:合理设置连接池参数
  • 日志级别:生产环境使用INFO或WARN级别

通过以上配置,你可以在同一台ECS实例上稳定运行多个Spring Boot应用。建议根据实际业务需求和服务器资源进行适当调整。

未经允许不得转载:CLOUD云枢 » 如何在阿里云ECS上同时运行多个SpringBoot应用?