最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

一文詳解SpringBoot如何同時(shí)監(jiān)聽多個(gè)端口

 更新時(shí)間:2025年10月21日 08:19:29   作者:風(fēng)象南  
這篇文章主要為大家詳細(xì)介紹了SpringBoot如何同時(shí)監(jiān)聽多個(gè)端口,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的小伙伴可以了解下

前言

在日常開發(fā)中,我們通常構(gòu)建的 Spring Boot 應(yīng)用都是"單面"的——一個(gè)端口,一套服務(wù)邏輯。但在某些實(shí)際場景中,我們可能需要一個(gè)應(yīng)用能夠"一心二用":同時(shí)提供兩套完全不同的服務(wù),分別在不同的端口上運(yùn)行。

比如:

  • 一個(gè)端口面向外部用戶,提供 API 服務(wù)
  • 另一個(gè)端口面向內(nèi)部管理,提供監(jiān)控和運(yùn)維功能
  • 或者在一個(gè)應(yīng)用中同時(shí)集成管理后臺(tái)和用戶前臺(tái)

場景示例

假設(shè)我們要開發(fā)一個(gè)電商平臺(tái),需要同時(shí)滿足:

用戶端服務(wù)(端口8082)

  • 商品瀏覽
  • 購物車管理
  • 訂單處理

管理端服務(wù)(端口8083)

  • 商品管理
  • 訂單管理
  • 數(shù)據(jù)統(tǒng)計(jì)

這兩套服務(wù)功能完全不同,但需要部署在同一個(gè)應(yīng)用中。

技術(shù)實(shí)現(xiàn)方案

方案一:多 Tomcat Connector 配置

最直接的方式是配置多個(gè) Tomcat Connector。

1. 創(chuàng)建基礎(chǔ)項(xiàng)目結(jié)構(gòu)

// 主應(yīng)用類
@SpringBootApplication
public class DualPortApplication {
    public static void main(String[] args) {
        SpringApplication.run(DualPortApplication.class, args);
    }
}

2. 配置雙端口

@Configuration
public class DualPortConfiguration {

    @Bean
    public ServletWebServerFactory servletContainer() {
        TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();

        // 添加第一個(gè)連接器(用戶端)
        factory.addAdditionalTomcatConnectors(createUserPortConnector());
        // 添加第二個(gè)連接器(管理端)
        factory.addAdditionalTomcatConnectors(createAdminPortConnector());

        return factory;
    }

    private Connector createUserPortConnector() {
        Connector connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL);
        connector.setPort(8080);
        connector.setProperty("connectionTimeout", "20000");
        return connector;
    }

    private Connector createAdminPortConnector() {
        Connector connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL);
        connector.setPort(8081);
        connector.setProperty("connectionTimeout", "20000");
        return connector;
    }
}

3. 路由分離策略

現(xiàn)在我們需要為不同端口提供不同的路由處理:

@Component
public class PortBasedFilter implements Filter {

    private static final String USER_PORT_HEADER = "X-User-Port";
    private static final String ADMIN_PORT_HEADER = "X-Admin-Port";

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        HttpServletRequest httpRequest = (HttpServletRequest) request;
        int port = httpRequest.getLocalPort();

        if (port == 8082) {
            // 用戶端請求
            httpRequest.setAttribute("serviceType", "USER");
        } else if (port == 8083) {
            // 管理端請求
            httpRequest.setAttribute("serviceType", "ADMIN");
        }

        chain.doFilter(request, response);
    }
}

4. 創(chuàng)建分離的 Controller

// 用戶端 Controller
@RestController
@RequestMapping("/api/user")
public class UserController {

    @GetMapping("/products")
    public String getProducts() {
        return "User Products API";
    }

    @PostMapping("/cart")
    public String addToCart() {
        return "Add to cart";
    }
}

// 管理端 Controller
@RestController
@RequestMapping("/api/admin")
public class AdminController {

    @GetMapping("/products")
    public String manageProducts() {
        return "Admin Products Management";
    }

    @GetMapping("/statistics")
    public String getStatistics() {
        return "Admin Statistics";
    }
}

方案二:基于路徑前綴的更優(yōu)雅方案

上述方案雖然可行,但在實(shí)際使用中可能會(huì)有一些問題。讓我們采用更優(yōu)雅的方案。

1. 自定義 Web MVC 配置

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        // 為用戶端配置前綴
        configurer.addPathPrefix("/user", cls -> cls.isAnnotationPresent(UserApi.class));
        // 為管理端配置前綴
        configurer.addPathPrefix("/admin", cls -> cls.isAnnotationPresent(AdminApi.class));
    }
}

// 定義注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface UserApi {}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AdminApi {}

2. 使用注解標(biāo)記 Controller

@RestController
@RequestMapping("/products")
@UserApi
public class UserProductController {

    @GetMapping
    public String getProducts() {
        return "用戶端商品列表";
    }

    @GetMapping("/{id}")
    public String getProduct(@PathVariable String id) {
        return "商品詳情: " + id;
    }
}

@RestController
@RequestMapping("/products")
@AdminApi
public class AdminProductController {

    @GetMapping
    public String getAllProducts() {
        return "管理端商品管理列表";
    }

    @PostMapping
    public String createProduct() {
        return "創(chuàng)建商品";
    }

    @PutMapping("/{id}")
    public String updateProduct(@PathVariable String id) {
        return "更新商品: " + id;
    }
}

高級特性實(shí)現(xiàn)

1. 端口感知的攔截器

@Component
public class PortAwareInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                           Object handler) throws Exception {
        int port = request.getLocalPort();

        if (port == 8082) {
            // 用戶端邏輯
            validateUserRequest(request);
        } else if (port == 8083) {
            // 管理端邏輯
            validateAdminRequest(request);
        }

        return true;
    }

    private void validateUserRequest(HttpServletRequest request) {
        // 用戶端請求驗(yàn)證邏輯
        String userAgent = request.getHeader("User-Agent");
        if (userAgent == null) {
            throw new SecurityException("Invalid user request");
        }
    }

    private void validateAdminRequest(HttpServletRequest request) {
        // 管理端請求驗(yàn)證邏輯
        String authHeader = request.getHeader("Authorization");
        if (authHeader == null || !authHeader.startsWith("Bearer ")) {
            throw new SecurityException("Admin authentication required");
        }
    }
}

2. 端口特定的異常處理

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleException(
            Exception e, HttpServletRequest request) {

        int port = request.getLocalPort();
        ErrorResponse error = new ErrorResponse();

        if (port == 8082) {
            error.setCode("USER_ERROR_" + e.hashCode());
            error.setMessage("用戶服務(wù)異常: " + e.getMessage());
        } else if (port == 8083) {
            error.setCode("ADMIN_ERROR_" + e.hashCode());
            error.setMessage("管理服務(wù)異常: " + e.getMessage());
        }

        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(error);
    }
}

3. 動(dòng)態(tài)端口配置

@Configuration
@ConfigurationProperties(prefix = "dual.port")
@Data
public class DualPortProperties {
    private int userPort = 8082;
    private int adminPort = 8083;

    @Bean
    public ServletWebServerFactory servletContainer(DualPortProperties properties) {
        TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();

        factory.addAdditionalTomcatConnectors(
            createConnector("user", properties.getUserPort()));
        factory.addAdditionalTomcatConnectors(
            createConnector("admin", properties.getAdminPort()));

        return factory;
    }

    private Connector createConnector(String name, int port) {
        Connector connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL);
        connector.setPort(port);
        connector.setName(name + "-connector");
        return connector;
    }
}

監(jiān)控和日志

1. 分端口日志記錄

@Configuration
public class LoggingConfiguration {

    @Bean
    public Logger userLogger() {
        return LoggerFactory.getLogger("USER-PORT");
    }

    @Bean
    public Logger adminLogger() {
        return LoggerFactory.getLogger("ADMIN-PORT");
    }
}

@Component
public class PortAwareLogger {

    private final Logger userLogger;
    private final Logger adminLogger;

    public PortAwareLogger(Logger userLogger, Logger adminLogger) {
        this.userLogger = userLogger;
        this.adminLogger = adminLogger;
    }

    public void logRequest(HttpServletRequest request) {
        int port = request.getLocalPort();
        String uri = request.getRequestURI();
        String method = request.getMethod();

        if (port == 8082) {
            userLogger.info("用戶端請求: {} {}", method, uri);
        } else if (port == 8083) {
            adminLogger.info("管理端請求: {} {}", method, uri);
        }
    }
}

2. 端口特定的健康檢查

@Component
public class DualPortHealthIndicator implements HealthIndicator {

    @Override
    public Health health() {
        return Health.up()
                .withDetail("user-port", 8082)
                .withDetail("admin-port", 8083)
                .withDetail("status", "Both ports are active")
                .build();
    }
}

@RestController
@RequestMapping("/health")
public class HealthController {

    @GetMapping("/user")
    public Map<String, Object> userHealth() {
        Map<String, Object> health = new HashMap<>();
        health.put("port", 8082);
        health.put("status", "UP");
        health.put("service", "user-api");
        return health;
    }

    @GetMapping("/admin")
    public Map<String, Object> adminHealth() {
        Map<String, Object> health = new HashMap<>();
        health.put("port", 8083);
        health.put("status", "UP");
        health.put("service", "admin-api");
        return health;
    }
}

安全考慮

端口訪問控制

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(authz -> authz
                .requestMatchers(req -> req.getLocalPort() == 8082)
                    .permitAll()
                .requestMatchers(req -> req.getLocalPort() == 8083)
                    .hasRole("ADMIN")
                .anyRequest().denyAll()
            )
            .formLogin(form -> form
                .loginPage("/admin/login")
                .permitAll()
            );

        return http.build();
    }
}

總結(jié)

構(gòu)建"雙面" Spring Boot 應(yīng)用是一個(gè)有趣且實(shí)用的技術(shù)挑戰(zhàn)。通過本文介紹的多種實(shí)現(xiàn)方案,我們可以根據(jù)實(shí)際需求選擇最適合的方式:

多 Connector 方案:適合簡單場景,實(shí)現(xiàn)直接

路徑前綴方案:適合需要清晰 API 結(jié)構(gòu)的場景

在某些特定場景下確實(shí)能夠簡化系統(tǒng)架構(gòu),降低運(yùn)維成本。但同時(shí)也要注意避免過度復(fù)雜化,確保系統(tǒng)的可維護(hù)性和可擴(kuò)展性。

到此這篇關(guān)于一文詳解SpringBoot如何同時(shí)監(jiān)聽多個(gè)端口的文章就介紹到這了,更多相關(guān)SpringBoot監(jiān)聽多個(gè)端口內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論

遂溪县| 鹿邑县| 平遥县| 常熟市| 沅陵县| 新乡市| 禹州市| 瑞安市| 枣强县| 乌鲁木齐县| 西安市| 论坛| 颍上县| 丰县| 于田县| 德昌县| 来安县| 隆化县| 湾仔区| 崇阳县| 博野县| 井研县| 滨海县| 兰州市| 临颍县| 东乌| 景洪市| 横峰县| 青冈县| 改则县| 冷水江市| 凌海市| 祁阳县| 贡山| 荆州市| 顺平县| 瑞安市| 绿春县| 上饶县| 元谋县| 伊通|