Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dubbo2.7.1官网上优雅停机示例方法没有 #4288

Closed
zhaohaiyu opened this issue Jun 12, 2019 · 6 comments
Closed

dubbo2.7.1官网上优雅停机示例方法没有 #4288

zhaohaiyu opened this issue Jun 12, 2019 · 6 comments
Assignees
Milestone

Comments

@zhaohaiyu
Copy link

Environment

  • Dubbo version: 2.7.1
  • Operating System version: centos7
  • Java version: 1.8

Steps to reproduce this issue

  1. 官网http://dubbo.apache.org/zh-cn/docs/user/demos/graceful-shutdown.html上示例:
    使用tomcat等容器部署的場景,建议通过扩展ContextListener等自行调用以下代码实现优雅停机:
    ProtocolConfig.destroyAll();
  2. 在dubbo2.7.1和2.7.2中ProtocolConfig没有destroyAll这个静态方法

Expected Result

在2.7.1和2.7.2中被替换成什么?怎么用

@web1992
Copy link
Contributor

web1992 commented Jun 12, 2019

可以用下面这个试试:

org.apache.dubbo.config.DubboShutdownHook

@ryuhi
Copy link

ryuhi commented Jun 25, 2019

2.6.X的时候,必须使用com.alibaba.dubbo.container.Main作为项目启动主入口,并在src\main\resources下面建立META-INF\spring目录,将项目的一些配置文件拷贝到这里,并将项目打成可执行jar包执行,这样在需要优雅停机的时候,就可以选择ctrl+c停机,或者kill pid的模式停机,选择该类执行时,Dubbo会自动调起Spring的配置,初始化整个spring项目后再完成Dubbo的配置。

但到2.7.X的时候,org.apache.dubbo.container.Main这个类依然存在,但是已经不能自动调起spring的配置了。我使用的是SpringBoot 2.1.3,使用dubbo的starter,dubbo2.7.2,zk3.4.11,apache的curator 4.2.0

因此我也想追问下,org.apache.dubbo.container.Main这个类还能用于优雅停机吗?原来2.6.X的那种启动模式还存在吗?如果存在,要怎么配置才能实现?

@ryuhi
Copy link

ryuhi commented Jul 31, 2019

我自己研究过了,结论是Dubbo 2.7.X不支持Main类以Java config模式配置的方式启动,以XML配置的方式可以

@XiaoDongJi
Copy link

我用的dubbo2.7.2 ,配置文件是XML ,Main方法以spring Container方式启动,如果dubbo.shutdown.hook 未配置,Main类中就不会注册钩子,优雅停机是通过AbstractConfig 中的DubboShutdownHook.getDubboShutdownHook().register(); 实现的吗? 这个怎么避免使用到Spring已经销毁的Bean呢;如果是SpringBoot 方式启动 ShutdownHookListener 监听 ContextClosedEvent 来优雅关机,有点疑问不知道哪位大佬能解答下

@beiwei30 beiwei30 added this to the 2.7.5 milestone Aug 6, 2019
@superleo-cn
Copy link

superleo-cn commented Sep 22, 2019

先说结论:如果是和Spring集成那么这个不用担心2个冲突执行的问题,因为初始化的时候dubbo就从runtime改到spring context上去了:

源码追踪如下:

Dubbo源码 org.apache.dubbo.config.spring.extension.SpringExtensionFactory.java:

public class SpringExtensionFactory implements ExtensionFactory {
    private static final Logger logger = LoggerFactory.getLogger(SpringExtensionFactory.class);

    private static final Set<ApplicationContext> CONTEXTS = new ConcurrentHashSet<ApplicationContext>();
    private static final ApplicationListener SHUTDOWN_HOOK_LISTENER = new ShutdownHookListener();

    public static void addApplicationContext(ApplicationContext context) {
        CONTEXTS.add(context);
        if (context instanceof ConfigurableApplicationContext) {
            // 注册到spring上面
            ((ConfigurableApplicationContext) context).registerShutdownHook();
            // 从java.lang.Runtime.getRuntime() 中取消注册
            DubboShutdownHook.getDubboShutdownHook().unregister();
        }
        BeanFactoryUtils.addApplicationListener(context, SHUTDOWN_HOOK_LISTENER);
    }

然后在 Spring接到kill信号的时候,进入hook回调,然后spring在依次去调用Application Event通知各个注册在spring hook的bean去执行他们自己的hook回调:

这个是dubbo和spring集成后的hook操作

private static class ShutdownHookListener implements ApplicationListener {
        @Override
        public void onApplicationEvent(ApplicationEvent event) {
            if (event instanceof ContextClosedEvent) {
                DubboShutdownHook shutdownHook = DubboShutdownHook.getDubboShutdownHook();
                shutdownHook.doDestroy();
            }
        }
    }

等到执行完成后,spring才去销毁对象。

Spring 源码 org.springframework.context.support.AbstractApplicationContext.java:

protected void doClose() {
	// Check whether an actual close attempt is necessary...
	if (this.active.get() && this.closed.compareAndSet(false, true)) {
		if (logger.isDebugEnabled()) {
			logger.debug("Closing " + this);
		}

		LiveBeansView.unregisterApplicationContext(this);

		try {
			// 看这里,通知各个hook去关闭
			publishEvent(new ContextClosedEvent(this));
		}
		catch (Throwable ex) {
			logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
		}

		// Stop all Lifecycle beans, to avoid delays during individual destruction.
		if (this.lifecycleProcessor != null) {
			try {
				this.lifecycleProcessor.onClose();
			}
			catch (Throwable ex) {
				logger.warn("Exception thrown from LifecycleProcessor on context close", ex);
			}
		}

		// 开始销毁单例对象等
		destroyBeans();

		// 销毁Bean工厂
		closeBeanFactory();

		// Let subclasses do some final clean-up if they wish...
		onClose();

		// Reset local application listeners to pre-refresh state.
		if (this.earlyApplicationListeners != null) {
			this.applicationListeners.clear();
			this.applicationListeners.addAll(this.earlyApplicationListeners);
		}

		// Switch to inactive.
		this.active.set(false);
		...
	}
}	

@beiwei30
Copy link
Member

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants