ฉันจะเขียนแอปพลิเคชัน Java ที่สามารถอัปเดตตัวเองที่รันไทม์ได้อย่างไร


92

ฉันต้องการติดตั้งแอปพลิเคชัน java (แอปพลิเคชันเซิร์ฟเวอร์) ที่สามารถดาวน์โหลดเวอร์ชันใหม่ (ไฟล์. jar) จาก URL ที่กำหนดจากนั้นอัปเดตตัวเองเมื่อรันไทม์

วิธีใดดีที่สุดในการทำเช่นนี้และเป็นไปได้หรือไม่?

ฉันเดาว่าแอปพลิเคชันสามารถดาวน์โหลดไฟล์. jar ใหม่และเริ่มต้นได้ แต่ฉันจะส่งมอบอย่างไรเช่นรู้ว่าเมื่อแอปพลิเคชันใหม่เริ่มทำงานแล้วจึงออก หรือมีวิธีที่ดีกว่านี้หรือไม่?


4
คุณเคยดู Java Web Start หรือไม่? ฉันเชื่อว่ามันไม่อัปเดตตัวเองเมื่อรันไทม์ (จำเป็นต้องรีสตาร์ท) เพราะคุณอาจต้องดู OSGi
Thilo

@Thilo: ฉันคิดว่าการดาวน์โหลดไฟล์จาก url ที่กำหนดจะเป็นเรื่องง่ายจากนั้นเริ่มด้วยคำสั่ง linux จากไฟล์ jar ที่กำลังทำงานอยู่
Jonas

1
การออกแบบ Java WebStart API ทำให้ไม่สามารถอัปเดตขณะทำงานได้ น่าเสียดาย.
Thorbjørn Ravn Andersen


@ meain boss you rock, you made my day :)
iltaf khalid

คำตอบ:


69

โครงสร้างพื้นฐานของโซลูชันมีดังนี้:

  • มีลูปหลักที่รับผิดชอบในการโหลดแอปเวอร์ชันล่าสุดซ้ำ ๆ (หากจำเป็น) และเปิดใช้งาน

  • แอปพลิเคชันทำสิ่งต่างๆ แต่จะตรวจสอบ URL ดาวน์โหลดเป็นระยะ หากตรวจพบเวอร์ชันใหม่จะออกจากตัวเรียกใช้งาน

มีหลายวิธีที่คุณสามารถนำไปใช้ได้ ตัวอย่างเช่น:

  • ตัวเรียกใช้งานอาจเป็นสคริปต์ wrapper หรือไบนารีแอปพลิเคชันที่เริ่ม JVM ใหม่เพื่อเรียกใช้แอปพลิเคชันจากไฟล์ JAR ที่ถูกแทนที่

  • ตัวเรียกใช้งานอาจเป็นแอ็พพลิเคชัน Java ที่สร้าง classloader สำหรับ JAR ใหม่โหลดคลาส entrypoint และเรียกใช้เมธอดบางอย่าง หากคุณทำเช่นนี้คุณต้องคอยระวังการรั่วไหลของพื้นที่จัดเก็บ classloader แต่นั่นไม่ใช่เรื่องยาก (คุณต้องตรวจสอบให้แน่ใจว่าไม่มีอ็อบเจ็กต์ที่มีคลาสที่โหลดจาก JAR สามารถเข้าถึงได้หลังจากที่คุณเปิดใหม่)

ข้อดีของวิธีการห่อหุ้มภายนอกคือ:

  • คุณต้องการเพียงหนึ่งไห
  • คุณสามารถแทนที่แอป Java ทั้งหมด
  • เธรดรองใด ๆ ที่สร้างโดยแอพ ฯลฯ จะหายไปโดยไม่มีตรรกะการปิดระบบพิเศษและ
  • คุณยังสามารถจัดการกับการกู้คืนจากการขัดข้องของแอปพลิเคชัน ฯลฯ

