1. Java应用迁移概述

Java应用迁移是运维工作中的重要环节,涉及应用部署、配置管理、性能优化和故障处理等多个方面。本文将详细介绍Java应用迁移与部署运维的实战经验,包括应用迁移策略、部署管理、性能优化、故障处理的完整解决方案。

1.1 核心功能

  1. 应用迁移: 不同环境间的Java应用迁移
  2. 部署管理: 自动化部署和版本管理
  3. 配置管理: 环境配置和参数管理
  4. 性能优化: 应用性能调优和监控
  5. 故障处理: 常见故障的诊断和处理

1.2 技术架构

1
2
3
源环境 → 应用打包 → 配置迁移 → 目标环境 → 部署验证
↓ ↓ ↓ ↓ ↓
备份策略 → 版本管理 → 环境配置 → 自动化部署 → 健康检查

2. 环境准备

2.1 系统要求检查

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
112
113
114
115
116
117
118
#!/bin/bash
# check_java_app_env.sh - Java应用环境检查脚本
# @author 运维实战

# 日志函数
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}

# 检查系统版本
check_os_version() {
log "检查系统版本..."

if [ -f /etc/os-release ]; then
. /etc/os-release
log "操作系统: $NAME $VERSION"
else
log "无法确定操作系统版本"
fi
}

# 检查Java版本
check_java_version() {
log "检查Java版本..."

JAVA_VERSION=$(java -version 2>&1 | head -1 | cut -d'"' -f2)
log "Java版本: $JAVA_VERSION"

# 检查Java版本是否符合要求
if [[ $JAVA_VERSION == 1.8* ]] || [[ $JAVA_VERSION == 11* ]] || [[ $JAVA_VERSION == 17* ]]; then
log "Java版本符合要求"
else
log "Java版本不符合要求,需要Java 8+"
exit 1
fi
}

# 检查Maven版本
check_maven_version() {
log "检查Maven版本..."

if command -v mvn > /dev/null 2>&1; then
MAVEN_VERSION=$(mvn -version | head -1 | cut -d' ' -f3)
log "Maven版本: $MAVEN_VERSION"
else
log "Maven未安装"
exit 1
fi
}

# 检查应用端口
check_application_ports() {
log "检查应用端口..."

# 检查常用端口
PORTS=(8080 8081 8082 9090 9091)

for port in "${PORTS[@]}"; do
if netstat -tlnp | grep ":$port " > /dev/null; then
log "端口 $port 已被占用"
else
log "端口 $port 可用"
fi
done
}

# 检查磁盘空间
check_disk_space() {
log "检查磁盘空间..."

df -h | grep -E "(/$|/var|/tmp|/home)" | while read line; do
log "磁盘使用: $line"
done
}

# 检查内存使用
check_memory() {
log "检查内存使用..."

TOTAL_MEM=$(free -h | grep Mem | awk '{print $2}')
USED_MEM=$(free -h | grep Mem | awk '{print $3}')
AVAIL_MEM=$(free -h | grep Mem | awk '{print $7}')

log "总内存: $TOTAL_MEM"
log "已使用: $USED_MEM"
log "可用内存: $AVAIL_MEM"
}

# 检查网络连接
check_network() {
log "检查网络连接..."

# 检查网络连通性
if ping -c 3 8.8.8.8 > /dev/null 2>&1; then
log "网络连接正常"
else
log "网络连接异常"
exit 1
fi
}

# 主函数
main() {
log "开始Java应用环境检查..."

check_os_version
check_java_version
check_maven_version
check_application_ports
check_disk_space
check_memory
check_network

log "Java应用环境检查完成"
}

# 执行主函数
main "$@"

3. 应用打包脚本

3.1 应用打包脚本

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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
#!/bin/bash
# java_app_package.sh - Java应用打包脚本
# @author 运维实战

