上一个同事使用storm框架,在Bolt的prepare中执行定时任务,将缓存数据利用HDFS原生API定时写入HDFS。
最近将缓存系统改成了Redis,改写代码过程中,发现多线程调用HDFS的API进行追加写的代码各种报错。
下面将踩坑总结如下:
Hadoop的官方说明书明确指明,HDFS不可以多线程写,可以多线程读。
通过测试,使用redis公平锁的方式,可以将数据多线程写入HDFS中。
具体方式见最后代码。
关闭FileSystem的实例的时候,注意使用类锁的方式。
否则会报java.io.IOException: Filesystem closed 的错误
HDFS的租约机制(** lease holder**)
应在关闭文件流之后,再将公平锁打开。否则由于租约机制会导致追加异常。
报错如下:
org.apache.hadoop.ipc.RemoteException(org.apache.hadoop.hdfs.protocol.AlreadyBeingCreatedException): Failed to APPEND_FILE /文件目录 on because DFSClient_NONMAPREDUCE_-2020766150_154 is already the current lease holder.
HDFS只创建了文件没有数据的问题。
一定注意程序的最后先将文件流closed(flush并不能把数据刷到HDFS中)。
通常情况下关流和关闭FileSystem实例会放在Finally中执行,但是由于实时数据处理框架的特点,应注意调用完毕文件流之后就直接关闭文件流,以保证数据写入。
public class RedissonUtils {
public static RedissonClient client;
static{
client=getInstance();
}
public static RedissonClient getClient(){
if(client==null){
client = RedissonUtils.getInstance();
}
return client;
}
public static RedissonClient getInstance(){
Config config = new Config();
config.useClusterServers()
.addNodeAddress(Constant.REDIS_PTAH.split(","))
.setMasterConnectionMinimumIdleSize(10)
.setMasterConnectionPoolSize(64)
.setSlaveConnectionMinimumIdleSize(10)
.setSlaveConnectionPoolSize(64);
RedissonClient redisson = Redisson.create(config);
return redisson;
}
}
//拿到redis的客户端
RedissonClient client = RedissonUtils.getClient();
//redis的公平锁
RLock lock = client.getFairLock("durationLock");
boolean tryLock = lock.tryLock(30, 30, TimeUnit.SECONDS);
if (tryLock) {
//创建hdfs路径
fileSystem = FileSystemUtil.getFileSystem();
if (!fileSystem.exists(hdfsPath)) {
fileOutputStream = fileSystem.create(hdfsPath, false);
} else {
fileOutputStream = fileSystem.append(hdfsPath);
}
//传入写文件逻辑
fileOutputStream.close();
FileSystemUtil.shutdown();
lock.unlock();
}
public synchronized static void shutdown(){
if(fileSystem!=null){
try {
fileSystem.close();
fileSystem=null;
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class RedissonUtils {
public static RedissonClient client;
static{
client=getInstance();
}
public static RedissonClient getClient(){
if(client==null){
client = RedissonUtils.getInstance();
}
return client;
}
public static RedissonClient getInstance(){
Config config = new Config();
config.useClusterServers()
.addNodeAddress(Constant.REDIS_PTAH.split(","))
.setMasterConnectionMinimumIdleSize(10)
.setMasterConnectionPoolSize(64)
.setSlaveConnectionMinimumIdleSize(10)
.setSlaveConnectionPoolSize(64);
RedissonClient redisson = Redisson.create(config);
return redisson;
}
}
<dependency>
<groupId>org.redissongroupId>
<artifactId>redissonartifactId>
<version>2.8.1version>
dependency>