แนวทางที่สองต้องใช้ JAR สองอัน แต่มีข้อดีดังต่อไปนี้:

  • โซลูชันนี้เป็น Java ที่บริสุทธิ์และพกพาได้
  • การเปลี่ยนแปลงจะเร็วขึ้นและ
  • คุณสามารถรักษาสถานะในการรีสตาร์ทได้ง่ายขึ้น (ปัญหาการรั่วของโมดูโล)

วิธีที่ "ดีที่สุด" ขึ้นอยู่กับข้อกำหนดเฉพาะของคุณ

ควรสังเกตด้วยว่า:

  • การอัปเดตอัตโนมัติมีความเสี่ยงด้านความปลอดภัย โดยทั่วไปหากเซิร์ฟเวอร์ที่ให้การอัปเดตถูกบุกรุกหรือหากกลไกในการให้การอัปเดตนั้นเสี่ยงต่อการถูกโจมตีการอัปเดตอัตโนมัติอาจทำให้ไคลเอ็นต์ถูกบุกรุกได้

  • การส่งการอัปเดตไปยังลูกค้าที่ก่อให้เกิดความเสียหายต่อลูกค้าอาจมีความเสี่ยงทางกฎหมายและความเสี่ยงต่อชื่อเสียงของธุรกิจของคุณ


หากคุณสามารถหาวิธีหลีกเลี่ยงการสร้างล้อขึ้นมาใหม่ได้ก็คงจะดี ดูคำตอบอื่น ๆ สำหรับคำแนะนำ


43

ฉันกำลังพัฒนา JAVA Linux Daemon และจำเป็นต้องใช้กลไกการอัปเดตอัตโนมัติ ฉันต้องการ จำกัด แอปพลิเคชันของฉันไว้ที่ไฟล์ jar หนึ่งไฟล์และหาวิธีง่ายๆ:

บรรจุแอปพลิเคชันตัวอัปเดตในการอัปเดตเอง

แอปพลิเคชัน : เมื่อแอปพลิเคชันตรวจพบเวอร์ชันที่ใหม่กว่าจะดำเนินการดังต่อไปนี้:

  1. ดาวน์โหลดอัปเดต (Zipfile)
  2. แยก Application และ ApplicationUpdater (ทั้งหมดใน zipfile)
  3. เรียกใช้ตัวอัปเดต

ApplicationUpdater : เมื่อตัวอัพเดตรันจะทำสิ่งต่อไปนี้:

  1. หยุดแอ็พพลิเคชัน (ในกรณีของฉันคือ daemon ผ่าน init.d)
  2. คัดลอกไฟล์ jar ที่ดาวน์โหลดมาเพื่อเขียนทับแอปพลิเคชันปัจจุบัน
  3. เริ่มแอปพลิเคชัน
  4. ทำความสะอาด.

หวังว่ามันจะช่วยใครบางคน


13

นี่เป็นปัญหาที่ทราบแล้วและฉันไม่แนะนำให้สร้างวงล้อใหม่ - อย่าเขียนแฮ็คของคุณเองเพียงแค่ใช้สิ่งที่คนอื่นทำไปแล้ว

สองสถานการณ์ที่คุณต้องพิจารณา:

  1. แอปต้องสามารถอัปเดตได้ด้วยตนเองและยังคงทำงานต่อไปแม้ในระหว่างการอัปเดต (แอปเซิร์ฟเวอร์แอปแบบฝัง) ไปกับ OSGi: การรวมกลุ่มหรือEquinox p2

  2. แอปเป็นแอปบนเดสก์ท็อปและมีตัวติดตั้ง มีตัวติดตั้งหลายตัวพร้อมตัวเลือกการอัปเดต ตรวจสอบรายชื่อผู้ติดตั้ง


12

ฉันเพิ่งสร้างupdate4jซึ่งเข้ากันได้กับระบบโมดูลของ Java 9 อย่างสมบูรณ์

มันจะเริ่มเวอร์ชันใหม่อย่างราบรื่นโดยไม่ต้องรีสตาร์ท


