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

适配线程池延迟初始化 #875

Closed
wulangcode opened this issue Oct 31, 2022 · 7 comments · Fixed by #886
Closed

适配线程池延迟初始化 #875

wulangcode opened this issue Oct 31, 2022 · 7 comments · Fixed by #886

Comments

@wulangcode
Copy link
Member

需求建议

hippo4j-spring-boot-start-example配置文件中加入spring.main.lazy-initialization=true
客户端clientwork 长轮训线程进入阻塞,并未被唤醒
image

修改方式

  1. 框架加入一个继承CommandLineRunner的bean,项目启动后唤醒客户端长轮训线程,去除事件监听器逻辑,健康检查等相关逻辑同步修改。
  2. 进入长轮训判断取消。
    2.1 因为客户端可能存在懒加载的bean,当map.isEmpty()时,依旧进入。
    2.2 长轮训方法中加入判断逻辑,再加入一层唤醒机制,当本地注册的DynamicThreadPoolExecutor不为空时,取消睡眠

如果认为这的确需要去适配,稍后我会去实现他。

您的功能请求是否与问题有关?

描述你想要的功能

@pirme
Copy link
Member

pirme commented Oct 31, 2022

请教几个问题:

  1. 去除事件监听器逻辑,健康检查等相关逻辑同步修改 -- 这个能具体说说是为什么么
  2. 因为客户端可能存在懒加载的bean,当map.isEmpty()时,依旧进入。 如果真的没有动态线程池 bean,何时退出呢

@iwangjie
Copy link
Member

可否实现在延迟启动成功后,再进行 ClientWorker 初始化。来避免 @pirme 描述的问题 2。

@wulangcode
Copy link
Member Author

  1. 我前面想的是去除监听器逻辑,因为ApplicationReadyEvent在懒加载的情况下是不会发送这个事件的。可以替换成ApplicationStartedEvent的事件或者ApplicationRunner 去修改健康检查中的AbstractHealthCheck#contextInitComplete字段。去除唤醒客户端的监听事件。
  2. 没有动态线程池 bean时,线程挂起,我觉得是应该在长轮训中挂起,而不是判断为map.isEmpty()时不进入长轮训。而睡眠我是觉得在长轮训中睡眠比较合适,因为只要是存在DynamicThreadPoolExecutor默认都是需要注册的。

进入长轮训代码

        this.executor.schedule(() -> {
            try {
                // awaitApplicationComplete.await();
                // if (CollectionUtil.isNotEmpty(cacheMap)) {
                    executorService.execute(new LongPollingRunnable());
                // }
            } catch (Throwable ex) {
                log.error("Sub check rotate check error.", ex);
            }
        }, 1L, TimeUnit.MILLISECONDS);

长轮训代码

        public void run() {
            if (CollectionUtil.isEmpty(cacheMap)) {
                awaitApplicationComplete.await();
            }
            serverHealthCheck.isHealthStatus();
            ......
        }

改为在增加缓存时唤醒

    public void addTenantListeners(String namespace, String itemId, String threadPoolId, List<? extends Listener> listeners) {
        CacheData cacheData = addCacheDataIfAbsent(namespace, itemId, threadPoolId);
        for (Listener listener : listeners) {
            cacheData.addListener(listener);
        }
        awaitApplicationComplete.countDown();
    }

@magestacks
Copy link
Member

最早我在测试长轮询时,遇到过特殊情况,具体不太清楚了,需要等待所有动态线程池准备就绪才会触发监听逻辑。

而如果按照你这种方式的话,就不具备全部准备就绪这种特性了。

@magestacks
Copy link
Member

能否不改动长轮询全局准备就绪逻辑,而是就 spring.main.lazy-initialization=true 这种情况来解决

@wulangcode
Copy link
Member Author

最早我在测试长轮询时,遇到过特殊情况,具体不太清楚了,需要等待所有动态线程池准备就绪才会触发监听逻辑。

而如果按照你这种方式的话,就不具备全部准备就绪这种特性了。

  1. spring boot提供这种懒加载的情况下,等待全部动态线程池准备就绪应该是个伪命题。当然就算开启了spring.main.lazy-initialization=true也可以使用@Lazy(value = false)来避开这个懒加载。
  2. 只针对spring.main.lazy-initialization=true这种情况来解决,应该可以根据beanFactory中是否存在LazyInitializationBeanFactoryPostProcessor这个bean来解决,但我并不觉得这样优雅。
  3. 在启用spring.main.lazy-initialization=true的情况下,是没有ApplicationReadyEvent这个事件的,长轮训入口那里是会一直wait(),存在一个懒加载,一个不懒加载的情况下,也不会进入长轮训。因为依靠ApplicationReadyEvent来唤醒是不可靠的。

以上,我个人还是觉得长轮训是必须进入的,在里面挂起可以方便后面唤醒,而在入口那里拦截,是不妥的。

@magestacks
Copy link
Member

那我没问题了,可以尝试进行改造,注意做好不同情况下测试即可。

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

Successfully merging a pull request may close this issue.

4 participants