Skip to content

Commit

Permalink
ioc容器增加concurrentStart支持,配置nop.ioc.app-beans-container.concurrent-sta…
Browse files Browse the repository at this point in the history
…rt=true会导致异步执行ioc容器的初始化,不阻塞启动过程
  • Loading branch information
entropy-cloud committed Nov 18, 2024
1 parent 64f1afa commit 6420484
Show file tree
Hide file tree
Showing 17 changed files with 238 additions and 30 deletions.
1 change: 1 addition & 0 deletions docs/performance/optimize-app.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
* `nop.auth.login.allow-create-default-user`不设置或者设置为false,启动时就不会检查数据库中是否至少存在一个用户。
* `nop.web.auto-load-dynamic-file`不设置或者设置为false, 启动时就不会自动将xjs翻译为js文件。
* `nop.graphql.eager-init-biz-object`设置为false, 启动时不会立刻解析meta文件等创建BizObject,而是访问到某个对象时再延迟创建
* `nop.ioc.app-beans-container.concurrent-start`设置为true, 则启动时在线程池上执行bean容器的初始化操作,不阻塞主线程

## 优化应用运行时性能
* `nop.debug` 设置为false, 就不会在dump目录下产生输出
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ public interface IBeanContainer extends IBeanProvider {

void start();

/**
* 异步启动时强制等待启动完毕
*/
default void awaitStartFinished(){

}

void stop();

void restart();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,9 @@
<bean id="io.nop.graphql.core.web.SysBizModel" ioc:type="@bean:id"/>

<!--
BizModel模型初始化时需要校验obj字典是否有效,因此需要注册对应的DictLoader
BizModel模型初始化时需要校验obj字典是否有效,因此需要注册对应的DictLoader。为了避免并行初始化报错,这里还需要强制指定依赖于ormTemplate
-->
<bean id="nopObjDictLoader" class="io.nop.biz.dict.ObjDictLoader" ioc:before="nopBizObjectManager"/>
<bean id="nopObjDictLoader" class="io.nop.biz.dict.ObjDictLoader" ioc:before="nopBizObjectManager" depends-on="nopOrmTemplate"/>

<bean id="nopContextHttpServerFilter" class="io.nop.http.api.server.ContextHttpServerFilter">
<ioc:condition>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public interface CommonConfigs {

@Description("全局工作线程池的大小")
IConfigReference<Integer> CFG_CONCURRENT_GLOBAL_WORKER_MAX_POOL_SIZE = varRef(s_loc,
"nop.commons.concurrent.global-worker.maxPoolSize", Integer.class, 50);
"nop.commons.concurrent.global-worker.maxPoolSize", Integer.class, 30);

@Description("限制StringHelper.repeat函数的参数值小于指定值,不会过大")
IConfigReference<Integer> CFG_UTILS_STRING_MAX_REPEAT_LEN = varRef(s_loc, "nop.commons.util.string-max-repeat-len",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6558,6 +6558,29 @@
"methods": [],
"name": "io.nop.integration.qrcode.ZxingQrcodeService"
},
{
"allDeclaredConstructors": true,
"allDeclaredMethods": true,
"allPublicConstructors": true,
"allPublicFields": true,
"allPublicMethods": true,
"fields": [
{
"name": "beanId"
},
{
"name": "created"
},
{
"name": "depends"
},
{
"name": "threadId"
}
],
"methods": [],
"name": "io.nop.ioc.impl.BeanContainerImpl$BeanCreation"
},
{
"allDeclaredConstructors": true,
"allDeclaredMethods": true,
Expand Down
9 changes: 6 additions & 3 deletions nop-demo/nop-quarkus-demo/src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ nop:

auth:
login:
allow-create-default-user: true # 如果用户表为空,则自动创建缺省账户nop, 密码nop-test
allow-create-default-user: false # 如果用户表为空,则自动创建缺省账户nop, 密码nop-test

datasource:
driver-class-name: org.h2.Driver
Expand All @@ -84,11 +84,11 @@ nop:
password:

web:
validate-page-model: true
validate-page-model: false
auto-load-dynamic-file: false

orm:
init-database-schema: true
init-database-schema: false
db-differ:
auto-upgrade-database: false

Expand All @@ -97,6 +97,9 @@ nop:
schema-introspection:
enabled: true

ioc:
app-beans-container:
concurrent-start: true

# devservices 会启用testcontainers来管理测试数据库
quarkus:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@
/nop/core/registry/dao-wf.register-model.xml
/nop/core/registry/dialect.register-model.xml
/nop/core/registry/dict.register-model.xml
/nop/core/registry/fsm.register-model.xml
/nop/core/registry/gateway.register-model.xml
/nop/core/registry/graph-designer.register-model.xml
/nop/core/registry/imp.register-model.xml
Expand Down Expand Up @@ -433,9 +434,7 @@
/nop/dyn/model/NopDynPage/_NopDynPage.xbiz
/nop/dyn/model/NopDynPage/_NopDynPage.xmeta
/nop/dyn/model/NopDynPatch/NopDynPatch.xbiz
/nop/dyn/model/NopDynPatch/NopDynPatch.xmeta
/nop/dyn/model/NopDynPatch/_NopDynPatch.xbiz
/nop/dyn/model/NopDynPatch/_NopDynPatch.xmeta
/nop/dyn/model/NopDynPatchFile/NopDynPatchFile.xbiz
/nop/dyn/model/NopDynPatchFile/NopDynPatchFile.xmeta
/nop/dyn/model/NopDynPatchFile/_NopDynPatchFile.xbiz
Expand Down Expand Up @@ -510,11 +509,6 @@
/nop/dyn/pages/NopDynPage/main.page.yaml
/nop/dyn/pages/NopDynPage/picker.page.yaml
/nop/dyn/pages/NopDynPage/ref-module.page.yaml
/nop/dyn/pages/NopDynPatch/NopDynPatch.lib.xjs
/nop/dyn/pages/NopDynPatch/NopDynPatch.view.xml
/nop/dyn/pages/NopDynPatch/_gen/_NopDynPatch.view.xml
/nop/dyn/pages/NopDynPatch/main.page.yaml
/nop/dyn/pages/NopDynPatch/picker.page.yaml
/nop/dyn/pages/NopDynPatchFile/NopDynPatchFile.lib.xjs
/nop/dyn/pages/NopDynPatchFile/NopDynPatchFile.view.xml
/nop/dyn/pages/NopDynPatchFile/_gen/_NopDynPatchFile.view.xml
Expand Down
3 changes: 3 additions & 0 deletions nop-ioc/src/main/java/io/nop/ioc/IocConfigs.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ public interface IocConfigs {
IConfigReference<String> CFG_IOC_APP_BEANS_CONTAINER_START_MODE = AppConfig
.varRef(s_loc, "nop.ioc.app-beans-container.start-mode", String.class, null);

@Description("是否初始化IoC容器")
IConfigReference<Boolean> CFG_IOC_APP_BEANS_CONCURRENT_START = AppConfig.varRef(s_loc, "nop.ioc.app-beans-container.concurrent-start", Boolean.class, false);

@Description("是否自动装载所有模块下的beans/app-*.beans.xml文件")
IConfigReference<Boolean> CFG_IOC_APP_BEANS_FILE_ENABLED = AppConfig.varRef(s_loc, "nop.ioc.app-beans-file.enabled",
Boolean.class, true);
Expand Down
151 changes: 136 additions & 15 deletions nop-ioc/src/main/java/io/nop/ioc/impl/BeanContainerImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,14 @@
import io.nop.api.core.exceptions.NopException;
import io.nop.api.core.ioc.BeanContainerStartMode;
import io.nop.api.core.ioc.IBeanContainer;
import io.nop.api.core.util.FutureHelper;
import io.nop.api.core.util.ICancellable;
import io.nop.commons.concurrent.executor.GlobalExecutors;
import io.nop.commons.lang.IClassLoader;
import io.nop.commons.lang.impl.Cancellable;
import io.nop.commons.util.ClassHelper;
import io.nop.core.lang.xml.XNode;
import io.nop.core.model.graph.TaskExecutionGraph;
import io.nop.ioc.api.BeanScopeContext;
import io.nop.ioc.api.IBeanContainerImplementor;
import io.nop.ioc.api.IBeanDefinition;
Expand All @@ -35,6 +40,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;

import static io.nop.ioc.IocConstants.PRODUCER_BEAN_PREFIX;
Expand Down Expand Up @@ -62,7 +68,7 @@ public class BeanContainerImpl implements IBeanContainerImplementor {

private final List<BeanDefinition> orderedBeans;
private volatile boolean running;
private boolean started;
private volatile boolean started;

private final Map<Class<?>, BeanTypeMapping> beansByType = new ConcurrentHashMap<>();
private final Map<Class<? extends Annotation>, List<BeanDefinition>> beansByAnnotation = new ConcurrentHashMap<>();
Expand All @@ -77,6 +83,12 @@ public class BeanContainerImpl implements IBeanContainerImplementor {
private BeanContainerStartMode startMode = BeanContainerStartMode.DEFAULT;
private Map<String, AliasName> aliases;

private Cancellable cancellable;

private boolean concurrentStart;

private CompletableFuture<?> startFuture;

public BeanContainerImpl(String id, Map<String, BeanDefinition> enabledBeans,
Collection<BeanDefinition> optionalBeans,
Map<String, AliasName> aliases, IBeanContainer parentContainer) {
Expand Down Expand Up @@ -138,6 +150,10 @@ public void setClassIntrospection(IBeanClassIntrospection classIntrospection) {
this.classIntrospection = classIntrospection;
}

public void setConcurrentStart(boolean concurrentStart) {
this.concurrentStart = concurrentStart;
}

public String getId() {
return id;
}
Expand Down Expand Up @@ -356,12 +372,17 @@ private Object getBean0(BeanDefinition beanDef, boolean onlyProducer, boolean in
if (includeCreating && beanScope != null) {
Object bean = beanScope.get(beanDef.getId());
if (bean != null) {
bean = beanDef.getBeanInstance(bean, onlyProducer);
if (bean == null)
throw new NopException(ERR_IOC_PRODUCER_BEAN_NOT_INITED).param(ARG_BEAN, beanDef)
.param(ARG_BEAN_NAME, beanDef.getId());
Object createdBean = beanDef.getBeanInstance(bean, onlyProducer);
if (createdBean == null) {
// 如果是并行启动,则允许等待一段时间
if (concurrentStart && !started)
createdBean = ((ProducedBeanInstance) bean).awaitGetBean(10000);
if (createdBean == null)
throw new NopException(ERR_IOC_PRODUCER_BEAN_NOT_INITED).param(ARG_BEAN, beanDef)
.param(ARG_BEAN_NAME, beanDef.getId());
}

return bean;
return createdBean;
}
}

Expand All @@ -378,6 +399,7 @@ private Object getBean0(BeanDefinition beanDef, boolean onlyProducer, boolean in
if (bean == null) {
LOG.info("nop.new-bean:{}", beanDef);
bean = beanDef.newObject(beanScope, this);
LOG.trace("nop.new-bean-completed:{}", beanDef);
if (isStarted() && beanDef.hasDelayMethod()) {
beanDef.runDelayMethod(bean, beanScope, this);
}
Expand Down Expand Up @@ -439,35 +461,129 @@ public void start() {
if (running)
throw new NopException(ERR_IOC_CONTAINER_ALREADY_STARTED).param(ARG_CONTAINER_ID, id);

startFuture = null;
LOG.info("nop.ioc.start-container:containerId={}", getId());

running = true;
singletonScope = new BeanScopeImpl(ApiConstants.BEAN_SCOPE_SINGLETON, XLang.newEvalScope(), this);

try {
List<BeanDefinition> startBeans = new ArrayList<>();

for (BeanDefinition bean : orderedBeans) {
if (bean.isSingleton()) {
if (startMode == BeanContainerStartMode.ALL_LAZY) {
// 只创建具有delayMethod的bean
if (bean.hasDelayMethod() && !bean.isLazyInit() || bean.isIocForceInit()) {
getBean0(bean, true, true);
startBean(startBeans, bean);
}
} else if (startMode == BeanContainerStartMode.ALL_EAGER || !bean.isLazyInit()) {
getBean0(bean, true, true);
startBean(startBeans, bean);
}
}
}

runDelayMethod();
} catch (Exception e) {
try {
stop();
} catch (Exception e2) {
LOG.error("nop.err.ioc.stop-failed", e2);
if (concurrentStart) {
startFuture = asyncStartBeans(startBeans).whenComplete((ret, err) -> {
if (err != null) {
handleStartError(err);
} else {
runDelayMethod();
started = true;
}
LOG.info("nop.ioc.async-start-finished:{}",getId());
});
} else {
runDelayMethod();
started = true;
LOG.info("nop.ioc.start-finished:{}",getId());
}
} catch (Exception e) {
// dumpCreations();
handleStartError(e);
throw e;
}
started = true;
}

void handleStartError(Throwable e) {
LOG.error("nop.err.ioc.start-failed", e);
try {
stop();
} catch (Exception e2) {
LOG.error("nop.err.ioc.stop-failed", e2);
}
}

@Override
public void awaitStartFinished() {
if (startFuture != null)
FutureHelper.syncGet(startFuture);
}

/*
@DataBean
public static class BeanCreation {
final String threadName;
final String beanId;
final Set<String> depends;
final boolean created;
public BeanCreation(String threadName, String beanId, Set<String> depends, boolean created) {
this.threadName = threadName;
this.beanId = beanId;
this.depends = depends;
this.created = created;
}
public String toString() {
return StringHelper.rightPad(threadName,25,' ') + " " + beanId + (created ? depends : "");
}
public String getThreadName() {
return threadName;
}
public String getBeanId() {
return beanId;
}
public Set<String> getDepends() {
return depends;
}
public boolean isCreated() {
return created;
}
}
Queue<BeanCreation> beanCreations = new LinkedTransferQueue<>();
void dumpCreations() {
System.out.println(StringHelper.join(beanCreations,"\r\n"));
}*/

CompletableFuture<Void> asyncStartBeans(List<BeanDefinition> startBeans) {
TaskExecutionGraph graph = new TaskExecutionGraph("ioc-container-start");
for (BeanDefinition bean : startBeans) {
graph.addTaskWithDepends(bean.getId(),
() -> {
// beanCreations.add(new BeanCreation(Thread.currentThread().getName(), bean.getId(), graph.getDepends(bean.getId()), false));
getBean0(bean, true, false);
// beanCreations.add(new BeanCreation(Thread.currentThread().getName(), bean.getId(), graph.getDepends(bean.getId()), true));
},
bean.getDependBeanIds());
}
graph.analyze();
this.cancellable = new Cancellable();
return graph.runOnExecutor(GlobalExecutors.globalWorker(), this.cancellable);
}

void startBean(List<BeanDefinition> startBeans, BeanDefinition bean) {
if (concurrentStart) {
startBeans.add(bean);
} else {
getBean0(bean, true, false);
}
}

void runDelayMethod() {
Expand All @@ -479,13 +595,18 @@ void runDelayMethod() {
beanDef.runDelayMethod(instance, beanScope, this);
}
}
LOG.info("nop.ioc.run-delay-method-finished");
}

@Override
public void stop() {
LOG.info("nop.ioc.stop-container:containerId={}", getId());
running = false;
started = false;
if (cancellable != null) {
cancellable.cancel(ICancellable.CANCEL_REASON_STOP);
cancellable = null;
}
if (singletonScope != null)
singletonScope.close();
BeanScopeContext.instance().onContainerStop(this);
Expand Down
Loading

0 comments on commit 6420484

Please sign in to comment.