Liferay 模块(OSGi 捆绑包)停留在“Stopping”

Posted

技术标签:

【中文标题】Liferay 模块(OSGi 捆绑包)停留在“Stopping”【英文标题】:Liferay module (OSGi bundle) stays in "Stopping" 【发布时间】:2018-01-07 23:06:34 【问题描述】:

有时当我停止我的 Liferay 模块时(例如,当我将其 JAR 的新版本放入 deploy/ 时)模块拒绝停止。

虽然模块应该进入“已解决”状态,但它会永远处于“停止”状态:

通常是由于某个线程未在某处终止,或者网络连接未正确关闭,通常很难调查。

我的问题:如何更有效地找出这个 Liferay 模块的问题是什么?

我尝试了什么:

在 Gogo Shell 中,diag <module id> 似乎没有提供任何有价值的信息来说明模块拒绝离开“停止”状态的原因。 jstack 输出数千行,其中绝大多数在所讨论的 Liferay 模块之外。如果有办法只显示我的模块的 jstack 信息,那就太好了。

【问题讨论】:

您是否在模块中使用了激活器?状态停止意味着 Activator.stop() 方法被调用但尚未返回。你的一个线程应该在那个方法中。 @ChristianSchneider:添加了我的 ServiceActivator。 您在解决 jstack 问题方面做得很好。它创建了一个线程转储,但大多数线程大约有 5-10 行。寻找更大的线程,尤其是其中调用了 ServiceActivator.stop 方法的线程。然后您将看到导致死锁或长时间休眠的原因。 @BalazsZsoldos:寻找 ServiceActivator.stop 是一个很好的提示!你想写一个关于它的答案吗?或者你没时间我可以写。 @NicolasRaoul 我认为查看线程转储并尝试查找代码 sn-p 与其说是真正的答案,不如说是一个建议。如果您想就阻止的原因做出回应,请随意。 【参考方案1】:

首先,找到你的 webapp 服务器的 PID:

ps aux | grep tomcat

如果您正在运行除 tomcat 之外的其他服务器,或者如果您有多个实例正在运行,请调整该命令。

然后,将该服务器的所有线程转储到一个文件中:

jstack 12345 > jstack.txt

其中 12345 是您在第一步中找到的 PID。

然后,查看捆绑包的源代码,并找到服务激活器。它通常看起来像这样:

package fr.free.nrw;

[import section]

public class ServiceActivator implements BundleActivator 

    private ServiceRegistration registration;

    @Override
    public void start(BundleContext context) throws Exception 
        registration = context.registerService(
            MyService.class.getName(), new MyServiceImpl(), null);
    

    @Override
    public void stop(BundleContext context) throws Exception 
        registration.unregister();
    

注意:

命名空间, 类名, 停止方法名称。

例如在上面的例子中它们是fr.free.nrwServiceActivatorstop,从这三个中得到全名fr.free.nrw.ServiceActivator.stop

现在打开jstack.txt 并搜索全名。即使文件长达数千行,也很可能只有一次命中,这就是有问题的线程:

at org.eclipse.osgi.internal.serviceregistry.ServiceRegistrationImpl.unregister(ServiceRegistrationImpl.java:222)
at fr.free.nrw.ServiceActivator.stop(ServiceActivator.java:30)
at org.eclipse.osgi.internal.framework.BundleContextImpl$4.run(BundleContextImpl.java:830)
at org.eclipse.osgi.internal.framework.BundleContextImpl$4.run(BundleContextImpl.java:1)
at java.security.AccessController.doPrivileged(Native Method)
at org.eclipse.osgi.internal.framework.BundleContextImpl.stop(BundleContextImpl.java:823)

在这个文件中,向上到段落的开头,这将是这样的:

"fileinstall-/home/nico/p/liferay/osgi/modules" #37 daemon prio=5 os_prio=0 tid=0x00007f39480e3000 nid=0x384f waiting on condition [0x00007f395d169000]
  java.lang.Thread.State: WAITING (parking)
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for  <0x00000000eb8defb8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)

有了这些信息,您将知道发生了什么样的线程问题,并且您将能够使用通常的 Java 线程调试技术来解决它 (12)。

【讨论】:

【参考方案2】:

您共享的 Activator 永远不应阻塞 stop 方法。所以我怀疑它会导致你描述的行为。

【讨论】:

可以的。 Activator的stop方法是同步调用的。他取消注册服务,这会导致同步服务侦听器(和服务跟踪器)的调用。在 ServiceTracker 上可能会死锁。 啊..有趣。所以原因可能是其他包中的一些写得很糟糕的服务跟踪器或监听器。 可能是。我说的是服务跟踪器级别,但在更高级别上也很容易犯错误。例如:在 DS 组件的服务引用(或激活方法)的设置器中编写一些休眠或等待的业务逻辑。这些代码 sn-ps 通常在服务注册完成时在同一线程上调用,因为它们基于同步服务跟踪器。

以上是关于Liferay 模块(OSGi 捆绑包)停留在“Stopping”的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Maven 将第 3 方 OSGi 捆绑包添加到部署包中?

Liferay7 BPM门户开发之38: OSGi Bndtools开发入门

Gradle + OSGi Liferay7 模块,包含传递依赖

Liferay 7 OSGi的一些小技巧

OSGi 捆绑包从非捆绑包 jar 导入包:为它们创建捆绑包?

Apache Karaf 和 OSGI 捆绑包