Làm cách nào để viết một ứng dụng Java có thể tự cập nhật trong thời gian chạy?


91

Tôi muốn triển khai một ứng dụng java (ứng dụng máy chủ) có thể tải xuống phiên bản mới (tệp .jar) từ một url nhất định, sau đó tự cập nhật trong thời gian chạy.

Cách tốt nhất để làm điều này là gì và nó có khả thi không?

Tôi đoán rằng ứng dụng có thể tải xuống tệp .jar mới và khởi động nó. Nhưng tôi nên thực hiện việc chuyển giao như thế nào, ví dụ: biết khi nào ứng dụng mới được khởi động và sau đó thoát. Hoặc là có một cách tốt hơn để làm điều này?


4
Bạn đã xem Java Web Start chưa? Tuy nhiên, tôi tin rằng nó không tự cập nhật trong thời gian chạy (yêu cầu khởi động lại), vì vậy bạn có thể cần phải xem OSGi.
Thilo 23/10/10

@Thilo: Tôi nghĩ sẽ dễ dàng tải xuống tệp từ một url nhất định và sau đó khởi động nó bằng lệnh linux từ tệp jar đang chạy.
Jonas

1
Thiết kế của Java WebStart API làm cho nó không thể cập nhật khi đang chạy. Không may.
Thorbjørn Ravn Andersen


@meain boss, bạn rock, bạn đã làm nên ngày của tôi :)
iltaf khalid

Câu trả lời:


68

Cấu trúc cơ bản của một giải pháp như sau:

  • Có một vòng lặp chính chịu trách nhiệm tải liên tục phiên bản mới nhất của ứng dụng (nếu được yêu cầu) và khởi chạy nó.

  • Ứng dụng thực hiện công việc của mình, nhưng kiểm tra định kỳ URL tải xuống. Nếu nó phát hiện một phiên bản mới, nó sẽ thoát trở lại trình khởi chạy.

Có một số cách bạn có thể thực hiện điều này. Ví dụ:

  • Trình khởi chạy có thể là một tập lệnh trình bao bọc hoặc ứng dụng nhị phân khởi động một JVM mới để chạy ứng dụng từ tệp JAR được thay thế.

  • Trình khởi chạy có thể là một ứng dụng Java tạo ra một trình nạp lớp cho JAR mới, tải một lớp điểm vào và gọi một số phương thức trên đó. Nếu làm theo cách này, bạn phải để ý xem có rò rỉ bộ nhớ của classloader hay không, nhưng điều đó không khó. (Bạn chỉ cần đảm bảo rằng không có đối tượng nào có các lớp được tải từ JAR có thể truy cập được sau khi bạn khởi chạy lại.)

Ưu điểm của phương pháp bao bọc bên ngoài là:

  • bạn chỉ cần một JAR,
  • bạn có thể thay thế toàn bộ ứng dụng Java,
  • mọi luồng phụ do ứng dụng tạo, v.v. sẽ biến mất mà không có logic tắt đặc biệt và
  • bạn cũng có thể giải quyết việc khôi phục sau sự cố ứng dụng, v.v.

Cách tiếp cận thứ hai yêu cầu hai JAR, nhưng có những ưu điểm sau:

  • giải pháp là Java thuần túy và di động,
  • việc thay đổi sẽ nhanh hơn, và
  • bạn có thể dễ dàng giữ lại trạng thái khi khởi động lại (vấn đề rò rỉ mô-đun).

Cách "tốt nhất" phụ thuộc vào yêu cầu cụ thể của bạn.

Cũng cần lưu ý rằng:

  • Có những rủi ro bảo mật với tự động cập nhật. Nói chung, nếu máy chủ cung cấp bản cập nhật bị xâm phạm hoặc nếu cơ chế cung cấp bản cập nhật dễ bị tấn công, thì việc tự động cập nhật có thể dẫn đến sự xâm phạm của (các) máy khách.

  • Việc đẩy bản cập nhật cho khách hàng gây thiệt hại cho khách hàng có thể có rủi ro pháp lý và rủi ro đối với danh tiếng doanh nghiệp của bạn.


Nếu bạn có thể tìm ra cách để tránh phát minh lại bánh xe, điều đó sẽ tốt. Xem các câu trả lời khác để biết gợi ý.


43

Tôi hiện đang phát triển JAVA Linux Daemon và cũng có nhu cầu triển khai cơ chế tự động cập nhật. Tôi muốn giới hạn ứng dụng của mình trong một tệp jar và đã đưa ra một giải pháp đơn giản:

