http请求处理流程优化.

This commit is contained in:
aoshiguchen
2022-07-26 22:44:39 +08:00
parent 67f86131fb
commit 2a069b62a3
5 changed files with 166 additions and 99 deletions
@@ -32,6 +32,7 @@ import fun.asgc.neutrino.core.web.annotation.RequestBody;
import fun.asgc.neutrino.core.web.annotation.RequestParam;
import fun.asgc.neutrino.core.web.param.HttpContextHolder;
import fun.asgc.neutrino.core.web.param.HttpRequestParser;
import fun.asgc.neutrino.core.web.param.WebContextHolder;
import fun.asgc.neutrino.core.web.router.DefaultHttpRouter;
import fun.asgc.neutrino.core.web.router.HttpRouteParam;
import fun.asgc.neutrino.core.web.router.HttpRouteResult;
@@ -61,59 +62,60 @@ public class HttpRequestHandler {
private ApplicationConfig applicationConfig;
@Autowired
private DefaultHttpRouter defaultHttpRouter;
private static volatile String httpContextPath;
public void handle() {
ChannelHandlerContext context = HttpContextHolder.getChannelHandlerContext();
HttpRequestParser requestParser = HttpContextHolder.getHttpRequestParser();
log.info("HttpRequest method:{} url:{} query:{}", requestParser.getMethod().name(), requestParser.getUrl(), requestParser.getQueryParamMap());
String routePath = getRoutePath(requestParser.getUrl());
HttpMethod httpMethod = HttpMethod.of(requestParser.getMethod().name());
HttpRouteResult httpRouteResult = defaultHttpRouter.route(new HttpRouteParam().setMethod(httpMethod).setUrl(routePath));
if (null == httpRouteResult) {
HttpServerUtil.send404Response(context, requestParser.getUrl());
release();
return;
}
if (HttpRouterType.METHOD == httpRouteResult.getType()) {
try {
Object invokeResult = invoke(httpRouteResult.getInstance(), httpRouteResult.getMethod());
String res = String.valueOf(invokeResult);
if (null != invokeResult && !TypeUtil.isNormalBasicType(invokeResult.getClass())) {
res = JSONObject.toJSONString(invokeResult);
try {
String routePath = requestParser.getRoutePath();
HttpMethod httpMethod = HttpMethod.of(requestParser.getMethod().name());
HttpRouteResult httpRouteResult = defaultHttpRouter.route(new HttpRouteParam().setMethod(httpMethod).setUrl(routePath));
if (null == httpRouteResult) {
HttpServerUtil.send404Response(context, requestParser.getUrl());
return;
}
if (HttpRouterType.METHOD == httpRouteResult.getType()) {
Object invokeResult = invoke(httpRouteResult.getInstance(), httpRouteResult.getMethod());
String res = String.valueOf(invokeResult);
if (null != invokeResult && !TypeUtil.isNormalBasicType(invokeResult.getClass())) {
res = JSONObject.toJSONString(invokeResult);
}
FullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(res.getBytes()));
fullHttpResponse.headers().add(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON);
context.writeAndFlush(fullHttpResponse).addListener(ChannelFutureListener.CLOSE);
return;
} else if(HttpRouterType.PAGE == httpRouteResult.getType()) {
// 前端页面
String mimeType = MimeType.getMimeType(MimeType.parseSuffix(httpRouteResult.getPageLocation()));
if (mimeType.startsWith("text/")) {
mimeType += ";charset=utf-8";
}
FullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(res.getBytes()));
fullHttpResponse.headers().add(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON);
FullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(FileUtil.readBytes(httpRouteResult.getPageLocation())));
fullHttpResponse.headers().add(HttpHeaderNames.CONTENT_TYPE, mimeType);
fullHttpResponse.headers().add(HttpHeaderNames.CONTENT_LANGUAGE, "zh-CN");
fullHttpResponse.headers().add(HttpHeaderNames.SERVER, MetaDataConstant.SERVER_VS);
fullHttpResponse.headers().add(HttpHeaderNames.DATE, new Date());
context.writeAndFlush(fullHttpResponse).addListener(ChannelFutureListener.CLOSE);
return;
} catch (Exception e) {
log.error("Http处理异常", e);
} finally {
release();
} else {
HttpServerUtil.send404Response(context, requestParser.getUrl());
return;
}
} else if(HttpRouterType.PAGE == httpRouteResult.getType()) {
// 前端页面
String mimeType = MimeType.getMimeType(MimeType.parseSuffix(httpRouteResult.getPageLocation()));
if (mimeType.startsWith("text/")) {
mimeType += ";charset=utf-8";
}
FullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(FileUtil.readBytes(httpRouteResult.getPageLocation())));
fullHttpResponse.headers().add(HttpHeaderNames.CONTENT_TYPE, mimeType);
fullHttpResponse.headers().add(HttpHeaderNames.CONTENT_LANGUAGE, "zh-CN");
fullHttpResponse.headers().add(HttpHeaderNames.SERVER, MetaDataConstant.SERVER_VS);
fullHttpResponse.headers().add(HttpHeaderNames.DATE, new Date());
context.writeAndFlush(fullHttpResponse).addListener(ChannelFutureListener.CLOSE);
} catch (Throwable e) {
exceptionHandler(e);
} finally {
release();
return;
} else {
// TODO
HttpServerUtil.send404Response(context, requestParser.getUrl());
release();
return;
}
}
private void exceptionHandler(Throwable e) {
log.error("Http处理异常", e);
HttpServerUtil.send500Response(HttpContextHolder.getChannelHandlerContext(), e);
}
private Object invoke(Object instance, Method method) throws InvocationTargetException, IllegalAccessException {
Object[] params = new Object[method.getParameterCount()];
if (method.getParameterCount() > 0) {
@@ -155,36 +157,4 @@ public class HttpRequestHandler {
private void release() {
HttpContextHolder.remove();
}
private String getRoutePath(String url) {
String httpContextPath = getHttpContextPath();
if (StringUtil.isEmpty(httpContextPath)) {
return url;
}
String res = url.substring(httpContextPath.length());
if (StringUtil.isEmpty(res)) {
return "/";
}
return res;
}
private String getHttpContextPath() {
return LockUtil.doubleCheckProcessForNoException(
() -> null == httpContextPath,
this,
() -> {
httpContextPath = applicationConfig.getHttp().getContextPath();
if (StringUtil.isEmpty(httpContextPath)) {
httpContextPath = "/";
}
if (!httpContextPath.startsWith("/")) {
httpContextPath = "/" + httpContextPath;
}
if (httpContextPath.endsWith("/")) {
httpContextPath = httpContextPath.substring(0, httpContextPath.length() - 1);
}
},
() -> httpContextPath
);
}
}
@@ -29,6 +29,7 @@ import fun.asgc.neutrino.core.context.ApplicationConfig;
import fun.asgc.neutrino.core.context.ApplicationRunner;
import fun.asgc.neutrino.core.util.*;
import fun.asgc.neutrino.core.web.param.HttpContextHolder;
import fun.asgc.neutrino.core.web.param.WebContextHolder;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
@@ -55,25 +56,17 @@ public class WebApplicationServer implements ApplicationRunner {
private EventLoopGroup workerGroup;
private ServerBootstrap serverBootstrap;
private ChannelFuture channelFuture;
private byte[] faviconBytes;
@Init
public void init() {
ApplicationConfig.Http http = applicationConfig.getHttp();
if (StringUtil.notEmpty(http.getMaxContentLengthDesc()) && null == http.getMaxContentLength()) {
http.setMaxContentLength(NumberUtil.descriptionToSize(http.getMaxContentLengthDesc(), 64 * 1024));
}
WebContextHolder.init(applicationConfig.getHttp());
this.bossGroup = new NioEventLoopGroup();
this.workerGroup = new NioEventLoopGroup();
this.serverBootstrap = new ServerBootstrap();
this.initFavicon();
}
@Override
public void run(String[] args) throws Exception {
ApplicationConfig.Http http = applicationConfig.getHttp();
this.serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
@@ -86,7 +79,7 @@ public class WebApplicationServer implements ApplicationRunner {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new HttpServerCodec());
pipeline.addLast(new ChunkedWriteHandler());
pipeline.addLast(new HttpObjectAggregator(http.getMaxContentLength().intValue()));
pipeline.addLast(new HttpObjectAggregator(WebContextHolder.getMaxContentLength().intValue()));
pipeline.addLast(new SimpleChannelInboundHandler<FullHttpRequest>() {
@Override
protected void channelRead0(ChannelHandlerContext context, FullHttpRequest request) throws Exception {
@@ -94,10 +87,10 @@ public class WebApplicationServer implements ApplicationRunner {
String uri = request.uri();
log.debug("http request: {}", uri);
if (http.getContextPath().equals("/") || uri.startsWith(http.getContextPath() + "/") || uri.equals(http.getContextPath())) {
if (WebContextHolder.getHttpContextPath().equals("/") || uri.startsWith(WebContextHolder.getHttpContextPath() + "/") || uri.equals(WebContextHolder.getHttpContextPath())) {
httpRequestHandler.handle();
} else if (uri.equals("/favicon.ico") && null != faviconBytes) {
FullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(faviconBytes));
} else if (uri.equals("/favicon.ico") && null != WebContextHolder.getFaviconBytes()) {
FullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(WebContextHolder.getFaviconBytes()));
fullHttpResponse.headers().add(HttpHeaderNames.CONTENT_TYPE, "image/x-icon");
context.writeAndFlush(fullHttpResponse).addListener(ChannelFutureListener.CLOSE);
} else {
@@ -107,20 +100,8 @@ public class WebApplicationServer implements ApplicationRunner {
});
}
});
channelFuture = this.serverBootstrap.bind(http.getPort()).sync();
log.info("HTTP服务启动,端口:{} context-path{}", http.getPort(), http.getContextPath());
}
private void initFavicon() {
if (null == applicationConfig.getHttp().getStaticResource() || CollectionUtil.isEmpty(applicationConfig.getHttp().getStaticResource().getLocations())) {
return;
}
for (String location : applicationConfig.getHttp().getStaticResource().getLocations()) {
this.faviconBytes = FileUtil.readBytes(location.concat("favicon.ico"));
if (null != faviconBytes) {
break;
}
}
channelFuture = this.serverBootstrap.bind(WebContextHolder.getPort()).sync();
log.info("HTTP服务启动,端口:{} context-path{}", WebContextHolder.getPort(), WebContextHolder.getHttpContextPath());
}
@Destroy
@@ -43,6 +43,7 @@ public class HttpRequestParser {
private String url;
private String queryString;
private Map<String, String> queryParamMap = null;
private String routePath;
private HttpRequestParser(FullHttpRequest request) {
this.request = request;
@@ -56,6 +57,7 @@ public class HttpRequestParser {
}
url = getUrl().substring(0, index);
}
this.routePath = getRoutePath(url);
}
public static HttpRequestParser create(FullHttpRequest request) {
@@ -66,6 +68,10 @@ public class HttpRequestParser {
return url;
}
public String getRoutePath() {
return routePath;
}
public String getQueryString() {
return queryString;
}
@@ -170,4 +176,16 @@ public class HttpRequestParser {
}
return result;
}
private String getRoutePath(String url) {
String httpContextPath = WebContextHolder.getHttpContextPath();
if (StringUtil.isEmpty(httpContextPath)) {
return url;
}
String res = url.substring(httpContextPath.length());
if (StringUtil.isEmpty(res)) {
return "/";
}
return res;
}
}
@@ -0,0 +1,95 @@
/**
* Copyright (c) 2022 aoshiguchen
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package fun.asgc.neutrino.core.web.param;
import fun.asgc.neutrino.core.context.ApplicationConfig;
import fun.asgc.neutrino.core.util.*;
/**
*
* @author: aoshiguchen
* @date: 2022/7/26
*/
public abstract class WebContextHolder {
private static ApplicationConfig.Http http;
private static volatile String httpContextPath;
private static volatile byte[] faviconBytes;
private static volatile Long maxContentLength;
private static volatile Integer port;
public static void init(ApplicationConfig.Http http) {
Assert.notNull(http, "http配置不能为空!");
WebContextHolder.http = http;
initHttpContextPath();
initFavicon();
initMaxContentLength();
port = http.getPort();
}
public static String getHttpContextPath() {
return httpContextPath;
}
public static byte[] getFaviconBytes() {
return faviconBytes;
}
public static Long getMaxContentLength() {
return maxContentLength;
}
public static Integer getPort() {
return port;
}
private static void initHttpContextPath() {
httpContextPath = http.getContextPath();
if (StringUtil.isEmpty(httpContextPath)) {
httpContextPath = "/";
}
if (!httpContextPath.startsWith("/")) {
httpContextPath = "/" + httpContextPath;
}
if (httpContextPath.endsWith("/")) {
httpContextPath = httpContextPath.substring(0, httpContextPath.length() - 1);
}
}
private static void initFavicon() {
if (null == http.getStaticResource() || CollectionUtil.isEmpty(http.getStaticResource().getLocations())) {
return;
}
for (String location : http.getStaticResource().getLocations()) {
faviconBytes = FileUtil.readBytes(location.concat("favicon.ico"));
if (null != faviconBytes) {
break;
}
}
}
private static void initMaxContentLength() {
maxContentLength = http.getMaxContentLength();
if (StringUtil.notEmpty(http.getMaxContentLengthDesc()) && null == http.getMaxContentLength()) {
maxContentLength = NumberUtil.descriptionToSize(http.getMaxContentLengthDesc(), 64 * 1024);
}
}
}
@@ -41,7 +41,10 @@ public class Test1Controller {
@GetMapping("hello")
public String hello() {
System.out.println("拿到参数 a = " + HttpContextHolder.getHttpRequestParser().getParameterForInteger("a"));
System.out.println("拿到参数 a = " + HttpContextHolder.getHttpRequestParser().getParameter("a"));
if ("aaa".equals(HttpContextHolder.getHttpRequestParser().getParameter("a"))) {
throw new RuntimeException("hhhh 异常了!");
}
return test1Service.hello();
}