- JIRA Data Center Asynchronous Cache replication failing health check
- Problem
- Affected Versions:
- Cause
- Resolution
- java.rmi.NoSuchObjectException: no such object in table
- Solution 2
- Solution 3
- Solution 4
- Блог
- Получение исключения java.rmi.NoSuchObjectException при вызове метода на удаленном объекте
- Вопрос:
- Комментарии:
- Ответ №1:
- Комментарии:
JIRA Data Center Asynchronous Cache replication failing health check
Platform notice: Server and Data Center only. This article only applies to Atlassian products on the server and data center platforms .
This article applies to to Jira Data Center documentation and is not relevant for a standard JIRA installation.
Problem
Cluster health check is failing for cache replication and the following stack trace is present in Atlassian-jira.log
from cache replication queue: [queueId=queue_node1_6_4701d15959342d6659cddeeebe1bdbe7, queuePath=/var/atlassian/jira-home/localq/queue_nodexxxxxx7], failuresCount: 10/10. Removing from queue. Error: java.rmi.NoSuchObjectException: no such object in table com.atlassian.jira.cluster.distribution.localq.LocalQCacheOpSender$UnrecoverableFailure: java.rmi.NoSuchObjectException: no such object in table at com.atlassian.jira.cluster.distribution.localq.rmi.LocalQCacheOpRMISender.send(LocalQCacheOpRMISender.java:88) at com.atlassian.jira.cluster.distribution.localq.LocalQCacheOpReader.run(LocalQCacheOpReader.java:83) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745)
localq-reader-3 ERROR [c.a.j.c.distribution.localq.LocalQCacheOpReader] [LOCALQ] [VIA-COPY] Abandoning sending: LocalQCacheOp. failuresCount: 1/1. Removing from queue. Error: java.rmi.UnknownHostException: Unknown host: node1; nested exception is: java.net.UnknownHostException: node1
Affected Versions:
Jira Data Center versions 7.9 and above with Clustering enabled
Cause
Individual hostnames are not resolvable by other Jira Data Center nodes in the cluster.
This can also occur if there’s an invalid hostname set by a JVM parameter:
-Djava.rmi.server.hostname=localhost
» -Djava.rmi.hostname /bin/setenv.sh» will override the parameter » ehcache.listener.hostName /cluster.properties»
Resolution
There are 3 possible solutions for this issue:
- Remove the offending input from the file » /bin/setenv.sh»
- Set this parameter value to the machine’s IP address;
- Set this parameter value to a resolvable hostname;
JIRA must be restarted for this change to take effect
java.rmi.NoSuchObjectException: no such object in table
Below is a short program that demonstrates a java.rmi.NoSuchObjectException . The script is self-contained, creating an RMI registry as well as a «client» and a «server» in a single JVM.
Simply copy this code and save it in a file named RMITest.java . Compile and invoke with your choice of command line arguments:
- -gc (default) Explicitly instruct the JVM to make «a best effort» to run the garbage collector after the server is started, but before the client connects to the server. This will likely cause the Remote object to be reclaimed by the garbage collector if the strong reference to the Remote object is released. A java.rmi.NoSuchObjectException is observed when the client connects after the Remote object is reclaimed.
- -nogc Do not explicitly request garbage collection. This will likely cause the Remote object to remain accessible by the client regardless of whether a strong reference is held or released unless there is a sufficient delay between the server start and the client call such that the system «naturally» invokes the garbage collector and reclaims the Remote object.
- -hold Retain a strong reference to the Remote object. In this case, a class variable refers to the Remote object.
- -release (default) A strong reference to the Remote object will be released. In this case, a method variable refers to the Remote object. After the method returns, the strong reference is lost.
- -delay The number of seconds to wait between server start and the client call. Inserting a delay provides time for the garbage collector to run «naturally.» This simulates a process that «works» initially, but fails after some significant time has passed. Note there is no space before the number of seconds. Example: -delay5 will make the client call 5 seconds after the server is started.
Program behavior will likely vary from machine to machine and JVM to JVM because things like System.gc() are only hints and setting the -delay option is a guessing game with respect to the behavior of the garbage collector.
On my machine, after javac RMITest.java to compile, I see this behavior:
$ java RMITest -nogc -hold received: foo $ java RMITest -nogc -release received: foo $ java RMITest -gc -hold received: foo $ java RMITest -gc -release Exception in thread "main" java.rmi.NoSuchObjectException: no such object in table at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:255) at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:233) at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:142) at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(RemoteObjectInvocationHandler.java:178) at java.rmi.server.RemoteObjectInvocationHandler.invoke(RemoteObjectInvocationHandler.java:132) at $Proxy0.remoteOperation(Unknown Source) at RMITest.client(RMITest.java:69) at RMITest.main(RMITest.java:46)
import java.rmi.Remote; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.UnicastRemoteObject; import static java.util.concurrent.TimeUnit.*; interface RemoteOperations extends Remote < String remoteOperation() throws RemoteException; >public final class RMITest implements RemoteOperations < private static final String REMOTE_NAME = RemoteOperations.class.getName(); private static final RemoteOperations classVariable = new RMITest(); private static boolean holdStrongReference = false; private static boolean invokeGarbageCollector = true; private static int delay = 0; public static void main(final String. args) throws Exception < for (final String arg : args) < if ("-gc".equals(arg)) < invokeGarbageCollector = true; >else if ("-nogc".equals(arg)) < invokeGarbageCollector = false; >else if ("-hold".equals(arg)) < holdStrongReference = true; >else if ("-release".equals(arg)) < holdStrongReference = false; >else if (arg.startsWith("-delay")) < delay = Integer.parseInt(arg.substring("-delay".length())); >else < System.err.println("usage: javac RMITest.java && java RMITest [-gc] [-nogc] [-hold] [-release] [-delay]"); System.exit(1); > > server(); if (invokeGarbageCollector) < System.gc(); >if (delay > 0) < System.out.println("delaying " + delay + " seconds"); final long milliseconds = MILLISECONDS.convert(delay, SECONDS); Thread.sleep(milliseconds); >client(); System.exit(0); // stop RMI server thread > @Override public String remoteOperation() < return "foo"; >private static void server() throws Exception < // This reference is eligible for GC after this method returns final RemoteOperations methodVariable = new RMITest(); final RemoteOperations toBeStubbed = holdStrongReference ? classVariable : methodVariable; final Remote remote = UnicastRemoteObject.exportObject(toBeStubbed, 0); final Registry registry = LocateRegistry.createRegistry(Registry.REGISTRY_PORT); registry.bind(REMOTE_NAME, remote); >private static void client() throws Exception < final Registry registry = LocateRegistry.getRegistry(); final Remote remote = registry.lookup(REMOTE_NAME); final RemoteOperations stub = RemoteOperations.class.cast(remote); final String message = stub.remoteOperation(); System.out.println("received: " + message); >>
Solution 2
Some other questions to consider — First are you referencing an object instance or is the stub interface itself gone? If some object instance is gone, its for the usual reasons, it got dereferenced and GC’d, but if it’s the interface then your RMI server end point loop quit for some reason.
The best debugging tool I’ve found so far is to turn on the java.rmi.server.logCalls=true property (see http://java.sun.com/j2se/1.5.0/docs/guide/rmi/javarmiproperties.html) and watch all the wonderfull information stream down your log window. This tells me what’s up every time.
Solution 3
I have the same problem and now I’ve solved it. The solution is simple, you MUST create strong reference ‘object’ to avoid the object being GC’d.
for example in your server class:
. private static ServiceImpl serviceImpl = null; public static void register (int port) < serviceImpl = new ServiceImpl(); Registry registry = LocateRegistry.createRegistry(port); registry.rebind ("serviceImpl", serviceImpl); >public static void main(String[] args) throws RemoteException, NotBoundException
So, it protects «serviceImpl» object from being GC’d. CMIIW
Solution 4
there is one point missing in the above discussion. There is something that is called distributed garbage collection (DGC). If there are no living local and remote references to a distributed object the GC is allowed to remove the object from memory. There is a sophisticated algorithm to verify this. The nice code snippet from above is indeed a good demonstration of the effectiveness of the DGC.
What somehow looks like a feature is nothing but the designed behavior!
Блог
Получение исключения java.rmi.NoSuchObjectException при вызове метода на удаленном объекте
Вопрос:
Я знаю, что такие вопросы задавались раньше.Но моя проблема немного отличается. У меня есть 2 службы, A и B.A использует методы B через RMI. B экспортирует себя с помощью следующих 3-х утверждений:-
Remote stub = UnicastRemoteObject.exportObject( rs, port,socketFactory,socketFactory); Registry registry = LocateRegistry.createRegistry( port,socketFactory,socketFactory ); registry.rebind( serviceBindName, stub );
где rs — реализация удаленного объекта.
Теперь A ищет B с помощью приведенных ниже 2 инструкций :-
Registry registry = LocateRegistry.getRegistry( BsIP, BsRMIPort); RemoteServiceRegistry rs = ( RemoteServiceRegistry ) registry.lookup( serviceBindName);
Теперь все работает гладко.A получает заглушку из B через поиск и кэширует ее для всех последующих запросов.
Но проблемы начинаются при перезапуске B. У меня есть механизм, с помощью которого всякий раз, когда B перезапускается, я очищаю кеш и выполняю новый поиск B в A. Но почему-то он не работает и выдает исключения как в A, так и в B.
Я ничего не знаю об этом. Пожалуйста, помогите.Дайте мне знать, если вам нужна дополнительная информация.
java.rmi.NoSuchObjectException: no such object in table at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:255) at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:233) at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:142) at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(RemoteObjectInvocationHandler.java:178) at java.rmi.server.RemoteObjectInvocationHandler.invoke(RemoteObjectInvocationHandler.java:132)
Исключение в B (после добавления свойства в JVM, -Djava.rmi.server.logCalls=true)
FINE: RMI TCP Connection(10)-192.168.50.243: [192.168.50.243] exception: java.rmi.NoSuchObjectException: no such object in table at sun.rmi.transport.Transport.serviceCall(Transport.java:135) at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:535) at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:790) at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:649) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) at java.lang.Thread.run(Thread.java:662)
Комментарии:
1. Одно замечание . Проблема исчезает после перезапуска A. Но это решение для меня неприемлемо. Где-то что-то не так. Пожалуйста, помогите в диагностике этого.
Ответ №1:
Это исключение означает, что удаленная заглушка «устарела», т.Е. Ссылается на объект, который больше не экспортируется из JVM, из которого была получена заглушка. Когда вы получаете это исключение, вы должны повторно получить заглушку тем же способом, которым вы ее получили в первую очередь:
- если через a lookup() , повторите поиск.
- если с помощью вашего собственного удаленного вызова метода, повторите его и обратите внимание, что это, в свою очередь, может завершиться ошибкой с тем же исключением и так далее рекурсивно.
.У меня есть механизм, с помощью которого всякий раз, когда B перезапускается, я очищаю кеш и выполняю новый поиск B в
Очевидно, что этот механизм не работает или не вызывается.
ПРИМЕЧАНИЕ. Вы всегда должны сохранять результат LocateRegistry.createRegistry() в статической переменной, чтобы предотвратить сбор мусора в реестре.
Комментарии:
1. Привет, EJP, спасибо за ваш ответ. Я проверил это, введя регистраторы, и обнаружил, что при перезапуске B A выполняет новый поиск RMI с помощью способа, который я упомянул в коде выше. Есть ли какие-либо другие проблемы, которые вы подозреваете?
2. Вы получаете это при самом поиске? или при вызове удаленного метода для полученной заглушки?
3. При вызове удаленного метода для полученной заглушки.
4. Кроме того, если реестр в B получает GCed, то почему все работает гладко после перезапуска A?
5. Тогда вы явно смотрите на устаревший реестр с устаревшими заглушками в нем. Таким образом, процесс перезапуска / реэкспорта / повторного связывания в B как-то не работает.