# 配置参数
APP_NAME="demo-app"
APP_VERSION="1.0.0"
SOURCE_DIR="/opt/source"
TARGET_DIR="/opt/target"
LOG_FILE="/var/log/java_app_package.log"

# 日志函数
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a $LOG_FILE
}

# 创建目标目录
create_target_dir() {
log "创建目标目录..."

PACKAGE_DATE=$(date +%Y%m%d_%H%M%S)
PACKAGE_DIR="$TARGET_DIR/${APP_NAME}-${APP_VERSION}-${PACKAGE_DATE}"

mkdir -p $PACKAGE_DIR

if [ $? -eq 0 ]; then
log "目标目录创建成功: $PACKAGE_DIR"
else
log "目标目录创建失败"
exit 1
fi
}

# 编译应用
compile_application() {
log "编译应用..."

cd $SOURCE_DIR

# 清理之前的编译结果
mvn clean

if [ $? -eq 0 ]; then
log "Maven清理成功"
else
log "Maven清理失败"
exit 1
fi

# 编译应用
mvn compile

if [ $? -eq 0 ]; then
log "应用编译成功"
else
log "应用编译失败"
exit 1
fi

# 运行测试
mvn test

if [ $? -eq 0 ]; then
log "测试执行成功"
else
log "测试执行失败"
exit 1
fi
}

# 打包应用
package_application() {
log "打包应用..."

cd $SOURCE_DIR

# 打包应用
mvn package -DskipTests

if [ $? -eq 0 ]; then
log "应用打包成功"
else
log "应用打包失败"
exit 1
fi

# 查找生成的JAR文件
JAR_FILE=$(find target -name "*.jar" -not -name "*sources.jar" -not -name "*javadoc.jar" | head -1)

if [ -n "$JAR_FILE" ]; then
log "找到JAR文件: $JAR_FILE"
else
log "未找到JAR文件"
exit 1
fi
}

# 复制应用文件
copy_application_files() {
log "复制应用文件..."

# 复制JAR文件
cp $SOURCE_DIR/$JAR_FILE $PACKAGE_DIR/

if [ $? -eq 0 ]; then
log "JAR文件复制成功"
else
log "JAR文件复制失败"
exit 1
fi

# 复制配置文件
if [ -d "$SOURCE_DIR/src/main/resources" ]; then
cp -r $SOURCE_DIR/src/main/resources $PACKAGE_DIR/
log "配置文件复制成功"
fi

# 复制启动脚本
if [ -f "$SOURCE_DIR/start.sh" ]; then
cp $SOURCE_DIR/start.sh $PACKAGE_DIR/
chmod +x $PACKAGE_DIR/start.sh
log "启动脚本复制成功"
fi

# 复制停止脚本
if [ -f "$SOURCE_DIR/stop.sh" ]; then
cp $SOURCE_DIR/stop.sh $PACKAGE_DIR/
chmod +x $PACKAGE_DIR/stop.sh
log "停止脚本复制成功"
fi
}

# 生成部署脚本
generate_deployment_scripts() {
log "生成部署脚本..."

# 生成启动脚本
cat > $PACKAGE_DIR/start.sh << 'EOF'
#!/bin/bash
# 应用启动脚本
# @author 运维实战

APP_NAME="demo-app"
APP_VERSION="1.0.0"
JAR_FILE="demo-app-1.0.0.jar"
JAVA_OPTS="-Xms512m -Xmx1024m -XX:+UseG1GC"
LOG_DIR="/var/log/$APP_NAME"

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

# 启动应用
nohup java $JAVA_OPTS -jar $JAR_FILE > $LOG_DIR/app.log 2>&1 &

# 保存进程ID
echo $! > $APP_NAME.pid

echo "应用启动成功,PID: $!"
EOF

# 生成停止脚本
cat > $PACKAGE_DIR/stop.sh << 'EOF'
#!/bin/bash
# 应用停止脚本
# @author 运维实战

APP_NAME="demo-app"
PID_FILE="$APP_NAME.pid"

if [ -f "$PID_FILE" ]; then
PID=$(cat $PID_FILE)

if ps -p $PID > /dev/null; then
kill $PID
echo "应用停止成功,PID: $PID"
else
echo "应用进程不存在"
fi

rm -f $PID_FILE
else
echo "PID文件不存在"
fi
EOF

# 设置脚本权限
chmod +x $PACKAGE_DIR/start.sh
chmod +x $PACKAGE_DIR/stop.sh

log "部署脚本生成成功"
}

