Jak bezpiecznie zatrzymać proces Java w systemie Linux i Windows?
Kiedy dzwonią Runtime.getRuntime().addShutdownHook
, a kiedy nie?
A co z finalizatorami, czy oni tu pomagają?
Czy mogę wysłać jakiś sygnał do procesu Java z powłoki?
Szukam najlepiej przenośnych rozwiązań.
Odpowiedzi:
Haki zamykania są wykonywane we wszystkich przypadkach, gdy maszyna wirtualna nie jest zabijana na siłę. Tak więc, jeśli miałbyś wydać „standardowe” zabicie (
SIGTERM
z polecenia zabicia), to wykonają. Podobnie wykonają się po wywołaniuSystem.exit(int)
.Jednak twarde zabicie (
kill -9
lubkill -SIGKILL
) nie zostanie wykonane. Podobnie (i oczywiście) nie wykonają się, jeśli wyciągniesz zasilanie z komputera, wrzucisz go do kadzi z wrzącą lawą lub rozbijesz procesor na kawałki młotem. Prawdopodobnie już to wiedziałeś.Finalizatory naprawdę również powinny działać, ale najlepiej nie polegać na tym podczas czyszczenia po zamknięciu, ale raczej polegać na hakach zamykania, aby zatrzymać wszystko w czysty sposób. I, jak zawsze, uważaj na zakleszczenia (widziałem zbyt wiele haków zamykających zawieszających cały proces)!
źródło
Ok, po tych wszystkich możliwościach, które wybrałem do pracy z "Java Monitoring and Management"
Przegląd jest tutaj,
który pozwala w stosunkowo łatwy sposób kontrolować jedną aplikację od drugiej. Możesz wywołać aplikację sterującą ze skryptu, aby bezpiecznie zatrzymać kontrolowaną aplikację przed jej zabiciem.
Oto uproszczony kod:
Aplikacja kontrolowana:
uruchom ją z następującymi parametrami maszyny wirtualnej:
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port = 9999
-Dcom.sun.management.jmxremote.authenticate = false
-Dcom.sun.management. jmxremote.ssl = false
//ThreadMonitorMBean.java public interface ThreadMonitorMBean { String getName(); void start(); void stop(); boolean isRunning(); } // ThreadMonitor.java public class ThreadMonitor implements ThreadMonitorMBean { private Thread m_thrd = null; public ThreadMonitor(Thread thrd) { m_thrd = thrd; } @Override public String getName() { return "JMX Controlled App"; } @Override public void start() { // TODO: start application here System.out.println("remote start called"); } @Override public void stop() { // TODO: stop application here System.out.println("remote stop called"); m_thrd.interrupt(); } public boolean isRunning() { return Thread.currentThread().isAlive(); } public static void main(String[] args) { try { System.out.println("JMX started"); ThreadMonitorMBean monitor = new ThreadMonitor(Thread.currentThread()); MBeanServer server = ManagementFactory.getPlatformMBeanServer(); ObjectName name = new ObjectName("com.example:type=ThreadMonitor"); server.registerMBean(monitor, name); while(!Thread.interrupted()) { // loop until interrupted System.out.println("."); try { Thread.sleep(1000); } catch(InterruptedException ex) { Thread.currentThread().interrupt(); } } } catch(Exception e) { e.printStackTrace(); } finally { // TODO: some final clean up could be here also System.out.println("JMX stopped"); } } }
Kontrolowanie aplikacji:
uruchom ją ze stop lub start jako argumentem wiersza poleceń
public class ThreadMonitorConsole { public static void main(String[] args) { try { // connecting to JMX System.out.println("Connect to JMX service."); JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://:9999/jmxrmi"); JMXConnector jmxc = JMXConnectorFactory.connect(url, null); MBeanServerConnection mbsc = jmxc.getMBeanServerConnection(); // Construct proxy for the the MBean object ObjectName mbeanName = new ObjectName("com.example:type=ThreadMonitor"); ThreadMonitorMBean mbeanProxy = JMX.newMBeanProxy(mbsc, mbeanName, ThreadMonitorMBean.class, true); System.out.println("Connected to: "+mbeanProxy.getName()+", the app is "+(mbeanProxy.isRunning() ? "" : "not ")+"running"); // parse command line arguments if(args[0].equalsIgnoreCase("start")) { System.out.println("Invoke \"start\" method"); mbeanProxy.start(); } else if(args[0].equalsIgnoreCase("stop")) { System.out.println("Invoke \"stop\" method"); mbeanProxy.stop(); } // clean up and exit jmxc.close(); System.out.println("Done."); } catch(Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
Otóż to. :-)
źródło
Inny sposób: Twoja aplikacja może otworzyć społeczność serwerów i czekać na nadejście informacji. Na przykład łańcuch z "magicznym" słowem :), a następnie zareaguj, aby zamknąć: System.exit (). Możesz wysłać takie informacje do gniazda za pomocą zewnętrznej aplikacji, takiej jak telnet.
źródło
Oto trochę trudne, ale przenośne rozwiązanie:
Zaimplementowałem agenta Java. Jest dostępny na Github: https://github.com/everit-org/javaagent-shutdown
Szczegółowy opis rozwiązania dostępny jest tutaj: https://everitorg.wordpress.com/2016/06/15/shutting-down-a-jvm-process/
źródło
Podobne pytanie tutaj
Finalizatory w Javie są złe. Dodają dużo narzutów do zbierania śmieci. Unikaj ich, gdy tylko jest to możliwe.
ShutdownHook zostanie wywołany tylko podczas zamykania maszyny wirtualnej. Myślę, że bardzo dobrze może zrobić, co chcesz.
źródło
Sygnalizacja w Linuksie może być wykonana za pomocą "kill" (man kill dla dostępnych sygnałów), do tego potrzebny byłby identyfikator procesu. (ps ax | grep java) lub coś w tym rodzaju, lub przechowuj identyfikator procesu, gdy proces zostanie utworzony (jest to używane w większości plików startowych Linuksa, patrz /etc/init.d)
Sygnalizacja przenośna może zostać zrealizowana poprzez integrację SocketServer z aplikacją Java. Nie jest to takie trudne i daje swobodę wysyłania dowolnych poleceń.
Jeśli miałeś na myśli klauzule ostateczne zamiast finalizatorów; nie są uruchamiane, gdy wywoływana jest funkcja System.exit (). Finalizatory powinny działać, ale tak naprawdę nie powinny robić nic bardziej znaczącego, ale drukować instrukcję debugowania. Są niebezpieczne.
źródło
Dzięki za odpowiedzi. Haczyki zamykające szwy jak coś, co by działało w moim przypadku. Ale natknąłem się również na coś, co nazywa się fasolami monitorowania i zarządzania:
http://java.sun.com/j2se/1.5.0/docs/guide/management/overview.html
Daje to kilka fajnych możliwości zdalnego monitorowania i manipulacji procesu java. (Został wprowadzony w Javie 5)
źródło