练习环境准备
在开始之前,请确保你有一台Linux环境(虚拟机、WSL、云服务器或本地Docker均可)。
# 如果没有练习环境,可以用Docker快速启动一个CentOS容器
docker run -it --name linux-lab centos:7 /bin/bash
第一阶段:初来乍到 - 连接与基础探索
1.1 SSH连接(模拟)
说明:如果你的环境是本机Docker或虚拟机,可跳过此步。如果是远程服务器,使用此命令。
# 连接到远程服务器(替换为实际IP和用户)
ssh root@192.168.1.100
# 退出连接
exit
1.2 文件与目录管理
# 1. 查看当前位置
pwd -全称 Print Working Directory 打印工作目录
# 2. 创建练习工作目录
mkdir -p ~/opt/java-lab/{logs,backup,config}
cd ~/opt/java-lab -全称是 Change Directory,切换目录
命令拆解:
| 组成部分 | 全称 | 含义 |
|---|---|---|
| mkdir | Make Directory | 创建目录(文件夹)。 |
| -p | Parents | 递归创建。如果父目录不存在,会一并创建;如果目录已存在,也不会报错。 |
| ~ | Home Directory | 当前用户的家目录(例如 /home/username)。 |
| {...} | Brace Expansion | 花括号扩展。这是 Shell 的一种语法,用于批量生成相似的路径或文件名。 |
# 3. 查看目录内容
ls -la
命令拆解:
| 组成部分 | 英文全称 | 中文含义 | 作用 |
|---|---|---|---|
| ls | List | 列出 | 基础命令,用于显示目录内容。 |
| -l | Long format | 长格式 | 显示详细信息(权限、所有者、大小、时间等)。 |
| -a | All | 所有 | 显示所有文件,包括以 . 开头的隐藏文件。 |
# 4. 创建、复制、移动、重命名文件
touch app.jar # 创建一个模拟的jar包文件
cp app.jar backup/app_backup.jar # 复制文件
mv app.jar old_app.jar # 重命名
mv old_app.jar deploy/ # 移动到目录
# 5. 删除文件(注意:rm -rf 非常危险,练习时请确认路径)
mkdir test_dir && touch test_dir/dummy.txt
rm -rf test_dir # 递归强制删除
命令拆解:
| 组成部分 | 英文全称 | 中文含义 | 作用 |
|---|---|---|---|
| rm | Remove | 删除 | 基础命令,用于删除文件或目录。 |
| -r | Recursive | 递归的 | 告诉系统"层层深入",删除目录及其内部所有的子目录和文件。 |
| -f | Force | 强制 | 告诉系统"强制执行",不询问确认,忽略不存在的文件。 |
| test_dir | (目标名称) | 目标目录 | 你要删除的那个文件夹的名字。 |
第二阶段:编写配置 - vim编辑器练习
# 1. 创建一个Spring Boot配置文件 application.yml
vim application.yml
# 2. 在vim中执行以下操作:
# - 按 i 进入插入模式
# - 输入以下内容:
# server:
# port: 8080
# logging:
# file: /var/log/app.log
# - 按 Esc 退出插入模式
# - 输入 :wq -全称Write and Quit 保存并退出
# 3. 查看刚创建的文件
cat application.yml - Concatenate,中文意思是连接
# 4. 练习不保存退出
vim application.yml
# 随意修改一些内容,然后按 Esc,输入 :q! 强制退出
第三阶段:日志查看大练兵
# 1. 创建一个大日志文件用于练习
for i in {1..500}; do echo "[$i] INFO - 这是第$i条日志" >> app.log; done
for i in {1..50}; do echo "[$i] ERROR - 模拟异常NullPointerException" >> app.log; done
命令拆解:
| 组成部分 | 含义 | 详细解释 |
|---|---|---|
for i in {1..500} |
循环头 | 创建一个循环。变量 i 会从数字 1 开始,每次增加 1,直到 500 结束。 |
do ... done |
循环体 | 包裹在中间的部分是每次循环都要执行的具体动作。 |
echo "..." |
输出 | 汉语翻译为"回声",打印双引号内的字符串。 |
$i |
变量引用 | 取出当前的数字。第一次是 1,第二次是 2... 最后一次是 500。 |
>> app.log |
追加重定向 | 关键点!将输出的内容追加 到 app.log 文件末尾。如果用 > 则会覆盖原文件。 |
# 2. 查看全部内容(小文件可用)
cat app.log | head -20 # 看前20行
命令拆解:
| 部分 | 含义 | 作用 |
|---|---|---|
cat app.log |
读取 app.log 文件内容 | 输出整个文件到屏幕 |
| **` | `(管道符)** | 将左边命令的输出传递给右边命令 |
head -20 |
显示前20行 | 只输出前20行内容 |
# 3. 查看尾部
tail -n 10 app.log # 最后10行
tail -f app.log # 实时追踪(按 Ctrl+C 退出)
命令拆解:
|-----------|-------------------------------------------------|
| tail | 尾巴 - 显示文件尾部内容 |
| -n | lines(行数)参数 |
| -f | follow (跟踪/跟随),文件有新内容写入 时,会自动显示在屏幕上 |
| app.log | 目标文件名 |
# 4. 搜索过滤
grep "ERROR" app.log
grep -n "NullPointer" app.log # 显示行号-n 是lines 是行的意思
拆解含义
| 部分 | 英文 | 中文含义 |
|---|---|---|
| Global | 全局 | 搜索整个文件,不局限于某一行 |
| Regular | 正则 | 支持正则表达式模式匹配 |
| Expression | 表达式 | 要搜索的文本模式 |
| 打印 | 输出匹配的行 |
通俗理解
grep = 在文本中全局 搜索符合正则表达式 的内容,并打印出匹配的行
# 5. 管道组合(关键技能)
cat app.log | grep "ERROR" | head -5
#6. 分页查看大文件
less app.log
# 在less中:按 Space 翻页,按 /ERROR 搜索,按 n 下一个,按 q 退出
命名哲学
| 命令 | 含义 | 特点 |
|---|---|---|
more |
更多 | 早期的分页查看器,功能较少 |
less |
更少 | 功能更强大的分页查看器,但名字叫"更少" |
💡 著名的 Unix 格言:"Less is more, more or less"(少即是多,或多或少)
意思是:
less命令的功能比more更强大,但名字却叫"更少"
官方定义
less 的全称可以理解为:"与 more 相反,但功能更多" 的分页查看器
含义 :用 less 分页查看器打开 app.log 文件
为什么用 less?
-
打开大文件时,不会一次性全部读入内存
-
可以上下翻页(more 只能向下)
-
支持搜索、跳转等多种功能
7. 实时监控日志中的错误(模拟)
新开一个终端窗口执行:echo "新的ERROR日志" >> app.log
原窗口执行:
tail -f app.log | grep --line-buffered "ERROR"
| 部分 | 全称/含义 |
|---|---|
grep |
Global Regular Expression Print - 全局正则表达式打印 |
--line-buffered |
行缓冲模式 - 每收到一行就立即输出,不等待缓冲区满 |
"ERROR" |
要搜索的字符串 |
| 隐含参数 | 从标准输入读取(因为前面有管道) |
第四阶段:部署真实Java项目
4.1 上传JAR包到服务器
# 在你的本地电脑上执行(不是在服务器上)
# 上传JAR包到服务器的~/java-lab/目录
scp /path/to/your/my-app.jar root@192.168.1.100:~/java-lab/
# 如果还没有JAR包,可以先从网络下载一个Demo JAR包
# wget https://github.com/spring-projects/spring-boot/raw/main/spring-boot-project/spring-boot-docs/spring-boot-docs-modular/build/libs/demo.jar -O my-app.jar
4.2 部署并后台运行
# 进入服务器上的工作目录
cd ~/java-lab
# 查看JAR包是否上传成功
ls -lh *.jar
# 后台运行真实Java应用
# - -Dserver.port=8081:指定端口(避免冲突)
# - -Xms256m -Xmx512m:设置JVM堆内存
# - 日志输出到 logs/app.log
nohup java -Dserver.port=8081 -Xms256m -Xmx512m -jar my-app.jar > logs/app.log 2>&1 &
# 保存进程ID,方便后续管理
echo $! > app.pid
echo "应用已启动,PID: $(cat app.pid)"
# 验证进程是否正常运行
ps -ef | grep java
# 或使用jps(需要JDK环境)
jps -l
4.3 验证应用是否可访问
# 如果应用有HTTP接口,可以用curl测试
curl http://localhost:8081/actuator/health
# 或通用的端口监听检查
netstat -tulnp | grep 8081
4.4 查看真实应用日志
# 查看启动日志
tail -50 logs/app.log
# 实时追踪日志
tail -f logs/app.log
# 搜索错误日志
grep -i "error" logs/app.log
grep -i "exception" logs/app.log
# 查看特定时间段的日志(假设日志格式包含时间)
grep "2025-05-04 10:" logs/app.log
4.5 进程管理
# 查看Java进程
ps -ef | grep my-app.jar
ps aux | grep java
# 优雅停止(推荐)
kill -15 $(cat app.pid)
# 强制停止(慎用,可能导致数据丢失)
# kill -9 $(cat app.pid)
# 重新启动
nohup java -Dserver.port=8081 -Xms256m -Xmx512m -jar my-app.jar > logs/app.log 2>&1 &
echo $! > app.pid
第五阶段:权限与用户管理
# 1. 查看文件权限
ls -l my-app.jar
# 2. 修改权限(JAR包通常不需要执行权限,但可以练习)
chmod 755 my-app.jar # rwxr-xr-x
# 3. 用户管理(需要root权限)
# 创建一个专门运行Java应用的用户
sudo useradd -m javauser
# 修改文件所有者
sudo chown javauser:javauser my-app.jar
sudo chown -R javauser:javauser logs/
ls -l
第六阶段:网络与磁盘排查
# 1. 磁盘空间查看
df -h
# 2. 目录大小查看
du -sh ~/java-lab/
du -sh ~/java-lab/logs/
# 3. 检查端口占用(重点)
netstat -tulnp | grep 8081
# 查看哪个进程占用了端口
lsof -i:8081 # 需要先安装 lsof: yum install lsof
# 4. 测试网络连通性
ping -c 3 google.com
curl -I http://localhost:8081
第七阶段:远程传输与压缩打包
# 1. 打包整个Java项目目录
tar -zcvf myapp-full.tar.gz ~/java-lab/
ls -lh myapp-full.tar.gz
# 2. 解压练习
mkdir extract_test
tar -zxvf myapp-full.tar.gz -C extract_test/
# 3. zip格式练习
zip -r myapp-backup.zip ~/java-lab/logs/
unzip -l myapp-backup.zip # 查看压缩包内容
unzip myapp-backup.zip -d unzip_test/
# 4. 从服务器下载日志到本地(在本地机器执行)
scp root@192.168.1.100:~/java-lab/logs/app.log ./app.log
# 5. 直接从网络下载依赖(在服务器上执行)
wget https://repo1.maven.org/maven2/org/springframework/boot/spring-boot/2.7.18/spring-boot-2.7.18.jar
第八阶段:进阶故障排查(使用真实JVM工具)
前提:服务器上已安装JDK(不仅仅是JRE),且Java应用正在运行。
8.1 jps - 查看Java进程
# 列出所有Java进程
jps -l
# 输出示例:
# 12345 my-app.jar
# 67890 sun.tools.jps.Jps
# 获取你应用的PID
APP_PID=$(jps -l | grep my-app | awk '{print $1}')
echo $APP_PID
8.2 jstat - 查看GC情况
# 每1秒输出一次GC统计,共输出5次
jstat -gcutil $APP_PID 1000 5
# 重点关注:
# - YGC: Young GC次数
# - FGC: Full GC次数
# - FGCT: Full GC总耗时
# 持续监控(按Ctrl+C停止)
jstat -gcutil $APP_PID 2000
8.3 jstack - 查看线程堆栈
# 生成线程快照(排查死锁、线程阻塞)
jstack $APP_PID > thread_dump_$(date +%Y%m%d_%H%M%S).txt
# 查看文件内容
less thread_dump_*.txt
# 快速检查是否有死锁
jstack $APP_PID | grep -A 10 "deadlock"
8.4 top + jstack 定位CPU飙升问题
# 1. 用top查看哪个Java进程占用CPU高
top -c
# 2. 按 H 键显示线程(在top界面中)
# 找到CPU占用最高的线程ID(TID)
# 3. 假设TID=12345,转换为16进制
printf "%x\n" 12345
# 输出: 3039
# 4. 用jstack查看该线程的堆栈
jstack $APP_PID | grep -A 20 "0x3039"
8.5 jmap - 查看堆内存
警告 :
jmap -dump:live会触发Full GC,导致应用暂停,生产环境谨慎使用!
# 查看堆内存概况
jmap -heap $APP_PID
# 查看堆中对象统计(不会STW,相对安全)
jmap -histo $APP_PID | head -30
# 生成堆转储文件(会触发Full GC,谨慎!)
jmap -dump:live,format=b,file=heap_$(date +%Y%m%d).hprof $APP_PID
# 生成的文件可以用MAT、VisualVM等工具分析
第九阶段:补充命令练习
9.1 软链接和环境变量
# 1. 创建软链接(方便版本切换)
ln -s ~/java-lab/my-app.jar ~/current-app.jar
ls -la ~/ | grep current
# 2. 查看历史命令
history | tail -20
# 3. 搜索历史(Ctrl + R 交互式搜索)
# 按 Ctrl + R,然后输入 "grep"
# 4. 环境变量练习
export JAVA_OPTS="-Xms256m -Xmx512m"
echo $JAVA_OPTS
# 持久化配置(写入bashrc)
echo 'export JAPP_HOME=~/java-lab' >> ~/.bashrc
source ~/.bashrc
echo $JAPP_HOME
9.2 定时任务 - 自动重启/清理日志
# 1. 创建一个重启脚本
cat > scripts/restart_app.sh << 'EOF'
#!/bin/bash
APP_DIR=/root/java-lab
PID_FILE=$APP_DIR/app.pid
LOG_FILE=$APP_DIR/logs/app.log
# 优雅停止
if [ -f $PID_FILE ]; then
kill -15 $(cat $PID_FILE)
sleep 5
fi
# 启动新进程
cd $APP_DIR
nohup java -Dserver.port=8081 -Xms256m -Xmx512m -jar my-app.jar >> $LOG_FILE 2>&1 &
echo $! > $PID_FILE
echo "$(date): 应用已重启" >> $LOG_FILE
EOF
chmod +x scripts/restart_app.sh
# 2. 创建日志清理脚本(保留最近7天日志)
cat > scripts/cleanup_logs.sh << 'EOF'
#!/bin/bash
find /root/java-lab/logs -name "*.log" -mtime +7 -delete
find /root/java-lab -name "*.hprof" -mtime +3 -delete
EOF
chmod +x scripts/cleanup_logs.sh
# 3. 添加定时任务
crontab -e
# 在编辑器中输入:
# 每天凌晨2点重启应用
# 0 2 * * * /root/java-lab/scripts/restart_app.sh
# 每天凌晨3点清理旧日志
# 0 3 * * * /root/java-lab/scripts/cleanup_logs.sh
# 4. 查看定时任务
crontab -l
9.3 screen会话练习
# 1. 创建新会话(适合长时间运行的监控任务)
screen -S app-monitor
# 2. 在会话中实时监控日志
cd ~/java-lab && tail -f logs/app.log
# 3. 分离会话:按 Ctrl + A,然后按 D
# 4. 列出所有会话
screen -ls
# 5. 重新连接
screen -r app-monitor
# 6. 在会话中执行Java应用(另一种运行方式,不用nohup)
screen -S java-app
java -jar my-app.jar
# 按 Ctrl+A+D 分离,应用继续运行
第十阶段:sed和awk练习(日志分析)
# 1. 准备日志文件(从真实应用日志中提取部分行)
head -100 logs/app.log > sample.log
# 2. sed - 替换和删除
# 替换IP地址
sed -i 's/192.168.1.100/127.0.0.1/g' sample.log
# 删除空行
sed -i '/^$/d' sample.log
# 3. awk - 按列处理
# 假设日志格式:2025-05-04 10:00:00 INFO [com.demo] 消息内容
# 统计每种日志级别的数量
awk '{print $3}' logs/app.log | sort | uniq -c | sort -nr
# 统计每个小时的请求数(如果日志有对应格式)
awk '{print $2}' logs/app.log | cut -d: -f1 | sort | uniq -c
第十一阶段:应用下线与清理
# 1. 优雅停止应用
kill -15 $(cat app.pid)
# 2. 确认进程已停止
ps -ef | grep my-app.jar
# 3. 清理定时任务
crontab -e
# 删除之前添加的任务行
# 4. 备份重要数据
tar -zcvf backup_$(date +%Y%m%d).tar.gz logs/ application.yml
# 5. 删除练习目录(慎用!)
# rm -rf ~/java-lab