# 生成配置文件
generate_config_files() {
log "生成配置文件..."

# 生成应用配置文件
cat > $PACKAGE_DIR/application.yml << 'EOF'
# 应用配置文件
server:
port: 8080
servlet:
context-path: /api

spring:
application:
name: demo-app
profiles:
active: prod

# 日志配置
logging:
level:
root: INFO
com.demo: DEBUG
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
file:
name: /var/log/demo-app/app.log
max-size: 100MB
max-history: 30

# 数据库配置
spring:
datasource:
url: jdbc:mysql://localhost:3306/demo_db?useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
maximum-pool-size: 20
minimum-idle: 5
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000

# 监控配置
management:
endpoints:
web:
exposure:
include: "*"
metrics:
export:
prometheus:
enabled: true
EOF

log "配置文件生成成功"
}

# 压缩应用包
compress_package() {
log "压缩应用包..."

cd $TARGET_DIR

# 压缩应用包
tar -czf "${APP_NAME}-${APP_VERSION}-${PACKAGE_DATE}.tar.gz" ${APP_NAME}-${APP_VERSION}-${PACKAGE_DATE}

if [ $? -eq 0 ]; then
log "应用包压缩成功"

# 检查压缩包大小
PACKAGE_SIZE=$(ls -lh "${APP_NAME}-${APP_VERSION}-${PACKAGE_DATE}.tar.gz" | awk '{print $5}')
log "应用包大小: $PACKAGE_SIZE"

# 删除未压缩的目录
rm -rf ${APP_NAME}-${APP_VERSION}-${PACKAGE_DATE}
else
log "应用包压缩失败"
exit 1
fi
}

# 生成打包报告
generate_package_report() {
log "生成打包报告..."

REPORT_FILE="$TARGET_DIR/package_report_${PACKAGE_DATE}.txt"

echo "Java应用打包报告" > $REPORT_FILE
echo "打包时间: $(date)" >> $REPORT_FILE
echo "应用名称: $APP_NAME" >> $REPORT_FILE
echo "应用版本: $APP_VERSION" >> $REPORT_FILE
echo "打包文件: ${APP_NAME}-${APP_VERSION}-${PACKAGE_DATE}.tar.gz" >> $REPORT_FILE
echo "文件大小: $(ls -lh $TARGET_DIR/${APP_NAME}-${APP_VERSION}-${PACKAGE_DATE}.tar.gz | awk '{print $5}')" >> $REPORT_FILE
echo "================================" >> $REPORT_FILE

log "打包报告生成: $REPORT_FILE"
}

# 主函数
main() {
log "开始Java应用打包..."

# 创建目标目录
create_target_dir

# 编译应用
compile_application

# 打包应用
package_application

# 复制应用文件
copy_application_files

# 生成部署脚本
generate_deployment_scripts

# 生成配置文件
generate_config_files

# 压缩应用包
compress_package

# 生成打包报告
generate_package_report

log "Java应用打包完成"
}

# 执行主函数
main "$@"

4. 应用部署脚本

4.1 应用部署脚本

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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
#!/bin/bash
# java_app_deploy.sh - Java应用部署脚本
# @author 运维实战

# 配置参数
APP_NAME="demo-app"
APP_VERSION="1.0.0"
PACKAGE_FILE="/opt/target/demo-app-1.0.0.tar.gz"
DEPLOY_DIR="/opt/apps"
BACKUP_DIR="/opt/backup"
LOG_FILE="/var/log/java_app_deploy.log"

