启动与停止的扩展点
注册中心、配置中心这类库需要在应用启动的特定时刻做事情。tio-boot 提供三个扩展点,库引入后即可工作,应用代码不需要配合。
EnvLoadedListener:在读取服务配置之前补充配置
tio-boot 启动时先加载命令行参数、app.properties 及其 profile 文件、环境变量,然后才读取 server.port 等服务配置。EnvLoadedListener 在这两步之间执行,适合从配置中心、数据库或加密文件中补充配置,补充的值对整个启动过程生效。
package demo.env;
import nexus.io.tio.boot.env.EnvLoadedListener;
import nexus.io.tio.utils.environment.EnvUtils;
import nexus.io.tio.utils.environment.Prop;
import nexus.io.tio.utils.environment.PropUtils;
/**
* 没有配置 server.port 时,从环境变量 PORT 读取(很多容器平台用它指定端口)
*/
public class PortEnvLoadedListener implements EnvLoadedListener {
@Override
public void onEnvLoaded(String[] args) {
String port = System.getenv("PORT");
if (port != null && EnvUtils.get("server.port") == null) {
Prop prop = new Prop();
prop.getProperties().setProperty("server.port", port);
PropUtils.append(prop);
}
}
}在 META-INF/services/nexus.io.tio.boot.env.EnvLoadedListener 中登记实现类,tio-boot 通过 ServiceLoader 发现它:
demo.env.PortEnvLoadedListener写入 PropUtils 的值和配置文件处于同一层,命令行参数和环境变量仍然可以覆盖;如果要强制生效,使用 EnvUtils.set(key, value)。Nacos 配置中心就是这样实现的:tio-boot-cloud-nacos 登记了自己的 EnvLoadedListener,引入后自动加载 nacos.config.data-ids,Nacos 中的配置可以设置端口。
监听器拿到的 args 就是命令行参数;启动后的任何时候也可以用 EnvUtils.getArgs() 获取。
addServerListener:多个服务监听器
TioBootServer.me().setTioBootServerListener(listener) 是应用自己的监听器,只能有一个。库使用 addServerListener 追加监听器,和应用的监听器互不覆盖,按添加顺序在应用监听器之后执行:
package demo.listener;
import nexus.io.context.Context;
import nexus.io.context.ServerListener;
public class RegistryListener implements ServerListener {
@Override
public void beforeStart(Class<?>[] primarySources, String[] args) {
}
@Override
public void afterStarted(Class<?>[] primarySources, String[] args, Context context) {
// 端口已经开始监听,可以注册到注册中心
System.out.println("listening on " + context.getPort());
}
@Override
public void beforeStop() {
// 停止服务之前,先从注册中心下线
System.out.println("deregister");
}
@Override
public void afterStopped() {
}
}package demo;
import demo.listener.RegistryListener;
import nexus.io.tio.boot.TioApplication;
import nexus.io.tio.boot.server.TioBootServer;
public class ListenerApp {
public static void main(String[] args) {
TioBootServer.me().addServerListener(new RegistryListener());
TioApplication.run(ListenerApp.class, () -> {
}, args);
}
}TioBootNacos.start() 就是通过 addServerListener 在 afterStarted 中注册实例、在 beforeStop 中注销实例。
server.shutdown.hook:优雅关闭
server.shutdown.hook=true开启后 tio-boot 注册 JVM 关闭钩子,进程收到 SIGTERM(kill、docker stop、Kubernetes 停止 Pod)或 Ctrl+C 时执行完整的关闭流程:依次调用监听器的 beforeStop、停止服务、执行 HookCan 中的销毁方法、关闭 Aop 容器,最后调用 afterStopped。关闭流程只执行一次,代码中先调用了 close() 时不会重复执行。
kill -TERM <pid>Stopping server...
LISTENER beforeStop
LISTENER afterStopped