Đóng gói ứng dụng trình cập nhật trong chính bản cập nhật.

Ứng dụng : Khi ứng dụng phát hiện ra phiên bản mới hơn, ứng dụng sẽ thực hiện như sau:

  1. Tải xuống bản cập nhật (Zipfile)
  2. Giải nén ứng dụng và ứng dụngUpdater (tất cả trong tệp zip)
  3. Chạy trình cập nhật

ApplicationUpdater : Khi trình cập nhật chạy, nó sẽ thực hiện như sau:

  1. Dừng ứng dụng (trong trường hợp của tôi là daemon thông qua init.d)
  2. Sao chép tệp jar đã tải xuống để ghi đè Ứng dụng hiện tại
  3. Khởi động ứng dụng
  4. Dọn dẹp.

Hy vọng nó sẽ giúp một ai đó.


12

Đây là một vấn đề đã biết và tôi khuyên bạn không nên phát minh lại bánh xe - đừng viết bản hack của riêng bạn, chỉ sử dụng những gì người khác đã làm.

Hai tình huống bạn cần xem xét:

  1. Ứng dụng cần phải tự cập nhật và tiếp tục chạy ngay cả trong quá trình cập nhật (ứng dụng máy chủ, ứng dụng được nhúng). Đi với OSGi: Bundles hoặc Equinox p2 .

  2. Ứng dụng là một ứng dụng dành cho máy tính để bàn và có một trình cài đặt. Có nhiều trình cài đặt với tùy chọn cập nhật. Kiểm tra danh sách người cài đặt .


12

Gần đây tôi đã tạo update4j hoàn toàn tương thích với hệ thống mô-đun của Java 9.

Nó sẽ khởi động phiên bản mới một cách liền mạch mà không cần khởi động lại.


Bằng cách sử dụng mô hình bootstrap / ứng dụng kinh doanh. Bootstrap tải và dỡ ứng dụng kinh doanh và bootstrap thường thực hiện cập nhật.
Mordechai

10

Tôi đã viết một ứng dụng Java có thể tải các plugin trong thời gian chạy và bắt đầu sử dụng chúng ngay lập tức, lấy cảm hứng từ một cơ chế tương tự trong jEdit. jEdit là mã nguồn mở nên bạn có thể tùy chọn tìm kiếm để xem nó hoạt động như thế nào.

Giải pháp sử dụng một ClassLoader tùy chỉnh để tải các tệp từ jar. Sau khi chúng được tải, bạn có thể gọi một số phương thức từ jar mới sẽ hoạt động như mainphương thức của nó . Sau đó, phần khó khăn là đảm bảo bạn loại bỏ tất cả các tham chiếu đến mã cũ để nó có thể được thu thập. Tôi không phải là một chuyên gia về phần đó, tôi đã làm cho nó hoạt động nhưng nó không dễ dàng.


Tôi không biết về Java, nhưng trong C #, bạn có thể sử dụng AppDomains để dỡ mã một cách rõ ràng. Có thể có một khái niệm tương tự trong Java.
Merlyn Morgan-Graham

1
Cảm ơn, đây có vẻ là con đường để đi. Có một ví dụ về một NetworkClassLoadertrên JavaDoc cho ClassLoader
Jonas

Bạn đã gặp phải sự cố với các biến tĩnh trong cùng một JVM hay thế hệ vĩnh viễn thì sao? Tôi đã nghe nói những điều này có thể trình bày các vấn đề liên quan đến việc dỡ lớp.
Ide

5
  1. Cách đầu tiên: sử dụng tomcat và nó triển khai các phương tiện.
  2. Cách thứ hai: chia ứng dụng thành hai phần (chức năng và cập nhật) và để phần cập nhật thay thế phần chức năng.
  3. Cách thứ ba: Trong ứng dụng máy chủ của bạn chỉ cần tải xuống phiên bản mới, sau đó phiên bản cũ phát hành cổng ràng buộc, sau đó phiên bản cũ chạy phiên bản mới (bắt đầu quá trình), sau đó phiên bản cũ gửi yêu cầu trên cổng ứng dụng đến phiên bản mới để xóa phiên bản cũ, phiên bản cũ chấm dứt và phiên bản mới xóa phiên bản cũ. Như thế này: văn bản thay thế

Cách thứ hai của bạn có vẻ là một thiết kế thú vị.
Jonas

2

Đây không hẳn là cách tốt nhất , nhưng nó có thể phù hợp với bạn.