# 日志函数
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a $LOG_FILE
}

# 检查部署环境
check_deploy_environment() {
log "检查部署环境..."

# 检查Java环境
if ! command -v java > /dev/null 2>&1; then
log "Java环境未安装"
exit 1
fi

# 检查部署目录
if [ ! -d "$DEPLOY_DIR" ]; then
mkdir -p $DEPLOY_DIR
log "部署目录创建: $DEPLOY_DIR"
fi

# 检查备份目录
if [ ! -d "$BACKUP_DIR" ]; then
mkdir -p $BACKUP_DIR
log "备份目录创建: $BACKUP_DIR"
fi

# 检查应用包文件
if [ ! -f "$PACKAGE_FILE" ]; then
log "应用包文件不存在: $PACKAGE_FILE"
exit 1
fi

log "部署环境检查完成"
}

# 停止旧应用
stop_old_application() {
log "停止旧应用..."

APP_DIR="$DEPLOY_DIR/$APP_NAME"

if [ -d "$APP_DIR" ]; then
# 检查是否有运行中的应用
if [ -f "$APP_DIR/$APP_NAME.pid" ]; then
PID=$(cat $APP_DIR/$APP_NAME.pid)

if ps -p $PID > /dev/null; then
log "停止运行中的应用,PID: $PID"
kill $PID

# 等待应用停止
sleep 5

# 强制停止
if ps -p $PID > /dev/null; then
kill -9 $PID
log "强制停止应用,PID: $PID"
fi
fi
fi

# 备份旧应用
BACKUP_DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_PATH="$BACKUP_DIR/${APP_NAME}-${BACKUP_DATE}"

mv $APP_DIR $BACKUP_PATH
log "旧应用已备份到: $BACKUP_PATH"
else
log "未找到旧应用"
fi
}

