บทช่วยสอน HDFS: Archiการสอน อ่านและเขียน Operaการ

⚡ สรุปอย่างชาญฉลาด

HDFS คือเลเยอร์จัดเก็บข้อมูลแบบกระจายของ Hadoop ซึ่งแบ่งไฟล์ขนาดใหญ่มากออกเป็นบล็อกที่จำลองแบบกระจายบนเครื่องคอมพิวเตอร์ทั่วไป เพื่อให้ NameNode เดียวสามารถใช้งานได้ tracเมตาเดต้าของ ks ในขณะที่ DataNode จำนวนมากให้บริการคำขออ่านและเขียนได้อย่างน่าเชื่อถือ

  • 🔘 Archiเทคเจอร์: NameNode จะเก็บอิมเมจของเนมสเปซและบันทึกการแก้ไข ในขณะที่ DataNode จะจัดเก็บสำเนาบล็อกจริง
  • ☑️ การกำหนดขนาดบล็อก: Hadoop 1.x ใช้บล็อกขนาด 64 MB เป็นค่าเริ่มต้น ในขณะที่ Hadoop 2.x และ 3.x ใช้บล็อกขนาด 128 MB เป็นค่าเริ่มต้นแทน
  • การจำลองแบบ: โดยค่าเริ่มต้น จะมีการทำสำเนา 3 ชุดต่อบล็อก กระจายอยู่ทั่วแร็ค เพื่อป้องกันไม่ให้ข้อมูลสูญหายหากฮาร์ดแวร์ขัดข้อง
  • 🧪 เส้นทางการอ่าน: FSDataInputStream และ DFSInputStream ดึงตำแหน่งบล็อกจาก NameNode จากนั้นสตรีมไบต์โดยตรงจาก DataNodes
  • 🛠️ เขียนเส้นทาง: DFSOutputStream ทำหน้าที่จัดคิวแพ็กเก็ต DataStreamer สร้างไปป์ไลน์ DataNode และ Ack Queue ทำหน้าที่ป้องกันการสูญหายของแพ็กเก็ต
  • 🧭 ตัวเลือกการเข้าถึง: org.apache.hadoop.fs Java ทั้ง API และ hdfs dfs shell ครอบคลุมฟังก์ชันการเปิด การอ่าน การเขียน และการปิด

บทช่วยสอน HDFS

HDFS คืออะไร?

HDFS คือระบบไฟล์แบบกระจายสำหรับจัดเก็บไฟล์ข้อมูลขนาดใหญ่มาก โดยทำงานบนคลัสเตอร์ของฮาร์ดแวร์ทั่วไป มีความทนทานต่อความผิดพลาด ปรับขนาดได้ และขยายได้ง่ายมาก Hadoop มาพร้อมกับ HDFS (Hadoop Distributed File System)

เมื่อข้อมูลเกินความจุของหน่วยเก็บข้อมูลบนเครื่องกายภาพเครื่องเดียว จำเป็นต้องแบ่งข้อมูลดังกล่าวออกเป็นเครื่องแยกกันหลายเครื่อง ระบบไฟล์ที่จัดการการทำงานเฉพาะหน่วยเก็บข้อมูลบนเครือข่ายเครื่องเรียกว่าระบบไฟล์แบบกระจาย HDFS คือซอฟต์แวร์ประเภทหนึ่ง

เอชดีเอฟเอส Archiเทคเจอร์

คลัสเตอร์ HDFS ประกอบด้วยส่วนประกอบหลักๆ ดังนี้ ชื่อโหนด ซึ่งจัดการเมตาเดตาของระบบไฟล์และ โหนดข้อมูล ที่เก็บข้อมูลจริง

  • ชื่อโหนด: NameNode เปรียบเสมือนตัวควบคุมหลักของระบบ ทำหน้าที่ดูแลโครงสร้างไฟล์และข้อมูลเมตาสำหรับไฟล์และไดเร็กทอรีทั้งหมดที่มีอยู่ในระบบ โดยใช้ไฟล์สองไฟล์คือ 'namespace image' และ 'edit log' ในการจัดเก็บข้อมูลเมตา NameNode รู้จัก DataNode ทั้งหมดที่เก็บบล็อกข้อมูลสำหรับไฟล์ที่กำหนด แต่ไม่ได้จัดเก็บตำแหน่งของบล็อกอย่างถาวร ข้อมูลนี้จะถูกสร้างขึ้นใหม่ทุกครั้งจาก DataNode เมื่อระบบเริ่มต้นทำงาน
  • โหนดข้อมูล: DataNodes คือเครื่องลูกข่ายที่ติดตั้งอยู่บนเครื่องแต่ละเครื่องในคลัสเตอร์ และทำหน้าที่เป็นพื้นที่จัดเก็บข้อมูลจริง โดยมีหน้าที่รับผิดชอบในการตอบสนองคำขออ่านและเขียนข้อมูลจากเครื่องลูกข่าย

