我目前正在用Spring Boot 2.4.0编写一个应用程序,它需要侦听多个端口(具体地说是3个,但将来可能是4个)。其想法是,每个端口提供一个不同的API,供其他服务/应用程序连接。
因此,对于一个最小的工作示例,我会说我们有一个如下所示的SpringBootApp:
@SpringBootApplication
public class MultiportSpringBoot {
public static void main(String[] args)
{
SpringApplication.run(MultiportSpringBoot.class, args);
}
}现在,我想让它监听3个不同的端口,比如8080、8081和8082。为所有人(!)请求到这些端口之一时,应该由特定的控制器“负责”。这一要求的原因之一是,一个控制器需要处理(常规) web前端,而另一个控制器需要处理API。在接收到无效请求的情况下,API控制器需要给出一个不同于前端应该给出的错误消息。因此,给出的要求是明确的分离。
因此,我设想多个控制器用于不同的端口,例如:
@Controller
public class Controller8080
{
@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView test8080()
{
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("test8080");
return modelAndView;
}
}对其他端口使用类似的控制器:
@Controller
public class Controller8081
{
@RequestMapping(value = "/", method = RequestMethod.GET)
public ResponseEntity test8081()
{
JSONObject stuff = doSomeStuffForPort8081();
return new ResponseEntity<String>(stuff, HttpStatus.OK);
}
}我希望一个类似于@RequestMapping的注解能够匹配和修复控制器的端口号,但这似乎没有选择,因为这样的注解似乎不存在。
现在,这个主题似乎有点具体,这可能是为什么你在网上找不到太多信息的原因。我找到了Starting Spring boot REST controller in two ports,但我也只能运行一个实例。我看过https://tech.asimio.net/2016/12/15/Configuring-Tomcat-to-Listen-on-Multiple-ports-using-Spring-Boot.html,但这对于Spring Boot2.4.0来说已经过时了,而且JavaMelody示例有点臃肿。
任何人都可以提供一个最小的工作示例来解决这个问题?
--
编辑:
再澄清一点:我需要多个独立的RESTControllers,每个都在不同的端口上处理请求。即,对domain.com:8080/的请求应该由与对domain.com:8081/的请求不同的控制器来处理。
例如,考虑以下两个控制器,这两个控制器应分别处理端口8080和8081上的请求:
//controller for port 8080
@RestController
public class ControllerA
{
@GetMapping("/")
String helloA(HttpServletRequest request)
{
return "ControllerA at port " + request.getLocalPort();
}
}和
//controller for port 8081
@RestController
public class ControllerB
{
@GetMapping("/")
String helloB(HttpServletRequest request)
{
return "ControllerB at port " + request.getLocalPort();
}
}发布于 2020-12-07 04:14:07
tomcat类名稍有更改,因此您提供的链接具有旧代码,但对于新代码来说已经足够了。下面的代码展示了如何在spring Boot2.4中打开多个端口
@Bean
public ServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
tomcat.addAdditionalTomcatConnectors(additionalConnector());
return tomcat;
}
private Connector[] additionalConnector() {
if (!StringUtils.hasLength(this.additionalPorts)) {
return null;
}
String[] ports = this.additionalPorts.split(",");
List<Connector> result = new ArrayList<>();
for (String port : ports) {
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
connector.setScheme("http");
connector.setPort(Integer.valueOf(port));
result.add(connector);
}
return result.toArray(new Connector[]{});
} 对于使用不同控制器响应不同端口,您可以实现类似于check getLocalPort的逻辑,并相应地响应它。
@GetMapping("/hello")
String hello(HttpServletRequest request) {
return "hello from " + request.getLocalPort();
}或者,您可以在filter中编写逻辑控制器。下面的示例代码
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain fc) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
if (req.getLocalPort() == 8882 && req.getRequestURI().startsWith("/somefunction")) {
res.sendError(HttpServletResponse.SC_FORBIDDEN);
} else {
fc.doFilter(request, response);
}
}你可以在这里找到所有正在运行的例子https://github.com/ozkanpakdil/spring-examples/tree/master/multiport
这就是它在我本地的样子

为了让不同的控制器有相同的路径,你可以在类(check)之上使用@RequestMapping("/controllerNO"),NO应该是数字1,2,否则spring会报错“你有相同的路径”,并会给出这个异常
Caused by: java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'testController2' method
com.mascix.multiport.TestController2#hello(HttpServletRequest)
to {GET [/hello]}: There is already 'testController1' bean method因为从设计spring开始,只允许一条路径对应一个控制器,所以在请求映射之后,您可以将过滤器更改为this。关于反射的好事情是,你会学到非常不同的异常。java.lang.NoSuchMethodException或java.lang.IllegalArgumentException
最新代码在我的本地如何工作

我必须说,这种方法不是正确的,并且违背了spring的设计,为了有不同的端口和不同的控制器,有多个JVM。如果你混淆了逻辑,你将更难解决未来的问题和实现新的功能。
如果您必须在一个jvm中执行此操作,请编写一个服务层,从一个控制器中单独调用函数,并编写如下逻辑
@GetMapping("/hello")
String hello(HttpServletRequest request) {
if (request.getLocalPort() == 8888) {
return service.hellofrom8888();
}
if (request.getLocalPort() == 8889) {
return service.hellofrom8889();
}
return "no repsonse ";
}至少这将是易于维护和调试的。不过看起来还是很“丑陋”:)
发布于 2020-12-09 23:40:52
Özkan已经提供了关于如何通过提供您自己的基于TomcatServletWebServerFactory的ServletWebServerFactory @Bean来让Tomcat侦听多个端口的详细信息。
至于映射,这个方法怎么样:
将@RequestMapping("/8080")添加到您的控制器(方法保留其特定的@RequestMapping)
@Controller
@RequestMapping("/8080")
public class Controller8080
{
@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView test8080()
{
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("test8080");
return modelAndView;
}
}将您自己的RequestMappingHandlerMapping定义为
public class PortBasedRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
@Override
protected HandlerMethod lookupHandlerMethod(final String lookupPath, final HttpServletRequest request) throws Exception {
return super.lookupHandlerMethod(request.getLocalPort() + lookupPath, request);
}
}并通过以下方式使用它
@Bean
public WebMvcRegistrations webMvcRegistrationsHandlerMapping() {
return new WebMvcRegistrations() {
@Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
return new PortBasedRequestMappingHandlerMapping();
}
};
}这将尝试将对端口8080上的/foobar的请求映射到/8080/foobar。
发布于 2021-09-30 19:42:20
另一种方法是使用我认为更干净的https://stackoverflow.com/a/69397870/6166627的org.springframework.web.servlet.mvc.condition.RequestCondition
https://stackoverflow.com/questions/65109059
复制相似问题