0. 目錄
- 1. 需求背景
- 2. 實現
- 3. 額外 - 其它Servlet容器實現
- 3.1 Undertow
- 3.2 Tomcat
- 4. 相關
1. 需求背景
瀏覽器針對單個網站地址(ip+port)存在“6個請求”限制;通過多子域配置可以突破這個限制,增加網站的響應效率,尤其是針對三維服務這類大請求量服務。
注意:
- 多子域的目的是為了突破瀏覽器"6個請求"的限制(【瀏覽器請求并發限制】,與后端應用提供的業務功能無關。
- 這個配置項的底層實現原理其實就是為當前網站增加幾個訪問端口,比如除了原始的123端口之外,還可以通過456,777端口訪問;僅此而已,沒有任何業務功能上的變化和影響。
- 瀏覽器對于"同一個網址"采取的是 ip + port 的組合認定,只有兩者都相同才會認定為同一個網站,這也是通過開放多個端口就可以突破該項限制的原因。
2. 實現
本項功能需求的應用場景比較窄,因此這里直接給出解決方案,不做原理性解讀。
以下三個類拷貝到項目中即可。
// ============================== 1/3 MultiportNettyWebServer
// 組合模式
public class MultiportNettyWebServer implements WebServer {private final List<NettyWebServer> servers;public MultiportNettyWebServer(List<NettyWebServer> servers) {this.servers = servers;}@Overridepublic void start() throws WebServerException {servers.stream().forEach(server -> server.start());}@Overridepublic void shutDownGracefully(GracefulShutdownCallback callback) {servers.stream().forEach(server -> server.shutDownGracefully(callback));}@Overridepublic void stop() throws WebServerException {servers.stream().forEach(server -> server.stop());}@Overridepublic int getPort() {return -1;}}// ============================== 2/3 MultiportNettyReactiveWebServerFactory
public class MultiportNettyReactiveWebServerFactory extends NettyReactiveWebServerFactory {@Overridepublic WebServer getWebServer(HttpHandler httpHandler) {NettyWebServer webServer = (NettyWebServer)super.getWebServer(httpHandler);List<NettyWebServer> newArrayList = CollUtil.newArrayList(webServer);// 這里并不是原地修改, 生效的是方法的返回值對象; 所以這里還得塞回去...// 這個思路其實很好, 保證對象的不可變....HttpServer httpServer = Convert.convert(HttpServer.class, ReflectUtil.getFieldValue(webServer, "httpServer"));httpServer = httpServer.port(81);ReflectUtil.setFieldValue(webServer, "httpServer", httpServer);webServer = (NettyWebServer)super.getWebServer(httpHandler);newArrayList.add(webServer);httpServer = Convert.convert(HttpServer.class, ReflectUtil.getFieldValue(webServer, "httpServer"));httpServer = httpServer.port(82);ReflectUtil.setFieldValue(webServer, "httpServer", httpServer);return new MultiportNettyWebServer(newArrayList);}
}// ============================== 3/3 MultiportNettyReactiveWebServerFactory
/*** COPY FROM {@code ReactiveWebServerFactoryConfiguration&EmbeddedNetty}*/
@Configuration
public class MultiportNettyWebServerConfig {@Bean@ConditionalOnMissingBeanReactorResourceFactory reactorServerResourceFactory() {return new ReactorResourceFactory();}@BeanNettyReactiveWebServerFactory nettyReactiveWebServerFactory(ReactorResourceFactory resourceFactory,ObjectProvider<NettyRouteProvider> routes, ObjectProvider<NettyServerCustomizer> serverCustomizers) {NettyReactiveWebServerFactory serverFactory = new MultiportNettyReactiveWebServerFactory();serverFactory.setResourceFactory(resourceFactory);routes.orderedStream().forEach(serverFactory::addRouteProviders);serverFactory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList()));return serverFactory;}}
3. 額外 - 其它Servlet容器實現
3.1 Undertow
@Slf4j
@Configuration
@ConditionalOnClass({Servlet.class, Undertow.class, SslClientAuthMode.class})
// 參考自: ServletWebServerFactoryConfiguration.EmbeddedUndertow.java
public class WebServerMultiPortConfigByUndertow implements WebServerFactoryCustomizer<UndertowServletWebServerFactory> {/*** 多個端口*/@Overridepublic void customize(UndertowServletWebServerFactory factory) {if (ArrayUtil.isEmpty(multiports)) {log.warn("### there is no multi-port config. so skip it");return;}final String[] finalMultiports = new String[]{"81", "82"};factory.addBuilderCustomizers(s -> {log.warn("### 啟用Undertow容器, 配置多端口 [ {} ]", Arrays.toString(finalMultiports));Arrays.stream(finalMultiports).forEach(port -> s.addHttpListener(Convert.toInt(port), "0.0.0.0"));});}
}
3.2 Tomcat
@Slf4j
@Configuration
// 參考自: ServletWebServerFactoryConfiguration.EmbeddedTomcat.java
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
public class WebServerMultiPortConfigByTomcat implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {/*** 多個端口*/@Overridepublic void customize(TomcatServletWebServerFactory factory) {factory.addAdditionalTomcatConnectors(addtionalConnetors());}private Connector[] addtionalConnetors() {final String[] multiPorts = new String[]{"81", "82"};log.warn("### 啟用Tomcat容器, 配置多端口 [ {} ]", Arrays.toString(multiPorts));List<Connector> connectors = new ArrayList<>();for (String s : multiPorts) {Connector connector = new Connector("HTTP/1.1");connector.setPort(Integer.parseInt(s));connector.setScheme("http");connectors.add(connector);}return connectors.toArray(new Connector[] {});}
}
4. 相關
- 【瀏覽器請求并發限制】
- Spring Boot中為Tomcat配置多個監聽端口