一、问题:

启动springboot项目后,定时启动项目中一个main方法,报错端口号被占用,kafka自启

2021-12-28 10:08:59.014 ERROR 801973 --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   : 

***************************
APPLICATION FAILED TO START
***************************

Description:

Web server failed to start. Port 18828 was already in use.

Action:

Identify and stop the process that's listening on port 18828 or configure this application to listen on another port.

2021-12-28 10:08:59.023  INFO 801973 --- [ntainer#0-0-C-1] o.a.k.c.c.internals.AbstractCoordinator  : [Consumer clientId=test-1, groupId=test] (Re-)joining group
2021-12-28 10:08:59.024  INFO 801973 --- [ntainer#0-3-C-1] o.a.k.c.c.internals.AbstractCoordinator  : [Consumer clientId=test-4, groupId=test] (Re-)joining group

二、原因:

yml配置文件中:

spring:
  main:
    web-application-type: SERVLET

  kafka:    
    listener:   
      auto-startup: true

三、异常详解:

1、定时启动main方法时,项目启动

web环境检测:WebApplicationType分析
先来看SpringBoot支持那些Web类型:

ClassUtils.isPresent(String className, ClassLoader classLoader) 方法并不是简简单单看看classLoader之中有没有出现过传参的class。它的底层调用的是java.lang.Class#forName(java.lang.String, boolean, java.lang.ClassLoader)。这个方法会加载你指定的className到jvm,如果给回你要的class给你,常常在反射中使用。所以

public enum WebApplicationType {

	/**
	 * 不启动内嵌的WebServer,不是运行web application
	 */
	NONE,

	/**
	 * 启动内嵌的基于servlet的web server
	 */
	SERVLET,

	/**
	 * 启动内嵌的reactive web server,这个application是一个reactive web application
	 */
	REACTIVE;
    
    private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
			"org.springframework.web.context.ConfigurableWebApplicationContext" };

	private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";

	private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";

	private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";

	private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";

	private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";

    
    static WebApplicationType deduceFromClasspath() {
        // 尝试加载org.springframework.web.reactive.DispatcherHandler,如果成功并且加载org.springframework.web.servlet.DispatcherServlet和org.glassfish.jersey.servlet.ServletContainer失败,则这个application是WebApplicationType.REACTIVE类型。
		if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
			return WebApplicationType.REACTIVE;
		}
		for (String className : SERVLET_INDICATOR_CLASSES) {
            // 如果ClassLoader里面同时加载这两个 javax.servlet.Servlet和 org.springframework.web.context.ConfigurableWebApplicationContext成功。则application是WebApplicationType.NONE 类型。
			if (!ClassUtils.isPresent(className, null)) { 
				return WebApplicationType.NONE;
			}
		}
        // application是 WebApplicationType.SERVLET 类型。
		return WebApplicationType.SERVLET;
	}

	static WebApplicationType deduceFromApplicationContext(Class<?> applicationContextClass) {
		if (isAssignable(SERVLET_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
			return WebApplicationType.SERVLET;
		}
		if (isAssignable(REACTIVE_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
			return WebApplicationType.REACTIVE;
		}
		return WebApplicationType.NONE;
	}
    //省略其他代码

可以看到启动springboot项目之后,读取配置文件 web-application-type: SERVLET,启动内嵌的基于servlet的web server,当再次执行main方法时,加载配置文件,项目又被重复启动,所有会报端口号冲突的错误。

2.kafka自动启动

yml中配置kafka:listener: auto-startup: true,加载配置文件,kafka自动开始监听

四、解决方案

1、取消yml配置文件中的这两个参数
2、在启动命令中指定参数

/usr/bin/java  -jar 
-Dlogging.file.name=/home/dev/logs/test.log
-Dspring.kafka.listener.auto-startup=true 
-Dspring.main.web-application-type=SERVLET 
/home/dev/test.jar 
> /dev/null

3、再次定时启动main方法时,不指定这两个参数

Logo

Kafka开源项目指南提供详尽教程,助开发者掌握其架构、配置和使用,实现高效数据流管理和实时处理。它高性能、可扩展,适合日志收集和实时数据处理,通过持久化保障数据安全,是企业大数据生态系统的核心。

更多推荐