hadoop分散モードの初期セットアップが完了しました。これはコマンドラインまたはWebインターフェースから使用できるようです。次のステップに進むことができます。これは、さらに検証されたと言えるか、HDFS関連の学習であると言えます。
HDFSは、ファイルを追加、削除、変更、およびチェックできる分散ファイルストレージシステムです。ネイティブサポートには、基本的なコマンドラインがあり、その後、さまざまな言語のクライアントがあります。
このパートでは、主に基本的な操作を記録して練習し、以前の環境のインストールが使用可能かどうかをさらに確認します。
環境の説明
次の内容はhadoop3.1.3
バージョンに基づいています。
コマンドライン操作
ディレクトリを作成する
ファイルシステムが現実のものとなる場合、それは当然ディレクトリとファイルであるため、最初はファイルディレクトリの作成です。
hdfs dfs -mkdir /test1
多くのオンラインチュートリアルがのhadoop
代わりにここに記述されていますhdfs
が、コマンドの古いバージョンである可能性があります。このhadoop
操作を使用すると、可能ですが、プロンプトが出力されて置き換えられhdfs
ます。
ファイルとディレクトリを一覧表示する
上記でディレクトリが作成されました。ディレクトリが正常に作成されたかどうかをさらに確認するには、次のコマンドを実行してファイルディレクトリを一覧表示します。
hdfs dfs -ls /
上記は、hdfsファイルシステムのルートディレクトリ内のディレクトリとファイルを一覧表示することを意味します。これは、上記のコマンドが実行されるシステムのファイルシステムではなく、hdfsファイルシステムであることを繰り返し強調する必要があります。
私と同じように、以前にいくつかのディレクトリを作成したため、上記のコマンドを実行すると、次のコンテンツが表示されます。
Found 4 items
drwxr-xr-x - root supergroup 0 2020-08-07 22:23 /demo1
drwxr-xr-x - root supergroup 0 2020-08-07 01:27 /foodir
drwxr-xr-x - root supergroup 0 2020-08-10 18:56 /hbase
drwxr-xr-x - root supergroup 0 2020-08-10 19:15 /test1
Linuxでのファイル作成
ディレクトリを作成したら、ファイルをhdfsにアップロードできます。これは、Linuxでのファイル作成の簡単な操作の拡張です。まず、echo
前述のように、echoは次の方法でファイルを作成できます。
echo "test" >test2.txt
echo "test1" >> test3.txt
上記の2つの操作では、ファイルが存在しない場合、ファイルが作成され、コンテンツが書き込まれます。ファイルが存在する場合、2番目のファイルがファイルに追加され、最初のファイルはファイル内の元のコンテンツを上書きします。
ただし、上記のエコーでファイルを作成するには、書き込む内容が必要です。このecho test2.txt
場合、ファイルは作成されず、コンソールに出力されるだけです。空のファイルを作成するには、touch test2.txt
この操作が必要です。
hdfsへのファイルのアップロード
ファイルを入手したら、ローカルファイルをhdfsファイルシステムにアップロードできます。
hdfs dfs -put test3.txt /test1/
上記のコマンドは、コマンドが実行される現在のディレクトリのtest3.txtファイルをhdfsの/ test1ディレクトリにアップロードすることを意味します。
ここで、アップロードコマンドを再度実行すると、アップロードできなくなったことがわかりますが、エラーが報告されます。
2020-08-10 19:35:54,862 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
put: `/test1/test3.txt': File exists
ファイルがすでに存在している場合、再度アップロードすることはできません。これには、実際にはhdfsファイルストレージの設計思想が含まれます。
HDFSは分散ファイルオペレーティングシステムであり、最下層は多数の小さなファイルで構成され、これらの小さなファイルは大きなファイルをバイト配列に解析し、それらをバイトで分割します。
最後の小さなファイルを除いて、他の小さなファイルのバイト数は同じであり、各小さなファイルにはオフセットがあり、それによって大量のデータの後続の計算パフォーマンスが高くなります。
この設計のため、ファイルを変更する場合、各小さなファイルのオフセットとバイトの再編成と分割が含まれる可能性があり、ここで多くの問題が発生します。
したがって、hdfsのファイル変更は、実際にはファイルの末尾にコンテンツを追加することのみをサポートし、既存のコンテンツへの変更を許可しません。そのため、ファイルがすでに存在する場合、ファイルを再度アップロードすることはできず、基礎となるレイヤーは想像されるほど簡単に上書きされません。
ファイル内容を追加
上記のように、hdfsのファイル変更は実際には最後の追加のみをサポートします。これは、最後に追加する知恵が最後の小さなファイルの処理に関係し、すべての小さなファイルを操作することはできないためです。ファイルコンテンツを追加する操作は次のとおりです。
hdfs dfs -appendToFile test3.txt /test1/test3.txt
hdfsでファイルのコンテンツを表示する
ファイルが正常にアップロードされたら、上のファイルリストビューを使用して、アップロードが成功したかどうかを確認できますが、これはファイルの一般的な情報しか表示できません。ファイルのコンテンツが書き込まれたかどうかを確認する場合は、ファイルのコンテンツを表示する操作を使用できます。
hdfs dfs -cat /test1/test3.txt
ここで操作すると、以下の情報が出力され、最後の行がファイルの内容です。
2020-08-10 19:31:00,019 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
2020-08-10 19:31:02,046 INFO sasl.SaslDataTransferClient: SASL encryption trust check: localHostTrusted = false, remoteHostTrusted = false
test1
ファイルを削除する
上記に追加・変更があり、ファイルシステムとしては当然、以下のように操作を削除する必要があります。
hdfs dfs -rm /test1/test2.txt
上記は、hdfsの基本的な操作を実行するためのhadoopに付属するコマンドラインに基づいています。繰り返しますが、上のディレクトリにあるコマンドを使用するには、hadoopに関連する環境変数を設定する必要があります。
Javaプログラマーとして、hadoopの学習がコマンドラインに限定されている場合、それは当然信頼できません。そのため、Javaクライアントでも上記のコマンドラインと同様の簡単な操作をいくつか実行してみました。
以下の内容は、基本的にインターネット上の内容をベースにしており、操作例が単純すぎるため、基本的な検証と記録のみで、変更点は少なく、説明もあまりありません。
Javaオペレーション
依存パッケージのインポート
1つは依存パッケージのインポートです。私のプロジェクトはSpringBootプロジェクトですが、SpringBoot関連の正式なHadoopスターターがないため、Hadoop jarを直接導入する必要があります。依存関係の競合のため、除外され、最終的なMaven構成次のように:
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>3.1.3</version>
<exclusions>
<exclusion> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId></exclusion>
<exclusion> <groupId>log4j</groupId> <artifactId>log4j</artifactId> </exclusion>
<exclusion> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> </exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>3.1.3</version>
<exclusions>
<exclusion> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId></exclusion>
<exclusion> <groupId>log4j</groupId> <artifactId>log4j</artifactId> </exclusion>
<exclusion> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> </exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>3.1.3</version>
<exclusions>
<exclusion> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId></exclusion>
<exclusion> <groupId>log4j</groupId> <artifactId>log4j</artifactId> </exclusion>
<exclusion> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> </exclusion>
</exclusions>
</dependency>
hdfsファイルシステムオブジェクトを取得する
Javaでhdfsを操作するには、最初にファイルオブジェクトを取得し、URLやユーザー名などを実行する必要があります。実際のプロジェクトで必要な場合は、多くの接続構成を追加する必要があります。基本的な使用可能な単純なコードは次のとおりです。
private static String hdfsPath = "hdfs://192.168.139.9:9000";
/**
* 获取HDFS文件系统对象
*
* @return
* @throws Exception
*/
private static FileSystem getFileSystem() throws Exception
{
FileSystem fileSystem = FileSystem.get(new URI(hdfsPath), getConfiguration(), "root");
return fileSystem;
}
/**
* 获取HDFS配置信息
*
* @return
*/
private static Configuration getConfiguration() {
Configuration configuration = new Configuration();
configuration.set("fs.defaultFS", hdfsPath);
return configuration;
}
JavaでのHDFSベースの追加、削除、変更、チェック
hdfsに接続してファイルシステムオブジェクトを取得したら、以下に示すように、関連する操作を実行できます。
/**
* 在HDFS创建文件夹
*
* @param path
* @return
* @throws Exception
*/
public static void mkdir(String path) throws Exception
{
FileSystem fs = getFileSystem();
// 目标路径
Path srcPath = new Path(path);
boolean isOk = fs.mkdirs(srcPath);
fs.close();
}
/**
* 读取HDFS目录信息
*
* @param path
* @return
* @throws Exception
*/
public static void readPathInfo(String path)
throws Exception
{
FileSystem fs = getFileSystem();
// 目标路径
Path newPath = new Path(path);
FileStatus[] statusList = fs.listStatus(newPath);
List<Map<String, Object>> list = new ArrayList<>();
if (null != statusList && statusList.length > 0) {
for (FileStatus fileStatus : statusList) {
System.out.print("filePath:"+fileStatus.getPath());
System.out.println(",fileStatus:"+ fileStatus.toString());
}
}
}
/**
* HDFS创建文件
*
* @throws Exception
*/
public static void createFile()
throws Exception
{
File myFile = new File("C:\\Users\\tuzongxun\\Desktop\\tzx.txt");
FileInputStream fis = new FileInputStream(myFile);
String fileName = myFile.getName();
FileSystem fs = getFileSystem();
// 上传时默认当前目录,后面自动拼接文件的目录
Path newPath = new Path("/demo1/" + fileName);
// 打开一个输出流
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] b = new byte[1024];
int n;
while ((n = fis.read(b)) != -1) {
bos.write(b, 0, n);
}
fis.close();
bos.close();
FSDataOutputStream outputStream = fs.create(newPath);
outputStream.write(bos.toByteArray());
outputStream.close();
fs.close();
}
/**
* 读取文件列表
* @param path
* @throws Exception
*/
public static void listFile(String path)
throws Exception
{
FileSystem fs = getFileSystem();
// 目标路径
Path srcPath = new Path(path);
// 递归找到所有文件
RemoteIterator<LocatedFileStatus> filesList = fs.listFiles(srcPath, true);
while (filesList.hasNext()) {
LocatedFileStatus next = filesList.next();
String fileName = next.getPath().getName();
Path filePath = next.getPath();
System.out.println("##########################fileName:" + fileName);
System.out.println("##########################filePath:" + filePath.toString());
}
fs.close();
}
/**
* 读取HDFS文件内容
*
* @param path
* @return
* @throws Exception
*/
public static String readFile(String path) throws Exception
{
FileSystem fs = getFileSystem();
// 目标路径
Path srcPath = new Path(path);
FSDataInputStream inputStream = null;
try {
inputStream = fs.open(srcPath);
// 防止中文乱码
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String lineTxt = "";
StringBuffer sb = new StringBuffer();
while ((lineTxt = reader.readLine()) != null) {
sb.append(lineTxt);
}
return sb.toString();
}
finally {
inputStream.close();
fs.close();
}
}
/**
* 上传HDFS文件
*
* @param path
* @param uploadPath
* @throws Exception
*/
public static void uploadFile(String path, String uploadPath) throws Exception
{
if (StringUtils.isEmpty(path) || StringUtils.isEmpty(uploadPath)) {
return;
}
FileSystem fs = getFileSystem();
// 上传路径
Path clientPath = new Path(path);
// 目标路径
Path serverPath = new Path(uploadPath);
// 调用文件系统的文件复制方法,第一个参数是否删除原文件true为删除,默认为false
fs.copyFromLocalFile(false, clientPath, serverPath);
fs.close();
}
/**
* 调用
* @param args
*/
public static void main(String[] args) {
try {
//创建目录
//mkdir("/test2");
//列出目录列表
readPathInfo("/");
//列出文件列表
// listFile("/");
// 创建文件
// createFile();
// 读取文件内容
String a = readFile("/test/test2.txt");
// System.out.println("###########################" + a);
//上传文件
//uploadFile("C:\\Users\\tuzongxun\\Desktop\\tzx.txt", "/test2");
}
catch (Exception e) {
e.printStackTrace();
}
}
注:上記の依存パッケージは、springbootバージョンに変更することもでき、common-lang3関連のjarを追加で導入する必要があります。
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-hadoop</artifactId>
<version>2.5.0.RELEASE</version>
</dependency>