การพึ่งพา Java "สีเทา" คืออะไร?


74

นักพัฒนา JVM ที่นี่ เมื่อเร็ว ๆ นี้ฉันเคยเห็น banter ในห้องแชทของ IRC และแม้แต่ในสำนักงานของฉันเองเกี่ยวกับห้องสมุด Java ที่เรียกว่า " แรเงา " บริบทของการใช้งานจะเป็นดังนี้:

" เช่นนั้นมอบไคลเอ็นต์" ที่แรเงา "สำหรับ XYZ "

ตัวอย่างที่สมบูรณ์แบบคือปัญหาของ Jira สำหรับ HBase : " เผยแพร่สิ่งประดิษฐ์ของลูกค้าที่มีการขึ้นต่อกันของสีเทา "

ดังนั้นฉันถาม: JAR สีเทาหมายความว่าอะไร "แรเงา"?

คำตอบ:


86

การแรเงาการพึ่งพาเป็นกระบวนการของการรวมและการเปลี่ยนชื่อการพึ่งพา (ดังนั้นการย้ายคลาสและการเขียนซ้ำที่ได้รับผลกระทบจากโค้ดและทรัพยากร) เพื่อสร้างสำเนาส่วนตัวที่คุณรวมไว้ข้างรหัสของคุณ

แนวคิดนี้มักเกี่ยวข้องกับuber-jars (aka fat jars )

มีความสับสนเกี่ยวกับคำว่าเนื่องจากปลั๊กอินเฉดสี Maven ซึ่งภายใต้ชื่อเดียวนั้นจะทำ2 สิ่ง (อ้างถึงหน้าของตัวเอง):

ปลั๊กอินนี้ให้ความสามารถในการแพคเกจสิ่งประดิษฐ์ใน uber-jar รวมถึงการอ้างอิงและแรเงา - เช่นเปลี่ยนชื่อ - แพคเกจของการอ้างอิงบางอย่าง

ดังนั้นส่วนที่แรเงานั้นเป็นทางเลือก: ปลั๊กอินอนุญาตให้รวมการพึ่งพาใน jar ของคุณ (jar ไขมัน) และการเปลี่ยนชื่อการแรเงา (แรเงา) ซึ่งเป็นทางเลือก

เพิ่มแหล่งอื่น :

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

ในทางเทคนิคการพูดการพึ่งพาจะถูกแรเงา แต่เป็นเรื่องปกติที่จะอ้างถึง fat-jar-with-shaded-dependencies ว่า "shaded jar" และหาก jar นั้นเป็นไคลเอนต์สำหรับระบบอื่นก็สามารถเรียกได้ว่าเป็น "shaded client"

นี่คือชื่อปัญหา Jira สำหรับ HBase ที่คุณเชื่อมโยงในคำถามของคุณ:

เผยแพร่สิ่งประดิษฐ์ของไคลเอ็นต์ที่มีการขึ้นต่อกันที่เป็นร่มเงา

ดังนั้นในโพสต์นี้ฉันพยายามเสนอแนวคิดทั้งสองโดยไม่ทำให้พวกเขาสับสน

ดี

Uber-jars มักใช้เพื่อจัดส่งแอปพลิเคชันเป็นไฟล์เดียว (ทำให้ง่ายต่อการปรับใช้และเรียกใช้) พวกเขายังสามารถใช้ในการจัดส่งห้องสมุดพร้อมกับบางส่วน (หรือทั้งหมด) ของการพึ่งพาของพวกเขาแรเงาเพื่อหลีกเลี่ยงความขัดแย้งเมื่อใช้โดยโปรแกรมอื่น ๆ (ซึ่งอาจใช้รุ่นที่แตกต่างกันของห้องสมุดเหล่านั้น)

มีหลายวิธีในการสร้าง uber-jars แต่maven-shade-pluginไปอีกขั้นหนึ่งด้วยคุณสมบัติการย้ายตำแหน่ง :

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

(หมายเหตุประวัติ: ลิงก์ Jar Jarนำเสนอคุณลักษณะการย้ายตำแหน่งก่อนหน้า)

ด้วยวิธีนี้คุณสามารถทำให้การอ้างอิงไลบรารีของคุณมีรายละเอียดการใช้งานได้เว้นแต่คุณจะเปิดเผยคลาสจากไลบรารีเหล่านั้นใน API ของคุณ

สมมติว่าฉันมีโครงการ ACME Quantanizer ™ซึ่งจัดเตรียมDecayingSyncQuantanizerคลาสและขึ้นอยู่กับ Apache Commons-rng (เพราะแน่นอนว่าต้องมีปริมาณที่เหมาะสมที่คุณต้องการ a XorShift1024Star, duh)

ถ้าฉันใช้ plugin maven เงาเพื่อสร้าง uber-jar และฉันมองเข้าไปข้างในฉันเห็นไฟล์คลาสเหล่านี้:

com/acme/DecayingSyncQuantanizer.class
org/apache/commons/rng/RandomProviderState.class
org/apache/commons/rng/RestorableUniformRandomProvider.class
...
org/apache/commons/rng/core/source64/XorShift1024Star.class
org/apache/commons/rng/core/util/NumberFactory.class

ตอนนี้ถ้าฉันใช้คุณสมบัติการย้ายคลาส:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-shade-plugin</artifactId>
  <version>3.0.0</version>
  <executions>
    <execution>
      <phase>package</phase>
      <goals>
        <goal>shade</goal>
      </goals>
      <configuration>
        <relocations>
          <relocation>
            <pattern>org.apache.commons</pattern>
            <shadedPattern>com.acme.shaded.apachecommons</shadedPattern>
          </relocation>
        </relocations>
      </configuration>
    </execution>
  </executions>
</plugin>

เนื้อหาของ uber-jar มีลักษณะดังนี้:

com/acme/DecayingSyncQuantanizer.class
com/acme/shaded/apachecommons/rng/RandomProviderState.class
com/acme/shaded/apachecommons/rng/RestorableUniformRandomProvider.class
...
com/acme/shaded/apachecommons/rng/core/source64/XorShift1024Star.class
com/acme/shaded/apachecommons/rng/core/util/NumberFactory.class

มันไม่เพียงแค่เปลี่ยนชื่อไฟล์เท่านั้น แต่จะเขียน bytecode ที่อ้างอิงคลาสที่ย้ายที่ตั้งใหม่ (ดังนั้นคลาส & คอมมอนส์ rng ของฉันเองจะถูกแปลงทั้งหมด)

นอกจากนี้ปลั๊กอิน Shade จะสร้าง POM ( dependency-reduced-pom.xml) ใหม่ซึ่งการพึ่งพาการแรเงาจะถูกลบออกจาก<dependencies>ส่วน สิ่งนี้จะช่วยให้ใช้โถที่แรเงาเป็นที่พึ่งพิงสำหรับโครงการอื่น ดังนั้นคุณสามารถเผยแพร่ jar นั้นแทนเบสแรกหรือทั้งสองอย่าง (ใช้ qualifier สำหรับ jar สีเทา)

เพื่อให้มีประโยชน์มาก ...

เลว

... แต่มันก็มีปัญหาหลายอย่าง การรวมการพึ่งพาทั้งหมดไว้ใน "namespace" เดียวภายใน jar สามารถทำให้ยุ่งเหยิงและต้องการการแรเงาและ messing กับทรัพยากร

ตัวอย่างเช่น: วิธีจัดการกับไฟล์ทรัพยากรที่มีชื่อคลาสหรือแพ็คเกจ ไฟล์ทรัพยากรเช่นคำอธิบายของผู้ให้บริการซึ่งทุกคนอยู่ภายใต้META-INF/services?

ปลั๊กอินสีนำเสนอตัวแปลงทรัพยากรที่สามารถช่วยได้:

การรวมคลาส / ทรัพยากรจากสิ่งประดิษฐ์หลาย ๆ อย่างไว้ใน uber JAR หนึ่งตัวจะถูกส่งต่อไปตราบใดที่ไม่มีการทับซ้อนกัน มิฉะนั้นต้องใช้ตรรกะบางอย่างเพื่อรวมทรัพยากรจาก JAR หลาย ๆ ตัวเข้าด้วยกัน นี่คือที่ที่ตัวแปลงทรัพยากรเตะเข้า

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

โดยรวมแล้วการปรับใช้ jar แบบไขมันในฐานะที่เป็นแอพ / บริการแบบสแตนด์อโลนยังคงเป็นเรื่องปกติมากคุณเพียงแค่ต้องระวัง gotchas และสำหรับบางคนที่คุณอาจต้องการการแรเงาหรือลูกเล่นอื่น ๆ

น่าเกลียด

มีปัญหาที่ยากขึ้นมากมาย (การดีบักการทดสอบความเข้ากันได้กับ OSGi และเครื่องมือสร้างคลาสที่แปลกใหม่ ... )

แต่ที่สำคัญกว่านั้นคือเมื่อคุณสร้างห้องสมุดประเด็นต่าง ๆ ที่คุณคิดว่าคุณสามารถควบคุมได้นั้นซับซ้อนมากขึ้นเรื่อย ๆ เนื่องจาก jar ของคุณจะถูกนำไปใช้ในบริบทที่แตกต่างกันมากมาย ในสภาพแวดล้อมที่มีการควบคุม)

ตัวอย่างเช่น ElasticSearch ใช้เพื่อแรเงาการอ้างอิงบางอย่างในขวดที่จัดส่ง แต่พวกเขาตัดสินใจที่จะหยุดการทำเช่นนั้น :