# 部署新应用
deploy_new_application() {
log "部署新应用..."

# 创建应用目录
APP_DIR="$DEPLOY_DIR/$APP_NAME"
mkdir -p $APP_DIR

# 解压应用包
tar -xzf $PACKAGE_FILE -C $APP_DIR --strip-components=1

if [ $? -eq 0 ]; then
log "应用包解压成功"
else
log "应用包解压失败"
exit 1
fi

# 设置文件权限
chown -R app:app $APP_DIR
chmod +x $APP_DIR/*.sh

log "新应用部署成功"
}

# 配置应用
configure_application() {
log "配置应用..."

APP_DIR="$DEPLOY_DIR/$APP_NAME"

# 更新配置文件
if [ -f "$APP_DIR/application.yml" ]; then
# 根据环境更新配置
sed -i "s/localhost:3306/prod-db-server:3306/g" $APP_DIR/application.yml
sed -i "s/root: password/prod-user:prod-password/g" $APP_DIR/application.yml
sed -i "s/demo_db/prod_demo_db/g" $APP_DIR/application.yml

log "配置文件更新成功"
fi

# 更新启动脚本
if [ -f "$APP_DIR/start.sh" ]; then
# 根据环境更新JVM参数
sed -i "s/-Xms512m/-Xms1024m/g" $APP_DIR/start.sh
sed -i "s/-Xmx1024m/-Xmx2048m/g" $APP_DIR/start.sh

log "启动脚本更新成功"
fi
}

# 启动应用
start_application() {
log "启动应用..."

APP_DIR="$DEPLOY_DIR/$APP_NAME"

# 启动应用
cd $APP_DIR
./start.sh

if [ $? -eq 0 ]; then
log "应用启动成功"
else
log "应用启动失败"
exit 1
fi

# 等待应用启动
sleep 10

# 检查应用状态
if [ -f "$APP_DIR/$APP_NAME.pid" ]; then
PID=$(cat $APP_DIR/$APP_NAME.pid)

if ps -p $PID > /dev/null; then
log "应用运行正常,PID: $PID"
else
log "应用启动失败"
exit 1
fi
else
log "PID文件不存在"
exit 1
fi
}

# 验证部署
verify_deployment() {
log "验证部署..."

APP_DIR="$DEPLOY_DIR/$APP_NAME"

# 检查应用进程
if [ -f "$APP_DIR/$APP_NAME.pid" ]; then
PID=$(cat $APP_DIR/$APP_NAME.pid)

if ps -p $PID > /dev/null; then
log "应用进程检查通过,PID: $PID"
else
log "应用进程检查失败"
exit 1
fi
fi

# 检查应用端口
if netstat -tlnp | grep ":8080 " > /dev/null; then
log "应用端口检查通过"
else
log "应用端口检查失败"
exit 1
fi

# 检查应用健康状态
if command -v curl > /dev/null 2>&1; then
if curl -f http://localhost:8080/api/health > /dev/null 2>&1; then
log "应用健康检查通过"
else
log "应用健康检查失败"
exit 1
fi
fi

# 检查应用日志
if [ -f "/var/log/$APP_NAME/app.log" ]; then
ERROR_COUNT=$(grep -c "ERROR" /var/log/$APP_NAME/app.log)
if [ $ERROR_COUNT -eq 0 ]; then
log "应用日志检查通过"
else
log "应用日志检查失败,错误数量: $ERROR_COUNT"
fi
fi
}

# 生成部署报告
generate_deployment_report() {
log "生成部署报告..."

DEPLOY_DATE=$(date +%Y%m%d_%H%M%S)
REPORT_FILE="$BACKUP_DIR/deployment_report_${DEPLOY_DATE}.txt"

echo "Java应用部署报告" > $REPORT_FILE
echo "部署时间: $(date)" >> $REPORT_FILE
echo "应用名称: $APP_NAME" >> $REPORT_FILE
echo "应用版本: $APP_VERSION" >> $REPORT_FILE
echo "部署目录: $DEPLOY_DIR/$APP_NAME" >> $REPORT_FILE
echo "备份目录: $BACKUP_DIR" >> $REPORT_FILE
echo "================================" >> $REPORT_FILE

# 添加应用信息
echo "应用信息:" >> $REPORT_FILE
if [ -f "$DEPLOY_DIR/$APP_NAME/$APP_NAME.pid" ]; then
PID=$(cat $DEPLOY_DIR/$APP_NAME/$APP_NAME.pid)
echo "进程ID: $PID" >> $REPORT_FILE
fi

echo "端口状态:" >> $REPORT_FILE
netstat -tlnp | grep ":8080 " >> $REPORT_FILE

log "部署报告生成: $REPORT_FILE"
}

# 主函数
main() {
log "开始Java应用部署..."

# 检查部署环境
check_deploy_environment

# 停止旧应用
stop_old_application

# 部署新应用
deploy_new_application

# 配置应用
configure_application

# 启动应用
start_application

# 验证部署
verify_deployment

# 生成部署报告
generate_deployment_report

log "Java应用部署完成"
}

# 执行主函数
main "$@"

5. 性能监控脚本

5.1 性能监控脚本

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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
#!/bin/bash
# java_app_monitor.sh - Java应用性能监控脚本
# @author 运维实战

# 配置参数
APP_NAME="demo-app"
APP_DIR="/opt/apps/$APP_NAME"
LOG_FILE="/var/log/java_app_monitor.log"
MONITOR_INTERVAL=30

# 日志函数
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a $LOG_FILE
}

# 检查应用状态
check_application_status() {
log "检查应用状态..."

if [ -f "$APP_DIR/$APP_NAME.pid" ]; then
PID=$(cat $APP_DIR/$APP_NAME.pid)

if ps -p $PID > /dev/null; then
log "应用运行正常,PID: $PID"
return 0
else
log "应用进程不存在,PID: $PID"
return 1
fi
else
log "PID文件不存在"
return 1
fi
}

# 监控应用性能
monitor_application_performance() {
log "监控应用性能..."

if [ -f "$APP_DIR/$APP_NAME.pid" ]; then
PID=$(cat $APP_DIR/$APP_NAME.pid)

# 获取CPU使用率
CPU_USAGE=$(ps -p $PID -o %cpu --no-headers | awk '{print $1}')
log "CPU使用率: $CPU_USAGE%"

# 获取内存使用
MEMORY_USAGE=$(ps -p $PID -o %mem --no-headers | awk '{print $1}')
log "内存使用率: $MEMORY_USAGE%"

# 获取内存使用量
MEMORY_SIZE=$(ps -p $PID -o rss --no-headers | awk '{print $1}')
MEMORY_SIZE_MB=$((MEMORY_SIZE / 1024))
log "内存使用量: ${MEMORY_SIZE_MB}MB"

# 获取线程数
THREAD_COUNT=$(ps -p $PID -o nlwp --no-headers | awk '{print $1}')
log "线程数: $THREAD_COUNT"

# 获取文件描述符数
FD_COUNT=$(lsof -p $PID | wc -l)
log "文件描述符数: $FD_COUNT"
fi
}

# 监控应用日志
monitor_application_logs() {
log "监控应用日志..."

LOG_FILE_PATH="/var/log/$APP_NAME/app.log"

if [ -f "$LOG_FILE_PATH" ]; then
# 检查错误日志
ERROR_COUNT=$(tail -100 $LOG_FILE_PATH | grep -c "ERROR")
if [ $ERROR_COUNT -gt 0 ]; then
log "发现错误日志,数量: $ERROR_COUNT"
fi

# 检查警告日志
WARN_COUNT=$(tail -100 $LOG_FILE_PATH | grep -c "WARN")
if [ $WARN_COUNT -gt 0 ]; then
log "发现警告日志,数量: $WARN_COUNT"
fi

# 检查异常日志
EXCEPTION_COUNT=$(tail -100 $LOG_FILE_PATH | grep -c "Exception")
if [ $EXCEPTION_COUNT -gt 0 ]; then
log "发现异常日志,数量: $EXCEPTION_COUNT"
fi
else
log "日志文件不存在: $LOG_FILE_PATH"
fi
}

# 监控应用端口
monitor_application_ports() {
log "监控应用端口..."

# 检查应用端口
if netstat -tlnp | grep ":8080 " > /dev/null; then
log "应用端口8080正常"
else
log "应用端口8080异常"
fi

# 检查管理端口
if netstat -tlnp | grep ":9090 " > /dev/null; then
log "管理端口9090正常"
else
log "管理端口9090异常"
fi
}

# 监控应用健康状态
monitor_application_health() {
log "监控应用健康状态..."

# 检查健康端点
if command -v curl > /dev/null 2>&1; then
if curl -f http://localhost:8080/api/health > /dev/null 2>&1; then
log "应用健康检查通过"
else
log "应用健康检查失败"
fi

# 检查指标端点
if curl -f http://localhost:9090/actuator/metrics > /dev/null 2>&1; then
log "应用指标端点正常"
else
log "应用指标端点异常"
fi
else
log "curl命令不可用,跳过健康检查"
fi
}

# 监控系统资源
monitor_system_resources() {
log "监控系统资源..."

# 检查CPU使用率
CPU_USAGE=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)
log "系统CPU使用率: $CPU_USAGE%"

# 检查内存使用
MEMORY_USAGE=$(free | grep Mem | awk '{printf "%.2f", $3/$2 * 100.0}')
log "系统内存使用率: $MEMORY_USAGE%"

# 检查磁盘使用
DISK_USAGE=$(df -h / | awk 'NR==2{print $5}' | cut -d'%' -f1)
log "系统磁盘使用率: $DISK_USAGE%"

# 检查负载
LOAD_AVERAGE=$(uptime | awk -F'load average:' '{print $2}')
log "系统负载: $LOAD_AVERAGE"
}

# 生成监控报告
generate_monitor_report() {
log "生成监控报告..."

MONITOR_DATE=$(date +%Y%m%d_%H%M%S)
REPORT_FILE="/var/log/monitor_report_${MONITOR_DATE}.txt"

echo "Java应用监控报告" > $REPORT_FILE
echo "监控时间: $(date)" >> $REPORT_FILE
echo "应用名称: $APP_NAME" >> $REPORT_FILE
echo "================================" >> $REPORT_FILE

# 添加应用状态
echo "应用状态:" >> $REPORT_FILE
if [ -f "$APP_DIR/$APP_NAME.pid" ]; then
PID=$(cat $APP_DIR/$APP_NAME.pid)
echo "进程ID: $PID" >> $REPORT_FILE
echo "CPU使用率: $(ps -p $PID -o %cpu --no-headers | awk '{print $1}')%" >> $REPORT_FILE
echo "内存使用率: $(ps -p $PID -o %mem --no-headers | awk '{print $1}')%" >> $REPORT_FILE
fi

# 添加系统状态
echo "系统状态:" >> $REPORT_FILE
echo "CPU使用率: $(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1)%" >> $REPORT_FILE
echo "内存使用率: $(free | grep Mem | awk '{printf "%.2f", $3/$2 * 100.0}')%" >> $REPORT_FILE
echo "磁盘使用率: $(df -h / | awk 'NR==2{print $5}' | cut -d'%' -f1)%" >> $REPORT_FILE

log "监控报告生成: $REPORT_FILE"
}

# 主函数
main() {
log "开始Java应用监控..."

# 检查应用状态
if ! check_application_status; then
log "应用状态异常,停止监控"
exit 1
fi

# 监控应用性能
monitor_application_performance

# 监控应用日志
monitor_application_logs

# 监控应用端口
monitor_application_ports

# 监控应用健康状态
monitor_application_health

# 监控系统资源
monitor_system_resources

# 生成监控报告
generate_monitor_report

log "Java应用监控完成"
}

# 执行主函数
main "$@"

6. 故障处理脚本

6.1 故障诊断脚本

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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
#!/bin/bash
# java_app_troubleshoot.sh - Java应用故障诊断脚本
# @author 运维实战

# 配置参数
APP_NAME="demo-app"
APP_DIR="/opt/apps/$APP_NAME"
LOG_FILE="/var/log/java_app_troubleshoot.log"

# 日志函数
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a $LOG_FILE
}

# 检查应用状态
check_application_status() {
log "检查应用状态..."

if [ -f "$APP_DIR/$APP_NAME.pid" ]; then
PID=$(cat $APP_DIR/$APP_NAME.pid)

if ps -p $PID > /dev/null; then
log "应用进程运行中,PID: $PID"
return 0
else
log "应用进程未运行,PID: $PID"
return 1
fi
else
log "PID文件不存在"
return 1
fi
}

# 检查应用日志
check_application_logs() {
log "检查应用日志..."

LOG_FILE_PATH="/var/log/$APP_NAME/app.log"

if [ -f "$LOG_FILE_PATH" ]; then
log "日志文件存在: $LOG_FILE_PATH"

# 检查最近的错误
tail -50 $LOG_FILE_PATH | grep -i error

# 检查最近的异常
tail -50 $LOG_FILE_PATH | grep -i exception

# 检查最近的警告
tail -50 $LOG_FILE_PATH | grep -i warn
else
log "日志文件不存在: $LOG_FILE_PATH"
fi
}

# 检查应用配置
check_application_config() {
log "检查应用配置..."

# 检查配置文件
if [ -f "$APP_DIR/application.yml" ]; then
log "配置文件存在: $APP_DIR/application.yml"

# 检查关键配置
grep -E "^(server|spring|logging)" $APP_DIR/application.yml
else
log "配置文件不存在: $APP_DIR/application.yml"
fi

# 检查启动脚本
if [ -f "$APP_DIR/start.sh" ]; then
log "启动脚本存在: $APP_DIR/start.sh"

# 检查JVM参数
grep -E "^-X" $APP_DIR/start.sh
else
log "启动脚本不存在: $APP_DIR/start.sh"
fi
}

# 检查系统资源
check_system_resources() {
log "检查系统资源..."

# 检查内存使用
free -h

# 检查磁盘空间
df -h

# 检查CPU使用
top -bn1 | grep "Cpu(s)"

# 检查I/O使用
iostat -x 1 1
}

# 检查网络连接
check_network() {
log "检查网络连接..."

# 检查端口监听
netstat -tlnp | grep -E "(8080|9090)"

# 检查防火墙
if command -v ufw > /dev/null 2>&1; then
ufw status
elif command -v firewall-cmd > /dev/null 2>&1; then
firewall-cmd --list-all
else
iptables -L
fi
}

# 检查Java环境
check_java_environment() {
log "检查Java环境..."

# 检查Java版本
java -version

# 检查Java进程
ps aux | grep java | grep -v grep

# 检查Java堆内存
if [ -f "$APP_DIR/$APP_NAME.pid" ]; then
PID=$(cat $APP_DIR/$APP_NAME.pid)

if ps -p $PID > /dev/null; then
# 获取Java堆内存信息
jstat -gc $PID
fi
fi
}

# 修复常见问题
fix_common_issues() {
log "修复常见问题..."

# 检查并修复权限问题
if [ -d "$APP_DIR" ]; then
chown -R app:app $APP_DIR
chmod +x $APP_DIR/*.sh
log "修复应用目录权限"
fi

# 检查并修复日志目录权限
LOG_DIR="/var/log/$APP_NAME"

if [ -d "$LOG_DIR" ]; then
chown -R app:app $LOG_DIR
chmod 755 $LOG_DIR
log "修复日志目录权限"
fi
}

# 重启应用
restart_application() {
log "重启应用..."

# 停止应用
if [ -f "$APP_DIR/stop.sh" ]; then
cd $APP_DIR
./stop.sh
sleep 5
fi

# 启动应用
if [ -f "$APP_DIR/start.sh" ]; then
cd $APP_DIR
./start.sh
sleep 10
fi

# 检查应用状态
if check_application_status; then
log "应用重启成功"
return 0
else
log "应用重启失败"
return 1
fi
}

# 主函数
main() {
log "开始Java应用故障诊断..."

# 检查应用状态
if ! check_application_status; then
log "应用状态异常,尝试重启..."
restart_application
fi

# 检查应用日志
check_application_logs

# 检查应用配置
check_application_config

# 检查系统资源
check_system_resources

# 检查网络连接
check_network

# 检查Java环境
check_java_environment

# 修复常见问题
fix_common_issues

log "Java应用故障诊断完成"
}

# 执行主函数
main "$@"

7. 总结

Java应用迁移与部署运维是运维工作中的重要组成部分。通过本文的详细介绍,我们了解了:

  1. 应用迁移: 不同环境间的Java应用迁移
  2. 部署管理: 自动化部署和版本管理
  3. 配置管理: 环境配置和参数管理
  4. 性能监控: 应用性能监控和告警
  5. 故障处理: 常见故障的诊断和处理

通过合理的运维策略和工具,可以确保Java应用的稳定运行和高性能。


运维实战要点:

  • 应用迁移前做好备份,确保数据安全
  • 部署时注意环境配置和参数设置
  • 定期监控应用状态,及时发现问题
  • 性能调优需要根据实际业务场景进行
  • 故障处理要有完整的诊断流程

代码注解说明:

  • 日志函数: 统一日志格式,便于问题追踪
  • 错误处理: 完善的错误检查和异常处理
  • 配置管理: 灵活的配置参数管理
  • 监控告警: 实时监控和告警机制
  • 自动化运维: 脚本化运维提高效率