@PostConstruct 注解导致 Tomcat 的端口号没有启动
问题背景
在开发一个项目的时候,使用了 @PostConstruct 注解,被注解的方法里面有 GRPC 服务端,所以启动了 GRPC 服务端,然而这个注解又是同步的,所以服务端启动后一直就卡在那里了,不在继续执行其他的初始化,我这里是使 Tomcat 的端口初始化暂停了,其他的程序可能会出现其他的问题,大同小异。问题代码如下:
@PostConstruct
public void init() throws IOException, InterruptedException {
start();
blockUntilShutdown();
}
public void start() throws IOException {
server = ServerBuilder.forPort(port)
.addService(mapImpl)
.build()
.start();
log.info("server start up...");
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
IdmappingServer.this.stop();
log.error("Running error, server shutdown");
}
});
}
public void blockUntilShutdown() throws InterruptedException {
if (server != null) {
server.awaitTermination();
}
}
在application.yml我设定的端口为server.port=3108,启动程序后:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.6.1)
2021-12-17 10:07:25.325 INFO [main] c.d.d.a.AuthenticationManagerApplication.logStarting(StartupInfoLogger.java:55): Starting AuthenticationManagerApplication using Java 1.8.0_172 on B-YUAN-G with PID 32708 (D:\code\AuthenticationManager\target\classes started by yuan.g in D:\code\AuthenticationManager)
2021-12-17 10:07:25.328 INFO [main] c.d.d.a.AuthenticationManagerApplication.logStartupProfileInfo(SpringApplication.java:639): The following profiles are active: dev
2021-12-17 10:07:26.105 INFO [main] o.s.b.w.e.t.TomcatWebServer.initialize(TomcatWebServer.java:108): Tomcat initialized with port(s): 3108 (http)
2021-12-17 10:07:26.111 INFO [main] o.a.c.h.Http11NioProtocol.log(DirectJDKLog.java:173): Initializing ProtocolHandler ["http-nio-3108"]
2021-12-17 10:07:26.111 INFO [main] o.a.c.c.StandardService.log(DirectJDKLog.java:173): Starting service [Tomcat]
2021-12-17 10:07:26.112 INFO [main] o.a.c.c.StandardEngine.log(DirectJDKLog.java:173): Starting Servlet engine: [Apache Tomcat/9.0.55]
2021-12-17 10:07:26.178 INFO [main] o.a.c.c.C.[.[.[/].log(DirectJDKLog.java:173): Initializing Spring embedded WebApplicationContext
2021-12-17 10:07:26.178 INFO [main] o.s.b.w.s.c.ServletWebServerApplicationContext.prepareWebApplicationContext(ServletWebServerApplicationContext.java:290): Root WebApplicationContext: initialization completed in 799 ms
2021-12-17 10:07:27.036 INFO [main] c.d.d.a.g.s.Server.start(IdmappingServer.java:34): server start up...
可以看到 Tomcat initialized with port(s): 3108 (http) 这句话,但这句话并没有把端口真正的启动,在端口真正启动前被 Server.start(IdmappingServer.java:34): Idmapping server start up… 阻塞了。
解决方案
只要把 @PostConstruct 注解的方法变为异步就可以解决问题,变为异步的方式也很简单,以我的程序为例,在 start( ) 方法上加上 @Async 注解,在 springboot 的启动程序上,也就是 @SpringBootApplication 上面添加 @EnableAsync 使能异步注解,不能把初始化写在MappingServer中,分开两个类写,否则异步线程无法开启,注意循环依赖,如下所示:
类1
@Autowired
MappingServer mappingServer;
//不能把初始化写在MappingServer中,否则异步线程无法开启
@PostConstruct
public void init() {
mappingServer.start();
mappingServer.blockUntilShutdown();
}
类2
@Async
public void start() throws IOException {
server = ServerBuilder.forPort(port)
.addService(mapImpl)
.build()
.start();
log.info("server start up...");
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
IdmappingServer.this.stop();
log.error("Running error, server shutdown");
}
});
}
public void blockUntilShutdown() throws InterruptedException {
if (server != null) {
server.awaitTermination();
}
}
@EnableAsync
@SpringBootApplication
public class AuthenticationManagerApplication
注意 @Async 没有添加在 init( ) 上,因为启动GRPC服务端的方法是 start( ) 方法,产生的结果如下图所示:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.6.1)
2021-12-17 10:15:17.300 INFO [main] c.d.d.a.AuthenticationManagerApplication.logStarting(StartupInfoLogger.java:55): Starting AuthenticationManagerApplication using Java 1.8.0_172 on B-YUAN-G with PID 34472 (D:\code\AuthenticationManager\target\classes started by yuan.g in D:\code\AuthenticationManager)
2021-12-17 10:15:17.302 INFO [main] c.d.d.a.AuthenticationManagerApplication.logStartupProfileInfo(SpringApplication.java:639): The following profiles are active: dev
2021-12-17 10:15:18.077 INFO [main] o.s.b.w.e.t.TomcatWebServer.initialize(TomcatWebServer.java:108): Tomcat initialized with port(s): 3108 (http)
2021-12-17 10:15:18.084 INFO [main] o.a.c.h.Http11NioProtocol.log(DirectJDKLog.java:173): Initializing ProtocolHandler ["http-nio-3108"]
2021-12-17 10:15:18.084 INFO [main] o.a.c.c.StandardService.log(DirectJDKLog.java:173): Starting service [Tomcat]
2021-12-17 10:15:18.084 INFO [main] o.a.c.c.StandardEngine.log(DirectJDKLog.java:173): Starting Servlet engine: [Apache Tomcat/9.0.55]
2021-12-17 10:15:18.152 INFO [main] o.a.c.c.C.[.[.[/].log(DirectJDKLog.java:173): Initializing Spring embedded WebApplicationContext
2021-12-17 10:15:18.152 INFO [main] o.s.b.w.s.c.ServletWebServerApplicationContext.prepareWebApplicationContext(ServletWebServerApplicationContext.java:290): Root WebApplicationContext: initialization completed in 800 ms
2021-12-17 10:15:18.697 INFO [main] o.a.c.h.Http11NioProtocol.log(DirectJDKLog.java:173): Starting ProtocolHandler ["http-nio-3108"]
2021-12-17 10:15:18.705 INFO [main] o.s.b.w.e.t.TomcatWebServer.start(TomcatWebServer.java:220): Tomcat started on port(s): 3108 (http) with context path ''
2021-12-17 10:15:18.713 INFO [main] c.d.d.a.AuthenticationManagerApplication.logStarted(StartupInfoLogger.java:61): Started AuthenticationManagerApplication in 1.892 seconds (JVM running for 2.787)
2021-12-17 10:15:19.089 INFO [task-1] c.d.d.a.g.s.Server.start(Server.java:34): server start up...
可以看到与之前的对比,多出了三句话,这个时候 Tomcat 的端口才算真正启动了:
2021-12-17 10:15:18.697 INFO [main] o.a.c.h.Http11NioProtocol.log(DirectJDKLog.java:173): Starting ProtocolHandler ["http-nio-3108"]
2021-12-17 10:15:18.705 INFO [main] o.s.b.w.e.t.TomcatWebServer.start(TomcatWebServer.java:220): Tomcat started on port(s): 3108 (http) with context path ''
2021-12-17 10:15:18.713 INFO [main] c.d.d.a.AuthenticationManagerApplication.logStarted(StartupInfoLogger.java:61): Started AuthenticationManagerApplication in 1.892 seconds (JVM running for 2.787)
而且可以看到GRPC服务器已经使用的一个线程进行启动的:
2021-12-17 10:15:19.089 INFO [task-1] c.d.d.a.g.s.Server.start(Server.java:34): server start up…
问题总结
- @PostConstruct 注解是同步启动,如果里面的过程时间比较长会影响其他初始化程序,建议改为异步。
- 使用 @Async 注解进行方法异步,一定记住添加使能注解 @EnableAsync,否则无效。
- 导致 Tomcat 端口不能使用,也有可能是其他原因,比如端口被占用,多排查一下一定会找到问题的。
作为程序员第一篇开源文章,每次写一句歌词记录一下,看看人生有几首歌的时间,wahahaha …
Lyric: 这蜿蜒的微笑
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/110875.html