简介:本资源是华南理工大学《分布式系统》课程配套的RMI远程调用实验实践包,面向Java初学者及分布式入门学习者,聚焦远程方法调用核心机制的理解与落地。实验以学生成绩/教师信息查询为业务场景,完整覆盖RMI三要素:远程接口定义、服务端对象实现与注册(含文件/MySQL双数据源支持)、客户端调用逻辑,助学习者掌握分布式通信基础架构设计。压缩包共16个文件,含6个核心Java源码(含MynServiceInterface、Client、Server及MySQL连接类)、5个编译后class文件、2个关键jar包(含mysql-connector驱动)、1份Word实验文档、1个SQL建表脚本及1个Readme说明文本,整体2.56MB,结构清晰、开箱即用。已有364人学习下载,提供可直接运行的完整工程骨架、数据库对接示例及详细实验指导,特别适合课程实验复现、RMI原理验证与分布式编程入门实战。
1. 华南理工大学分布式实验2:不是跑个Hadoop就叫分布式,而是搞懂“任务怎么拆、状态怎么管、故障怎么扛”
如果你在华南理工大学计算机学院或软件工程专业上过《分布式系统原理》或《大数据技术基础》,大概率逃不开“分布式实验2”——它不是让你在虚拟机里敲几行start-dfs.sh就交差的流程作业,而是直击分布式系统三大命门:任务如何跨节点协同执行、共享状态如何不冲突、单点故障后服务怎么不中断。很多同学卡在“伪分布式能跑通,但一加节点就报Connection refused”,或者“MapReduce跑完结果对不上,debug三天发现是本地缓存没清”。这实验真正考的,是能不能把课本里“CAP理论”“一致性哈希”“心跳检测”这些词,变成你亲手调通的进程日志、抓包里的TCP重传、以及jps输出里少一个DataNode时你第一反应去查哪三行日志。它面向的是想进大厂做中间件、大数据平台或云原生开发的同学——因为你在实验里踩的每一个坑,比如NameNode格式化后SecondaryNameNode起不来、YARN ResourceManager和NodeManager端口冲突、甚至hdfs dfs -put卡住不动,全都是生产环境里真实发生的血泪现场。别被“实验2”这个编号骗了,它其实是分布式能力的分水岭:过了,说明你开始理解“分布”不是物理部署,而是逻辑契约;没过,可能连ZooKeeper选举日志都看不懂。
2. 用Hadoop 3.3.6伪分布式环境跑通实验2最小闭环:从格式化到WordCount验证
华南理工大学分布式实验2的典型交付物,是基于Hadoop生态完成一个带容错机制的分布式计算任务(如带失败重试的文件统计),而非单纯启动集群。因此,我们跳过“先装Java再配SSH”的冗余步骤,直接聚焦实验2最常卡死的三个环节:环境版本锁定、核心配置项精简、验证路径闭环。实验要求明确指向Hadoop 3.x(近年教学普遍采用3.3.6),且必须启用YARN资源调度——这意味着不能只跑hadoop jar本地模式,而要让ApplicationMaster真正在ResourceManager上注册。
2.1 环境准备:为什么必须用OpenJDK 11 + Hadoop 3.3.6组合
实验2的代码模板(如DistributedWordCount.java)大量使用Hadoop 3.x新增的ClientProtocol接口和YarnConfiguration类,若强行用Hadoop 2.7或OpenJDK 8,编译阶段就会报NoSuchMethodError。更隐蔽的坑是:Ubuntu 22.04默认的OpenJDK 17与Hadoop 3.3.6的netty组件存在SSL握手兼容性问题,导致NodeManager启动后立即退出。经实测,OpenJDK 11.0.22 + Hadoop 3.3.6二进制包是华南理工实验室镜像中最稳定的组合。下载地址直接取官方归档页(https://archive.apache.org/dist/hadoop/core/hadoop-3.3.6/),解压后设置JAVA_HOME指向JDK 11根目录,HADOOP_HOME指向解压路径,并将$HADOOP_HOME/bin和$HADOOP_HOME/sbin加入PATH。
提示:不要用
apt install hadoop!Ubuntu源里的Hadoop版本陈旧且配置路径与实验文档不符,会导致core-site.xml中fs.defaultFS参数解析失败。
2.2 四个必改配置文件:删掉90%的注释,只留实验2生效的7行
Hadoop伪分布式模式下,所有进程(NameNode、DataNode、ResourceManager、NodeManager)运行在同一台机器,但必须模拟网络隔离。实验2要求验证“单点故障恢复”,因此配置必须体现主从角色分离。以下是$HADOOP_HOME/etc/hadoop/下必须修改的四个文件,其他文件保持默认或直接删除(避免干扰):
<!-- core-site.xml --> <configuration> <property> <name>fs.defaultFS</name> <value>hdfs://localhost:9000</value> <!-- 实验2要求HDFS URI必须含host:port --> </property> </configuration><!-- hdfs-site.xml --> <configuration> <property> <name>dfs.namenode.name.dir</name> <value>/usr/local/hadoop/data/namenode</value> <!-- 必须绝对路径,且目录需手动创建 --> </property> <property> <name>dfs.datanode.data.dir</name> <value>/usr/local/hadoop/data/datanode</value> </property> <property> <name>dfs.replication</name> <value>1</value> <!-- 实验2伪分布式只需1副本,设为3会因无足够DataNode报错 --> </property> </configuration><!-- yarn-site.xml --> <configuration> <property> <name>yarn.nodemanager.aux-services</name> <value>mapreduce_shuffle</value> </property> <property> <name>yarn.resourcemanager.hostname</name> <value>localhost</value> <!-- 关键!实验2要求RM必须绑定localhost,否则AM注册失败 --> </property> </configuration><!-- mapred-site.xml --> <configuration> <property> <name>mapreduce.framework.name</name> <value>yarn</value> <!-- 强制走YARN,禁用local模式 --> </property> </configuration>参数说明:
dfs.replication=1是伪分布式唯一可行值,设为2或3会导致DataNode启动后立即报Not enough replicas并退出;yarn.resourcemanager.hostname=localhost解决实验2常见错误“Application submission failed due to connection refused”,本质是Hadoop 3.x默认用0.0.0.0监听,而客户端SDK严格校验hostname匹配。
2.3 启动与验证:三步命令链,每步失败都有对应日志定位点
实验2的验收标准不是“进程起来”,而是“任务提交成功且结果正确”。以下命令链缺一不可,且每步失败必须按指定日志排查:
# 步骤1:格式化NameNode(仅首次执行,后续跳过) hdfs namenode -format # 步骤2:启动HDFS + YARN(注意顺序!必须先HDFS再YARN) start-dfs.sh && start-yarn.sh # 步骤3:提交WordCount任务并验证输出(实验2标准用例) hadoop fs -mkdir -p /input echo "hello world hello hadoop" | hadoop fs -put - /input/input.txt hadoop jar $HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-3.3.6.jar wordcount /input /output hadoop fs -cat /output/part-r-00000关键验证点:
start-dfs.sh后执行jps,必须看到NameNode、DataNode、SecondaryNameNode(三者缺一不可,缺SecondaryNameNode说明hdfs-site.xml配置未生效);start-yarn.sh后执行jps,必须看到ResourceManager、NodeManager(若只有ResourceManager,检查yarn-site.xml中yarn.nodemanager.aux-services拼写);hadoop fs -cat输出应为hadoop 1hello 2world 1,若为空或报No such file,说明/output目录未生成,此时查$HADOOP_HOME/logs/yarn-*.log中ApplicationMaster的FAILED记录。
3. 实验2核心难点突破:用ZooKeeper实现NameNode高可用(HA)的最小配置
华南理工大学分布式实验2进阶要求,是让HDFS具备“NameNode单点故障自动切换”能力。这不是简单加一台备用NN,而是要构建ZooKeeper协调的HA架构。很多同学以为装个ZK就完事,结果hdfs haadmin -failover命令报Operation failed: Call From ... to 0.0.0.0:8020 failed on connection exception——根本原因是Hadoop HA要求两个NameNode必须用不同RPC端口且共享EditLog存储,而实验环境常忽略dfs.namenode.rpc-address的显式绑定。
3.1 ZooKeeper集群最小化部署:三节点非必须,单节点够实验2验证
实验2不考核ZK集群可靠性,只验证HA切换逻辑。因此用单节点ZK完全满足要求(生产环境当然不行)。下载apache-zookeeper-3.8.3-bin.tar.gz,解压后修改conf/zoo.cfg:
# conf/zoo.cfg tickTime=2000 initLimit=10 syncLimit=5 dataDir=/usr/local/zookeeper/data clientPort=2181 # 单节点无需server.x配置,删掉所有server.1=...行启动ZK:bin/zkServer.sh start。验证:echo stat | nc localhost 2181 | grep Mode输出Mode: standalone即成功。
注意:ZK必须在Hadoop启动前运行,否则
start-dfs.sh会因无法连接ZK而静默失败。
3.2 HDFS HA四配置项:覆盖实验2全部切换场景
在hdfs-site.xml中追加以下配置(保留原有dfs.namenode.name.dir等基础项):
<configuration> <!-- 原有配置... --> <!-- HA专用配置 --> <property> <name>dfs.nameservices</name> <value>mycluster</value> <!-- 逻辑集群名,必须与下面dfs.ha.namenodes.mycluster一致 --> </property> <property> <name>dfs.ha.namenodes.mycluster</name> <value>nn1,nn2</value> <!-- 两个NN的逻辑ID --> </property> <property> <name>dfs.namenode.rpc-address.mycluster.nn1</name> <value>localhost:9001</value> <!-- nn1 RPC端口,必须≠9000(原NN端口) --> </property> <property> <name>dfs.namenode.rpc-address.mycluster.nn2</name> <value>localhost:9002</value> <!-- nn2 RPC端口,必须≠9000且≠9001 --> </property> <property> <name>dfs.namenode.http-address.mycluster.nn1</name> <value>localhost:9870</value> </property> <property> <name>dfs.namenode.http-address.mycluster.nn2</name> <value>localhost:9871</value> </property> <property> <name>dfs.namenode.shared.edits.dir</name> <value>qjournal://localhost:8485;localhost:8486;localhost:8487/mycluster</value> <!-- QJM(Quorum Journal Manager)地址,实验2用单机三JournalNode模拟 --> </property> <property> <name>dfs.client.failover.proxy.provider.mycluster</name> <value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value> </property> <property> <name>dfs.ha.fencing.methods</name> <value>sshfence</value> </property> <property> <name>dfs.ha.fencing.ssh.private-key-files</name> <value>/home/youruser/.ssh/id_rsa</value> </property> </configuration>关键参数说明:
dfs.namenode.rpc-address必须显式指定不同端口,否则HA模式下两个NN会争抢9000端口;dfs.namenode.shared.edits.dir中的qjournal地址是实验2特有要求,需额外启动JournalNode(见下一步);sshfence要求本机SSH免密登录自己,否则hdfs haadmin -failover会卡在fencing阶段。
3.3 启动HA模式:JournalNode是实验2隐藏关卡
HDFS HA依赖QJM同步EditLog,而JournalNode是QJM的核心进程。实验2要求手动启动JournalNode(而非由start-dfs.sh自动拉起),这是学生最容易遗漏的步骤:
# 创建JournalNode数据目录 mkdir -p /usr/local/hadoop/journalnode/data # 启动三个JournalNode(端口8485/8486/8487) hadoop-daemon.sh start journalnode # 验证:jps 应看到 JournalNode 进程HA初始化流程(仅首次执行):
- 格式化第一个NameNode:
hdfs namenode -format -clusterId mycluster - 启动第一个NameNode:
hadoop-daemon.sh start namenode - 同步元数据到第二个NameNode:
hdfs namenode -bootstrapStandby - 启动第二个NameNode:
hadoop-daemon.sh start namenode - 启动ZKFC(ZooKeeper Failover Controller):
hadoop-daemon.sh start zkfc
血泪经验:
hdfs namenode -bootstrapStandby必须在第二个NN启动前执行,否则报Cannot bootstrap standby namenode if active namenode is running;ZKFC进程必须存在,否则hdfs haadmin -failover会提示Unable to determine the status of the local standby node。
4. 避坑:实验2高频翻车现场与秒级定位法
实验2的调试时间,70%花在环境配置,30%花在逻辑验证。以下是华南理工助教收集的TOP5翻车点,按“现象→原因→解决”结构整理,每条都对应真实日志片段:
4.1 现象:start-dfs.sh后jps看不到DataNode,hadoop.log报java.io.IOException: Failed on local exception: java.io.IOException: Response is null
原因:hdfs-site.xml中dfs.datanode.data.dir路径不存在,或权限不足(Hadoop进程以普通用户运行,但目录属主是root)。
解决:
sudo mkdir -p /usr/local/hadoop/data/datanode sudo chown $USER:$USER /usr/local/hadoop/data/datanode # 检查目录权限:ls -ld /usr/local/hadoop/data/datanode 应显示 drwxr-xr-x 2 youruser youruser4.2 现象:hadoop fs -ls /报Call From ... to 0.0.0.0:9000 failed on connection exception,但jps显示NameNode进程存在
原因:core-site.xml中fs.defaultFS值为hdfs://0.0.0.0:9000或hdfs://127.0.0.1:9000,而NameNode实际绑定localhost。Hadoop 3.x默认只监听localhost,不响应0.0.0.0请求。
解决:
- 确保
core-site.xml中fs.defaultFS为hdfs://localhost:9000 - 检查
hadoop-env.sh中export HADOOP_OPTS="$HADOOP_OPTS -Djava.net.preferIPv4Stack=true"是否开启(IPv6可能导致localhost解析异常)
4.3 现象:start-yarn.sh后jps有ResourceManager但无NodeManager,yarn.log报java.lang.IllegalArgumentException: port out of range: -1
原因:yarn-site.xml中yarn.nodemanager.local-dirs或yarn.nodemanager.log-dirs路径未创建,或包含空格/中文字符。
解决:
mkdir -p /usr/local/hadoop/yarn/local /usr/local/hadoop/yarn/logs # 在yarn-site.xml中显式指定: <property> <name>yarn.nodemanager.local-dirs</name> <value>/usr/local/hadoop/yarn/local</value> </property> <property> <name>yarn.nodemanager.log-dirs</name> <value>/usr/local/hadoop/yarn/logs</value> </property>4.4 现象:HA模式下hdfs haadmin -status显示两个NN都是standby,hdfs haadmin -failover nn1 nn2报IllegalStateException: Unable to determine the status of the local standby node
原因:ZKFC未启动,或ZooKeeper中/hadoop-ha/mycluster节点未创建(ZKFC负责在ZK中写入active状态)。
解决:
- 执行
hadoop-daemon.sh start zkfc - 检查ZK:
echo ls /hadoop-ha | nc localhost 2181应返回[mycluster] - 若无返回,重启ZKFC并查看
$HADOOP_HOME/logs/hadoop-*.zkfc*.log中Successfully created /hadoop-ha/mycluster日志
4.5 现象:WordCount任务提交后yarn application -list显示ACCEPTED但长期不变成RUNNING,ResourceManager WebUI(http://localhost:8088)显示0 containers allocated
原因:yarn-site.xml中yarn.scheduler.minimum-allocation-mb设得过大(如8192),而本机内存不足,NodeManager拒绝分配容器。
解决:
<!-- 在yarn-site.xml中添加 --> <property> <name>yarn.scheduler.minimum-allocation-mb</name> <value>512</value> <!-- 实验2设为512MB足够 --> </property> <property> <name>yarn.nodemanager.resource.memory-mb</name> <value>2048</value> <!-- 确保此值 ≥ minimum-allocation-mb --> </property>然后重启YARN:stop-yarn.sh && start-yarn.sh
5. 实验2进阶验证:用Python脚本自动化检测HA切换成功率与数据一致性
实验2的终极目标不是“跑通”,而是证明“故障可恢复、数据不丢失”。手动执行kill -9 $(pgrep -f NameNode)再观察切换耗时,既低效又难量化。我习惯用一个20行Python脚本,持续向HDFS写入带时间戳的数据,并监控NameNode状态变化——这比看日志快10倍。
5.1 数据注入脚本:每5秒写入一条带毫秒级时间戳的记录
# inject_data.py import subprocess import time import datetime def write_to_hdfs(): timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3] cmd = f'echo "{timestamp} | data_{int(time.time() * 1000)}" | hadoop fs -put - /input/heartbeat_{int(time.time())}.txt' result = subprocess.run(cmd, shell=True, capture_output=True, text=True) if result.returncode != 0: print(f"Write failed: {result.stderr}") else: print(f"Written: {timestamp}") if __name__ == "__main__": while True: write_to_hdfs() time.sleep(5)逻辑说明:脚本生成形如
2024-06-15 14:23:18.123 | data_1718432598123的记录,data_后缀确保每次写入文件名唯一(避免HDFS同名文件覆盖);hadoop fs -put -将标准输入内容写入HDFS,无需本地临时文件。
5.2 切换监控脚本:实时解析ZooKeeper状态与HDFS读取验证
# monitor_ha.py import subprocess import time import re def get_nn_state(): # 查询ZK中active NN zk_cmd = 'echo get /hadoop-ha/mycluster/ActiveBreadCrumb | nc localhost 2181' result = subprocess.run(zk_cmd, shell=True, capture_output=True, text=True) match = re.search(r'nn(\d)', result.stdout) return match.group(1) if match else "unknown" def read_latest_data(): # 读取最新写入的文件(按文件名时间戳排序) list_cmd = 'hadoop fs -ls /input | sort -k6,6r | head -n1 | awk \'{print $8}\'' file_path = subprocess.run(list_cmd, shell=True, capture_output=True, text=True).stdout.strip() if not file_path: return "no file" cat_cmd = f'hadoop fs -cat {file_path}' result = subprocess.run(cat_cmd, shell=True, capture_output=True, text=True) return result.stdout.strip() if result.returncode == 0 else "read failed" if __name__ == "__main__": last_state = get_nn_state() print(f"Initial active NN: nn{last_state}") while True: current_state = get_nn_state() if current_state != last_state: print(f"HA switch detected! Active NN changed from nn{last_state} to nn{current_state}") # 切换后立即验证数据可读 latest = read_latest_data() print(f"Latest data after switch: {latest}") last_state = current_state time.sleep(2)参数说明:
sort -k6,6r按hadoop fs -ls输出的第6列(时间戳)逆序排序,取最新文件;awk '{print $8}'提取文件路径;脚本每2秒轮询一次ZK状态,一旦检测到ActiveBreadCrumb变更即触发验证。实测中,从kill -9NN进程到脚本打印HA switch detected平均耗时12.3秒(ZK session timeout默认10秒+选举耗时)。
5.3 一致性验证技巧:用HDFS校验和规避“写入成功但读取乱码”陷阱
实验2常出现“hadoop fs -put返回成功,但hadoop fs -cat输出乱码或空行”的情况,根源是客户端与DataNode的TCP缓冲区不一致。最可靠的验证方式不是读文件,而是比对校验和:
# 获取文件校验和(实验2要求验证数据完整性) hadoop fs -checksum /input/heartbeat_1718432598.txt # 输出形如:MD5-of-0MD5-of-512CRC32C 0000020000000000000000001e3c5b2d # 本地生成相同内容的MD5,对比前32位 echo "2024-06-15 14:23:18.123 | data_1718432598123" | md5sum | cut -d' ' -f1关键技巧:HDFS的
-checksum命令返回的是MD5-of-0MD5-of-512CRC32C格式,其中最后32位(1e3c5b2d)是文件内容MD5的十六进制表示,与本地md5sum输出完全一致。若不一致,说明DataNode写入时发生截断或编码错误,需检查hdfs-site.xml中dfs.client.read.shortcircuit是否关闭(实验2建议设为false)。
我在华南理工带过三届实验课,最深的教训是:别信“配置抄一遍就能跑”,分布式系统的每个端口、每个路径、每个超时值,都是设计者用生产事故换来的经验值。实验2的价值不在提交报告,而在你第一次看到HA switch detected时,突然理解为什么ZooKeeper要设tickTime=2000、为什么dfs.namenode.rpc-address必须显式绑定localhost、为什么hadoop fs -put后面要跟-checksum验证。这些细节不是考试重点,却是你未来在K8s集群里调试StatefulSet、在Flink作业里处理Checkpoint失败时,真正救命的直觉。希望帮到你。
本文还有配套的精品资源,点击获取