เนื่องจาก NameNode เพียงตัวเดียวจะเป็นจุดล้มเหลวเพียงจุดเดียว คลัสเตอร์ในปัจจุบันจึงใช้ NameNode หลักควบคู่ไปกับ NameNode สำรองที่แชร์บันทึกการแก้ไขผ่านกลุ่ม JournalNode โดยมีการสลับการทำงานอัตโนมัติระหว่างทั้งสอง

การอ่านและเขียนข้อมูลใน HDFS ทำงานในระดับบล็อก ไฟล์ข้อมูลใน HDFS จะถูกแบ่งออกเป็นส่วนย่อยขนาดบล็อก ซึ่งจัดเก็บเป็นหน่วยอิสระ ขนาดบล็อกเริ่มต้นคือ 64 MB ใน Hadoop 1.x ตั้งแต่ Hadoop 2.x เป็นต้นไป ค่าเริ่มต้นจะเป็น... ขนาดบล็อก dfs คือ 128 MB.

HDFS ทำงานบนแนวคิดการจำลองข้อมูล โดยจะสร้างสำเนาข้อมูลหลายชุดและกระจายไปยังโหนดต่างๆ ทั่วทั้งคลัสเตอร์ เพื่อให้สามารถเข้าถึงข้อมูลได้สูงในกรณีที่โหนดใดโหนดหนึ่งล้มเหลว ปัจจัยการจำลองข้อมูลเริ่มต้นคือสาม (การจำลองแบบ dfsและเมื่อ DataNode หยุดส่งสัญญาณ Heartbeat แล้ว NameNode จะทำการจำลองบล็อกข้อมูลไปยังที่อื่นโดยอัตโนมัติ

คุณรู้หรือไม่? ไฟล์ใน HDFS ซึ่งมีขนาดเล็กกว่าบล็อกเดียว ไม่ได้ใช้พื้นที่เก็บข้อมูลเต็มของบล็อก

อ่าน Operaใน HDFS

คำขออ่านข้อมูลจะได้รับการประมวลผลโดย HDFS, NameNode และ DataNodes เราจะเรียกผู้ที่อ่านข้อมูลว่า 'ไคลเอ็นต์' แผนภาพด้านล่างแสดงการดำเนินการอ่านไฟล์ใน Hadoop

การไหลของข้อมูลการอ่าน HDFS ระหว่างไคลเอ็นต์ NameNode และ DataNode

  1. ไคลเอ็นต์เริ่มต้นคำขออ่านโดยการเรียกเมธอด 'open()' ของอ็อบเจ็กต์ FileSystem ซึ่งเป็นอ็อบเจ็กต์ประเภท DistributedFileSystem
  2. อ็อบเจ็กต์นี้เชื่อมต่อกับ NameNode โดยใช้ RPC และรับข้อมูลเมตา เช่น ตำแหน่งของบล็อกต่างๆ ในไฟล์ โปรดทราบว่าที่อยู่เหล่านี้เป็นที่อยู่ของบล็อกแรกๆ ของไฟล์
  3. เมื่อได้รับคำขอข้อมูลเมตา ระบบจะส่งที่อยู่ของ DataNode ที่มีสำเนาของบล็อกนั้นกลับมา
  4. เมื่อได้รับที่อยู่ของ DataNode แล้ว อ็อบเจ็กต์ประเภท FSDataInputStream จะถูกส่งกลับไปยังไคลเอ็นต์ FSDataInputStream ประกอบด้วย DFSInputStream ซึ่งทำหน้าที่จัดการการโต้ตอบกับ DataNode และ NameNode ในขั้นตอนที่ 4 ที่แสดงในแผนภาพด้านบน ไคลเอ็นต์เรียกใช้เมธอด 'read()' ซึ่งทำให้ DFSInputStream สร้างการเชื่อมต่อกับ DataNode ตัวแรกที่เก็บบล็อกแรกของไฟล์
  5. ข้อมูลจะถูกอ่านในรูปแบบของสตรีม โดยที่ไคลเอ็นต์เรียกใช้เมธอด 'read()' ซ้ำๆ กระบวนการเรียกใช้เมธอด read() นี้จะดำเนินต่อไปจนกว่าจะถึงจุดสิ้นสุดของบล็อก
  6. เมื่อถึงจุดสิ้นสุดของบล็อกแล้ว DFSInputStream จะปิดการเชื่อมต่อและดำเนินการค้นหา DataNode ถัดไปสำหรับบล็อกถัดไป
  7. เมื่อไคลเอนต์อ่านเสร็จแล้ว จะเรียกเมธอด close()

เขียน Operaใน HDFS

ในส่วนนี้ เราจะทำความเข้าใจวิธีการเขียนข้อมูลลงใน HDFS ผ่านไฟล์ต่างๆ ดังแสดงในแผนภาพด้านล่าง traces ที่เขียนเส้นทาง

ไปป์ไลน์การเขียนข้อมูลลง HDFS พร้อมด้วย DataQueue, DataStreamer และ Ack Queue

  1. ไคลเอ็นต์เริ่มต้นการดำเนินการเขียนโดยการเรียกเมธอด 'create()' ของอ็อบเจ็กต์ DistributedFileSystem ซึ่งจะสร้างไฟล์ใหม่ – ขั้นตอนที่ 1 ในแผนภาพด้านบน
  2. อ็อบเจ็กต์ DistributedFileSystem เชื่อมต่อกับ NameNode โดยใช้การเรียก RPC และเริ่มต้นการสร้างไฟล์ใหม่ อย่างไรก็ตาม การดำเนินการสร้างไฟล์นี้ไม่ได้เชื่อมโยงบล็อกใดๆ กับไฟล์นั้น NameNode มีหน้าที่ตรวจสอบว่าไฟล์ (ที่กำลังสร้าง) นั้นไม่มีอยู่แล้ว และไคลเอนต์มีสิทธิ์ที่ถูกต้องในการสร้างไฟล์ใหม่ หากไฟล์มีอยู่แล้วหรือไคลเอนต์ไม่มีสิทธิ์เพียงพอในการสร้างไฟล์ใหม่ ระบบจะส่ง IOException ไปยังไคลเอนต์ มิเช่นนั้น การดำเนินการจะสำเร็จและ NameNode จะสร้างเรคอร์ดใหม่สำหรับไฟล์นั้น
  3. เมื่อมีการสร้างเรคอร์ดใหม่ใน NameNode แล้ว ระบบจะส่งอ็อบเจ็กต์ประเภท FSDataOutputStream กลับไปยังไคลเอ็นต์ ไคลเอ็นต์จะใช้อ็อบเจ็กต์นี้ในการเขียนข้อมูลลงใน HDFS โดยจะเรียกใช้เมธอดการเขียนข้อมูล (ขั้นตอนที่ 3 ในแผนภาพ)
  4. FSDataOutputStream ประกอบด้วยอ็อบเจ็กต์ DFSOutputStream ซึ่งทำหน้าที่ดูแลการสื่อสารกับ DataNodes และ NameNode ในขณะที่ไคลเอ็นต์เขียนข้อมูลอย่างต่อเนื่อง DFSOutputStream ก็จะสร้างแพ็กเก็ตที่มีข้อมูลเหล่านั้นอย่างต่อเนื่อง แพ็กเก็ตเหล่านี้จะถูกส่งไปยังคิวที่เรียกว่า DataQueue
  5. นอกจากนี้ยังมีส่วนประกอบอีกอย่างหนึ่งชื่อ DataStreamer ซึ่งทำหน้าที่ประมวลผล DataQueue นี้ DataStreamer ยังขอให้ NameNode จัดสรรบล็อกใหม่ เพื่อเลือก DataNode ที่เหมาะสมที่จะนำมาใช้ในการจำลองข้อมูลด้วย
  6. ตอนนี้ กระบวนการจำลองแบบเริ่มต้นโดยการสร้างไปป์ไลน์โดยใช้ DataNodes ในกรณีของเรา เราได้เลือกระดับการจำลองเป็น 3 และด้วยเหตุนี้จึงมี DataNodes 3 ตัวในไปป์ไลน์
  7. DataStreamer เทแพ็กเก็ตลงใน DataNode แรกในไปป์ไลน์
  8. DataNode แต่ละตัวในไปป์ไลน์จะจัดเก็บแพ็กเก็ตที่ได้รับและส่งต่อไปยัง DataNode ตัวที่สองในไปป์ไลน์
  9. DFSOutputStream จะดูแลคิวอีกคิวหนึ่งที่เรียกว่า 'คิวยืนยัน' (Ack Queue) เพื่อจัดเก็บแพ็กเก็ตที่รอการยืนยันจาก DataNodes
  10. เมื่อได้รับการยืนยันสำหรับแพ็กเก็ตในคิวจาก DataNode ทั้งหมดในไปป์ไลน์ แพ็กเก็ตดังกล่าวจะถูกลบออกจาก 'Ack Queue' ในกรณีที่ DataNode ล้มเหลว แพ็กเก็ตจากคิวนี้จะถูกนำมาใช้เพื่อเริ่มการทำงานใหม่อีกครั้ง
  11. หลังจากที่ไคลเอ็นต์เขียนข้อมูลเสร็จแล้ว จะเรียกเมธอด close() (ขั้นตอนที่ 9 ในแผนภาพ) การเรียก close() จะส่งผลให้แพ็กเก็ตข้อมูลที่เหลือถูกส่งไปยังไปป์ไลน์ จากนั้นจึงรอการยืนยันการรับข้อมูล
  12. เมื่อได้รับการยืนยันขั้นสุดท้ายแล้ว ระบบจะติดต่อ NameNode เพื่อแจ้งว่าการเขียนไฟล์เสร็จสมบูรณ์แล้ว

เข้าถึง HDFS โดยใช้ Java API

ในส่วนนี้ เราจะพยายามทำความเข้าใจเกี่ยวกับ Java อินเทอร์เฟซที่ใช้สำหรับเข้าถึงระบบไฟล์ของ Hadoop

เพื่อให้สามารถโต้ตอบกับระบบไฟล์ของ Hadoop ผ่านทางโปรแกรมได้ Hadoop จึงมีวิธีการใช้งานหลายวิธี Java คลาสต่างๆ ในแพ็กเกจชื่อ org.apache.hadoop.fs ประกอบด้วยคลาสที่มีประโยชน์ในการจัดการไฟล์ในระบบไฟล์ของ Hadoop การดำเนินการเหล่านี้รวมถึงการเปิด การอ่าน การเขียน และการปิดไฟล์ API ไฟล์ของ Hadoop นั้นเป็นแบบทั่วไปและสามารถขยายเพื่อโต้ตอบกับระบบไฟล์อื่นๆ นอกเหนือจาก HDFS ได้

การอ่านไฟล์จาก HDFS โดยทางโปรแกรม

อ็อบเจ็กต์ java.netURL ใช้สำหรับอ่านเนื้อหาของไฟล์ ขั้นแรกเราต้องทำให้ Java รู้จัก HDFS ของ Hadoop URL แผนการนี้ทำได้โดยการเรียกชุดURLเมธอด StreamHandlerFactory บน URL โดยการส่งอินสแตนซ์ของ FsUrlStreamHandlerFactory ไปยังอ็อบเจ็กต์นั้น เมธอดนี้จะต้องถูกเรียกใช้เพียงครั้งเดียวต่อ JVMดังนั้นจึงถูกปิดล้อมอยู่ในบล็อกคงที่

รหัสตัวอย่างคือ-

public class URLCat {
    static {
        URL.setURLStreamHandlerFactory(new FsUrlStreamHandlerFactory());
    }
    public static void main(String[] args) throws Exception {
        InputStream in = null;
        try {
            in = new URL(args[0]).openStream();
            IOUtils.copyBytes(in, System.out, 4096, false);
        } finally {
            IOUtils.closeStream(in);
        }
    }
}

โค้ดนี้จะเปิดและอ่านเนื้อหาของไฟล์ โดยจะส่งพาธของไฟล์นั้นบน HDFS ให้กับโปรแกรมผ่านทางอาร์กิวเมนต์ในบรรทัดคำสั่ง

เข้าถึง HDFS โดยใช้ส่วนต่อประสานบรรทัดคำสั่ง

นี่เป็นหนึ่งในวิธีที่ง่ายที่สุดในการใช้งาน HDFS อินเทอร์เฟซบรรทัดคำสั่งรองรับการดำเนินการเกี่ยวกับระบบไฟล์ เช่น การอ่านไฟล์ การสร้างไดเร็กทอรี การย้ายไฟล์ การลบข้อมูล และการแสดงรายการไดเร็กทอรี

เราวิ่งได้ '$HADOOP_HOME/bin/hdfs dfs -help' เพื่อรับความช่วยเหลือโดยละเอียดเกี่ยวกับทุกคำสั่ง ในที่นี้ 'dfs' เป็นคำสั่งเชลล์ของ HDFS ซึ่งรองรับคำสั่งย่อยหลายคำสั่ง ใน Hadoop เวอร์ชันปัจจุบัน hdfs dfs เป็นรูปแบบที่นิยม ในขณะที่รูปแบบเก่ากว่า Hadoop FS คำสั่งนี้ทำงานได้เหมือนกันสำหรับระบบไฟล์ทุกระบบที่รองรับ

ด้านล่างนี้เป็นรายการคำสั่งที่ใช้กันอย่างแพร่หลายพร้อมทั้งรายละเอียดของแต่ละคำสั่ง

1. คัดลอกไฟล์จากระบบไฟล์ในเครื่องไปยัง HDFS

$HADOOP_HOME/bin/hdfs dfs -copyFromLocal temp.txt /

คำสั่งนี้จะคัดลอกไฟล์ temp.txt จากระบบไฟล์ในเครื่องไปยัง HDFS ดังที่แสดงในผลลัพธ์ด้านล่าง

คำสั่ง hdfs dfs -copyFromLocal กำลังคัดลอกไฟล์ temp.txt ไปยัง HDFS

2. เราสามารถแสดงรายการไฟล์ที่มีอยู่ในไดเร็กทอรีได้โดยใช้คำสั่ง -ls

$HADOOP_HOME/bin/hdfs dfs -ls /

จากรายการด้านล่าง เราจะเห็นไฟล์ 'temp.txt' (ที่คัดลอกไว้ก่อนหน้านี้) อยู่ในไดเร็กทอรี ' / '

ผลลัพธ์จากคำสั่ง hdfs dfs -ls แสดงรายการไฟล์ temp.txt ในไดเร็กทอรีรากของ HDFS

3. คำสั่งให้คัดลอกไฟล์ไปยังระบบไฟล์ในเครื่องจาก HDFS

$HADOOP_HOME/bin/hdfs dfs -copyToLocal /temp.txt

คำสั่งนี้ทำงานคล้ายกับคำสั่ง -get และรับพาธปลายทางในเครื่องเป็นอาร์กิวเมนต์ตัวที่สอง ผลลัพธ์ด้านล่างแสดงให้เห็นว่าไฟล์ temp.txt ถูกคัดลอกไปยังระบบไฟล์ในเครื่องแล้ว

ผลลัพธ์จากคำสั่ง hdfs dfs -copyToLocal แสดงให้เห็นว่าไฟล์ temp.txt ถูกคัดลอกไปยังระบบไฟล์ภายในเครื่องแล้ว

4. คำสั่งให้สร้างไดเร็กทอรีใหม่

$HADOOP_HOME/bin/hdfs dfs -mkdir /mydirectory

คำสั่งจะทำงานเสร็จสมบูรณ์โดยไม่มีข้อความแจ้งเตือนใดๆ ดังที่แสดงด้านล่าง

คำสั่ง hdfs dfs -mkdir จะสร้างโฟลเดอร์ /mydirectory ใน HDFS

ตรวจสอบว่าได้สร้างไดเร็กทอรีแล้วหรือไม่ ตอนนี้คุณน่าจะรู้วิธีทำแล้ว 😉

คำถามที่พบบ่อย

บล็อกขนาดใหญ่ช่วยให้เมตาเดต้าของ NameNode มีขนาดเล็ก และช่วยให้ตัวแมปเปอร์สามารถอ่านไบต์จำนวนมากตามลำดับได้ก่อนที่จะค้นหาอีกครั้ง ดังนั้นเวลาในการค้นหาบนดิสก์จึงกลายเป็นเพียงเศษเสี้ยวเล็กน้อยของเวลาในการถ่ายโอน ซึ่งเป็นสิ่งที่ทำให้การสแกนไฟล์แบบเต็มทำได้อย่างรวดเร็ว

NameNode จะหยุดรับสัญญาณชีพจร ทำเครื่องหมายโหนดว่าตายแล้ว และกำหนดเวลาการจำลองข้อมูลใหม่ของทุกบล็อกที่ต่ำกว่าค่าตัวคูณการจำลองไปยัง DataNode ที่ทำงานได้ปกติ การเขียนข้อมูลที่อยู่ระหว่างดำเนินการจะถูกกู้คืนจากคิวการยืนยัน (Ack Queue) ดังนั้นไคลเอนต์จะไม่สูญเสียแพ็กเก็ต

ไม่ใช่ในคลัสเตอร์ที่กำหนดค่าอย่างถูกต้อง HDFS High Availability ทำงานโดยใช้ NameNode หลักและ NameNode สำรองที่แชร์การแก้ไขผ่าน JournalNode และตัวควบคุมการสลับการทำงานของ ZooKeeper จะเลื่อนระดับไปยัง NameNode สำรองโดยอัตโนมัติเมื่อ NameNode หลักหยุดการทำงาน

ทั้งสองคำสั่งอัปโหลดข้อมูลไปยัง HDFS โดยตัวเลือก -copyFromLocal จะจำกัดแหล่งที่มาไว้ที่ระบบไฟล์ในเครื่อง ในขณะที่ -put ยอมรับแหล่งที่มาใด ๆ ที่รองรับ รวมถึงพาธ HDFS อื่นหรือ stdin การทำงานอื่น ๆ นั้นเหมือนกัน ดังนั้น -copyFromLocal จึงเป็นเพียงการอธิบายเจตนาเท่านั้น

ทุกไฟล์ ทุกไดเร็กทอรี และทุกบล็อกล้วนใช้หน่วยความจำใน NameNode ดังนั้นไฟล์ขนาดเล็กนับล้านไฟล์จึงใช้หน่วยความจำฮีปหมดก่อนที่ดิสก์จะเต็ม การรวมไฟล์เหล่านั้นเข้าด้วยกันเป็นไฟล์ลำดับ ไฟล์ Avro, ORC, Parquet หรือ HAR จะช่วยให้เมตาเดต้าสามารถจัดการได้ง่ายขึ้น

การจำลองข้อมูล (Replication) เก็บสำเนาข้อมูลทั้งหมดสามชุด ซึ่งทำให้สิ้นเปลืองพื้นที่จัดเก็บข้อมูลเพิ่มขึ้น 200 เปอร์เซ็นต์ ส่วนการเข้ารหัสแบบลบข้อมูล (Erasure coding) ที่เพิ่มเข้ามาใน Hadoop 3 นั้น จะเก็บเซลล์พาริตี้แทน และให้ความทนทานใกล้เคียงกันโดยมีค่าใช้จ่ายเพิ่มขึ้นประมาณ 50 เปอร์เซ็นต์ โดยแลกกับพื้นที่จัดเก็บข้อมูลที่ถูกกว่า แต่ต้องแลกกับค่าใช้จ่ายด้าน CPU และเครือข่ายที่สูงขึ้นในระหว่างการกู้คืนข้อมูล

โมเดลแมชชีนเลิร์นนิงที่ฝึกฝนด้วยบันทึกการตรวจสอบของ NameNode และเมตริกของ DataNode สามารถคาดการณ์การหมดความจุ ระบุบล็อกที่มีการใช้งานสูง และตรวจจับดิสก์ที่กำลังจะเสียก่อนที่จะใช้งานไม่ได้ นอกจากนี้ การตรวจจับความผิดปกติในรูปแบบการเข้าถึงยังช่วยให้ตรวจพบงานที่ทำงานผิดปกติได้ตั้งแต่เนิ่นๆ

Copilot ช่วยสร้างโค้ดพื้นฐานที่คุ้นเคยของ org.apache.hadoop.fs ได้อย่างรวดเร็ว เช่น การเรียกใช้ FileSystem.get, ลูป FSDataInputStream และ IOUtils.copyBytes ควรตรวจสอบโค้ดที่สร้างขึ้นกับเวอร์ชัน Hadoop ที่ใช้งานอยู่เสมอ เนื่องจากชื่อ API และเมธอดที่เลิกใช้งานแล้วจะแตกต่างกันอย่างเห็นได้ชัดระหว่าง Hadoop 2.x และ 3.x

สรุปโพสต์นี้ด้วย: