首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在具有不同控制器的多个端口上运行SpingBoot应用程序

在具有不同控制器的多个端口上运行SpingBoot应用程序
EN

Stack Overflow用户
提问于 2020-12-02 21:29:49
回答 3查看 453关注 0票数 2

我目前正在用Spring Boot 2.4.0编写一个应用程序,它需要侦听多个端口(具体地说是3个,但将来可能是4个)。其想法是,每个端口提供一个不同的API,供其他服务/应用程序连接。

因此,对于一个最小的工作示例,我会说我们有一个如下所示的SpringBootApp:

代码语言:javascript
复制
@SpringBootApplication
public class MultiportSpringBoot {

    public static void main(String[] args)
    {
        SpringApplication.run(MultiportSpringBoot.class, args);
    }
}

现在,我想让它监听3个不同的端口,比如8080、8081和8082。为所有人(!)请求到这些端口之一时,应该由特定的控制器“负责”。这一要求的原因之一是,一个控制器需要处理(常规) web前端,而另一个控制器需要处理API。在接收到无效请求的情况下,API控制器需要给出一个不同于前端应该给出的错误消息。因此,给出的要求是明确的分离。

因此,我设想多个控制器用于不同的端口,例如:

代码语言:javascript
复制
@Controller
public class Controller8080
{
    @RequestMapping(value = "/", method = RequestMethod.GET)
    public ModelAndView test8080()
    {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("test8080");
        return modelAndView;
    }
}

对其他端口使用类似的控制器:

代码语言:javascript
复制
@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上的请求:

代码语言:javascript
复制
//controller for port 8080
@RestController
public class ControllerA
{
    @GetMapping("/")
    String helloA(HttpServletRequest request)
    {
        return "ControllerA at port " + request.getLocalPort();
    }
}

代码语言:javascript
复制
//controller for port 8081
@RestController
public class ControllerB
{
    @GetMapping("/")
    String helloB(HttpServletRequest request)
    {
        return "ControllerB at port " + request.getLocalPort();
    }
}
EN

回答 3

Stack Overflow用户

发布于 2020-12-07 04:14:07

tomcat类名稍有更改,因此您提供的链接具有旧代码,但对于新代码来说已经足够了。下面的代码展示了如何在spring Boot2.4中打开多个端口

代码语言:javascript
复制
@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的逻辑,并相应地响应它。

代码语言:javascript
复制
@GetMapping("/hello")
String hello(HttpServletRequest request) {
    return "hello from " + request.getLocalPort();
}

或者,您可以在filter中编写逻辑控制器。下面的示例代码

代码语言:javascript
复制
 @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会报错“你有相同的路径”,并会给出这个异常

代码语言:javascript
复制
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.NoSuchMethodExceptionjava.lang.IllegalArgumentException

最新代码在我的本地如何工作

我必须说,这种方法不是正确的,并且违背了spring的设计,为了有不同的端口和不同的控制器,有多个JVM。如果你混淆了逻辑,你将更难解决未来的问题和实现新的功能。

如果您必须在一个jvm中执行此操作,请编写一个服务层,从一个控制器中单独调用函数,并编写如下逻辑

代码语言:javascript
复制
@GetMapping("/hello")
    String hello(HttpServletRequest request) {
        if (request.getLocalPort() == 8888) {
            return service.hellofrom8888();
        }
        if (request.getLocalPort() == 8889) {
            return service.hellofrom8889();
        }

        return "no repsonse ";
    }

至少这将是易于维护和调试的。不过看起来还是很“丑陋”:)

票数 1
EN

Stack Overflow用户

发布于 2020-12-09 23:40:52

Özkan已经提供了关于如何通过提供您自己的基于TomcatServletWebServerFactory的ServletWebServerFactory @Bean来让Tomcat侦听多个端口的详细信息。

至于映射,这个方法怎么样:

将@RequestMapping("/8080")添加到您的控制器(方法保留其特定的@RequestMapping)

代码语言:javascript
复制
@Controller
@RequestMapping("/8080")
public class Controller8080
{
    @RequestMapping(value = "/", method = RequestMethod.GET)
    public ModelAndView test8080()
    {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("test8080");
        return modelAndView;
    }
}

将您自己的RequestMappingHandlerMapping定义为

代码语言:javascript
复制
public class PortBasedRequestMappingHandlerMapping extends RequestMappingHandlerMapping {

    @Override
    protected HandlerMethod lookupHandlerMethod(final String lookupPath, final HttpServletRequest request) throws Exception {
        return super.lookupHandlerMethod(request.getLocalPort() + lookupPath, request);
    }
}

并通过以下方式使用它

代码语言:javascript
复制
@Bean
public WebMvcRegistrations webMvcRegistrationsHandlerMapping() {
    return new WebMvcRegistrations() {

        @Override
        public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
            return new PortBasedRequestMappingHandlerMapping();
        }
    };
}

这将尝试将对端口8080上的/foobar的请求映射到/8080/foobar。

票数 1
EN

Stack Overflow用户

发布于 2021-09-30 19:42:20

另一种方法是使用我认为更干净的https://stackoverflow.com/a/69397870/6166627org.springframework.web.servlet.mvc.condition.RequestCondition

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65109059

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档