HDFS多线程追加写数据踩坑

版权声明:转载请联系作者。 https://blog.csdn.net/weixin_44252761/article/details/89517393

上一个同事使用strom框架,在Bolt的prepare中执行定时任务,将缓存数据利用HDFS原生API定时写入HDFS。
最近将缓存系统改成了Redis,改写代码过程中,发现多线程调用HDFS的API进行追加写的代码各种报错。
下面将踩坑总结如下:
Hadoop的官方说明书明确指明,HDFS不可以多线程写,可以多线程读。
通过测试,使用redis公平锁的方式,可以将数据多线程写入HDFS中。
具体方式见最后代码。

注意事项:
  1. 关闭FileSystem的实例的时候,注意使用类锁的方式。
    否则会报java.io.IOException: Filesystem closed 的错误

  2. 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.

  3. 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;
   }
}
使用公平锁式锁向hdfs写入数据
//拿到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();
        }
    }
}
Redission客户端
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;
   }
}
Redisson依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>2.8.1</version>
</dependency>

猜你喜欢

转载自blog.csdn.net/weixin_44252761/article/details/89517393