Bạn có thể viết một ứng dụng bootstrap (ala là trình khởi chạy World of Warcraft, nếu bạn đã chơi WoW). Bootstrap đó chịu trách nhiệm kiểm tra các bản cập nhật.

  • Nếu có bản cập nhật, nó sẽ cung cấp cho người dùng, xử lý quá trình tải xuống, cài đặt, v.v.
  • Nếu ứng dụng được cập nhật, nó sẽ cho phép người dùng khởi chạy ứng dụng
  • Theo tùy chọn, bạn có thể cho phép người dùng khởi chạy ứng dụng, ngay cả khi nó chưa được cập nhật

Bằng cách này, bạn không phải lo lắng về việc buộc thoát ứng dụng của mình.

Nếu ứng dụng của bạn dựa trên web và nếu điều quan trọng là chúng phải có một ứng dụng khách cập nhật, thì bạn cũng có thể kiểm tra phiên bản trong khi ứng dụng chạy. Bạn có thể thực hiện chúng theo các khoảng thời gian, trong khi thực hiện giao tiếp bình thường với máy chủ (một số hoặc tất cả các cuộc gọi) hoặc cả hai.

Đối với sản phẩm mà tôi đã làm việc gần đây, chúng tôi đã kiểm tra phiên bản khi khởi chạy (không có ứng dụng bộ đệm khởi động, nhưng trước khi cửa sổ chính xuất hiện) và trong khi gọi đến máy chủ. Khi máy khách đã lỗi thời, chúng tôi dựa vào việc người dùng để thoát theo cách thủ công, nhưng cấm mọi hành động chống lại máy chủ.

Xin lưu ý rằng tôi không biết liệu Java có thể gọi mã giao diện người dùng hay không trước khi bạn hiển thị cửa sổ chính của mình. Chúng tôi đã sử dụng C # / WPF.


Cảm ơn, đó là một cách tốt để làm điều đó. Nhưng đó là một ứng dụng máy chủ nên không có người dùng nào có thể thực hiện một số hành động. Và tôi muốn rằng đó chỉ là một tệp jar duy nhất, vì vậy người dùng easyli có thể tải xuống một tệp duy nhất và khởi động nó ngay từ đầu, nhưng sau đó tôi không muốn có tương tác của người dùng.
Jonas

Khi bạn nói "ứng dụng máy chủ", bạn có nghĩa là nó là một ứng dụng được chạy trực tiếp trên máy chủ, trong khi đăng nhập?
Merlyn Morgan-Graham

@Jonas: Tôi không hiểu nhiều về cách hoạt động của tệp .jar, vì vậy tôi không thể phục vụ nhiều ở đó. Tôi có thể hiểu nếu bạn muốn một câu trả lời cụ thể cho Java. Hy vọng rằng điều này sẽ cho bạn thực phẩm cho rằng, ít nhất :)
Merlyn Morgan-Graham

@Merlyn: Tôi rất cảm ơn vì đã chia sẻ ý kiến ​​của bạn. Có, đó là một ứng dụng Java sẽ chạy trên máy chủ Linux và không ai đăng nhập trên máy chủ đó.
Jonas

1
@Jonas: Không phải bạn chưa nghĩ đến điều này, nhưng - Hầu hết các dịch vụ web tôi đã thấy đều có chiến lược triển khai thủ công. Bạn có thể muốn cẩn thận về cách bạn kiểm tra các bản cập nhật. Nếu bạn đẩy mã lỗi / mã bị hỏng hoặc chỉ hoàn thành một phần khi triển khai nhiều tệp, máy chủ có thể cố gắng tự cập nhật và khi đó bạn sẽ có một máy chủ bị hỏng.
Merlyn Morgan-Graham

2

Nếu bạn xây dựng ứng dụng của mình bằng plugin Equinox , bạn có thể sử dụng Hệ thống cấp phép P2 để có giải pháp sẵn sàng cho vấn đề này. Điều này sẽ yêu cầu máy chủ tự khởi động lại sau khi cập nhật.


1

Tôi gặp sự cố bảo mật khi tải xuống một jar mới (v.v.), ví dụ: kẻ ở giữa cuộc tấn công. Bạn luôn phải ký vào bản cập nhật có thể tải xuống của mình.

Vào JAX2015, Adam Bien đã nói về việc sử dụng JGit để cập nhật các tệp nhị phân. Đáng buồn là tôi không thể tìm thấy bất kỳ hướng dẫn nào.

Nguồn bằng tiếng Đức.

Adam Bien đã tạo trình cập nhật xem tại đây

Tôi đã tách nó ở đây với một số giao diện người dùng javaFX. Tôi cũng đang làm việc trên một bản ký tự động.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.