โดยใช้กระบวนทัศน์การบูต / แอปพลิเคชันทางธุรกิจ bootstrap จะโหลดและยกเลิกการโหลดแอปพลิเคชันทางธุรกิจและเป็น bootstrap ที่โดยทั่วไปทำการอัปเดต
หมอเดชชัย

10

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

โซลูชันนี้ใช้ ClassLoader ที่กำหนดเองเพื่อโหลดไฟล์จาก jar เมื่อโหลดเสร็จแล้วคุณสามารถเรียกใช้วิธีการบางอย่างจาก jar ใหม่ซึ่งจะทำหน้าที่เป็นmainวิธีการ จากนั้นส่วนที่ยุ่งยากคือการตรวจสอบให้แน่ใจว่าคุณได้กำจัดการอ้างอิงถึงรหัสเก่าทั้งหมดเพื่อให้สามารถรวบรวมขยะ ฉันไม่ใช่ผู้เชี่ยวชาญในส่วนนั้นฉันทำให้มันได้ผล แต่มันไม่ง่ายเลย


ฉันไม่รู้เกี่ยวกับ Java แต่ใน C # คุณสามารถใช้ AppDomains เพื่อยกเลิกการโหลดโค้ดอย่างชัดเจน อาจมีแนวคิดที่คล้ายกันใน Java
Merlyn Morgan-Graham

1
ขอบคุณดูเหมือนจะเป็นหนทางไป มีตัวอย่างNetworkClassLoaderบน JavaDoc สำหรับClassLoader
Jonas

คุณประสบปัญหากับตัวแปรคงที่ภายใน JVM เดียวกันหรือไม่หรือสิ่งที่เกี่ยวกับการสร้างแบบถาวร? ฉันได้ยินมาว่าสิ่งเหล่านี้สามารถนำเสนอปัญหาเกี่ยวกับการยกเลิกการโหลดคลาสได้
ide

5
  1. วิธีแรก: ใช้ tomcat และปรับใช้สิ่งอำนวยความสะดวก
  2. วิธีที่สอง: แยกแอปพลิเคชันออกเป็นสองส่วน (ใช้งานได้และอัปเดต) และให้ส่วนอัปเดตแทนที่ส่วนฟังก์ชัน
  3. วิธีที่สาม: ในแอปพลิเคชันเซิร์ฟเวอร์ของคุณเพียงดาวน์โหลดเวอร์ชันใหม่จากนั้นเวอร์ชันเก่าจะเผยแพร่พอร์ตที่ถูกผูกไว้จากนั้นเวอร์ชันเก่าจะเรียกใช้เวอร์ชันใหม่ (เริ่มกระบวนการ) จากนั้นเวอร์ชันเก่าจะส่งคำขอในพอร์ตแอปพลิเคชันไปยังเวอร์ชันใหม่เพื่อลบเวอร์ชันเก่าเวอร์ชันเก่า สิ้นสุดและเวอร์ชันใหม่จะลบเวอร์ชันเก่า แบบนี้: ข้อความแสดงแทน

วิธีที่สองของคุณดูเหมือนจะเป็นการออกแบบที่น่าสนใจ
Jonas

2

นี่ไม่ใช่วิธีที่ดีที่สุดแต่อาจเหมาะกับคุณ

คุณสามารถเขียนแอปพลิเคชั่น bootstrap (เช่นตัวเรียกใช้งาน World of Warcraft ถ้าคุณเคยเล่น WoW) bootstrap นั้นมีหน้าที่ตรวจสอบการอัปเดต

  • หากมีการอัปเดตจะเสนอให้กับผู้ใช้จัดการดาวน์โหลดติดตั้ง ฯลฯ
  • หากแอปพลิเคชันทันสมัยจะอนุญาตให้ผู้ใช้เปิดแอปพลิเคชัน
  • คุณสามารถเลือกที่จะอนุญาตให้ผู้ใช้เปิดแอปพลิเคชันได้แม้ว่าจะไม่เป็นปัจจุบันก็ตาม

