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配置多个监听端口