ก่อนเวอร์ชัน 2.0, Elasticsearch ถูกจัดให้เป็น JAR ที่มีการพึ่งพาทั่วไปบางส่วน (แต่ไม่ใช่ทั้งหมด) ที่แรเงาและบรรจุภายในสิ่งประดิษฐ์เดียวกัน สิ่งนี้ช่วยให้ผู้ใช้ Java ที่ฝัง Elasticsearch ไว้ในแอปพลิเคชันของตนเองเพื่อหลีกเลี่ยงความขัดแย้งของโมดูลเช่น Guava, Joda, Jackson และอื่น ๆ แน่นอนว่ายังมีรายการของการพึ่งพาอื่น ๆ ที่ไม่มีการแรเงาเช่น Lucene
น่าเสียดายที่การแรเงาเป็นกระบวนการที่ซับซ้อนและเกิดข้อผิดพลาดซึ่งแก้ไขปัญหาสำหรับบางคนในขณะที่สร้างปัญหาให้ผู้อื่น การแรเงาทำให้เป็นเรื่องยากสำหรับนักพัฒนาและผู้เขียนปลั๊กอินในการเขียนและตรวจแก้จุดบกพร่องรหัสอย่างถูกต้องเนื่องจากแพ็คเกจถูกเปลี่ยนชื่อในระหว่างการสร้าง ในที่สุดเราใช้ในการทดสอบ Elasticsearch ที่ไม่มีการแรเงาแล้วส่งขวดที่แรเงาและเราไม่ชอบที่จะจัดส่งสิ่งที่เราไม่ได้ทดสอบ
เราได้ตัดสินใจจัดส่ง Elasticsearch โดยไม่แรเงาตั้งแต่ 2.0 เป็นต้นไป

โปรดทราบว่าพวกเขาก็หมายถึงการพึ่งพาสีเทาไม่ใช่ขวดสีเทา


1
ขอบคุณที่สละเวลาอธิบายสิ่งนี้ เอกสารอย่างเป็นทางการของปลั๊กอินเฉดสี Maven นั้นไม่เพียงพอและไม่ได้กล่าวถึงสิ่งนี้หรือแม้แต่ต้องการกำหนด "uber jar" เอกสารนั้นเป็นป้านและไร้ประโยชน์ การเขียนของคุณมีประโยชน์
Cheeso

คำอธิบายที่ยอดเยี่ยมฉันคิดว่ามันควรจะรวมอยู่ในเอกสารอย่างเป็นทางการ
Adelin

7

ให้ฉันตอบคำถามด้วยความช่วยเหลือของซอฟต์แวร์ที่รับผิดชอบการสร้างไหสีเทา ... อย่างน้อยเมื่อใช้ maven

นำมาจากโฮมเพจปลั๊กอิน Apache Maven Shade :

ปลั๊กอินนี้ให้ความสามารถในการแพคเกจสิ่งประดิษฐ์ใน uber-jar รวมถึงการอ้างอิงและแรเงา - เช่นเปลี่ยนชื่อ - แพคเกจของการอ้างอิงบางอย่าง

jar สีเทา aka uber-jar aka fat jar โดยค่าเริ่มต้นจะมีการขึ้นต่อทุกครั้งที่ต้องใช้ในการเรียกใช้แอปพลิเคชัน Java เพื่อไม่ให้มีการขึ้นต่อกันเพิ่มเติมใด ๆ ที่จำเป็นใน classpath คุณต้องใช้เวอร์ชัน Java ที่ถูกต้องเพื่อเรียกใช้แอปพลิเคชันของคุณ jar ที่แรเงาจะช่วยหลีกเลี่ยงปัญหาการปรับใช้ / คลาสพา ธ แต่จะมีขนาดใหญ่กว่าแอ็พพลิเคชัน jar ดั้งเดิมมากและจะไม่ช่วยให้คุณหลีกเลี่ยงโถนรก


1
เกรงว่าคำตอบนี้ไม่สมบูรณ์: อธิบายว่าขวดไขมัน / uber เป็นอย่างไร แต่ไม่ได้อธิบายส่วนที่แรเงา และใช่การแรเงาคือ 100% ที่ควรช่วยด้วย "jar hell" (ซึ่งทำให้ส่วนสุดท้ายของคำตอบนั้นไม่ถูกต้อง) ดังนั้นจึงมีประโยชน์ในบางระดับ แต่เพิ่มความสับสน: - /
Hugues M.

1
@HuguesMoreau ฉันอาจตอบไม่ครบ 100% แต่ก็ยังคงนำประเด็นที่ฉันต้องการทำ ขอขอบคุณที่นำชิ้นส่วนที่ขาดหายไปบนโต๊ะ การแรเงาจะไม่หลีกเลี่ยงโถนรกนั่นคือสิ่งที่ฉันตั้งใจและเขียน แต่จะให้เครื่องมือบางอย่างที่ช่วยให้คุณแก้ไขปัญหาบางอย่างได้ แต่ไม่ใช่โดยอัตโนมัติ ซึ่งทำให้ส่วนสุดท้ายหากอ่านและตีความวิธีที่ฉันหมายถึงมันอย่างน้อยก็โอเค :)
Jesko R.
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.