วิธีนี้ทำให้คุณไม่ต้องกังวลกับการบังคับให้ออกจากแอปพลิเคชันของคุณ

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

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

โปรดทราบว่าฉันไม่รู้ว่า Java สามารถเรียกใช้รหัส UI ได้หรือไม่ก่อนที่คุณจะเปิดหน้าต่างหลักของคุณ เราใช้ C # / WPF


ขอบคุณนั่นเป็นวิธีที่ดีที่จะทำ แต่เป็นแอปพลิเคชันเซิร์ฟเวอร์ดังนั้นจึงไม่มีผู้ใช้ที่สามารถดำเนินการบางอย่างได้ และฉันต้องการให้มันเป็นเพียงไฟล์ jar เดียวดังนั้นผู้ใช้ easyli สามารถดาวน์โหลด jar เดียวและเริ่มต้นได้ตั้งแต่ต้น แต่หลังจากนั้นฉันไม่ต้องการให้ผู้ใช้โต้ตอบ
Jonas

เมื่อคุณพูดว่า "เซิร์ฟเวอร์แอปพลิเคชัน" หมายความว่าเป็นแอปพลิเคชันที่ทำงานบนเซิร์ฟเวอร์โดยตรงขณะล็อกอินหรือไม่
Merlyn Morgan-Graham

@Jonas: ฉันไม่ค่อยเข้าใจว่าไฟล์. jar ทำงานอย่างไรฉันจึงไม่สามารถให้บริการได้มากนัก ฉันเข้าใจว่าคุณต้องการคำตอบเฉพาะสำหรับ Java หรือไม่ หวังว่าสิ่งนี้จะช่วยให้คุณมีความคิดอย่างน้อย :)
Merlyn Morgan-Graham

@ เมอร์ลิน: ฉันขอบคุณสำหรับการแบ่งปันความคิดของคุณ ใช่มันเป็นแอปพลิเคชัน Java ที่จะทำงานบนเซิร์ฟเวอร์ Linux และไม่มีใครล็อกอินบนเซิร์ฟเวอร์นั้น
Jonas

1
@Jonas: ไม่ใช่ว่าคุณไม่เคยคิดเรื่องนี้มาก่อน แต่ - บริการบนเว็บส่วนใหญ่ที่ฉันเห็นมีกลยุทธ์การปรับใช้ด้วยตนเอง คุณอาจต้องระมัดระวังเกี่ยวกับวิธีตรวจสอบการอัปเดต หากคุณดันโค้ดบั๊กกี้ / เสียหรือทำเพียงบางส่วนพร้อมกับการปรับใช้ไฟล์หลายไฟล์เซิร์ฟเวอร์อาจพยายามอัปเดตตัวเองจากนั้นคุณจะมีเซิร์ฟเวอร์ที่เสีย
Merlyn Morgan-Graham

2

หากคุณสร้างแอปพลิเคชันโดยใช้ปลั๊กอินEquinoxคุณสามารถใช้P2 Provisioning Systemเพื่อรับโซลูชันสำเร็จรูปสำหรับปัญหานี้ สิ่งนี้จะต้องให้เซิร์ฟเวอร์รีสตาร์ทตัวเองหลังจากการอัปเดต


1

ฉันพบปัญหาด้านความปลอดภัยเมื่อดาวน์โหลด jar ใหม่ (ฯลฯ ) เช่นชายที่โจมตีกลาง คุณต้องลงนามการอัปเดตที่ดาวน์โหลดได้เสมอ

ใน JAX2015 Adam Bien บอกเกี่ยวกับการใช้ JGit เพื่ออัปเดตไบนารี น่าเศร้าที่ฉันไม่พบบทเรียนใด ๆ

แหล่งที่มาในภาษาเยอรมัน

Adam Bien สร้างตัวอัปเดตดูที่นี่

ฉันแยกมันที่นี่ด้วยส่วนหน้า javaFX ฉันกำลังทำงานเกี่ยวกับการลงนามอัตโนมัติ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.